diff options
Diffstat (limited to 'libs')
252 files changed, 32126 insertions, 22825 deletions
diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk index 13dc500d9445..f9d9f2592291 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 000000000000..47bbd04e1f91 --- /dev/null +++ b/libs/binder/CursorWindow.cpp @@ -0,0 +1,412 @@ +/* + * 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--; + LOG_WINDOW("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()) { + LOGV("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) { + LOGV("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("Failed to read row# %d, column# from a CursorWindow which has %d rows, %d columns.", + 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("Can't read row# %d, col# %d from CursorWindow. Make sure your Cursor is initialized correctly.", + row, column); + 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/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 13c58f04fcb1..95cfddf758f2 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -318,6 +318,16 @@ restart: goto restart; } +IPCThreadState* IPCThreadState::selfOrNull() +{ + if (gHaveTLS) { + const pthread_key_t k = gTLS; + IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k); + return st; + } + return NULL; +} + void IPCThreadState::shutdown() { gShutdown = true; diff --git a/libs/binder/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 624f7eba8470..9f501e277cea 100644 --- a/libs/binder/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -31,7 +31,7 @@ #include <binder/MemoryHeapBase.h> -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/android_pmem.h> #endif @@ -108,7 +108,7 @@ status_t MemoryHeapBase::mapfd(int fd, size_t size, uint32_t offset) { if (size == 0) { // try to figure out the size automatically -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS // first try the PMEM ioctl pmem_region reg; int err = ioctl(fd, PMEM_GET_TOTAL_SIZE, ®); diff --git a/libs/binder/MemoryHeapPmem.cpp b/libs/binder/MemoryHeapPmem.cpp index 16e92f939ee3..03322ea5d9c4 100644 --- a/libs/binder/MemoryHeapPmem.cpp +++ b/libs/binder/MemoryHeapPmem.cpp @@ -30,7 +30,7 @@ #include <binder/MemoryHeapPmem.h> #include <binder/MemoryHeapBase.h> -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/android_pmem.h> #endif @@ -72,7 +72,7 @@ SubRegionMemory::SubRegionMemory(const sp<MemoryHeapPmem>& heap, memset(start_ptr, 0xda, size); #endif -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS if (size > 0) { const size_t pagesize = getpagesize(); size = (size + pagesize-1) & ~(pagesize-1); @@ -107,7 +107,7 @@ void SubRegionMemory::revoke() // which means MemoryHeapPmem::revoke() wouldn't have been able to // promote() it. -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS if (mSize != 0) { const sp<MemoryHeapPmem>& heap(getHeap()); int our_fd = heap->heapID(); @@ -130,7 +130,7 @@ MemoryHeapPmem::MemoryHeapPmem(const sp<MemoryHeapBase>& pmemHeap, : MemoryHeapBase() { char const * const device = pmemHeap->getDevice(); -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS if (device) { int fd = open(device, O_RDWR | (flags & NO_CACHING ? O_SYNC : 0)); LOGE_IF(fd<0, "couldn't open %s (%s)", device, strerror(errno)); @@ -187,7 +187,7 @@ sp<MemoryHeapPmem::MemoryPmem> MemoryHeapPmem::createMemory( status_t MemoryHeapPmem::slap() { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS size_t size = getSize(); const size_t pagesize = getpagesize(); size = (size + pagesize-1) & ~(pagesize-1); @@ -205,7 +205,7 @@ status_t MemoryHeapPmem::slap() status_t MemoryHeapPmem::unslap() { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS size_t size = getSize(); const size_t pagesize = getpagesize(); size = (size + pagesize-1) & ~(pagesize-1); diff --git a/libs/camera/Android.mk b/libs/camera/Android.mk index 03ff229dacdf..2f16923de473 100644 --- a/libs/camera/Android.mk +++ b/libs/camera/Android.mk @@ -14,7 +14,8 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libhardware \ libsurfaceflinger_client \ - libui + libui \ + libgui LOCAL_MODULE:= libcamera_client diff --git a/libs/camera/Camera.cpp b/libs/camera/Camera.cpp index 743fbb21a795..e2883121e497 100644 --- a/libs/camera/Camera.cpp +++ b/libs/camera/Camera.cpp @@ -80,8 +80,9 @@ sp<Camera> Camera::create(const sp<ICamera>& camera) c->mStatus = NO_ERROR; c->mCamera = camera; camera->asBinder()->linkToDeath(c); + return c; } - return c; + return 0; } void Camera::init() @@ -167,32 +168,34 @@ status_t Camera::unlock() return c->unlock(); } -// pass the buffered ISurface to the camera service +// pass the buffered Surface to the camera service status_t Camera::setPreviewDisplay(const sp<Surface>& surface) { - LOGV("setPreviewDisplay"); + LOGV("setPreviewDisplay(%p)", surface.get()); sp <ICamera> c = mCamera; if (c == 0) return NO_INIT; if (surface != 0) { - return c->setPreviewDisplay(surface->getISurface()); + return c->setPreviewDisplay(surface); } else { LOGD("app passed NULL surface"); return c->setPreviewDisplay(0); } } -status_t Camera::setPreviewDisplay(const sp<ISurface>& surface) +// pass the buffered ISurfaceTexture to the camera service +status_t Camera::setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture) { - LOGV("setPreviewDisplay"); - if (surface == 0) { - LOGD("app passed NULL surface"); - } + LOGV("setPreviewTexture(%p)", surfaceTexture.get()); sp <ICamera> c = mCamera; if (c == 0) return NO_INIT; - return c->setPreviewDisplay(surface); + if (surfaceTexture != 0) { + return c->setPreviewTexture(surfaceTexture); + } else { + LOGD("app passed NULL surface"); + return c->setPreviewTexture(0); + } } - // start preview mode status_t Camera::startPreview() { @@ -202,6 +205,31 @@ status_t Camera::startPreview() return c->startPreview(); } +int32_t Camera::getNumberOfVideoBuffers() const +{ + LOGV("getNumberOfVideoBuffers"); + sp <ICamera> c = mCamera; + if (c == 0) return 0; + return c->getNumberOfVideoBuffers(); +} + +sp<IMemory> Camera::getVideoBuffer(int32_t index) const +{ + LOGV("getVideoBuffer: %d", index); + sp <ICamera> c = mCamera; + if (c == 0) return 0; + return c->getVideoBuffer(index); +} + +status_t Camera::storeMetaDataInBuffers(bool enabled) +{ + LOGV("storeMetaDataInBuffers: %s", + enabled? "true": "false"); + sp <ICamera> c = mCamera; + if (c == 0) return NO_INIT; + return c->storeMetaDataInBuffers(enabled); +} + // start recording mode, must call setPreviewDisplay first status_t Camera::startRecording() { @@ -273,12 +301,12 @@ status_t Camera::cancelAutoFocus() } // take a picture -status_t Camera::takePicture() +status_t Camera::takePicture(int msgType) { - LOGV("takePicture"); + LOGV("takePicture: 0x%x", msgType); sp <ICamera> c = mCamera; if (c == 0) return NO_INIT; - return c->takePicture(); + return c->takePicture(msgType); } // set preview/capture parameters - key/value pairs @@ -378,4 +406,3 @@ void Camera::DeathNotifier::binderDied(const wp<IBinder>& who) { } }; // namespace android - diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp index 83e5e57f1f73..0fd79a4eb961 100644 --- a/libs/camera/CameraParameters.cpp +++ b/libs/camera/CameraParameters.cpp @@ -73,6 +73,9 @@ const char CameraParameters::KEY_ZOOM_SUPPORTED[] = "zoom-supported"; const char CameraParameters::KEY_SMOOTH_ZOOM_SUPPORTED[] = "smooth-zoom-supported"; const char CameraParameters::KEY_FOCUS_DISTANCES[] = "focus-distances"; const char CameraParameters::KEY_VIDEO_FRAME_FORMAT[] = "video-frame-format"; +const char CameraParameters::KEY_VIDEO_SIZE[] = "video-size"; +const char CameraParameters::KEY_SUPPORTED_VIDEO_SIZES[] = "video-size-values"; +const char CameraParameters::KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO[] = "preferred-preview-size-for-video"; const char CameraParameters::TRUE[] = "true"; const char CameraParameters::FOCUS_DISTANCE_INFINITY[] = "Infinity"; @@ -129,10 +132,10 @@ const char CameraParameters::SCENE_MODE_PARTY[] = "party"; const char CameraParameters::SCENE_MODE_CANDLELIGHT[] = "candlelight"; const char CameraParameters::SCENE_MODE_BARCODE[] = "barcode"; -// Formats for setPreviewFormat and setPictureFormat. const char CameraParameters::PIXEL_FORMAT_YUV422SP[] = "yuv422sp"; const char CameraParameters::PIXEL_FORMAT_YUV420SP[] = "yuv420sp"; const char CameraParameters::PIXEL_FORMAT_YUV422I[] = "yuv422i-yuyv"; +const char CameraParameters::PIXEL_FORMAT_YUV420P[] = "yuv420p"; const char CameraParameters::PIXEL_FORMAT_RGB565[] = "rgb565"; const char CameraParameters::PIXEL_FORMAT_JPEG[] = "jpeg"; @@ -331,12 +334,41 @@ void CameraParameters::getPreviewSize(int *width, int *height) const parse_pair(p, width, height, 'x'); } +void CameraParameters::getPreferredPreviewSizeForVideo(int *width, int *height) const +{ + *width = *height = -1; + const char *p = get(KEY_PREFERRED_PREVIEW_SIZE_FOR_VIDEO); + if (p == 0) return; + parse_pair(p, width, height, 'x'); +} + void CameraParameters::getSupportedPreviewSizes(Vector<Size> &sizes) const { const char *previewSizesStr = get(KEY_SUPPORTED_PREVIEW_SIZES); parseSizesList(previewSizesStr, sizes); } +void CameraParameters::setVideoSize(int width, int height) +{ + char str[32]; + sprintf(str, "%dx%d", width, height); + set(KEY_VIDEO_SIZE, str); +} + +void CameraParameters::getVideoSize(int *width, int *height) const +{ + *width = *height = -1; + const char *p = get(KEY_VIDEO_SIZE); + if (p == 0) return; + parse_pair(p, width, height, 'x'); +} + +void CameraParameters::getSupportedVideoSizes(Vector<Size> &sizes) const +{ + const char *videoSizesStr = get(KEY_SUPPORTED_VIDEO_SIZES); + parseSizesList(videoSizesStr, sizes); +} + void CameraParameters::setPreviewFrameRate(int fps) { set(KEY_PREVIEW_FRAME_RATE, fps); diff --git a/libs/camera/ICamera.cpp b/libs/camera/ICamera.cpp index 13673b53ea4c..931b57d048aa 100644 --- a/libs/camera/ICamera.cpp +++ b/libs/camera/ICamera.cpp @@ -28,6 +28,7 @@ namespace android { enum { DISCONNECT = IBinder::FIRST_CALL_TRANSACTION, SET_PREVIEW_DISPLAY, + SET_PREVIEW_TEXTURE, SET_PREVIEW_CALLBACK_FLAG, START_PREVIEW, STOP_PREVIEW, @@ -45,6 +46,9 @@ enum { STOP_RECORDING, RECORDING_ENABLED, RELEASE_RECORDING_FRAME, + GET_NUM_VIDEO_BUFFERS, + GET_VIDEO_BUFFER, + STORE_META_DATA_IN_BUFFERS, }; class BpCamera: public BpInterface<ICamera> @@ -64,17 +68,29 @@ public: remote()->transact(DISCONNECT, data, &reply); } - // pass the buffered ISurface to the camera service - status_t setPreviewDisplay(const sp<ISurface>& surface) + // pass the buffered Surface to the camera service + status_t setPreviewDisplay(const sp<Surface>& surface) { LOGV("setPreviewDisplay"); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); - data.writeStrongBinder(surface->asBinder()); + Surface::writeToParcel(surface, &data); remote()->transact(SET_PREVIEW_DISPLAY, data, &reply); return reply.readInt32(); } + // pass the buffered SurfaceTexture to the camera service + status_t setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture) + { + LOGV("setPreviewTexture"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + sp<IBinder> b(surfaceTexture->asBinder()); + data.writeStrongBinder(b); + remote()->transact(SET_PREVIEW_TEXTURE, data, &reply); + return reply.readInt32(); + } + // set the preview callback flag to affect how the received frames from // preview are handled. See Camera.h for details. void setPreviewCallbackFlag(int flag) @@ -133,6 +149,37 @@ public: remote()->transact(RELEASE_RECORDING_FRAME, data, &reply); } + int32_t getNumberOfVideoBuffers() const + { + LOGV("getNumberOfVideoBuffers"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + remote()->transact(GET_NUM_VIDEO_BUFFERS, data, &reply); + return reply.readInt32(); + } + + sp<IMemory> getVideoBuffer(int32_t index) const + { + LOGV("getVideoBuffer: %d", index); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeInt32(index); + remote()->transact(GET_VIDEO_BUFFER, data, &reply); + sp<IMemory> mem = interface_cast<IMemory>( + reply.readStrongBinder()); + return mem; + } + + status_t storeMetaDataInBuffers(bool enabled) + { + LOGV("storeMetaDataInBuffers: %s", enabled? "true": "false"); + Parcel data, reply; + data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeInt32(enabled); + remote()->transact(STORE_META_DATA_IN_BUFFERS, data, &reply); + return reply.readInt32(); + } + // check preview state bool previewEnabled() { @@ -176,11 +223,12 @@ public: } // take a picture - returns an IMemory (ref-counted mmap) - status_t takePicture() + status_t takePicture(int msgType) { - LOGV("takePicture"); + LOGV("takePicture: 0x%x", msgType); Parcel data, reply; data.writeInterfaceToken(ICamera::getInterfaceDescriptor()); + data.writeInt32(msgType); remote()->transact(TAKE_PICTURE, data, &reply); status_t ret = reply.readInt32(); return ret; @@ -258,10 +306,17 @@ status_t BnCamera::onTransact( case SET_PREVIEW_DISPLAY: { LOGV("SET_PREVIEW_DISPLAY"); CHECK_INTERFACE(ICamera, data, reply); - sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder()); + sp<Surface> surface = Surface::readFromParcel(data); reply->writeInt32(setPreviewDisplay(surface)); return NO_ERROR; } break; + case SET_PREVIEW_TEXTURE: { + LOGV("SET_PREVIEW_TEXTURE"); + CHECK_INTERFACE(ICamera, data, reply); + sp<ISurfaceTexture> st = interface_cast<ISurfaceTexture>(data.readStrongBinder()); + reply->writeInt32(setPreviewTexture(st)); + return NO_ERROR; + } break; case SET_PREVIEW_CALLBACK_FLAG: { LOGV("SET_PREVIEW_CALLBACK_TYPE"); CHECK_INTERFACE(ICamera, data, reply); @@ -300,6 +355,26 @@ status_t BnCamera::onTransact( releaseRecordingFrame(mem); return NO_ERROR; } break; + case GET_NUM_VIDEO_BUFFERS: { + LOGV("GET_NUM_VIDEO_BUFFERS"); + CHECK_INTERFACE(ICamera, data, reply); + reply->writeInt32(getNumberOfVideoBuffers()); + return NO_ERROR; + } break; + case GET_VIDEO_BUFFER: { + LOGV("GET_VIDEO_BUFFER"); + CHECK_INTERFACE(ICamera, data, reply); + int32_t index = data.readInt32(); + reply->writeStrongBinder(getVideoBuffer(index)->asBinder()); + return NO_ERROR; + } break; + case STORE_META_DATA_IN_BUFFERS: { + LOGV("STORE_META_DATA_IN_BUFFERS"); + CHECK_INTERFACE(ICamera, data, reply); + bool enabled = data.readInt32(); + reply->writeInt32(storeMetaDataInBuffers(enabled)); + return NO_ERROR; + } break; case PREVIEW_ENABLED: { LOGV("PREVIEW_ENABLED"); CHECK_INTERFACE(ICamera, data, reply); @@ -327,7 +402,8 @@ status_t BnCamera::onTransact( case TAKE_PICTURE: { LOGV("TAKE_PICTURE"); CHECK_INTERFACE(ICamera, data, reply); - reply->writeInt32(takePicture()); + int msgType = data.readInt32(); + reply->writeInt32(takePicture(msgType)); return NO_ERROR; } break; case SET_PARAMETERS: { @@ -376,4 +452,3 @@ status_t BnCamera::onTransact( // ---------------------------------------------------------------------------- }; // namespace android - diff --git a/libs/rs/java/Film/Android.mk b/libs/diskusage/Android.mk index 9e6ed7e24b78..d54f8adf6059 100644 --- a/libs/rs/java/Film/Android.mk +++ b/libs/diskusage/Android.mk @@ -1,5 +1,4 @@ -# -# Copyright (C) 2008 The Android Open Source Project +# 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. @@ -12,16 +11,14 @@ # 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. -# -LOCAL_PATH := $(call my-dir) +LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := libdiskusage -LOCAL_SRC_FILES := $(call all-java-files-under, src) -#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript +LOCAL_MODULE_TAGS := optional -LOCAL_PACKAGE_NAME := Film +LOCAL_SRC_FILES := dirsize.c -include $(BUILD_PACKAGE) +include $(BUILD_STATIC_LIBRARY)
\ No newline at end of file diff --git a/libs/diskusage/MODULE_LICENSE_APACHE2 b/libs/diskusage/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/libs/diskusage/MODULE_LICENSE_APACHE2 diff --git a/libs/diskusage/dirsize.c b/libs/diskusage/dirsize.c new file mode 100644 index 000000000000..45e7b2a17790 --- /dev/null +++ b/libs/diskusage/dirsize.c @@ -0,0 +1,75 @@ +/* + * + * 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. + */ + +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> + +#include <diskusage/dirsize.h> + +int64_t stat_size(struct stat *s) +{ + int64_t blksize = s->st_blksize; + int64_t size = s->st_size; + + if (blksize) { + /* round up to filesystem block size */ + size = (size + blksize - 1) & (~(blksize - 1)); + } + + return size; +} + +int64_t calculate_dir_size(int dfd) +{ + int64_t size = 0; + struct stat s; + DIR *d; + struct dirent *de; + + d = fdopendir(dfd); + if (d == NULL) { + close(dfd); + return 0; + } + + while ((de = readdir(d))) { + const char *name = de->d_name; + if (de->d_type == DT_DIR) { + int subfd; + + /* always skip "." and ".." */ + if (name[0] == '.') { + if (name[1] == 0) + continue; + if ((name[1] == '.') && (name[2] == 0)) + continue; + } + + subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); + if (subfd >= 0) { + size += calculate_dir_size(subfd); + } + } else { + if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { + size += stat_size(&s); + } + } + } + closedir(d); + return size; +} diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index 249558ac8601..d1a6af1dc11b 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -4,17 +4,25 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ ISensorEventConnection.cpp \ ISensorServer.cpp \ + ISurfaceTexture.cpp \ Sensor.cpp \ SensorChannel.cpp \ SensorEventQueue.cpp \ - SensorManager.cpp + SensorManager.cpp \ + SurfaceTexture.cpp \ + SurfaceTextureClient.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ libbinder \ libhardware \ - libhardware_legacy + libhardware_legacy \ + libui \ + libEGL \ + libGLESv2 \ + libsurfaceflinger_client + LOCAL_MODULE:= libgui diff --git a/libs/gui/ISurfaceTexture.cpp b/libs/gui/ISurfaceTexture.cpp new file mode 100644 index 000000000000..d661fd57914e --- /dev/null +++ b/libs/gui/ISurfaceTexture.cpp @@ -0,0 +1,218 @@ +/* + * 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 <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Vector.h> +#include <utils/Timers.h> + +#include <binder/Parcel.h> +#include <binder/IInterface.h> + +#include <gui/ISurfaceTexture.h> + +namespace android { +// ---------------------------------------------------------------------------- + +enum { + REQUEST_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + SET_BUFFER_COUNT, + DEQUEUE_BUFFER, + QUEUE_BUFFER, + CANCEL_BUFFER, + SET_CROP, + SET_TRANSFORM, + GET_ALLOCATOR, +}; + + +class BpSurfaceTexture : public BpInterface<ISurfaceTexture> +{ +public: + BpSurfaceTexture(const sp<IBinder>& impl) + : BpInterface<ISurfaceTexture>(impl) + { + } + + virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(bufferIdx); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + data.writeInt32(usage); + remote()->transact(REQUEST_BUFFER, data, &reply); + sp<GraphicBuffer> buffer; + bool nonNull = reply.readInt32(); + if (nonNull) { + buffer = new GraphicBuffer(); + reply.read(*buffer); + } + return buffer; + } + + virtual status_t setBufferCount(int bufferCount) + { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + remote()->transact(SET_BUFFER_COUNT, data, &reply); + status_t err = reply.readInt32(); + return err; + } + + virtual status_t dequeueBuffer(int *buf) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + remote()->transact(DEQUEUE_BUFFER, data, &reply); + *buf = reply.readInt32(); + int result = reply.readInt32(); + return result; + } + + virtual status_t queueBuffer(int buf) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(buf); + remote()->transact(QUEUE_BUFFER, data, &reply); + status_t result = reply.readInt32(); + return result; + } + + virtual void cancelBuffer(int buf) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(buf); + remote()->transact(CANCEL_BUFFER, data, &reply); + } + + virtual status_t setCrop(const Rect& reg) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeFloat(reg.left); + data.writeFloat(reg.top); + data.writeFloat(reg.right); + data.writeFloat(reg.bottom); + remote()->transact(SET_CROP, data, &reply); + status_t result = reply.readInt32(); + return result; + } + + virtual status_t setTransform(uint32_t transform) { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + data.writeInt32(transform); + remote()->transact(SET_TRANSFORM, data, &reply); + status_t result = reply.readInt32(); + return result; + } + + virtual sp<IBinder> getAllocator() { + Parcel data, reply; + data.writeInterfaceToken(ISurfaceTexture::getInterfaceDescriptor()); + remote()->transact(GET_ALLOCATOR, data, &reply); + return reply.readStrongBinder(); + } +}; + +IMPLEMENT_META_INTERFACE(SurfaceTexture, "android.gui.SurfaceTexture"); + +// ---------------------------------------------------------------------- + +status_t BnSurfaceTexture::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case REQUEST_BUFFER: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int bufferIdx = data.readInt32(); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + uint32_t format = data.readInt32(); + uint32_t usage = data.readInt32(); + sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, w, h, format, + usage)); + reply->writeInt32(buffer != 0); + if (buffer != 0) { + reply->write(*buffer); + } + return NO_ERROR; + } break; + case SET_BUFFER_COUNT: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int bufferCount = data.readInt32(); + int result = setBufferCount(bufferCount); + reply->writeInt32(result); + return NO_ERROR; + } break; + case DEQUEUE_BUFFER: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int buf; + int result = dequeueBuffer(&buf); + reply->writeInt32(buf); + reply->writeInt32(result); + return NO_ERROR; + } break; + case QUEUE_BUFFER: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int buf = data.readInt32(); + status_t result = queueBuffer(buf); + reply->writeInt32(result); + return NO_ERROR; + } break; + case CANCEL_BUFFER: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + int buf = data.readInt32(); + cancelBuffer(buf); + return NO_ERROR; + } break; + case SET_CROP: { + Rect reg; + CHECK_INTERFACE(ISurfaceTexture, data, reply); + reg.left = data.readFloat(); + reg.top = data.readFloat(); + reg.right = data.readFloat(); + reg.bottom = data.readFloat(); + status_t result = setCrop(reg); + reply->writeInt32(result); + return NO_ERROR; + } break; + case SET_TRANSFORM: { + Rect reg; + CHECK_INTERFACE(ISurfaceTexture, data, reply); + uint32_t transform = data.readInt32(); + status_t result = setTransform(transform); + reply->writeInt32(result); + return NO_ERROR; + } break; + case GET_ALLOCATOR: { + CHECK_INTERFACE(ISurfaceTexture, data, reply); + sp<IBinder> result = getAllocator(); + reply->writeStrongBinder(result); + return NO_ERROR; + } break; + } + return BBinder::onTransact(code, data, reply, flags); +} + +// ---------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp new file mode 100644 index 000000000000..5c6d71b1501b --- /dev/null +++ b/libs/gui/SurfaceTexture.cpp @@ -0,0 +1,423 @@ +/* + * 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. + */ + +#define LOG_TAG "SurfaceTexture" +//#define LOG_NDEBUG 0 + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <gui/SurfaceTexture.h> + +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/SurfaceComposerClient.h> +#include <surfaceflinger/IGraphicBufferAlloc.h> + +#include <utils/Log.h> + +namespace android { + +// Transform matrices +static float mtxIdentity[16] = { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, +}; +static float mtxFlipH[16] = { + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 1, 0, 0, 1, +}; +static float mtxFlipV[16] = { + 1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 1, +}; +static float mtxRot90[16] = { + 0, 1, 0, 0, + -1, 0, 0, 0, + 0, 0, 1, 0, + 1, 0, 0, 1, +}; +static float mtxRot180[16] = { + -1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 1, 1, 0, 1, +}; +static float mtxRot270[16] = { + 0, -1, 0, 0, + 1, 0, 0, 0, + 0, 0, 1, 0, + 0, 1, 0, 1, +}; + +static void mtxMul(float out[16], const float a[16], const float b[16]); + +SurfaceTexture::SurfaceTexture(GLuint tex) : + mBufferCount(MIN_BUFFER_SLOTS), mCurrentTexture(INVALID_BUFFER_SLOT), + mCurrentTransform(0), mLastQueued(INVALID_BUFFER_SLOT), + mLastQueuedTransform(0), mNextTransform(0), mTexName(tex) { + LOGV("SurfaceTexture::SurfaceTexture"); + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[i].mEglDisplay = EGL_NO_DISPLAY; + mSlots[i].mOwnedByClient = false; + } + sp<ISurfaceComposer> composer(ComposerService::getComposerService()); + mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); +} + +SurfaceTexture::~SurfaceTexture() { + LOGV("SurfaceTexture::~SurfaceTexture"); + freeAllBuffers(); +} + +status_t SurfaceTexture::setBufferCount(int bufferCount) { + LOGV("SurfaceTexture::setBufferCount"); + + if (bufferCount < MIN_BUFFER_SLOTS) { + return BAD_VALUE; + } + + Mutex::Autolock lock(mMutex); + freeAllBuffers(); + mBufferCount = bufferCount; + mCurrentTexture = INVALID_BUFFER_SLOT; + mLastQueued = INVALID_BUFFER_SLOT; + return OK; +} + +sp<GraphicBuffer> SurfaceTexture::requestBuffer(int buf, + uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { + LOGV("SurfaceTexture::requestBuffer"); + Mutex::Autolock lock(mMutex); + if (buf < 0 || mBufferCount <= buf) { + LOGE("requestBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return 0; + } + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer(w, h, format, usage)); + if (graphicBuffer == 0) { + LOGE("requestBuffer: SurfaceComposer::createGraphicBuffer failed"); + } else { + mSlots[buf].mGraphicBuffer = graphicBuffer; + if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); + mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; + } + mAllocdBuffers.add(graphicBuffer); + } + return graphicBuffer; +} + +status_t SurfaceTexture::dequeueBuffer(int *buf) { + LOGV("SurfaceTexture::dequeueBuffer"); + Mutex::Autolock lock(mMutex); + int found = INVALID_BUFFER_SLOT; + for (int i = 0; i < mBufferCount; i++) { + if (!mSlots[i].mOwnedByClient && i != mCurrentTexture && i != mLastQueued) { + mSlots[i].mOwnedByClient = true; + found = i; + break; + } + } + if (found == INVALID_BUFFER_SLOT) { + return -EBUSY; + } + *buf = found; + return OK; +} + +status_t SurfaceTexture::queueBuffer(int buf) { + LOGV("SurfaceTexture::queueBuffer"); + Mutex::Autolock lock(mMutex); + if (buf < 0 || mBufferCount <= buf) { + LOGE("queueBuffer: slot index out of range [0, %d]: %d", + mBufferCount, buf); + return -EINVAL; + } else if (!mSlots[buf].mOwnedByClient) { + LOGE("queueBuffer: slot %d is not owned by the client", buf); + return -EINVAL; + } else if (mSlots[buf].mGraphicBuffer == 0) { + LOGE("queueBuffer: slot %d was enqueued without requesting a buffer", + buf); + return -EINVAL; + } + mSlots[buf].mOwnedByClient = false; + mLastQueued = buf; + mLastQueuedCrop = mNextCrop; + mLastQueuedTransform = mNextTransform; + if (mFrameAvailableListener != 0) { + mFrameAvailableListener->onFrameAvailable(); + } + return OK; +} + +void SurfaceTexture::cancelBuffer(int buf) { + LOGV("SurfaceTexture::cancelBuffer"); + Mutex::Autolock lock(mMutex); + if (buf < 0 || mBufferCount <= buf) { + LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount, + buf); + return; + } else if (!mSlots[buf].mOwnedByClient) { + LOGE("cancelBuffer: slot %d is not owned by the client", buf); + return; + } + mSlots[buf].mOwnedByClient = false; +} + +status_t SurfaceTexture::setCrop(const Rect& crop) { + LOGV("SurfaceTexture::setCrop"); + Mutex::Autolock lock(mMutex); + mNextCrop = crop; + return OK; +} + +status_t SurfaceTexture::setTransform(uint32_t transform) { + LOGV("SurfaceTexture::setTransform"); + Mutex::Autolock lock(mMutex); + mNextTransform = transform; + return OK; +} + +status_t SurfaceTexture::updateTexImage() { + LOGV("SurfaceTexture::updateTexImage"); + Mutex::Autolock lock(mMutex); + + // We always bind the texture even if we don't update its contents. + glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName); + + // Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT, + // so this check will fail until a buffer gets queued. + if (mCurrentTexture != mLastQueued) { + // Update the GL texture object. + EGLImageKHR image = mSlots[mLastQueued].mEglImage; + if (image == EGL_NO_IMAGE_KHR) { + EGLDisplay dpy = eglGetCurrentDisplay(); + sp<GraphicBuffer> graphicBuffer = mSlots[mLastQueued].mGraphicBuffer; + image = createImage(dpy, graphicBuffer); + mSlots[mLastQueued].mEglImage = image; + mSlots[mLastQueued].mEglDisplay = dpy; + } + + GLint error; + while ((error = glGetError()) != GL_NO_ERROR) { + LOGE("GL error cleared before updating SurfaceTexture: %#04x", error); + } + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image); + bool failed = false; + while ((error = glGetError()) != GL_NO_ERROR) { + LOGE("error binding external texture image %p (slot %d): %#04x", + image, mLastQueued, error); + failed = true; + } + if (failed) { + return -EINVAL; + } + + // Update the SurfaceTexture state. + mCurrentTexture = mLastQueued; + mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer; + mCurrentCrop = mLastQueuedCrop; + mCurrentTransform = mLastQueuedTransform; + } + return OK; +} + +void SurfaceTexture::getTransformMatrix(float mtx[16]) { + LOGV("SurfaceTexture::updateTexImage"); + Mutex::Autolock lock(mMutex); + + float xform[16]; + for (int i = 0; i < 16; i++) { + xform[i] = mtxIdentity[i]; + } + if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { + float result[16]; + mtxMul(result, xform, mtxFlipH); + for (int i = 0; i < 16; i++) { + xform[i] = result[i]; + } + } + if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) { + float result[16]; + mtxMul(result, xform, mtxFlipV); + for (int i = 0; i < 16; i++) { + xform[i] = result[i]; + } + } + if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + float result[16]; + mtxMul(result, xform, mtxRot90); + for (int i = 0; i < 16; i++) { + xform[i] = result[i]; + } + } + + sp<GraphicBuffer>& buf(mSlots[mCurrentTexture].mGraphicBuffer); + float tx, ty, sx, sy; + if (!mCurrentCrop.isEmpty()) { + // In order to prevent bilinear sampling at the of the crop rectangle we + // may need to shrink it by 2 texels in each direction. Normally this + // would just need to take 1/2 a texel off each end, but because the + // chroma channels will likely be subsampled we need to chop off a whole + // texel. This will cause artifacts if someone does nearest sampling + // with 1:1 pixel:texel ratio, but it's impossible to simultaneously + // accomodate the bilinear and nearest sampling uses. + // + // If nearest sampling turns out to be a desirable usage of these + // textures then we could add the ability to switch a SurfaceTexture to + // nearest-mode. Preferably, however, the image producers (video + // decoder, camera, etc.) would simply not use a crop rectangle (or at + // least not tell the framework about it) so that the GPU can do the + // correct edge behavior. + int xshrink = 0, yshrink = 0; + if (mCurrentCrop.left > 0) { + tx = float(mCurrentCrop.left + 1) / float(buf->getWidth()); + xshrink++; + } else { + tx = 0.0f; + } + if (mCurrentCrop.right < buf->getWidth()) { + xshrink++; + } + if (mCurrentCrop.bottom < buf->getHeight()) { + ty = (float(buf->getHeight() - mCurrentCrop.bottom) + 1.0f) / + float(buf->getHeight()); + yshrink++; + } else { + ty = 0.0f; + } + if (mCurrentCrop.top > 0) { + yshrink++; + } + sx = float(mCurrentCrop.width() - xshrink) / float(buf->getWidth()); + sy = float(mCurrentCrop.height() - yshrink) / float(buf->getHeight()); + } else { + tx = 0.0f; + ty = 0.0f; + sx = 1.0f; + sy = 1.0f; + } + float crop[16] = { + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, 1, 0, + tx, ty, 0, 1, + }; + + float mtxBeforeFlipV[16]; + mtxMul(mtxBeforeFlipV, crop, xform); + + // SurfaceFlinger expects the top of its window textures to be at a Y + // coordinate of 0, so SurfaceTexture must behave the same way. We don't + // want to expose this to applications, however, so we must add an + // additional vertical flip to the transform after all the other transforms. + mtxMul(mtx, mtxFlipV, mtxBeforeFlipV); +} + +void SurfaceTexture::setFrameAvailableListener( + const sp<FrameAvailableListener>& l) { + LOGV("SurfaceTexture::setFrameAvailableListener"); + Mutex::Autolock lock(mMutex); + mFrameAvailableListener = l; +} + +sp<IBinder> SurfaceTexture::getAllocator() { + LOGV("SurfaceTexture::getAllocator"); + return mGraphicBufferAlloc->asBinder(); +} + +void SurfaceTexture::freeAllBuffers() { + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + mSlots[i].mGraphicBuffer = 0; + mSlots[i].mOwnedByClient = false; + if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); + mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[i].mEglDisplay = EGL_NO_DISPLAY; + } + } + + int exceptBuf = -1; + for (size_t i = 0; i < mAllocdBuffers.size(); i++) { + if (mAllocdBuffers[i] == mCurrentTextureBuf) { + exceptBuf = i; + break; + } + } + mAllocdBuffers.clear(); + if (exceptBuf >= 0) { + mAllocdBuffers.add(mCurrentTextureBuf); + } + mGraphicBufferAlloc->freeAllGraphicBuffersExcept(exceptBuf); +} + +EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy, + const sp<GraphicBuffer>& graphicBuffer) { + EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); + EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE, + }; + EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs); + EGLint error = eglGetError(); + if (error != EGL_SUCCESS) { + LOGE("error creating EGLImage: %#x", error); + } else if (image == EGL_NO_IMAGE_KHR) { + LOGE("no error reported, but no image was returned by " + "eglCreateImageKHR"); + } + return image; +} + +static void mtxMul(float out[16], const float a[16], const float b[16]) { + out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3]; + out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3]; + out[2] = a[2]*b[0] + a[6]*b[1] + a[10]*b[2] + a[14]*b[3]; + out[3] = a[3]*b[0] + a[7]*b[1] + a[11]*b[2] + a[15]*b[3]; + + out[4] = a[0]*b[4] + a[4]*b[5] + a[8]*b[6] + a[12]*b[7]; + out[5] = a[1]*b[4] + a[5]*b[5] + a[9]*b[6] + a[13]*b[7]; + out[6] = a[2]*b[4] + a[6]*b[5] + a[10]*b[6] + a[14]*b[7]; + out[7] = a[3]*b[4] + a[7]*b[5] + a[11]*b[6] + a[15]*b[7]; + + out[8] = a[0]*b[8] + a[4]*b[9] + a[8]*b[10] + a[12]*b[11]; + out[9] = a[1]*b[8] + a[5]*b[9] + a[9]*b[10] + a[13]*b[11]; + out[10] = a[2]*b[8] + a[6]*b[9] + a[10]*b[10] + a[14]*b[11]; + out[11] = a[3]*b[8] + a[7]*b[9] + a[11]*b[10] + a[15]*b[11]; + + out[12] = a[0]*b[12] + a[4]*b[13] + a[8]*b[14] + a[12]*b[15]; + out[13] = a[1]*b[12] + a[5]*b[13] + a[9]*b[14] + a[13]*b[15]; + out[14] = a[2]*b[12] + a[6]*b[13] + a[10]*b[14] + a[14]*b[15]; + out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15]; +} + +}; // namespace android diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp new file mode 100644 index 000000000000..7f1d9cbf22c5 --- /dev/null +++ b/libs/gui/SurfaceTextureClient.cpp @@ -0,0 +1,332 @@ +/* + * 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. + */ + +#define LOG_TAG "SurfaceTextureClient" +//#define LOG_NDEBUG 0 + +#include <gui/SurfaceTextureClient.h> + +#include <utils/Log.h> + +namespace android { + +SurfaceTextureClient::SurfaceTextureClient( + const sp<ISurfaceTexture>& surfaceTexture): + mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(1), + mReqHeight(1), mReqFormat(DEFAULT_FORMAT), mReqUsage(0), mMutex() { + // Initialize the ANativeWindow function pointers. + ANativeWindow::setSwapInterval = setSwapInterval; + ANativeWindow::dequeueBuffer = dequeueBuffer; + ANativeWindow::cancelBuffer = cancelBuffer; + ANativeWindow::lockBuffer = lockBuffer; + ANativeWindow::queueBuffer = queueBuffer; + ANativeWindow::query = query; + ANativeWindow::perform = perform; + + // Get a reference to the allocator. + mAllocator = mSurfaceTexture->getAllocator(); +} + +sp<ISurfaceTexture> SurfaceTextureClient::getISurfaceTexture() const { + return mSurfaceTexture; +} + +int SurfaceTextureClient::setSwapInterval(ANativeWindow* window, int interval) { + SurfaceTextureClient* c = getSelf(window); + return c->setSwapInterval(interval); +} + +int SurfaceTextureClient::dequeueBuffer(ANativeWindow* window, + android_native_buffer_t** buffer) { + SurfaceTextureClient* c = getSelf(window); + return c->dequeueBuffer(buffer); +} + +int SurfaceTextureClient::cancelBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + SurfaceTextureClient* c = getSelf(window); + return c->cancelBuffer(buffer); +} + +int SurfaceTextureClient::lockBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + SurfaceTextureClient* c = getSelf(window); + return c->lockBuffer(buffer); +} + +int SurfaceTextureClient::queueBuffer(ANativeWindow* window, + android_native_buffer_t* buffer) { + SurfaceTextureClient* c = getSelf(window); + return c->queueBuffer(buffer); +} + +int SurfaceTextureClient::query(ANativeWindow* window, int what, int* value) { + SurfaceTextureClient* c = getSelf(window); + return c->query(what, value); +} + +int SurfaceTextureClient::perform(ANativeWindow* window, int operation, ...) { + va_list args; + va_start(args, operation); + SurfaceTextureClient* c = getSelf(window); + return c->perform(operation, args); +} + +int SurfaceTextureClient::setSwapInterval(int interval) { + return INVALID_OPERATION; +} + +int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) { + LOGV("SurfaceTextureClient::dequeueBuffer"); + Mutex::Autolock lock(mMutex); + int buf = -1; + status_t err = mSurfaceTexture->dequeueBuffer(&buf); + if (err < 0) { + LOGV("dequeueBuffer: ISurfaceTexture::dequeueBuffer failed: %d", err); + return err; + } + sp<GraphicBuffer>& gbuf(mSlots[buf]); + if (gbuf == 0 || gbuf->getWidth() != mReqWidth || + gbuf->getHeight() != mReqHeight || + uint32_t(gbuf->getPixelFormat()) != mReqFormat || + (gbuf->getUsage() & mReqUsage) != mReqUsage) { + gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight, + mReqFormat, mReqUsage); + if (gbuf == 0) { + LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed"); + return NO_MEMORY; + } + } + *buffer = gbuf.get(); + return OK; +} + +int SurfaceTextureClient::cancelBuffer(android_native_buffer_t* buffer) { + LOGV("SurfaceTextureClient::cancelBuffer"); + Mutex::Autolock lock(mMutex); + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i]->handle == buffer->handle) { + mSurfaceTexture->cancelBuffer(i); + return OK; + } + } + return BAD_VALUE; +} + +int SurfaceTextureClient::lockBuffer(android_native_buffer_t* buffer) { + LOGV("SurfaceTextureClient::lockBuffer"); + Mutex::Autolock lock(mMutex); + return OK; +} + +int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) { + LOGV("SurfaceTextureClient::queueBuffer"); + Mutex::Autolock lock(mMutex); + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (mSlots[i]->handle == buffer->handle) { + return mSurfaceTexture->queueBuffer(i); + } + } + LOGE("queueBuffer: unknown buffer queued"); + return BAD_VALUE; +} + +int SurfaceTextureClient::query(int what, int* value) { + LOGV("SurfaceTextureClient::query"); + Mutex::Autolock lock(mMutex); + switch (what) { + case NATIVE_WINDOW_WIDTH: + case NATIVE_WINDOW_HEIGHT: + // XXX: How should SurfaceTexture behave if setBuffersGeometry didn't + // override the size? + *value = 0; + return NO_ERROR; + case NATIVE_WINDOW_FORMAT: + *value = DEFAULT_FORMAT; + return NO_ERROR; + case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: + *value = MIN_UNDEQUEUED_BUFFERS; + return NO_ERROR; + case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: + // SurfaceTextureClient currently never queues frames to SurfaceFlinger. + *value = 0; + return NO_ERROR; + case NATIVE_WINDOW_CONCRETE_TYPE: + *value = NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT; + return NO_ERROR; + } + return BAD_VALUE; +} + +int SurfaceTextureClient::perform(int operation, va_list args) +{ + int res = NO_ERROR; + switch (operation) { + case NATIVE_WINDOW_CONNECT: + res = dispatchConnect(args); + break; + case NATIVE_WINDOW_DISCONNECT: + res = dispatchDisconnect(args); + break; + case NATIVE_WINDOW_SET_USAGE: + res = dispatchSetUsage(args); + break; + case NATIVE_WINDOW_SET_CROP: + res = dispatchSetCrop(args); + break; + case NATIVE_WINDOW_SET_BUFFER_COUNT: + res = dispatchSetBufferCount(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY: + res = dispatchSetBuffersGeometry(args); + break; + case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: + res = dispatchSetBuffersTransform(args); + break; + default: + res = NAME_NOT_FOUND; + break; + } + return res; +} + +int SurfaceTextureClient::dispatchConnect(va_list args) { + int api = va_arg(args, int); + return connect(api); +} + +int SurfaceTextureClient::dispatchDisconnect(va_list args) { + int api = va_arg(args, int); + return disconnect(api); +} + +int SurfaceTextureClient::dispatchSetUsage(va_list args) { + int usage = va_arg(args, int); + return setUsage(usage); +} + +int SurfaceTextureClient::dispatchSetCrop(va_list args) { + android_native_rect_t const* rect = va_arg(args, android_native_rect_t*); + return setCrop(reinterpret_cast<Rect const*>(rect)); +} + +int SurfaceTextureClient::dispatchSetBufferCount(va_list args) { + size_t bufferCount = va_arg(args, size_t); + return setBufferCount(bufferCount); +} + +int SurfaceTextureClient::dispatchSetBuffersGeometry(va_list args) { + int w = va_arg(args, int); + int h = va_arg(args, int); + int f = va_arg(args, int); + return setBuffersGeometry(w, h, f); +} + +int SurfaceTextureClient::dispatchSetBuffersTransform(va_list args) { + int transform = va_arg(args, int); + return setBuffersTransform(transform); +} + +int SurfaceTextureClient::connect(int api) { + LOGV("SurfaceTextureClient::connect"); + // XXX: Implement this! + return INVALID_OPERATION; +} + +int SurfaceTextureClient::disconnect(int api) { + LOGV("SurfaceTextureClient::disconnect"); + // XXX: Implement this! + return INVALID_OPERATION; +} + +int SurfaceTextureClient::setUsage(uint32_t reqUsage) +{ + LOGV("SurfaceTextureClient::setUsage"); + Mutex::Autolock lock(mMutex); + mReqUsage = reqUsage; + return OK; +} + +int SurfaceTextureClient::setCrop(Rect const* rect) +{ + LOGV("SurfaceTextureClient::setCrop"); + Mutex::Autolock lock(mMutex); + + Rect realRect; + if (rect == NULL || rect->isEmpty()) { + realRect = Rect(0, 0); + } else { + realRect = *rect; + } + + status_t err = mSurfaceTexture->setCrop(*rect); + LOGE_IF(err, "ISurfaceTexture::setCrop(...) returned %s", strerror(-err)); + + return err; +} + +int SurfaceTextureClient::setBufferCount(int bufferCount) +{ + LOGV("SurfaceTextureClient::setBufferCount"); + Mutex::Autolock lock(mMutex); + + status_t err = mSurfaceTexture->setBufferCount(bufferCount); + LOGE_IF(err, "ISurfaceTexture::setBufferCount(%d) returned %s", + bufferCount, strerror(-err)); + + if (err == NO_ERROR) { + freeAllBuffers(); + } + + return err; +} + +int SurfaceTextureClient::setBuffersGeometry(int w, int h, int format) +{ + LOGV("SurfaceTextureClient::setBuffersGeometry"); + Mutex::Autolock lock(mMutex); + + if (w<0 || h<0 || format<0) + return BAD_VALUE; + + if ((w && !h) || (!w && h)) + return BAD_VALUE; + + mReqWidth = w; + mReqHeight = h; + mReqFormat = format; + + status_t err = mSurfaceTexture->setCrop(Rect(0, 0)); + LOGE_IF(err, "ISurfaceTexture::setCrop(...) returned %s", strerror(-err)); + + return err; +} + +int SurfaceTextureClient::setBuffersTransform(int transform) +{ + LOGV("SurfaceTextureClient::setBuffersTransform"); + Mutex::Autolock lock(mMutex); + status_t err = mSurfaceTexture->setTransform(transform); + return err; +} + +void SurfaceTextureClient::freeAllBuffers() { + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + mSlots[i] = 0; + } +} + +}; // namespace android diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk new file mode 100644 index 000000000000..7516299446ab --- /dev/null +++ b/libs/gui/tests/Android.mk @@ -0,0 +1,51 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +ifneq ($(TARGET_SIMULATOR),true) + +LOCAL_MODULE := SurfaceTexture_test + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := \ + SurfaceTextureClient_test.cpp \ + SurfaceTexture_test.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libEGL \ + libGLESv2 \ + libandroid \ + libbinder \ + libcutils \ + libgui \ + libstlport \ + libsurfaceflinger_client \ + libui \ + libutils \ + +LOCAL_STATIC_LIBRARIES := \ + libgtest \ + libgtest_main \ + +LOCAL_C_INCLUDES := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + +include $(BUILD_EXECUTABLE) + +# Build the manual test programs. +include $(call all-subdir-makefiles) + +endif + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp new file mode 100644 index 000000000000..94b05bc2d3ca --- /dev/null +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -0,0 +1,103 @@ +/* + * 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 <EGL/egl.h> +#include <gtest/gtest.h> +#include <gui/SurfaceTextureClient.h> + +namespace android { + +class SurfaceTextureClientTest : public ::testing::Test { +protected: + virtual void SetUp() { + mST = new SurfaceTexture(123); + mSTC = new SurfaceTextureClient(mST); + } + + virtual void TearDown() { + mST.clear(); + mSTC.clear(); + } + + sp<SurfaceTexture> mST; + sp<SurfaceTextureClient> mSTC; +}; + +TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) { + sp<ISurfaceTexture> ist(mSTC->getISurfaceTexture()); + ASSERT_TRUE(ist != NULL); +} + +TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) { + sp<ANativeWindow> anw(mSTC); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(0, result); +} + +TEST_F(SurfaceTextureClientTest, ConcreteTypeIsSurfaceTextureClient) { + sp<ANativeWindow> anw(mSTC); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT, result); +} + +TEST_F(SurfaceTextureClientTest, ANativeWindowLockFails) { + sp<ANativeWindow> anw(mSTC); + ANativeWindow_Buffer buf; + ASSERT_EQ(BAD_VALUE, ANativeWindow_lock(anw.get(), &buf, NULL)); +} + +TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceFails) { + sp<ANativeWindow> anw(mSTC); + + EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_DISPLAY, dpy); + + EGLint majorVersion; + EGLint minorVersion; + EXPECT_TRUE(eglInitialize(dpy, &majorVersion, &minorVersion)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EGLConfig myConfig = {0}; + EGLint numConfigs = 0; + EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + EGL_NONE }; + EXPECT_TRUE(eglChooseConfig(dpy, configAttribs, &myConfig, 1, + &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, anw.get(), + NULL); + ASSERT_EQ(EGL_NO_SURFACE, eglSurface); + ASSERT_EQ(EGL_BAD_NATIVE_WINDOW, eglGetError()); + + eglTerminate(dpy); +} + +} diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp new file mode 100644 index 000000000000..4184463496d4 --- /dev/null +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -0,0 +1,621 @@ +/* + * 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 <gtest/gtest.h> +#include <gui/SurfaceTexture.h> +#include <gui/SurfaceTextureClient.h> +#include <ui/GraphicBuffer.h> +#include <utils/String8.h> + +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <ui/FramebufferNativeWindow.h> + +namespace android { + +class GLTest : public ::testing::Test { +protected: + + GLTest(): + mEglDisplay(EGL_NO_DISPLAY), + mEglSurface(EGL_NO_SURFACE), + mEglContext(EGL_NO_CONTEXT) { + } + + virtual void SetUp() { + EGLBoolean returnValue; + + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); + + EGLint majorVersion; + EGLint minorVersion; + EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + RecordProperty("EglVersionMajor", majorVersion); + RecordProperty("EglVersionMajor", minorVersion); + + EGLConfig myConfig = {0}; + EGLint numConfigs = 0; + EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig, + 1, &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS"); + if (displaySecsEnv != NULL) { + mDisplaySecs = atoi(displaySecsEnv); + if (mDisplaySecs < 0) { + mDisplaySecs = 0; + } + } else { + mDisplaySecs = 0; + } + + if (mDisplaySecs > 0) { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + mSurfaceControl = mComposerClient->createSurface(getpid(), + String8("Test Surface"), 0, + getSurfaceWidth(), getSurfaceHeight(), + PIXEL_FORMAT_RGB_888, 0); + + ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl->isValid()); + + ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction()); + ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000)); + ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); + ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction()); + + sp<ANativeWindow> window = mSurfaceControl->getSurface(); + mEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig, + window.get(), NULL); + } else { + EGLint pbufferAttribs[] = { + EGL_WIDTH, getSurfaceWidth(), + EGL_HEIGHT, getSurfaceHeight(), + EGL_NONE }; + + mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig, + pbufferAttribs); + } + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mEglSurface); + + mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, + getContextAttribs()); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mEglContext); + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EGLint w, h; + EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + RecordProperty("EglSurfaceWidth", w); + RecordProperty("EglSurfaceHeight", h); + + glViewport(0, 0, w, h); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + } + + virtual void TearDown() { + // Display the result + if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) { + eglSwapBuffers(mEglDisplay, mEglSurface); + sleep(mDisplaySecs); + } + + if (mComposerClient != NULL) { + mComposerClient->dispose(); + } + if (mEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mEglContext); + } + if (mEglSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEglDisplay, mEglSurface); + } + if (mEglDisplay != EGL_NO_DISPLAY) { + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + eglTerminate(mEglDisplay); + } + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + } + + virtual EGLint const* getConfigAttribs() { + static EGLint sDefaultConfigAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + EGL_NONE }; + + return sDefaultConfigAttribs; + } + + virtual EGLint const* getContextAttribs() { + static EGLint sDefaultContextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE }; + + return sDefaultContextAttribs; + } + + virtual EGLint getSurfaceWidth() { + return 64; + } + + virtual EGLint getSurfaceHeight() { + return 64; + } + + void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) { + GLuint shader = glCreateShader(shaderType); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (shader) { + glShaderSource(shader, 1, &pSource, NULL); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glCompileShader(shader); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (!compiled) { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (infoLen) { + char* buf = (char*) malloc(infoLen); + if (buf) { + glGetShaderInfoLog(shader, infoLen, NULL, buf); + printf("Shader compile log:\n%s\n", buf); + free(buf); + FAIL(); + } + } else { + char* buf = (char*) malloc(0x1000); + if (buf) { + glGetShaderInfoLog(shader, 0x1000, NULL, buf); + printf("Shader compile log:\n%s\n", buf); + free(buf); + FAIL(); + } + } + glDeleteShader(shader); + shader = 0; + } + } + ASSERT_TRUE(shader != 0); + *outShader = shader; + } + + void createProgram(const char* pVertexSource, const char* pFragmentSource, + GLuint* outPgm) { + GLuint vertexShader, fragmentShader; + { + SCOPED_TRACE("compiling vertex shader"); + loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader); + if (HasFatalFailure()) { + return; + } + } + { + SCOPED_TRACE("compiling fragment shader"); + loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader); + if (HasFatalFailure()) { + return; + } + } + + GLuint program = glCreateProgram(); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (program) { + glAttachShader(program, vertexShader); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glAttachShader(program, fragmentShader); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + 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("Program link log:\n%s\n", buf); + free(buf); + FAIL(); + } + } + glDeleteProgram(program); + program = 0; + } + } + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + ASSERT_TRUE(program != 0); + *outPgm = program; + } + + ::testing::AssertionResult checkPixel(int x, int y, int r, + int g, int b, int a) { + GLubyte pixel[4]; + String8 msg; + glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + GLenum err = glGetError(); + if (err != GL_NO_ERROR) { + msg += String8::format("error reading pixel: %#x", err); + while ((err = glGetError()) != GL_NO_ERROR) { + msg += String8::format(", %#x", err); + } + fprintf(stderr, "pixel check failure: %s\n", msg.string()); + return ::testing::AssertionFailure( + ::testing::Message(msg.string())); + } + if (r >= 0 && GLubyte(r) != pixel[0]) { + msg += String8::format("r(%d isn't %d)", pixel[0], r); + } + if (g >= 0 && GLubyte(g) != pixel[1]) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("g(%d isn't %d)", pixel[1], g); + } + if (b >= 0 && GLubyte(b) != pixel[2]) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("b(%d isn't %d)", pixel[2], b); + } + if (a >= 0 && GLubyte(a) != pixel[3]) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("a(%d isn't %d)", pixel[3], a); + } + if (!msg.isEmpty()) { + fprintf(stderr, "pixel check failure: %s\n", msg.string()); + return ::testing::AssertionFailure( + ::testing::Message(msg.string())); + } else { + return ::testing::AssertionSuccess(); + } + } + + int mDisplaySecs; + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mSurfaceControl; + + EGLDisplay mEglDisplay; + EGLSurface mEglSurface; + EGLContext mEglContext; +}; + +// XXX: Code above this point should live elsewhere + +class SurfaceTextureGLTest : public GLTest { +protected: + static const GLint TEX_ID = 123; + + virtual void SetUp() { + GLTest::SetUp(); + mST = new SurfaceTexture(TEX_ID); + mSTC = new SurfaceTextureClient(mST); + mANW = mSTC; + + const char vsrc[] = + "attribute vec4 vPosition;\n" + "varying vec2 texCoords;\n" + "uniform mat4 texMatrix;\n" + "void main() {\n" + " vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n" + " texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n" + " gl_Position = vPosition;\n" + "}\n"; + + const char fsrc[] = + "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + "uniform samplerExternalOES texSampler;\n" + "varying vec2 texCoords;\n" + "void main() {\n" + " gl_FragColor = texture2D(texSampler, texCoords);\n" + "}\n"; + + { + SCOPED_TRACE("creating shader program"); + createProgram(vsrc, fsrc, &mPgm); + if (HasFatalFailure()) { + return; + } + } + + mPositionHandle = glGetAttribLocation(mPgm, "vPosition"); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + ASSERT_NE(-1, mPositionHandle); + mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler"); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + ASSERT_NE(-1, mTexSamplerHandle); + mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix"); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + ASSERT_NE(-1, mTexMatrixHandle); + } + + // drawTexture draws the SurfaceTexture over the entire GL viewport. + void drawTexture() { + const GLfloat triangleVertices[] = { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f, + 1.0f, 1.0f, + }; + + glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glEnableVertexAttribArray(mPositionHandle); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + glUseProgram(mPgm); + glUniform1i(mTexSamplerHandle, 0); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + GLfloat texMatrix[16]; + mST->getTransformMatrix(texMatrix); + glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + } + + sp<SurfaceTexture> mST; + sp<SurfaceTextureClient> mSTC; + sp<ANativeWindow> mANW; + + GLuint mPgm; + GLint mPositionHandle; + GLint mTexSamplerHandle; + GLint mTexMatrixHandle; +}; + +// Fill a YV12 buffer with a multi-colored checkerboard pattern +void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) { + const int blockWidth = w > 16 ? w / 16 : 1; + const int blockHeight = h > 16 ? h / 16 : 1; + const int yuvTexOffsetY = 0; + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * h; + int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; + int yuvTexStrideU = yuvTexStrideV; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + int parityX = (x / blockWidth) & 1; + int parityY = (y / blockHeight) & 1; + unsigned char intensity = (parityX ^ parityY) ? 63 : 191; + buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; + if (x < w / 2 && y < h / 2) { + buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; + if (x * 2 < w / 2 && y * 2 < h / 2) { + buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = + buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = + buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = + buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = + intensity; + } + } + } + } +} + +// Fill a YV12 buffer with red outside a given rectangle and green inside it. +void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, + const android_native_rect_t& rect) { + const int yuvTexOffsetY = 0; + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * h; + int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; + int yuvTexStrideU = yuvTexStrideV; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + bool inside = rect.left <= x && x < rect.right && + rect.top <= y && y < rect.bottom; + buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64; + if (x < w / 2 && y < h / 2) { + bool inside = rect.left <= 2*x && 2*x < rect.right && + rect.top <= 2*y && 2*y < rect.bottom; + buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16; + buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = + inside ? 16 : 255; + } + } + } +} + +TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { + const int yuvTexWidth = 64; + const int yuvTexHeight = 66; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + android_native_buffer_t* anb; + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + + // Fill the buffer with the a checkerboard pattern + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillYV12Buffer(img, yuvTexWidth, yuvTexHeight, buf->getStride()); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + + mST->updateTexImage(); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 255, 127, 255, 255)); + EXPECT_TRUE(checkPixel(63, 0, 0, 133, 0, 255)); + EXPECT_TRUE(checkPixel(63, 63, 0, 133, 0, 255)); + EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255)); + + EXPECT_TRUE(checkPixel(22, 44, 247, 70, 255, 255)); + EXPECT_TRUE(checkPixel(45, 52, 209, 32, 235, 255)); + EXPECT_TRUE(checkPixel(52, 51, 100, 255, 73, 255)); + EXPECT_TRUE(checkPixel( 7, 31, 155, 0, 118, 255)); + EXPECT_TRUE(checkPixel(31, 9, 148, 71, 110, 255)); + EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255)); + EXPECT_TRUE(checkPixel(36, 22, 155, 29, 0, 255)); +} + +// XXX: This test is disabled because it it currently broken on all devices to +// which I have access. Some of the checkPixel calls are not correct because +// I just copied them from the npot test above and haven't bothered to figure +// out the correct values. +TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledYV12BufferPow2) { + const int yuvTexWidth = 64; + const int yuvTexHeight = 64; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + android_native_buffer_t* anb; + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + + // Fill the buffer with the a checkerboard pattern + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillYV12Buffer(img, yuvTexWidth, yuvTexHeight, buf->getStride()); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + + mST->updateTexImage(); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 255, 127, 255, 255)); + EXPECT_TRUE(checkPixel(63, 0, 0, 133, 0, 255)); + EXPECT_TRUE(checkPixel(63, 63, 0, 133, 0, 255)); + EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255)); + + EXPECT_TRUE(checkPixel(22, 19, 247, 70, 255, 255)); + EXPECT_TRUE(checkPixel(45, 11, 209, 32, 235, 255)); + EXPECT_TRUE(checkPixel(52, 12, 100, 255, 73, 255)); + EXPECT_TRUE(checkPixel( 7, 32, 155, 0, 118, 255)); + EXPECT_TRUE(checkPixel(31, 54, 148, 71, 110, 255)); + EXPECT_TRUE(checkPixel(29, 28, 255, 127, 255, 255)); + EXPECT_TRUE(checkPixel(36, 41, 155, 29, 0, 255)); +} + +TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { + const int yuvTexWidth = 64; + const int yuvTexHeight = 66; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + android_native_rect_t crops[] = { + {4, 6, 22, 36}, + {0, 6, 22, 36}, + {4, 0, 22, 36}, + {4, 6, yuvTexWidth, 36}, + {4, 6, 22, yuvTexHeight}, + }; + + for (int i = 0; i < 5; i++) { + const android_native_rect_t& crop(crops[i]); + SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left, + crop.top, crop.right, crop.bottom).string()); + + ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop)); + + android_native_buffer_t* anb; + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillYV12BufferRect(img, yuvTexWidth, yuvTexHeight, buf->getStride(), crop); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + + mST->updateTexImage(); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(63, 0, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(63, 63, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel( 0, 63, 82, 255, 35, 255)); + + EXPECT_TRUE(checkPixel(25, 14, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(35, 31, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(57, 6, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel( 5, 42, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(32, 33, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(16, 26, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(46, 51, 82, 255, 35, 255)); + } +} + +} diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk new file mode 100644 index 000000000000..f4a0161b2e1f --- /dev/null +++ b/libs/hwui/Android.mk @@ -0,0 +1,50 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# Only build libhwui when USE_OPENGL_RENDERER is +# defined in the current device/board configuration +ifeq ($(USE_OPENGL_RENDERER),true) + LOCAL_SRC_FILES:= \ + utils/SortedListImpl.cpp \ + FontRenderer.cpp \ + GammaFontRenderer.cpp \ + Caches.cpp \ + DisplayListRenderer.cpp \ + FboCache.cpp \ + GradientCache.cpp \ + LayerCache.cpp \ + LayerRenderer.cpp \ + Matrix.cpp \ + OpenGLRenderer.cpp \ + Patch.cpp \ + PatchCache.cpp \ + PathCache.cpp \ + Program.cpp \ + ProgramCache.cpp \ + ResourceCache.cpp \ + ShapeCache.cpp \ + SkiaColorFilter.cpp \ + SkiaShader.cpp \ + TextureCache.cpp \ + TextDropShadowCache.cpp + + LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) \ + $(LOCAL_PATH)/../../include/utils \ + external/skia/include/core \ + external/skia/include/effects \ + external/skia/include/images \ + external/skia/src/ports \ + external/skia/include/utils + + LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER + LOCAL_MODULE_CLASS := SHARED_LIBRARIES + LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui + LOCAL_MODULE := libhwui + LOCAL_MODULE_TAGS := optional + LOCAL_PRELINK_MODULE := false + + include $(BUILD_SHARED_LIBRARY) + + include $(call all-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp new file mode 100644 index 000000000000..4f5edd575926 --- /dev/null +++ b/libs/hwui/Caches.cpp @@ -0,0 +1,194 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <utils/Log.h> + +#include "Caches.h" +#include "Properties.h" +#include "LayerRenderer.h" + +namespace android { + +#ifdef USE_OPENGL_RENDERER +using namespace uirenderer; +ANDROID_SINGLETON_STATIC_INSTANCE(Caches); +#endif + +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO), + lastDstMode(GL_ZERO), currentProgram(NULL) { + GLint maxTextureUnits; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) { + LOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT); + } + + glGenBuffers(1, &meshBuffer); + glBindBuffer(GL_ARRAY_BUFFER, meshBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + + mCurrentBuffer = meshBuffer; + mRegionMesh = NULL; + + mDebugLevel = readDebugLevel(); + LOGD("Enabling debug mode %d", mDebugLevel); + +#if RENDER_LAYERS_AS_REGIONS + LOGD("Layers will be composited as regions"); +#endif +} + +Caches::~Caches() { + delete[] mRegionMesh; +} + +/////////////////////////////////////////////////////////////////////////////// +// Debug +/////////////////////////////////////////////////////////////////////////////// + +void Caches::dumpMemoryUsage() { + LOGD("Current memory usage / total memory usage (bytes):"); + LOGD(" TextureCache %8d / %8d", textureCache.getSize(), textureCache.getMaxSize()); + LOGD(" LayerCache %8d / %8d", layerCache.getSize(), layerCache.getMaxSize()); + LOGD(" GradientCache %8d / %8d", gradientCache.getSize(), gradientCache.getMaxSize()); + LOGD(" PathCache %8d / %8d", pathCache.getSize(), pathCache.getMaxSize()); + LOGD(" CircleShapeCache %8d / %8d", + circleShapeCache.getSize(), circleShapeCache.getMaxSize()); + LOGD(" OvalShapeCache %8d / %8d", + ovalShapeCache.getSize(), ovalShapeCache.getMaxSize()); + LOGD(" RoundRectShapeCache %8d / %8d", + roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize()); + LOGD(" RectShapeCache %8d / %8d", + rectShapeCache.getSize(), rectShapeCache.getMaxSize()); + LOGD(" ArcShapeCache %8d / %8d", + arcShapeCache.getSize(), arcShapeCache.getMaxSize()); + LOGD(" TextDropShadowCache %8d / %8d", dropShadowCache.getSize(), + dropShadowCache.getMaxSize()); + for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) { + const uint32_t size = fontRenderer.getFontRendererSize(i); + LOGD(" FontRenderer %d %8d / %8d", i, size, size); + } + LOGD("Other:"); + LOGD(" FboCache %8d / %8d", fboCache.getSize(), fboCache.getMaxSize()); + LOGD(" PatchCache %8d / %8d", patchCache.getSize(), patchCache.getMaxSize()); + + uint32_t total = 0; + total += textureCache.getSize(); + total += layerCache.getSize(); + total += gradientCache.getSize(); + total += pathCache.getSize(); + total += dropShadowCache.getSize(); + total += roundRectShapeCache.getSize(); + total += circleShapeCache.getSize(); + total += ovalShapeCache.getSize(); + total += rectShapeCache.getSize(); + total += arcShapeCache.getSize(); + for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) { + total += fontRenderer.getFontRendererSize(i); + } + + LOGD("Total memory usage:"); + LOGD(" %d bytes, %.2f MB", total, total / 1024.0f / 1024.0f); + LOGD("\n"); +} + +/////////////////////////////////////////////////////////////////////////////// +// Memory management +/////////////////////////////////////////////////////////////////////////////// + +void Caches::clearGarbage() { + textureCache.clearGarbage(); + gradientCache.clearGarbage(); + pathCache.clearGarbage(); + + Mutex::Autolock _l(mGarbageLock); + + size_t count = mLayerGarbage.size(); + for (size_t i = 0; i < count; i++) { + Layer* layer = mLayerGarbage.itemAt(i); + LayerRenderer::destroyLayer(layer); + } + mLayerGarbage.clear(); +} + +void Caches::deleteLayerDeferred(Layer* layer) { + Mutex::Autolock _l(mGarbageLock); + mLayerGarbage.push(layer); +} + +/////////////////////////////////////////////////////////////////////////////// +// VBO +/////////////////////////////////////////////////////////////////////////////// + +void Caches::bindMeshBuffer() { + bindMeshBuffer(meshBuffer); +} + +void Caches::bindMeshBuffer(const GLuint buffer) { + if (mCurrentBuffer != buffer) { + glBindBuffer(GL_ARRAY_BUFFER, buffer); + mCurrentBuffer = buffer; + } +} + +void Caches::unbindMeshBuffer() { + if (mCurrentBuffer) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + mCurrentBuffer = 0; + } +} + +TextureVertex* Caches::getRegionMesh() { + // Create the mesh, 2 triangles and 4 vertices per rectangle in the region + if (!mRegionMesh) { + mRegionMesh = new TextureVertex[REGION_MESH_QUAD_COUNT * 4]; + + uint16_t* regionIndices = new uint16_t[REGION_MESH_QUAD_COUNT * 6]; + for (int i = 0; i < REGION_MESH_QUAD_COUNT; i++) { + uint16_t quad = i * 4; + int index = i * 6; + regionIndices[index ] = quad; // top-left + regionIndices[index + 1] = quad + 1; // top-right + regionIndices[index + 2] = quad + 2; // bottom-left + regionIndices[index + 3] = quad + 2; // bottom-left + regionIndices[index + 4] = quad + 1; // top-right + regionIndices[index + 5] = quad + 3; // bottom-right + } + + glGenBuffers(1, &mRegionMeshIndices); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 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); + } + + return mRegionMesh; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h new file mode 100644 index 000000000000..0a9335f23288 --- /dev/null +++ b/libs/hwui/Caches.h @@ -0,0 +1,181 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_CACHES_H +#define ANDROID_HWUI_CACHES_H + +#ifndef LOG_TAG + #define LOG_TAG "OpenGLRenderer" +#endif + +#include <utils/Singleton.h> + +#include "Extensions.h" +#include "FontRenderer.h" +#include "GammaFontRenderer.h" +#include "TextureCache.h" +#include "LayerCache.h" +#include "GradientCache.h" +#include "PatchCache.h" +#include "ProgramCache.h" +#include "ShapeCache.h" +#include "PathCache.h" +#include "TextDropShadowCache.h" +#include "FboCache.h" +#include "ResourceCache.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////////////////////////// + +#define REQUIRED_TEXTURE_UNITS_COUNT 3 + +#define REGION_MESH_QUAD_COUNT 512 + +// Generates simple and textured vertices +#define FV(x, y, u, v) { { x, y }, { u, v } } + +// This array is never used directly but used as a memcpy source in the +// OpenGLRenderer constructor +static const TextureVertex gMeshVertices[] = { + FV(0.0f, 0.0f, 0.0f, 0.0f), + FV(1.0f, 0.0f, 1.0f, 0.0f), + FV(0.0f, 1.0f, 0.0f, 1.0f), + FV(1.0f, 1.0f, 1.0f, 1.0f) +}; +static const GLsizei gMeshStride = sizeof(TextureVertex); +static const GLsizei gMeshTextureOffset = 2 * sizeof(float); +static const GLsizei gMeshCount = 4; + +/////////////////////////////////////////////////////////////////////////////// +// Debug +/////////////////////////////////////////////////////////////////////////////// + +struct CacheLogger { + CacheLogger() { + LOGD("Creating OpenGL renderer caches"); + } +}; // struct CacheLogger + +/////////////////////////////////////////////////////////////////////////////// +// Caches +/////////////////////////////////////////////////////////////////////////////// + +class Caches: public Singleton<Caches> { + Caches(); + ~Caches(); + + friend class Singleton<Caches>; + + CacheLogger mLogger; + + GLuint mCurrentBuffer; + + // Used to render layers + TextureVertex* mRegionMesh; + GLuint mRegionMeshIndices; + + mutable Mutex mGarbageLock; + Vector<Layer*> mLayerGarbage; + +public: + /** + * Indicates whether the renderer is in debug mode. + * This debug mode provides limited information to app developers. + */ + DebugLevel getDebugLevel() const { + return mDebugLevel; + } + + /** + * Call this on each frame to ensure that garbage is deleted from + * GPU memory. + */ + void clearGarbage(); + + /** + * Can be used to delete a layer from a non EGL thread. + */ + void deleteLayerDeferred(Layer* layer); + + /** + * Binds the VBO used to render simple textured quads. + */ + void bindMeshBuffer(); + + /** + * Binds the specified VBO if needed. + */ + void bindMeshBuffer(const GLuint buffer); + + /** + * Unbinds the VBO used to render simple textured quads. + */ + void unbindMeshBuffer(); + + /** + * Returns the mesh used to draw regions. Calling this method will + * bind a VBO of type GL_ELEMENT_ARRAY_BUFFER that contains the + * indices for the region mesh. + */ + TextureVertex* getRegionMesh(); + + /** + * Displays the memory usage of each cache and the total sum. + */ + void dumpMemoryUsage(); + + bool blend; + GLenum lastSrcMode; + GLenum lastDstMode; + Program* currentProgram; + + // VBO to draw with + GLuint meshBuffer; + + // GL extensions + Extensions extensions; + + // Misc + GLint maxTextureSize; + + TextureCache textureCache; + LayerCache layerCache; + GradientCache gradientCache; + ProgramCache programCache; + PathCache pathCache; + RoundRectShapeCache roundRectShapeCache; + CircleShapeCache circleShapeCache; + OvalShapeCache ovalShapeCache; + RectShapeCache rectShapeCache; + ArcShapeCache arcShapeCache; + PatchCache patchCache; + TextDropShadowCache dropShadowCache; + FboCache fboCache; + GammaFontRenderer fontRenderer; + ResourceCache resourceCache; + +private: + DebugLevel mDebugLevel; +}; // class Caches + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_CACHES_H diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h new file mode 100644 index 000000000000..14471bc7b717 --- /dev/null +++ b/libs/hwui/Debug.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_DEBUG_H +#define ANDROID_HWUI_DEBUG_H + +// Turn on to check for OpenGL errors on each frame +#define DEBUG_OPENGL 1 + +// Turn on to enable initialization information +#define DEBUG_INIT 0 + +// Turn on to enable memory usage summary on each frame +#define DEBUG_MEMORY_USAGE 0 + +// Turn on to enable layers debugging when renderered as regions +#define DEBUG_LAYERS_AS_REGIONS 0 + +// Turn on to display debug info about vertex/fragment shaders +#define DEBUG_PROGRAMS 0 + +// Turn on to display info about layers +#define DEBUG_LAYERS 0 + +// Turn on to display debug infor about 9patch objects +#define DEBUG_PATCHES 0 +// Turn on to display vertex and tex coords data about 9patch objects +// This flag requires DEBUG_PATCHES to be turned on +#define DEBUG_PATCHES_VERTICES 0 +// Turn on to display vertex and tex coords data used by empty quads +// in 9patch objects +// This flag requires DEBUG_PATCHES to be turned on +#define DEBUG_PATCHES_EMPTY_VERTICES 0 + +// Turn on to display debug info about shapes +#define DEBUG_SHAPES 0 + +// Turn on to display debug info about textures +#define DEBUG_TEXTURES 0 + +// Turn on to display debug info about the layer renderer +#define DEBUG_LAYER_RENDERER 0 + +// Turn on to dump display list state +#define DEBUG_DISPLAY_LIST 0 + +#if DEBUG_INIT + #define INIT_LOGD(...) LOGD(__VA_ARGS__) +#else + #define INIT_LOGD(...) +#endif + +#endif // ANDROID_HWUI_DEBUG_H diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp new file mode 100644 index 000000000000..868290bf0f8b --- /dev/null +++ b/libs/hwui/DisplayListRenderer.cpp @@ -0,0 +1,846 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "DisplayListRenderer.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Display list +/////////////////////////////////////////////////////////////////////////////// + +const char* DisplayList::OP_NAMES[] = { + "Save", + "Restore", + "RestoreToCount", + "SaveLayer", + "SaveLayerAlpha", + "Translate", + "Rotate", + "Scale", + "Skew", + "SetMatrix", + "ConcatMatrix", + "ClipRect", + "DrawDisplayList", + "DrawLayer", + "DrawBitmap", + "DrawBitmapMatrix", + "DrawBitmapRect", + "DrawBitmapMesh", + "DrawPatch", + "DrawColor", + "DrawRect", + "DrawRoundRect", + "DrawCircle", + "DrawOval", + "DrawArc", + "DrawPath", + "DrawLines", + "DrawText", + "ResetShader", + "SetupShader", + "ResetColorFilter", + "SetupColorFilter", + "ResetShadow", + "SetupShadow", + "DrawGLFunction" +}; + +DisplayList::DisplayList(const DisplayListRenderer& recorder) { + initFromDisplayListRenderer(recorder); +} + +DisplayList::~DisplayList() { + clearResources(); +} + +void DisplayList::clearResources() { + sk_free((void*) mReader.base()); + + Caches& caches = Caches::getInstance(); + + for (size_t i = 0; i < mBitmapResources.size(); i++) { + caches.resourceCache.decrementRefcount(mBitmapResources.itemAt(i)); + } + mBitmapResources.clear(); + + for (size_t i = 0; i < mShaders.size(); i++) { + caches.resourceCache.decrementRefcount(mShaders.itemAt(i)); + } + mShaders.clear(); + + for (size_t i = 0; i < mPaints.size(); i++) { + delete mPaints.itemAt(i); + } + mPaints.clear(); + + for (size_t i = 0; i < mPaths.size(); i++) { + delete mPaths.itemAt(i); + } + mPaths.clear(); + for (size_t i = 0; i < mOriginalPaths.size(); i++) { + caches.resourceCache.decrementRefcount(mOriginalPaths.itemAt(i)); + } + mOriginalPaths.clear(); + + for (size_t i = 0; i < mMatrices.size(); i++) { + delete mMatrices.itemAt(i); + } + mMatrices.clear(); +} + +void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing) { + const SkWriter32& writer = recorder.writeStream(); + init(); + + if (writer.size() == 0) { + return; + } + + if (reusing) { + // re-using display list - clear out previous allocations + clearResources(); + } + + size_t size = writer.size(); + void* buffer = sk_malloc_throw(size); + writer.flatten(buffer); + mReader.setMemory(buffer, size); + + Caches& caches = Caches::getInstance(); + + const Vector<SkBitmap*> &bitmapResources = recorder.getBitmapResources(); + for (size_t i = 0; i < bitmapResources.size(); i++) { + SkBitmap* resource = bitmapResources.itemAt(i); + mBitmapResources.add(resource); + caches.resourceCache.incrementRefcount(resource); + } + + const Vector<SkiaShader*> &shaders = recorder.getShaders(); + for (size_t i = 0; i < shaders.size(); i++) { + SkiaShader* shader = shaders.itemAt(i); + mShaders.add(shader); + caches.resourceCache.incrementRefcount(shader); + } + + const Vector<SkPaint*> &paints = recorder.getPaints(); + for (size_t i = 0; i < paints.size(); i++) { + mPaints.add(paints.itemAt(i)); + } + + const Vector<SkPath*> &paths = recorder.getPaths(); + for (size_t i = 0; i < paths.size(); i++) { + mPaths.add(paths.itemAt(i)); + } + + const Vector<SkPath*> &originalPaths = recorder.getOriginalPaths(); + for (size_t i = 0; i < originalPaths.size(); i++) { + SkPath* path = originalPaths.itemAt(i); + mOriginalPaths.add(path); + caches.resourceCache.incrementRefcount(path); + } + + const Vector<SkMatrix*> &matrices = recorder.getMatrices(); + for (size_t i = 0; i < matrices.size(); i++) { + mMatrices.add(matrices.itemAt(i)); + } +} + +void DisplayList::init() { +} + +bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) { + bool needsInvalidate = false; + TextContainer text; + mReader.rewind(); + +#if DEBUG_DISPLAY_LIST + uint32_t count = (level + 1) * 2; + char indent[count + 1]; + for (uint32_t i = 0; i < count; i++) { + indent[i] = ' '; + } + indent[count] = '\0'; + DISPLAY_LIST_LOGD("%sStart display list (%p)", (char*) indent + 2, this); +#endif + + int saveCount = renderer.getSaveCount() - 1; + while (!mReader.eof()) { + int op = mReader.readInt(); + + switch (op) { + case DrawGLFunction: { + Functor *functor = (Functor *) getInt(); + DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], functor); + needsInvalidate |= renderer.callDrawGLFunction(functor, dirty); + } + break; + case Save: { + int rendererNum = getInt(); + DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], rendererNum); + renderer.save(rendererNum); + } + break; + case Restore: { + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); + renderer.restore(); + } + break; + case RestoreToCount: { + int restoreCount = saveCount + getInt(); + DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], restoreCount); + renderer.restoreToCount(restoreCount); + } + break; + case SaveLayer: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + SkPaint* paint = getPaint(); + 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); + renderer.saveLayer(f1, f2, f3, f4, paint, flags); + } + break; + case SaveLayerAlpha: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + int alpha = getInt(); + int flags = getInt(); + DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", (char*) indent, + OP_NAMES[op], f1, f2, f3, f4, alpha, flags); + renderer.saveLayerAlpha(f1, f2, f3, f4, alpha, flags); + } + break; + case Translate: { + float f1 = getFloat(); + float f2 = getFloat(); + DISPLAY_LIST_LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], f1, f2); + renderer.translate(f1, f2); + } + break; + case Rotate: { + float rotation = getFloat(); + DISPLAY_LIST_LOGD("%s%s %.2f", (char*) indent, OP_NAMES[op], rotation); + renderer.rotate(rotation); + } + break; + case Scale: { + float sx = getFloat(); + float sy = getFloat(); + DISPLAY_LIST_LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], sx, sy); + renderer.scale(sx, sy); + } + break; + case Skew: { + float sx = getFloat(); + float sy = getFloat(); + DISPLAY_LIST_LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], sx, sy); + renderer.skew(sx, sy); + } + break; + case SetMatrix: { + SkMatrix* matrix = getMatrix(); + DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], matrix); + renderer.setMatrix(matrix); + } + break; + case ConcatMatrix: { + SkMatrix* matrix = getMatrix(); + DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], matrix); + renderer.concatMatrix(matrix); + } + break; + case ClipRect: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + int regionOp = getInt(); + DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d", (char*) indent, OP_NAMES[op], + f1, f2, f3, f4, regionOp); + renderer.clipRect(f1, f2, f3, f4, (SkRegion::Op) regionOp); + } + break; + case DrawDisplayList: { + DisplayList* displayList = getDisplayList(); + uint32_t width = getUInt(); + uint32_t height = getUInt(); + DISPLAY_LIST_LOGD("%s%s %p, %dx%d, %d", (char*) indent, OP_NAMES[op], + displayList, width, height, level + 1); + needsInvalidate |= renderer.drawDisplayList(displayList, width, height, + dirty, level + 1); + } + break; + case DrawLayer: { + Layer* layer = (Layer*) getInt(); + float x = getFloat(); + float y = getFloat(); + SkPaint* paint = getPaint(); + DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], + layer, x, y, paint); + renderer.drawLayer(layer, x, y, paint); + } + break; + case DrawBitmap: { + SkBitmap* bitmap = getBitmap(); + float x = getFloat(); + float y = getFloat(); + SkPaint* paint = getPaint(); + DISPLAY_LIST_LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], + bitmap, x, y, paint); + renderer.drawBitmap(bitmap, x, y, paint); + } + break; + case DrawBitmapMatrix: { + SkBitmap* bitmap = getBitmap(); + SkMatrix* matrix = getMatrix(); + SkPaint* paint = getPaint(); + DISPLAY_LIST_LOGD("%s%s %p, %p, %p", (char*) indent, OP_NAMES[op], + bitmap, matrix, paint); + renderer.drawBitmap(bitmap, matrix, paint); + } + break; + case DrawBitmapRect: { + SkBitmap* bitmap = getBitmap(); + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + float f5 = getFloat(); + float f6 = getFloat(); + float f7 = getFloat(); + float f8 = getFloat(); + SkPaint* paint = getPaint(); + 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); + } + break; + case DrawBitmapMesh: { + int verticesCount = 0; + uint32_t colorsCount = 0; + + SkBitmap* bitmap = getBitmap(); + uint32_t meshWidth = getInt(); + uint32_t meshHeight = getInt(); + float* vertices = getFloats(verticesCount); + bool hasColors = getInt(); + int* colors = hasColors ? getInts(colorsCount) : NULL; + + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); + renderer.drawBitmapMesh(bitmap, meshWidth, meshHeight, vertices, colors, getPaint()); + } + break; + case DrawPatch: { + int32_t* xDivs = NULL; + int32_t* yDivs = NULL; + uint32_t* colors = NULL; + uint32_t xDivsCount = 0; + uint32_t yDivsCount = 0; + int8_t numColors = 0; + + SkBitmap* bitmap = getBitmap(); + + xDivs = getInts(xDivsCount); + yDivs = getInts(yDivsCount); + colors = getUInts(numColors); + + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); + renderer.drawPatch(bitmap, xDivs, yDivs, colors, xDivsCount, yDivsCount, + numColors, getFloat(), getFloat(), getFloat(), getFloat(), getPaint()); + } + break; + case DrawColor: { + int color = getInt(); + int xferMode = getInt(); + DISPLAY_LIST_LOGD("%s%s 0x%x %d", (char*) indent, OP_NAMES[op], color, xferMode); + renderer.drawColor(color, (SkXfermode::Mode) xferMode); + } + break; + case DrawRect: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + SkPaint* paint = getPaint(); + 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); + } + break; + case DrawRoundRect: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + float f5 = getFloat(); + float f6 = getFloat(); + SkPaint* paint = getPaint(); + 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); + } + break; + case DrawCircle: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + SkPaint* paint = getPaint(); + DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %p", + (char*) indent, OP_NAMES[op], f1, f2, f3, paint); + renderer.drawCircle(f1, f2, f3, paint); + } + break; + case DrawOval: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + SkPaint* paint = getPaint(); + 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); + } + break; + case DrawArc: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + float f5 = getFloat(); + float f6 = getFloat(); + int i1 = getInt(); + SkPaint* paint = getPaint(); + 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); + } + break; + case DrawPath: { + SkPath* path = getPath(); + SkPaint* paint = getPaint(); + DISPLAY_LIST_LOGD("%s%s %p, %p", (char*) indent, OP_NAMES[op], path, paint); + renderer.drawPath(path, paint); + } + break; + case DrawLines: { + int count = 0; + float* points = getFloats(count); + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); + renderer.drawLines(points, count, getPaint()); + } + break; + case DrawText: { + getText(&text); + 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); + } + break; + case ResetShader: { + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); + renderer.resetShader(); + } + break; + case SetupShader: { + SkiaShader* shader = getShader(); + DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], shader); + renderer.setupShader(shader); + } + break; + case ResetColorFilter: { + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); + renderer.resetColorFilter(); + } + break; + case SetupColorFilter: { + SkiaColorFilter *colorFilter = getColorFilter(); + DISPLAY_LIST_LOGD("%s%s %p", (char*) indent, OP_NAMES[op], colorFilter); + renderer.setupColorFilter(colorFilter); + } + break; + case ResetShadow: { + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); + renderer.resetShadow(); + } + break; + case SetupShadow: { + float radius = getFloat(); + float dx = getFloat(); + float dy = getFloat(); + int color = getInt(); + DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, 0x%x", (char*) indent, OP_NAMES[op], + radius, dx, dy, color); + renderer.setupShadow(radius, dx, dy, color); + } + break; + default: + DISPLAY_LIST_LOGD("Display List error: op not handled: %s%s", + (char*) indent, OP_NAMES[op]); + break; + } + } + + DISPLAY_LIST_LOGD("%sDone, returning %d", (char*) indent + 2, needsInvalidate); + return needsInvalidate; +} + +/////////////////////////////////////////////////////////////////////////////// +// Base structure +/////////////////////////////////////////////////////////////////////////////// + +DisplayListRenderer::DisplayListRenderer(): mWriter(MIN_WRITER_SIZE) { + mDisplayList = NULL; +} + +DisplayListRenderer::~DisplayListRenderer() { + reset(); +} + +void DisplayListRenderer::reset() { + mWriter.reset(); + + Caches& caches = Caches::getInstance(); + for (size_t i = 0; i < mBitmapResources.size(); i++) { + SkBitmap* resource = mBitmapResources.itemAt(i); + caches.resourceCache.decrementRefcount(resource); + } + mBitmapResources.clear(); + + for (size_t i = 0; i < mOriginalPaths.size(); i++) { + SkPath* resource = mOriginalPaths.itemAt(i); + caches.resourceCache.decrementRefcount(resource); + } + mOriginalPaths.clear(); + + for (size_t i = 0; i < mShaders.size(); i++) { + caches.resourceCache.decrementRefcount(mShaders.itemAt(i)); + } + mShaders.clear(); + mShaderMap.clear(); + + mPaints.clear(); + mPaintMap.clear(); + mPaths.clear(); + mPathMap.clear(); + mMatrices.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Operations +/////////////////////////////////////////////////////////////////////////////// + +DisplayList* DisplayListRenderer::getDisplayList() { + if (mDisplayList == NULL) { + mDisplayList = new DisplayList(*this); + } else { + mDisplayList->initFromDisplayListRenderer(*this, true); + } + return mDisplayList; +} + +void DisplayListRenderer::setViewport(int width, int height) { + mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + + mWidth = width; + mHeight = height; +} + +void DisplayListRenderer::prepareDirty(float left, float top, + float right, float bottom, bool opaque) { + mSnapshot = new Snapshot(mFirstSnapshot, + SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + mSaveCount = 1; + mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight); + mRestoreSaveCount = -1; +} + +void DisplayListRenderer::finish() { + insertRestoreToCount(); + OpenGLRenderer::finish(); +} + +void DisplayListRenderer::interrupt() { +} + +void DisplayListRenderer::resume() { +} + +bool DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) { + // Ignore dirty during recording, it matters only when we replay + addOp(DisplayList::DrawGLFunction); + addInt((int) functor); + return false; // No invalidate needed at record-time +} + +int DisplayListRenderer::save(int flags) { + addOp(DisplayList::Save); + addInt(flags); + return OpenGLRenderer::save(flags); +} + +void DisplayListRenderer::restore() { + addOp(DisplayList::Restore); + OpenGLRenderer::restore(); +} + +void DisplayListRenderer::restoreToCount(int saveCount) { + mRestoreSaveCount = saveCount; + OpenGLRenderer::restoreToCount(saveCount); +} + +int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom, + SkPaint* p, int flags) { + addOp(DisplayList::SaveLayer); + addBounds(left, top, right, bottom); + addPaint(p); + addInt(flags); + return OpenGLRenderer::save(flags); +} + +int DisplayListRenderer::saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int flags) { + addOp(DisplayList::SaveLayerAlpha); + addBounds(left, top, right, bottom); + addInt(alpha); + addInt(flags); + return OpenGLRenderer::save(flags); +} + +void DisplayListRenderer::translate(float dx, float dy) { + addOp(DisplayList::Translate); + addPoint(dx, dy); + OpenGLRenderer::translate(dx, dy); +} + +void DisplayListRenderer::rotate(float degrees) { + addOp(DisplayList::Rotate); + addFloat(degrees); + OpenGLRenderer::rotate(degrees); +} + +void DisplayListRenderer::scale(float sx, float sy) { + addOp(DisplayList::Scale); + addPoint(sx, sy); + OpenGLRenderer::scale(sx, sy); +} + +void DisplayListRenderer::skew(float sx, float sy) { + addOp(DisplayList::Skew); + addPoint(sx, sy); + OpenGLRenderer::skew(sx, sy); +} + +void DisplayListRenderer::setMatrix(SkMatrix* matrix) { + addOp(DisplayList::SetMatrix); + addMatrix(matrix); + OpenGLRenderer::setMatrix(matrix); +} + +void DisplayListRenderer::concatMatrix(SkMatrix* matrix) { + addOp(DisplayList::ConcatMatrix); + addMatrix(matrix); + OpenGLRenderer::concatMatrix(matrix); +} + +bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom, + SkRegion::Op op) { + addOp(DisplayList::ClipRect); + addBounds(left, top, right, bottom); + addInt(op); + return OpenGLRenderer::clipRect(left, top, right, bottom, op); +} + +bool DisplayListRenderer::drawDisplayList(DisplayList* displayList, + uint32_t width, uint32_t height, Rect& dirty, uint32_t level) { + // dirty is an out parameter and should not be recorded, + // it matters only when replaying the display list + addOp(DisplayList::DrawDisplayList); + addDisplayList(displayList); + addSize(width, height); + return false; +} + +void DisplayListRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { + addOp(DisplayList::DrawLayer); + addInt((int) layer); + addPoint(x, y); + addPaint(paint); +} + +void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, + SkPaint* paint) { + addOp(DisplayList::DrawBitmap); + addBitmap(bitmap); + addPoint(left, top); + addPaint(paint); +} + +void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, + SkPaint* paint) { + addOp(DisplayList::DrawBitmapMatrix); + addBitmap(bitmap); + addMatrix(matrix); + addPaint(paint); +} + +void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, SkPaint* paint) { + addOp(DisplayList::DrawBitmapRect); + addBitmap(bitmap); + addBounds(srcLeft, srcTop, srcRight, srcBottom); + addBounds(dstLeft, dstTop, dstRight, dstBottom); + addPaint(paint); +} + +void DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, + float* vertices, int* colors, SkPaint* paint) { + addOp(DisplayList::DrawBitmapMesh); + addBitmap(bitmap); + addInt(meshWidth); + addInt(meshHeight); + addFloats(vertices, (meshWidth + 1) * (meshHeight + 1) * 2); + if (colors) { + addInt(1); + addInts(colors, (meshWidth + 1) * (meshHeight + 1)); + } else { + addInt(0); + } + addPaint(paint); +} + +void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, + const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + float left, float top, float right, float bottom, SkPaint* paint) { + addOp(DisplayList::DrawPatch); + addBitmap(bitmap); + addInts(xDivs, width); + addInts(yDivs, height); + addUInts(colors, numColors); + addBounds(left, top, right, bottom); + addPaint(paint); +} + +void DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) { + addOp(DisplayList::DrawColor); + addInt(color); + addInt(mode); +} + +void DisplayListRenderer::drawRect(float left, float top, float right, float bottom, + SkPaint* paint) { + addOp(DisplayList::DrawRect); + addBounds(left, top, right, bottom); + addPaint(paint); +} + +void DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, SkPaint* paint) { + addOp(DisplayList::DrawRoundRect); + addBounds(left, top, right, bottom); + addPoint(rx, ry); + addPaint(paint); +} + +void DisplayListRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) { + addOp(DisplayList::DrawCircle); + addPoint(x, y); + addFloat(radius); + addPaint(paint); +} + +void DisplayListRenderer::drawOval(float left, float top, float right, float bottom, + SkPaint* paint) { + addOp(DisplayList::DrawOval); + addBounds(left, top, right, bottom); + addPaint(paint); +} + +void DisplayListRenderer::drawArc(float left, float top, float right, float bottom, + float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { + addOp(DisplayList::DrawArc); + addBounds(left, top, right, bottom); + addPoint(startAngle, sweepAngle); + addInt(useCenter ? 1 : 0); + addPaint(paint); +} + +void DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) { + addOp(DisplayList::DrawPath); + addPath(path); + addPaint(paint); +} + +void DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) { + addOp(DisplayList::DrawLines); + addFloats(points, count); + addPaint(paint); +} + +void DisplayListRenderer::drawText(const char* text, int bytesCount, int count, + float x, float y, SkPaint* paint) { + addOp(DisplayList::DrawText); + addText(text, bytesCount); + addInt(count); + addPoint(x, y); + addPaint(paint); +} + +void DisplayListRenderer::resetShader() { + addOp(DisplayList::ResetShader); +} + +void DisplayListRenderer::setupShader(SkiaShader* shader) { + addOp(DisplayList::SetupShader); + addShader(shader); +} + +void DisplayListRenderer::resetColorFilter() { + addOp(DisplayList::ResetColorFilter); +} + +void DisplayListRenderer::setupColorFilter(SkiaColorFilter* filter) { + addOp(DisplayList::SetupColorFilter); + addColorFilter(filter); +} + +void DisplayListRenderer::resetShadow() { + addOp(DisplayList::ResetShadow); +} + +void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) { + addOp(DisplayList::SetupShadow); + addFloat(radius); + addPoint(dx, dy); + addInt(color); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h new file mode 100644 index 000000000000..6fc315c0c1d6 --- /dev/null +++ b/libs/hwui/DisplayListRenderer.h @@ -0,0 +1,494 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_DISPLAY_LIST_RENDERER_H +#define ANDROID_HWUI_DISPLAY_LIST_RENDERER_H + +#include <SkChunkAlloc.h> +#include <SkFlattenable.h> +#include <SkMatrix.h> +#include <SkPaint.h> +#include <SkPath.h> +#include <SkRefCnt.h> +#include <SkTDArray.h> +#include <SkTSearch.h> + +#include "OpenGLRenderer.h" +#include "utils/Functor.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#define MIN_WRITER_SIZE 16384 + +// Debug +#if DEBUG_DISPLAY_LIST + #define DISPLAY_LIST_LOGD(...) LOGD(__VA_ARGS__) +#else + #define DISPLAY_LIST_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Display list +/////////////////////////////////////////////////////////////////////////////// + +class DisplayListRenderer; + +/** + * Replays recorded drawing commands. + */ +class DisplayList { +public: + DisplayList(const DisplayListRenderer& recorder); + ~DisplayList(); + + // IMPORTANT: Update the intialization of OP_NAMES in the .cpp file + // when modifying this file + enum Op { + Save = 0, + Restore, + RestoreToCount, + SaveLayer, + SaveLayerAlpha, + Translate, + Rotate, + Scale, + Skew, + SetMatrix, + ConcatMatrix, + ClipRect, + DrawDisplayList, + DrawLayer, + DrawBitmap, + DrawBitmapMatrix, + DrawBitmapRect, + DrawBitmapMesh, + DrawPatch, + DrawColor, + DrawRect, + DrawRoundRect, + DrawCircle, + DrawOval, + DrawArc, + DrawPath, + DrawLines, + DrawText, + ResetShader, + SetupShader, + ResetColorFilter, + SetupColorFilter, + ResetShadow, + SetupShadow, + DrawGLFunction, + }; + + static const char* OP_NAMES[]; + + void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false); + + bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0); + +private: + void init(); + + void clearResources(); + + class TextContainer { + public: + size_t length() const { + return mByteLength; + } + + const char* text() const { + return (const char*) mText; + } + + size_t mByteLength; + const char* mText; + }; + + SkBitmap* getBitmap() { + return (SkBitmap*) getInt(); + } + + SkiaShader* getShader() { + return (SkiaShader*) getInt(); + } + + SkiaColorFilter* getColorFilter() { + return (SkiaColorFilter*) getInt(); + } + + inline int getIndex() { + return mReader.readInt(); + } + + inline int getInt() { + return mReader.readInt(); + } + + inline uint32_t getUInt() { + return mReader.readU32(); + } + + SkMatrix* getMatrix() { + return (SkMatrix*) getInt(); + } + + SkPath* getPath() { + return (SkPath*) getInt(); + } + + SkPaint* getPaint() { + return (SkPaint*) getInt(); + } + + DisplayList* getDisplayList() { + return (DisplayList*) getInt(); + } + + inline float getFloat() { + return mReader.readScalar(); + } + + int32_t* getInts(uint32_t& count) { + count = getInt(); + return (int32_t*) mReader.skip(count * sizeof(int32_t)); + } + + uint32_t* getUInts(int8_t& count) { + count = getInt(); + return (uint32_t*) mReader.skip(count * sizeof(uint32_t)); + } + + float* getFloats(int& count) { + count = getInt(); + return (float*) mReader.skip(count * sizeof(float)); + } + + void getText(TextContainer* text) { + size_t length = text->mByteLength = getInt(); + text->mText = (const char*) mReader.skip(length); + } + + Vector<SkBitmap*> mBitmapResources; + Vector<SkiaColorFilter*> mFilterResources; + + Vector<SkPaint*> mPaints; + Vector<SkPath*> mPaths; + Vector<SkPath*> mOriginalPaths; + Vector<SkMatrix*> mMatrices; + Vector<SkiaShader*> mShaders; + + mutable SkFlattenableReadBuffer mReader; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Renderer +/////////////////////////////////////////////////////////////////////////////// + +/** + * Records drawing commands in a display list for latter playback. + */ +class DisplayListRenderer: public OpenGLRenderer { +public: + DisplayListRenderer(); + ~DisplayListRenderer(); + + DisplayList* getDisplayList(); + + void setViewport(int width, int height); + void prepareDirty(float left, float top, float right, float bottom, bool opaque); + void finish(); + + bool callDrawGLFunction(Functor *functor, Rect& dirty); + + void interrupt(); + void resume(); + + int save(int flags); + void restore(); + void restoreToCount(int saveCount); + + int saveLayer(float left, float top, float right, float bottom, + SkPaint* p, int flags); + int saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int flags); + + void translate(float dx, float dy); + void rotate(float degrees); + void scale(float sx, float sy); + void skew(float sx, float sy); + + void setMatrix(SkMatrix* matrix); + void concatMatrix(SkMatrix* matrix); + + bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); + + bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, + Rect& dirty, uint32_t level = 0); + void drawLayer(Layer* layer, float x, float y, SkPaint* paint); + void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint); + void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint); + void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, SkPaint* paint); + void drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, + float* vertices, int* colors, SkPaint* paint); + void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, + const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + float left, float top, float right, float bottom, SkPaint* paint); + void drawColor(int color, SkXfermode::Mode mode); + void drawRect(float left, float top, float right, float bottom, SkPaint* paint); + void drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, SkPaint* paint); + void drawCircle(float x, float y, float radius, SkPaint* paint); + void drawOval(float left, float top, float right, float bottom, SkPaint* paint); + void drawArc(float left, float top, float right, float bottom, + float startAngle, float sweepAngle, bool useCenter, SkPaint* paint); + void drawPath(SkPath* path, SkPaint* paint); + void drawLines(float* points, int count, SkPaint* paint); + void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint); + + void resetShader(); + void setupShader(SkiaShader* shader); + + void resetColorFilter(); + void setupColorFilter(SkiaColorFilter* filter); + + void resetShadow(); + void setupShadow(float radius, float dx, float dy, int color); + + void reset(); + + const SkWriter32& writeStream() const { + return mWriter; + } + + const Vector<SkBitmap*>& getBitmapResources() const { + return mBitmapResources; + } + + const Vector<SkiaShader*>& getShaders() const { + return mShaders; + } + + const Vector<SkPaint*>& getPaints() const { + return mPaints; + } + + const Vector<SkPath*>& getPaths() const { + return mPaths; + } + + const Vector<SkPath*>& getOriginalPaths() const { + return mOriginalPaths; + } + + const Vector<SkMatrix*>& getMatrices() const { + return mMatrices; + } + + const Vector<SkiaColorFilter*>& getFilterResources() const { + return mFilterResources; + } + +private: + void insertRestoreToCount() { + if (mRestoreSaveCount >= 0) { + mWriter.writeInt(DisplayList::RestoreToCount); + addInt(mRestoreSaveCount); + mRestoreSaveCount = -1; + } + } + + inline void addOp(DisplayList::Op drawOp) { + insertRestoreToCount(); + mWriter.writeInt(drawOp); + } + + inline void addInt(int value) { + mWriter.writeInt(value); + } + + inline void addSize(uint32_t w, uint32_t h) { + mWriter.writeInt(w); + mWriter.writeInt(h); + } + + void addInts(const int32_t* values, uint32_t count) { + mWriter.writeInt(count); + for (uint32_t i = 0; i < count; i++) { + mWriter.writeInt(values[i]); + } + } + + void addUInts(const uint32_t* values, int8_t count) { + mWriter.writeInt(count); + for (int8_t i = 0; i < count; i++) { + mWriter.writeInt(values[i]); + } + } + + inline void addFloat(float value) { + mWriter.writeScalar(value); + } + + void addFloats(const float* values, int count) { + mWriter.writeInt(count); + for (int i = 0; i < count; i++) { + mWriter.writeScalar(values[i]); + } + } + + inline void addPoint(float x, float y) { + mWriter.writeScalar(x); + mWriter.writeScalar(y); + } + + inline void addBounds(float left, float top, float right, float bottom) { + mWriter.writeScalar(left); + mWriter.writeScalar(top); + mWriter.writeScalar(right); + mWriter.writeScalar(bottom); + } + + inline void addText(const void* text, size_t byteLength) { + mWriter.writeInt(byteLength); + mWriter.writePad(text, byteLength); + } + + inline void addPath(SkPath* path) { + if (!path) { + addInt((int) NULL); + return; + } + + SkPath* pathCopy = mPathMap.valueFor(path); + if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) { + if (pathCopy == NULL) { + pathCopy = path; + mOriginalPaths.add(path); + Caches& caches = Caches::getInstance(); + caches.resourceCache.incrementRefcount(path); + } else { + pathCopy = new SkPath(*path); + mPaths.add(pathCopy); + } + mPathMap.add(path, pathCopy); + } + + addInt((int) pathCopy); + } + + inline void addPaint(SkPaint* paint) { + if (!paint) { + addInt((int) NULL); + return; + } + + SkPaint* paintCopy = mPaintMap.valueFor(paint); + if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) { + paintCopy = new SkPaint(*paint); + mPaintMap.add(paint, paintCopy); + mPaints.add(paintCopy); + } + + addInt((int) paintCopy); + } + + inline void addDisplayList(DisplayList* displayList) { + // TODO: To be safe, the display list should be ref-counted in the + // resources cache, but we rely on the caller (UI toolkit) to + // do the right thing for now + addInt((int) displayList); + } + + inline void addMatrix(SkMatrix* matrix) { + // Copying the matrix is cheap and prevents against the user changing the original + // matrix before the operation that uses it + addInt((int) new SkMatrix(*matrix)); + } + + inline void addBitmap(SkBitmap* bitmap) { + // Note that this assumes the bitmap is immutable. There are cases this won't handle + // correctly, such as creating the bitmap from scratch, drawing with it, changing its + // contents, and drawing again. The only fix would be to always copy it the first time, + // which doesn't seem worth the extra cycles for this unlikely case. + addInt((int) bitmap); + mBitmapResources.add(bitmap); + Caches& caches = Caches::getInstance(); + caches.resourceCache.incrementRefcount(bitmap); + } + + inline void addShader(SkiaShader* shader) { + if (!shader) { + addInt((int) NULL); + return; + } + + SkiaShader* shaderCopy = mShaderMap.valueFor(shader); + // TODO: We also need to handle generation ID changes in compose shaders + if (shaderCopy == NULL || shaderCopy->getGenerationId() != shader->getGenerationId()) { + shaderCopy = shader->copy(); + mShaderMap.add(shader, shaderCopy); + mShaders.add(shaderCopy); + Caches::getInstance().resourceCache.incrementRefcount(shaderCopy); + } + + addInt((int) shaderCopy); + } + + inline void addColorFilter(SkiaColorFilter* colorFilter) { + addInt((int) colorFilter); + mFilterResources.add(colorFilter); + Caches& caches = Caches::getInstance(); + caches.resourceCache.incrementRefcount(colorFilter); + } + + Vector<SkBitmap*> mBitmapResources; + Vector<SkiaColorFilter*> mFilterResources; + + Vector<SkPaint*> mPaints; + DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap; + + Vector<SkPath*> mOriginalPaths; + Vector<SkPath*> mPaths; + DefaultKeyedVector<SkPath*, SkPath*> mPathMap; + + Vector<SkiaShader*> mShaders; + DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap; + + Vector<SkMatrix*> mMatrices; + + SkWriter32 mWriter; + + DisplayList *mDisplayList; + + int mRestoreSaveCount; + + friend class DisplayList; + +}; // class DisplayListRenderer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_DISPLAY_LIST_RENDERER_H diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h new file mode 100644 index 000000000000..eceb5c1cb0df --- /dev/null +++ b/libs/hwui/Extensions.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_EXTENSIONS_H +#define ANDROID_HWUI_EXTENSIONS_H + +#include <utils/SortedVector.h> +#include <utils/String8.h> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#define DEBUG_EXTENSIONS 0 + +// Debug +#if DEBUG_EXTENSIONS + #define EXT_LOGD(...) LOGD(__VA_ARGS__) +#else + #define EXT_LOGD(...) +#endif + +class Extensions { +public: + Extensions() { + const char* buffer = (const char*) glGetString(GL_EXTENSIONS); + const char* current = buffer; + const char* head = current; + EXT_LOGD("Available GL extensions:"); + do { + head = strchr(current, ' '); + String8 s(current, head ? head - current : strlen(current)); + if (s.length()) { + mExtensionList.add(s); + EXT_LOGD(" %s", s.string()); + } + current = head + 1; + } while (head); + + mHasNPot = hasExtension("GL_OES_texture_npot"); + mHasDrawPath = hasExtension("GL_NV_draw_path"); + mHasCoverageSample = hasExtension("GL_NV_coverage_sample"); + mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch"); + + mExtensions = buffer; + } + + inline bool hasNPot() const { return mHasNPot; } + inline bool hasDrawPath() const { return mHasDrawPath; } + inline bool hasCoverageSample() const { return mHasCoverageSample; } + inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; } + + bool hasExtension(const char* extension) const { + const String8 s(extension); + return mExtensionList.indexOf(s) >= 0; + } + + void dump() { + LOGD("Supported extensions:\n%s", mExtensions); + } + +private: + SortedVector<String8> mExtensionList; + + const char* mExtensions; + + bool mHasNPot; + bool mHasDrawPath; + bool mHasCoverageSample; + bool mHasFramebufferFetch; +}; // class Extensions + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_EXTENSIONS_H diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp new file mode 100644 index 000000000000..beef7bea258d --- /dev/null +++ b/libs/hwui/FboCache.cpp @@ -0,0 +1,92 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <stdlib.h> + +#include "Debug.h" +#include "FboCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +FboCache::FboCache(): mMaxSize(DEFAULT_FBO_CACHE_SIZE) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_FBO_CACHE_SIZE, property, NULL) > 0) { + INIT_LOGD(" Setting fbo cache size to %s", property); + mMaxSize = atoi(property); + } else { + INIT_LOGD(" Using default fbo cache size of %d", DEFAULT_FBO_CACHE_SIZE); + } +} + +FboCache::~FboCache() { + clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t FboCache::getSize() { + return mCache.size(); +} + +uint32_t FboCache::getMaxSize() { + return mMaxSize; +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void FboCache::clear() { + for (size_t i = 0; i < mCache.size(); i++) { + const GLuint fbo = mCache.itemAt(i); + glDeleteFramebuffers(1, &fbo); + } + mCache.clear(); +} + +GLuint FboCache::get() { + GLuint fbo; + if (mCache.size() > 0) { + fbo = mCache.itemAt(mCache.size() - 1); + mCache.removeAt(mCache.size() - 1); + } else { + glGenFramebuffers(1, &fbo); + } + return fbo; +} + +bool FboCache::put(GLuint fbo) { + if (mCache.size() < mMaxSize) { + mCache.add(fbo); + return true; + } + + glDeleteFramebuffers(1, &fbo); + return false; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/FboCache.h b/libs/hwui/FboCache.h new file mode 100644 index 000000000000..ad6cc3ea96c9 --- /dev/null +++ b/libs/hwui/FboCache.h @@ -0,0 +1,79 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_FBO_CACHE_H +#define ANDROID_HWUI_FBO_CACHE_H + +#include <GLES2/gl2.h> + +#include <utils/SortedVector.h> + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Cache +/////////////////////////////////////////////////////////////////////////////// + +class FboCache { +public: + FboCache(); + ~FboCache(); + + /** + * Returns an FBO from the cache. If no FBO is available, a new one + * is created. If creating a new FBO fails, 0 is returned. + * + * When an FBO is obtained from the cache, it is removed and the + * total number of FBOs available in the cache decreases. + * + * @return The name of the FBO, or 0 if no FBO can be obtained. + */ + GLuint get(); + + /** + * Adds the specified FBO to the cache. + * + * @param fbo The FBO to add to the cache. + * + * @return True if the FBO was added, false otherwise. + */ + bool put(GLuint fbo); + + /** + * Clears the cache. This causes all FBOs to be deleted. + */ + void clear(); + + /** + * Returns the current size of the cache. + */ + uint32_t getSize(); + + /** + * Returns the maximum number of FBOs that the cache can hold. + */ + uint32_t getMaxSize(); + +private: + SortedVector<GLuint> mCache; + uint32_t mMaxSize; +}; // class FboCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_FBO_CACHE_H diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp new file mode 100644 index 000000000000..aa9b40e041cb --- /dev/null +++ b/libs/hwui/FontRenderer.cpp @@ -0,0 +1,878 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <SkUtils.h> + +#include <cutils/properties.h> + +#include <utils/Log.h> + +#include "Debug.h" +#include "FontRenderer.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#define DEFAULT_TEXT_CACHE_WIDTH 1024 +#define DEFAULT_TEXT_CACHE_HEIGHT 256 + +/////////////////////////////////////////////////////////////////////////////// +// Font +/////////////////////////////////////////////////////////////////////////////// + +Font::Font(FontRenderer* state, uint32_t fontId, float fontSize, + int flags, uint32_t italicStyle, uint32_t scaleX) : + mState(state), mFontId(fontId), mFontSize(fontSize), + mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX) { +} + + +Font::~Font() { + for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) { + if (mState->mActiveFonts[ct] == this) { + mState->mActiveFonts.removeAt(ct); + break; + } + } + + for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { + CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i); + delete glyph; + } +} + +void Font::invalidateTextureCache() { + for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { + mCachedGlyphs.valueAt(i)->mIsValid = false; + } +} + +void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) { + int nPenX = x + glyph->mBitmapLeft; + int nPenY = y + glyph->mBitmapTop; + + int width = (int) glyph->mBitmapWidth; + int height = (int) glyph->mBitmapHeight; + + if (bounds->bottom > nPenY) { + bounds->bottom = nPenY; + } + if (bounds->left > nPenX) { + bounds->left = nPenX; + } + if (bounds->right < nPenX + width) { + bounds->right = nPenX + width; + } + if (bounds->top < nPenY + height) { + bounds->top = nPenY + height; + } +} + +void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) { + int nPenX = x + glyph->mBitmapLeft; + int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; + + float u1 = glyph->mBitmapMinU; + float u2 = glyph->mBitmapMaxU; + float v1 = glyph->mBitmapMinV; + float v2 = glyph->mBitmapMaxV; + + 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); +} + +void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, + uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) { + 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(); + + 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 (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) { + LOGE("Skipping invalid index"); + continue; + } + uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX]; + bitmap[bY * bitmapW + bX] = tempCol; + } + } + +} + +Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) { + CachedGlyphInfo* cachedGlyph = NULL; + ssize_t index = mCachedGlyphs.indexOfKey(utfChar); + if (index >= 0) { + cachedGlyph = mCachedGlyphs.valueAt(index); + } else { + cachedGlyph = cacheGlyph(paint, utfChar); + } + + // Is the glyph still in texture cache? + if (!cachedGlyph->mIsValid) { + const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar); + updateGlyphCache(paint, skiaGlyph, cachedGlyph); + } + + return cachedGlyph; +} + +void Font::renderUTF(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) { + renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap, + bitmapW, bitmapH, NULL); + } else { + renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL); + } + +} + +void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, + int numGlyphs, Rect *bounds) { + if (bounds == NULL) { + LOGE("No return rectangle provided to measure text"); + return; + } + bounds->set(1e6, -1e6, -1e6, 1e6); + renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds); +} + +#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) + +void Font::renderUTF(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) { + if (numGlyphs == 0 || text == NULL || len == 0) { + return; + } + + SkFixed penX = SkIntToFixed(x); + int penY = y; + int glyphsLeft = 1; + if (numGlyphs > 0) { + glyphsLeft = numGlyphs; + } + + SkFixed prevRsbDelta = 0; + penX += SK_Fixed1 / 2; + + text += start; + + while (glyphsLeft > 0) { + int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text); + + // Reached the end of the string + if (utfChar < 0) { + break; + } + + CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar); + penX += 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) { + switch(mode) { + case FRAMEBUFFER: + drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY); + break; + case BITMAP: + drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH); + break; + case MEASURE: + measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds); + break; + } + } + + penX += cachedGlyph->mAdvanceX; + + // If we were given a specific number of glyphs, decrement + if (numGlyphs > 0) { + glyphsLeft--; + } + } +} + +void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) { + glyph->mAdvanceX = skiaGlyph.fAdvanceX; + glyph->mAdvanceY = skiaGlyph.fAdvanceY; + glyph->mBitmapLeft = skiaGlyph.fLeft; + glyph->mBitmapTop = skiaGlyph.fTop; + glyph->mLsbDelta = skiaGlyph.fLsbDelta; + glyph->mRsbDelta = skiaGlyph.fRsbDelta; + + uint32_t startX = 0; + uint32_t startY = 0; + + // Get the bitmap for the glyph + paint->findImage(skiaGlyph); + glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY); + + if (!glyph->mIsValid) { + return; + } + + uint32_t endX = startX + skiaGlyph.fWidth; + uint32_t endY = startY + skiaGlyph.fHeight; + + glyph->mStartX = startX; + glyph->mStartY = startY; + glyph->mBitmapWidth = skiaGlyph.fWidth; + glyph->mBitmapHeight = skiaGlyph.fHeight; + + uint32_t cacheWidth = mState->getCacheWidth(); + uint32_t cacheHeight = mState->getCacheHeight(); + + glyph->mBitmapMinU = (float) startX / (float) cacheWidth; + glyph->mBitmapMinV = (float) startY / (float) cacheHeight; + glyph->mBitmapMaxU = (float) endX / (float) cacheWidth; + glyph->mBitmapMaxV = (float) endY / (float) cacheHeight; + + mState->mUploadTexture = true; +} + +Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) { + CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); + mCachedGlyphs.add(glyph, newGlyph); + + const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph); + newGlyph->mGlyphIndex = skiaGlyph.fID; + newGlyph->mIsValid = false; + + updateGlyphCache(paint, skiaGlyph, newGlyph); + + return newGlyph; +} + +Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize, + int flags, uint32_t italicStyle, uint32_t scaleX) { + Vector<Font*> &activeFonts = state->mActiveFonts; + + for (uint32_t i = 0; i < activeFonts.size(); i++) { + Font* font = activeFonts[i]; + if (font->mFontId == fontId && font->mFontSize == fontSize && + font->mFlags == flags && font->mItalicStyle == italicStyle && + font->mScaleX == scaleX) { + return font; + } + } + + Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, scaleX); + activeFonts.push(newFont); + return newFont; +} + +/////////////////////////////////////////////////////////////////////////////// +// FontRenderer +/////////////////////////////////////////////////////////////////////////////// + +static bool sLogFontRendererCreate = true; + +FontRenderer::FontRenderer() { + if (sLogFontRendererCreate) { + INIT_LOGD("Creating FontRenderer"); + } + + mGammaTable = NULL; + mInitialized = false; + mMaxNumberOfQuads = 1024; + mCurrentQuadIndex = 0; + mTextureId = 0; + + mTextMeshPtr = NULL; + mTextTexture = NULL; + + mIndexBufferID = 0; + mPositionAttrSlot = -1; + mTexcoordAttrSlot = -1; + + mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH; + mCacheHeight = 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); + } else { + if (sLogFontRendererCreate) { + INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth); + } + } + + if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) { + if (sLogFontRendererCreate) { + INIT_LOGD(" Setting text cache width to %s pixels", property); + } + mCacheHeight = atoi(property); + } else { + if (sLogFontRendererCreate) { + INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight); + } + } + + sLogFontRendererCreate = false; +} + +FontRenderer::~FontRenderer() { + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + delete mCacheLines[i]; + } + mCacheLines.clear(); + + if (mInitialized) { + delete[] mTextMeshPtr; + delete[] mTextTexture; + } + + if (mTextureId) { + glDeleteTextures(1, &mTextureId); + } + + Vector<Font*> fontsToDereference = mActiveFonts; + for (uint32_t i = 0; i < fontsToDereference.size(); i++) { + delete fontsToDereference[i]; + } +} + +void FontRenderer::flushAllAndInvalidate() { + if (mCurrentQuadIndex != 0) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } + for (uint32_t i = 0; i < mActiveFonts.size(); i++) { + mActiveFonts[i]->invalidateTextureCache(); + } + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + mCacheLines[i]->mCurrentCol = 0; + } +} + +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 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { + LOGE("Font size to large to fit in cache. width, height = %i, %i", + (int) glyph.fWidth, (int) glyph.fHeight); + return false; + } + + // Now copy the bitmap into the cache texture + uint32_t startX = 0; + uint32_t startY = 0; + + bool bitmapFit = false; + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); + if (bitmapFit) { + break; + } + } + + // If the new glyph didn't fit, flush the state so far and invalidate everything + if (!bitmapFit) { + flushAllAndInvalidate(); + + // Try to fit it again + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY); + if (bitmapFit) { + break; + } + } + + // if we still don't fit, something is wrong and we shouldn't draw + if (!bitmapFit) { + LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", + (int) glyph.fWidth, (int) glyph.fHeight); + return false; + } + } + + *retOriginX = startX; + *retOriginY = startY; + + uint32_t endX = startX + glyph.fWidth; + uint32_t endY = startY + glyph.fHeight; + + uint32_t cacheWidth = mCacheWidth; + + uint8_t* cacheBuffer = mTextTexture; + uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage; + unsigned int stride = glyph.rowBytes(); + + uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; + for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) { + for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) { + uint8_t tempCol = bitmapBuffer[bY * stride + bX]; + cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; + } + } + + return true; +} + +void FontRenderer::initTextTexture() { + mTextTexture = new uint8_t[mCacheWidth * mCacheHeight]; + memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t)); + + mUploadTexture = false; + + glGenTextures(1, &mTextureId); + glBindTexture(GL_TEXTURE_2D, mTextureId); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + // Initialize texture dimentions + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, 0); + + mLinearFiltering = false; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Split up our cache texture into lines of certain widths + int nextLine = 0; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0)); +} + +// 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); + uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes); + + // Four verts, two triangles , six indices per quad + for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) { + int i6 = i * 6; + int i4 = i * 4; + + indexBufferData[i6 + 0] = i4 + 0; + indexBufferData[i6 + 1] = i4 + 1; + indexBufferData[i6 + 2] = i4 + 2; + + indexBufferData[i6 + 3] = i4 + 0; + indexBufferData[i6 + 4] = i4 + 2; + indexBufferData[i6 + 5] = i4 + 3; + } + + glGenBuffers(1, &mIndexBufferID); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 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 uvSize = 2; + uint32_t vertsPerQuad = 4; + uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize; + mTextMeshPtr = new float[vertexBufferSize]; +} + +// We don't want to allocate anything unless we actually draw text +void FontRenderer::checkInit() { + if (mInitialized) { + return; + } + + initTextTexture(); + initVertexArrayBuffers(); + + // We store a string with letters in a rough frequency of occurrence + mLatinPrecache = String16("eisarntolcdugpmhbyfvkwzxjq "); + mLatinPrecache += String16("EISARNTOLCDUGPMHBYFVKWZXJQ"); + mLatinPrecache += String16(",.?!()-+@;:`'"); + mLatinPrecache += String16("0123456789"); + + mInitialized = true; +} + +void FontRenderer::checkTextureUpdate() { + if (!mUploadTexture) { + return; + } + + glBindTexture(GL_TEXTURE_2D, mTextureId); + + // 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) { + uint32_t xOffset = 0; + uint32_t yOffset = cl->mCurrentRow; + uint32_t width = mCacheWidth; + uint32_t height = cl->mMaxHeight; + void* textureData = mTextTexture + yOffset*width; + + glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height, + GL_ALPHA, GL_UNSIGNED_BYTE, textureData); + + cl->mDirty = false; + } + } + + mUploadTexture = false; +} + +void FontRenderer::issueDrawCommand() { + checkTextureUpdate(); + + float* vtx = mTextMeshPtr; + float* tex = vtx + 3; + + glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx); + glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex); + + 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) { + if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) { + return; + } + + const uint32_t vertsPerQuad = 4; + const uint32_t floatsPerVert = 5; + 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; + + mCurrentQuadIndex++; + + if (mBounds) { + mBounds->left = fmin(mBounds->left, x1); + mBounds->top = fmin(mBounds->top, y3); + mBounds->right = fmax(mBounds->right, x3); + mBounds->bottom = fmax(mBounds->bottom, y1); + } + + if (mCurrentQuadIndex == mMaxNumberOfQuads) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } +} + +uint32_t FontRenderer::getRemainingCacheCapacity() { + uint32_t remainingCapacity = 0; + float totalPixels = 0; + for(uint32_t i = 0; i < mCacheLines.size(); i ++) { + remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); + totalPixels += mCacheLines[i]->mMaxWidth; + } + remainingCapacity = (remainingCapacity * 100) / totalPixels; + return remainingCapacity; +} + +void FontRenderer::precacheLatin(SkPaint* paint) { + // Remaining capacity is measured in % + uint32_t remainingCapacity = getRemainingCacheCapacity(); + uint32_t precacheIdx = 0; + while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { + mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]); + remainingCapacity = getRemainingCacheCapacity(); + precacheIdx ++; + } +} + +void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { + uint32_t currentNumFonts = mActiveFonts.size(); + int flags = 0; + if (paint->isFakeBoldText()) { + flags |= Font::kFakeBold; + } + + const float skewX = paint->getTextSkewX(); + uint32_t italicStyle = *(uint32_t*) &skewX; + const float scaleXFloat = paint->getTextScaleX(); + uint32_t scaleX = *(uint32_t*) &scaleXFloat; + mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, scaleX); + + const float maxPrecacheFontSize = 40.0f; + bool isNewFont = currentNumFonts != mActiveFonts.size(); + + if (isNewFont && fontSize <= maxPrecacheFontSize) { + precacheLatin(paint); + } +} + +FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) { + checkInit(); + + if (!mCurrentFont) { + DropShadow image; + image.width = 0; + image.height = 0; + image.image = NULL; + image.penX = 0; + image.penY = 0; + return image; + } + + Rect bounds; + mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds); + uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; + uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; + uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; + for (uint32_t i = 0; i < paddedWidth * paddedHeight; i++) { + dataBuffer[i] = 0; + } + + int penX = radius - bounds.left; + int penY = radius - bounds.bottom; + + mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY, + dataBuffer, paddedWidth, paddedHeight); + blurImage(dataBuffer, paddedWidth, paddedHeight, radius); + + DropShadow image; + image.width = paddedWidth; + image.height = paddedHeight; + 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) { + checkInit(); + + if (!mCurrentFont) { + LOGE("No font set"); + return false; + } + + if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) { + LOGE("Font renderer unable to draw, attribute slots undefined"); + return false; + } + + mDrawn = false; + mBounds = bounds; + mClip = clip; + mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y); + mBounds = NULL; + + if (mCurrentQuadIndex != 0) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } + + return mDrawn; +} + +void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { + // Compute gaussian weights for the blur + // e is the euler's number + float e = 2.718281828459045f; + float pi = 3.1415926535897932f; + // g(x) = ( 1 / sqrt( 2 * pi ) * sigma) * e ^ ( -x^2 / 2 * sigma^2 ) + // x is of the form [-radius .. 0 .. radius] + // and sigma varies with radius. + // Based on some experimental radius values and sigma's + // we approximately fit sigma = f(radius) as + // sigma = radius * 0.3 + 0.6 + // The larger the radius gets, the more our gaussian blur + // will resemble a box blur since with large sigma + // the gaussian curve begins to lose its shape + float sigma = 0.3f * (float) radius + 0.6f; + + // Now compute the coefficints + // We will store some redundant values to save some math during + // the blur calculations + // precompute some values + float coeff1 = 1.0f / (sqrt( 2.0f * pi ) * sigma); + float coeff2 = - 1.0f / (2.0f * sigma * sigma); + + float normalizeFactor = 0.0f; + for (int32_t r = -radius; r <= radius; r ++) { + float floatR = (float) r; + weights[r + radius] = coeff1 * pow(e, floatR * floatR * coeff2); + normalizeFactor += weights[r + radius]; + } + + //Now we need to normalize the weights because all our coefficients need to add up to one + normalizeFactor = 1.0f / normalizeFactor; + for (int32_t r = -radius; r <= radius; r ++) { + weights[r + radius] *= normalizeFactor; + } +} + +void FontRenderer::horizontalBlur(float* weights, int32_t radius, + const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { + float blurredPixel = 0.0f; + float currentPixel = 0.0f; + + for (int32_t y = 0; y < height; y ++) { + + const uint8_t* input = source + y * width; + uint8_t* output = dest + y * width; + + for (int32_t x = 0; x < width; x ++) { + blurredPixel = 0.0f; + const float* gPtr = weights; + // Optimization for non-border pixels + if (x > radius && x < (width - radius)) { + const uint8_t *i = input + (x - radius); + for (int r = -radius; r <= radius; r ++) { + currentPixel = (float) (*i); + blurredPixel += currentPixel * gPtr[0]; + gPtr++; + i++; + } + } else { + for (int32_t r = -radius; r <= radius; r ++) { + // Stepping left and right away from the pixel + int validW = x + r; + if (validW < 0) { + validW = 0; + } + if (validW > width - 1) { + validW = width - 1; + } + + currentPixel = (float) input[validW]; + blurredPixel += currentPixel * gPtr[0]; + gPtr++; + } + } + *output = (uint8_t)blurredPixel; + output ++; + } + } +} + +void FontRenderer::verticalBlur(float* weights, int32_t radius, + const uint8_t* source, uint8_t* dest, int32_t width, int32_t height) { + float blurredPixel = 0.0f; + float currentPixel = 0.0f; + + for (int32_t y = 0; y < height; y ++) { + + uint8_t* output = dest + y * width; + + for (int32_t x = 0; x < width; x ++) { + blurredPixel = 0.0f; + const float* gPtr = weights; + const uint8_t* input = source + x; + // Optimization for non-border pixels + if (y > radius && y < (height - radius)) { + const uint8_t *i = input + ((y - radius) * width); + for (int32_t r = -radius; r <= radius; r ++) { + currentPixel = (float)(*i); + blurredPixel += currentPixel * gPtr[0]; + gPtr++; + i += width; + } + } else { + for (int32_t r = -radius; r <= radius; r ++) { + int validH = y + r; + // Clamp to zero and width + if (validH < 0) { + validH = 0; + } + if (validH > height - 1) { + validH = height - 1; + } + + const uint8_t *i = input + validH * width; + currentPixel = (float) (*i); + blurredPixel += currentPixel * gPtr[0]; + gPtr++; + } + } + *output = (uint8_t) blurredPixel; + output ++; + } + } +} + + +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; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h new file mode 100644 index 000000000000..f685d5f98ad2 --- /dev/null +++ b/libs/hwui/FontRenderer.h @@ -0,0 +1,295 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_FONT_RENDERER_H +#define ANDROID_HWUI_FONT_RENDERER_H + +#include <utils/String8.h> +#include <utils/String16.h> +#include <utils/Vector.h> +#include <utils/KeyedVector.h> + +#include <SkScalerContext.h> +#include <SkPaint.h> + +#include <GLES2/gl2.h> + +#include "Rect.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +class FontRenderer; + +/** + * Represents a font, defined by a Skia font id and a font size. A font is used + * to generate glyphs and cache them in the FontState. + */ +class Font { +public: + enum Style { + kFakeBold = 1 + }; + + ~Font(); + + /** + * Renders the specified string of text. + * If bitmap is specified, it will be used as the render target + */ + void renderUTF(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); + /** + * Creates a new font associated with the specified font state. + */ + static Font* create(FontRenderer* state, uint32_t fontId, float fontSize, + int flags, uint32_t italicStyle, uint32_t scaleX); + +protected: + friend class FontRenderer; + + enum RenderMode { + FRAMEBUFFER, + BITMAP, + MEASURE, + }; + + void renderUTF(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); + + void measureUTF(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); + + DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs; + + void invalidateTextureCache(); + + CachedGlyphInfo* cacheGlyph(SkPaint* paint, int32_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); + + CachedGlyphInfo* getCachedUTFChar(SkPaint* paint, int32_t utfChar); + + FontRenderer* mState; + uint32_t mFontId; + float mFontSize; + int mFlags; + uint32_t mItalicStyle; + uint32_t mScaleX; +}; + +class FontRenderer { +public: + FontRenderer(); + ~FontRenderer(); + + void init(); + void deinit(); + + 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); + bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, + uint32_t len, int numGlyphs, int x, int y, Rect* bounds); + + struct DropShadow { + DropShadow() { }; + + DropShadow(const DropShadow& dropShadow): + width(dropShadow.width), height(dropShadow.height), + image(dropShadow.image), penX(dropShadow.penX), + penY(dropShadow.penY) { + } + + uint32_t width; + uint32_t height; + uint8_t* image; + int32_t penX; + int32_t penY; + }; + + // After renderDropShadow returns, the called owns the memory in DropShadow.image + // and is responsible for releasing it when it's done with it + DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex, + uint32_t len, int numGlyphs, uint32_t radius); + + GLuint getTexture(bool linearFiltering = false) { + checkInit(); + if (linearFiltering != mLinearFiltering) { + mLinearFiltering = linearFiltering; + const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST; + + glBindTexture(GL_TEXTURE_2D, 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; + } + + uint32_t getCacheHeight() const { + return mCacheHeight; + } + +protected: + friend class Font; + + 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 cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY); + + void flushAllAndInvalidate(); + void initVertexArrayBuffers(); + + void checkInit(); + + 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); + + uint32_t mCacheWidth; + uint32_t mCacheHeight; + + Vector<CacheTextureLine*> mCacheLines; + uint32_t getRemainingCacheCapacity(); + + Font* mCurrentFont; + Vector<Font*> mActiveFonts; + + // Texture to cache glyph bitmaps + uint8_t* mTextTexture; + const uint8_t* getTextTextureData() const { + return mTextTexture; + } + GLuint mTextureId; + void checkTextureUpdate(); + bool mUploadTexture; + + // Pointer to vertex data to speed up frame to frame work + float *mTextMeshPtr; + uint32_t mCurrentQuadIndex; + uint32_t mMaxNumberOfQuads; + + uint32_t mIndexBufferID; + + int32_t mPositionAttrSlot; + int32_t mTexcoordAttrSlot; + + const Rect* mClip; + Rect* mBounds; + bool mDrawn; + + bool mInitialized; + + bool mLinearFiltering; + + void computeGaussianWeights(float* weights, int32_t radius); + void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest, + int32_t width, int32_t height); + void verticalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest, + int32_t width, int32_t height); + void blurImage(uint8_t* image, int32_t width, int32_t height, int32_t radius); +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_FONT_RENDERER_H diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp new file mode 100644 index 000000000000..e8362dcd45d2 --- /dev/null +++ b/libs/hwui/GammaFontRenderer.cpp @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "Debug.h" +#include "GammaFontRenderer.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +GammaFontRenderer::GammaFontRenderer() { + INIT_LOGD("Creating gamma font renderer"); + + // Get the renderer properties + char property[PROPERTY_VALUE_MAX]; + + // Get the gamma + float gamma = DEFAULT_TEXT_GAMMA; + if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) { + INIT_LOGD(" Setting text gamma to %s", property); + gamma = atof(property); + } else { + INIT_LOGD(" Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA); + } + + // Get the black gamma threshold + mBlackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD; + if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) { + INIT_LOGD(" Setting text black gamma threshold to %s", property); + mBlackThreshold = atoi(property); + } else { + INIT_LOGD(" Using default text black gamma threshold of %d", + DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD); + } + + // Get the white gamma threshold + mWhiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD; + if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) { + INIT_LOGD(" Setting text white gamma threshold to %s", property); + mWhiteThreshold = atoi(property); + } else { + INIT_LOGD(" Using default white black gamma threshold of %d", + DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD); + } + + // Compute the gamma tables + const float blackGamma = gamma; + const float whiteGamma = 1.0f / gamma; + + for (uint32_t i = 0; i <= 255; i++) { + mDefault[i] = i; + + const float v = i / 255.0f; + const float black = pow(v, blackGamma); + const float white = pow(v, whiteGamma); + + mBlackGamma[i] = uint8_t((float)::floor(black * 255.0f + 0.5f)); + mWhiteGamma[i] = uint8_t((float)::floor(white * 255.0f + 0.5f)); + } + + // Configure the font renderers + mDefaultRenderer.setGammaTable(&mDefault[0]); + mBlackGammaRenderer.setGammaTable(&mBlackGamma[0]); + mWhiteGammaRenderer.setGammaTable(&mWhiteGamma[0]); +} + +FontRenderer& GammaFontRenderer::getFontRenderer(const SkPaint* paint) { + if (paint->getShader() == NULL) { + uint32_t c = paint->getColor(); + const int r = (c >> 16) & 0xFF; + const int g = (c >> 8) & 0xFF; + const int b = (c ) & 0xFF; + const int luminance = (r * 2 + g * 5 + b) >> 3; + + if (luminance <= mBlackThreshold) { + return mBlackGammaRenderer; + } else if (luminance >= mWhiteThreshold) { + return mWhiteGammaRenderer; + } + } + return mDefaultRenderer; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h new file mode 100644 index 000000000000..96d960cf2296 --- /dev/null +++ b/libs/hwui/GammaFontRenderer.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_GAMMA_FONT_RENDERER_H +#define ANDROID_HWUI_GAMMA_FONT_RENDERER_H + +#include <SkPaint.h> + +#include "FontRenderer.h" + +namespace android { +namespace uirenderer { + +struct GammaFontRenderer { + GammaFontRenderer(); + + FontRenderer& getFontRenderer(const SkPaint* paint); + + uint32_t getFontRendererCount() const { + return 3; + } + + uint32_t getFontRendererSize(uint32_t fontRenderer) const { + switch (fontRenderer) { + case 0: + return mDefaultRenderer.getCacheHeight() * mDefaultRenderer.getCacheWidth(); + case 1: + return mBlackGammaRenderer.getCacheHeight() * mBlackGammaRenderer.getCacheWidth(); + case 2: + return mWhiteGammaRenderer.getCacheHeight() * mWhiteGammaRenderer.getCacheWidth(); + } + return 0; + } + +private: + FontRenderer mDefaultRenderer; + FontRenderer mBlackGammaRenderer; + FontRenderer mWhiteGammaRenderer; + + int mBlackThreshold; + int mWhiteThreshold; + + uint8_t mDefault[256]; + uint8_t mBlackGamma[256]; + uint8_t mWhiteGamma[256]; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_GAMMA_FONT_RENDERER_H diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp new file mode 100644 index 000000000000..4a40a63485ac --- /dev/null +++ b/libs/hwui/GradientCache.cpp @@ -0,0 +1,192 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <GLES2/gl2.h> + +#include <SkCanvas.h> +#include <SkGradientShader.h> + +#include <utils/threads.h> + +#include "Debug.h" +#include "GradientCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +GradientCache::GradientCache(): + mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) { + INIT_LOGD(" Setting gradient cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + INIT_LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE); + } + + mCache.setOnEntryRemovedListener(this); +} + +GradientCache::GradientCache(uint32_t maxByteSize): + mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize) { + mCache.setOnEntryRemovedListener(this); +} + +GradientCache::~GradientCache() { + mCache.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t GradientCache::getSize() { + return mSize; +} + +uint32_t GradientCache::getMaxSize() { + return mMaxSize; +} + +void GradientCache::setMaxSize(uint32_t maxSize) { + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void GradientCache::operator()(SkShader*& shader, Texture*& texture) { + // Already locked here + if (shader) { + const uint32_t size = texture->width * texture->height * 4; + mSize -= size; + } + + if (texture) { + glDeleteTextures(1, &texture->id); + delete texture; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +Texture* GradientCache::get(SkShader* shader) { + return mCache.get(shader); +} + +void GradientCache::remove(SkShader* shader) { + mCache.remove(shader); +} + +void GradientCache::removeDeferred(SkShader* shader) { + Mutex::Autolock _l(mLock); + mGarbage.push(shader); +} + +void GradientCache::clearGarbage() { + Mutex::Autolock _l(mLock); + size_t count = mGarbage.size(); + for (size_t i = 0; i < count; i++) { + mCache.remove(mGarbage.itemAt(i)); + } + mGarbage.clear(); +} + +void GradientCache::clear() { + mCache.clear(); +} + +Texture* GradientCache::addLinearGradient(SkShader* shader, uint32_t* colors, + float* positions, int count, SkShader::TileMode tileMode) { + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1024, 1); + bitmap.allocPixels(); + bitmap.eraseColor(0); + + SkCanvas canvas(bitmap); + + SkPoint points[2]; + points[0].set(0.0f, 0.0f); + points[1].set(bitmap.width(), 0.0f); + + SkShader* localShader = SkGradientShader::CreateLinear(points, + reinterpret_cast<const SkColor*>(colors), positions, count, tileMode); + + SkPaint p; + p.setStyle(SkPaint::kStrokeAndFill_Style); + p.setShader(localShader)->unref(); + + canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 1.0f, p); + + // Asume the cache is always big enough + const uint32_t size = bitmap.rowBytes() * bitmap.height(); + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + + Texture* texture = new Texture; + generateTexture(&bitmap, texture); + + mSize += size; + mCache.put(shader, texture); + + return texture; +} + +void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) { + SkAutoLockPixels autoLock(*bitmap); + if (!bitmap->readyToDraw()) { + LOGE("Cannot generate texture from shader"); + return; + } + + texture->generation = bitmap->getGenerationID(); + texture->width = bitmap->width(); + texture->height = bitmap->height(); + + glGenTextures(1, &texture->id); + + glBindTexture(GL_TEXTURE_2D, texture->id); + glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); + + texture->blend = !bitmap->isOpaque(); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h new file mode 100644 index 000000000000..30da46209800 --- /dev/null +++ b/libs/hwui/GradientCache.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_GRADIENT_CACHE_H +#define ANDROID_HWUI_GRADIENT_CACHE_H + +#include <SkShader.h> + +#include <utils/Vector.h> + +#include "Texture.h" +#include "utils/GenerationCache.h" + +namespace android { +namespace uirenderer { + +/** + * A simple LRU gradient cache. The cache has a maximum size expressed in bytes. + * Any texture added to the cache causing the cache to grow beyond the maximum + * allowed size will also cause the oldest texture to be kicked out. + */ +class GradientCache: public OnEntryRemoved<SkShader*, Texture*> { +public: + GradientCache(); + GradientCache(uint32_t maxByteSize); + ~GradientCache(); + + /** + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. + */ + void operator()(SkShader*& shader, Texture*& texture); + + /** + * Adds a new linear gradient to the cache. The generated texture is + * returned. + */ + Texture* addLinearGradient(SkShader* shader, uint32_t* colors, float* positions, + int count, SkShader::TileMode tileMode = SkShader::kClamp_TileMode); + /** + * Returns the texture associated with the specified shader. + */ + Texture* get(SkShader* shader); + /** + * Removes the texture associated with the specified shader. + * Upon remove the texture is freed. + */ + void remove(SkShader* shader); + /** + * Removes the texture associated with the specified shader. This is meant + * to be called from threads that are not the EGL context thread. + */ + void removeDeferred(SkShader* shader); + /** + * Process deferred removals. + */ + void clearGarbage(); + /** + * Clears the cache. This causes all textures to be deleted. + */ + void clear(); + + /** + * Sets the maximum size of the cache in bytes. + */ + void setMaxSize(uint32_t maxSize); + /** + * Returns the maximum size of the cache in bytes. + */ + uint32_t getMaxSize(); + /** + * Returns the current size of the cache in bytes. + */ + uint32_t getSize(); + +private: + void generateTexture(SkBitmap* bitmap, Texture* texture); + + GenerationCache<SkShader*, Texture*> mCache; + + uint32_t mSize; + uint32_t mMaxSize; + + Vector<SkShader*> mGarbage; + mutable Mutex mLock; +}; // class GradientCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_GRADIENT_CACHE_H diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h new file mode 100644 index 000000000000..bb2843790ed7 --- /dev/null +++ b/libs/hwui/Layer.h @@ -0,0 +1,123 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_LAYER_H +#define ANDROID_HWUI_LAYER_H + +#include <sys/types.h> + +#include <GLES2/gl2.h> + +#include <ui/Region.h> + +#include <SkXfermode.h> + +#include "Rect.h" +#include "SkiaColorFilter.h" +#include "Vertex.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Layers +/////////////////////////////////////////////////////////////////////////////// + +/** + * A layer has dimensions and is backed by an OpenGL texture or FBO. + */ +struct Layer { + Layer(const uint32_t layerWidth, const uint32_t layerHeight): + width(layerWidth), height(layerHeight) { + mesh = NULL; + meshIndices = NULL; + meshElementCount = 0; + } + + ~Layer() { + if (mesh) delete mesh; + if (meshIndices) delete meshIndices; + } + + /** + * Bounds of the layer. + */ + Rect layer; + /** + * Texture coordinates of the layer. + */ + Rect texCoords; + + /** + * Name of the FBO used to render the layer. If the name is 0 + * this layer is not backed by an FBO, but a simple texture. + */ + GLuint fbo; + + /** + * Opacity of the layer. + */ + int alpha; + /** + * Blending mode of the layer. + */ + SkXfermode::Mode mode; + /** + * Indicates whether this layer should be blended. + */ + bool blend; + + /** + * Indicates whether this layer has been used already. + */ + bool empty; + + /** + * Name of the texture used to render the layer. + */ + GLuint texture; + /** + * Width of the layer texture. + */ + uint32_t width; + /** + * Height of the layer texture. + */ + uint32_t height; + + /** + * Dirty region indicating what parts of the layer + * have been drawn. + */ + Region region; + + /** + * Color filter used to draw this layer. Optional. + */ + SkiaColorFilter* colorFilter; + + /** + * If the layer can be rendered as a mesh, this is non-null. + */ + TextureVertex* mesh; + uint16_t* meshIndices; + GLsizei meshElementCount; +}; // struct Layer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_LAYER_H diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp new file mode 100644 index 000000000000..a9710ad6adbf --- /dev/null +++ b/libs/hwui/LayerCache.cpp @@ -0,0 +1,185 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <GLES2/gl2.h> + +#include <utils/Log.h> + +#include "Debug.h" +#include "LayerCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +LayerCache::LayerCache(): mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) { + INIT_LOGD(" Setting layer cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + INIT_LOGD(" Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE); + } +} + +LayerCache::~LayerCache() { + clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t LayerCache::getSize() { + return mSize; +} + +uint32_t LayerCache::getMaxSize() { + return mMaxSize; +} + +void LayerCache::setMaxSize(uint32_t maxSize) { + clear(); + mMaxSize = maxSize; +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void LayerCache::deleteLayer(Layer* layer) { + if (layer) { + mSize -= layer->width * layer->height * 4; + glDeleteTextures(1, &layer->texture); + delete layer; + } +} + +void LayerCache::clear() { + size_t count = mCache.size(); + for (size_t i = 0; i < count; i++) { + deleteLayer(mCache.itemAt(i).mLayer); + } + mCache.clear(); +} + +Layer* LayerCache::get(const uint32_t width, const uint32_t height) { + Layer* layer = NULL; + + LayerEntry entry(width, height); + ssize_t index = mCache.indexOf(entry); + + if (index >= 0) { + entry = mCache.itemAt(index); + mCache.removeAt(index); + + layer = entry.mLayer; + mSize -= layer->width * layer->height * 4; + + LAYER_LOGD("Reusing layer %dx%d", layer->width, layer->height); + } else { + LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight); + + layer = new Layer(entry.mWidth, entry.mHeight); + layer->blend = true; + layer->empty = true; + layer->fbo = 0; + layer->colorFilter = NULL; + + glGenTextures(1, &layer->texture); + glBindTexture(GL_TEXTURE_2D, layer->texture); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + +#if DEBUG_LAYERS + size_t size = mCache.size(); + for (size_t i = 0; i < size; i++) { + const LayerEntry& entry = mCache.itemAt(i); + LAYER_LOGD(" Layer size %dx%d", entry.mWidth, entry.mHeight); + } +#endif + } + + return layer; +} + +bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t height) { + // TODO: We should be smarter and see if we have a texture of the appropriate + // size already in the cache, and reuse it instead of creating a new one + + LayerEntry entry(width, height); + if (entry.mWidth <= layer->width && entry.mHeight <= layer->height) { + return true; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, layer->texture); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, entry.mWidth, entry.mHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + if (glGetError() != GL_NO_ERROR) { + return false; + } + + layer->width = entry.mWidth; + layer->height = entry.mHeight; + + return true; +} + +bool LayerCache::put(Layer* layer) { + const uint32_t size = layer->width * layer->height * 4; + // Don't even try to cache a layer that's bigger than the cache + if (size < mMaxSize) { + // TODO: Use an LRU + while (mSize + size > mMaxSize) { + size_t position = 0; +#if LAYER_REMOVE_BIGGEST + position = mCache.size() - 1; +#endif + Layer* victim = mCache.itemAt(position).mLayer; + deleteLayer(victim); + mCache.removeAt(position); + + LAYER_LOGD(" Deleting layer %.2fx%.2f", victim->layer.getWidth(), + victim->layer.getHeight()); + } + + LayerEntry entry(layer); + + mCache.add(entry); + mSize += size; + + return true; + } + return false; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h new file mode 100644 index 000000000000..d2d5f3960568 --- /dev/null +++ b/libs/hwui/LayerCache.h @@ -0,0 +1,150 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_LAYER_CACHE_H +#define ANDROID_HWUI_LAYER_CACHE_H + +#include "Debug.h" +#include "Layer.h" +#include "utils/SortedList.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Indicates whether to remove the biggest layers first, or the smaller ones +#define LAYER_REMOVE_BIGGEST 0 +// Textures used by layers must have dimensions multiples of this number +#define LAYER_SIZE 64 + +// Debug +#if DEBUG_LAYERS + #define LAYER_LOGD(...) LOGD(__VA_ARGS__) +#else + #define LAYER_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Cache +/////////////////////////////////////////////////////////////////////////////// + +class LayerCache { +public: + LayerCache(); + ~LayerCache(); + + /** + * Returns a layer large enough for the specified dimensions. If no suitable + * layer can be found, a new one is created and returned. If creating a new + * layer fails, NULL is returned. + * + * When a layer is obtained from the cache, it is removed and the total + * size of the cache goes down. + * + * @param width The desired width of the layer + * @param width The desired height of the layer + */ + Layer* get(const uint32_t width, const uint32_t height); + + /** + * Adds the layer to the cache. The layer will not be added if there is + * not enough space available. Adding a layer can cause other layers to + * be removed from the cache. + * + * @param layer The layer to add to the cache + * + * @return True if the layer was added, false otherwise. + */ + bool put(Layer* layer); + /** + * Clears the cache. This causes all layers to be deleted. + */ + void clear(); + /** + * Resize the specified layer if needed. + * + * @param layer The layer to resize + * @param width The new width of the layer + * @param height The new height of the layer + * + * @return True if the layer was resized or nothing happened, false if + * a failure occurred during the resizing operation + */ + bool resize(Layer* layer, const uint32_t width, const uint32_t height); + + /** + * Sets the maximum size of the cache in bytes. + */ + void setMaxSize(uint32_t maxSize); + /** + * Returns the maximum size of the cache in bytes. + */ + uint32_t getMaxSize(); + /** + * Returns the current size of the cache in bytes. + */ + uint32_t getSize(); + +private: + void deleteLayer(Layer* layer); + + struct LayerEntry { + LayerEntry(): + mLayer(NULL), mWidth(0), mHeight(0) { + } + + LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(NULL) { + mWidth = uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE); + mHeight = uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE); + } + + LayerEntry(const LayerEntry& entry): + mLayer(entry.mLayer), mWidth(entry.mWidth), mHeight(entry.mHeight) { + } + + LayerEntry(Layer* layer): + mLayer(layer), mWidth(layer->width), mHeight(layer->height) { + } + + bool operator<(const LayerEntry& rhs) const { + if (mWidth == rhs.mWidth) { + return mHeight < rhs.mHeight; + } + return mWidth < rhs.mWidth; + } + + bool operator==(const LayerEntry& rhs) const { + return mWidth == rhs.mWidth && mHeight == rhs.mHeight; + } + + Layer* mLayer; + uint32_t mWidth; + uint32_t mHeight; + }; // struct LayerEntry + + SortedList<LayerEntry> mCache; + + uint32_t mSize; + uint32_t mMaxSize; +}; // class LayerCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_LAYER_CACHE_H diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp new file mode 100644 index 000000000000..f92e20b841f9 --- /dev/null +++ b/libs/hwui/LayerRenderer.cpp @@ -0,0 +1,275 @@ +/* + * 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 "OpenGLRenderer" + +#include <ui/Rect.h> + +#include "LayerCache.h" +#include "LayerRenderer.h" +#include "Properties.h" +#include "Rect.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Rendering +/////////////////////////////////////////////////////////////////////////////// + +void LayerRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { + LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->fbo); + + glBindFramebuffer(GL_FRAMEBUFFER, mLayer->fbo); + + const float width = mLayer->layer.getWidth(); + const float height = mLayer->layer.getHeight(); + +#if RENDER_LAYERS_AS_REGIONS + Rect dirty(left, top, right, bottom); + if (dirty.isEmpty() || (dirty.left <= 0 && dirty.top <= 0 && + dirty.right >= width && dirty.bottom >= height)) { + mLayer->region.clear(); + dirty.set(0.0f, 0.0f, width, height); + } else { + dirty.intersect(0.0f, 0.0f, width, height); + android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom); + mLayer->region.subtractSelf(r); + } + + OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque); +#else + OpenGLRenderer::prepareDirty(0.0f, 0.0f, width, height, opaque); +#endif +} + +void LayerRenderer::finish() { + OpenGLRenderer::finish(); + + generateMesh(); + + LAYER_RENDERER_LOGD("Finished rendering into layer, fbo = %d", mLayer->fbo); + + // No need to unbind our FBO, this will be taken care of by the caller + // who will invoke OpenGLRenderer::resume() +} + +GLint LayerRenderer::getTargetFbo() { + return mLayer->fbo; +} + +/////////////////////////////////////////////////////////////////////////////// +// Dirty region tracking +/////////////////////////////////////////////////////////////////////////////// + +bool LayerRenderer::hasLayer() { + return true; +} + +Region* LayerRenderer::getRegion() { +#if RENDER_LAYERS_AS_REGIONS + if (getSnapshot()->flags & Snapshot::kFlagFboTarget) { + return OpenGLRenderer::getRegion(); + } + return &mLayer->region; +#else + return OpenGLRenderer::getRegion(); +#endif +} + +void LayerRenderer::generateMesh() { +#if RENDER_LAYERS_AS_REGIONS +#if RENDER_LAYERS_RECT_AS_RECT + if (mLayer->region.isRect() || mLayer->region.isEmpty()) { +#else + if (mLayer->region.isEmpty()) { +#endif + if (mLayer->mesh) { + delete mLayer->mesh; + delete mLayer->meshIndices; + + mLayer->mesh = NULL; + mLayer->meshIndices = NULL; + mLayer->meshElementCount = 0; + } + return; + } + + size_t count; + const android::Rect* rects = mLayer->region.getArray(&count); + + GLsizei elementCount = count * 6; + + if (mLayer->mesh && mLayer->meshElementCount < elementCount) { + delete mLayer->mesh; + delete mLayer->meshIndices; + + mLayer->mesh = NULL; + mLayer->meshIndices = NULL; + } + + bool rebuildIndices = false; + if (!mLayer->mesh) { + mLayer->mesh = new TextureVertex[count * 4]; + mLayer->meshIndices = new uint16_t[elementCount]; + rebuildIndices = true; + } + mLayer->meshElementCount = elementCount; + + const float texX = 1.0f / float(mLayer->width); + const float texY = 1.0f / float(mLayer->height); + const float height = mLayer->layer.getHeight(); + + TextureVertex* mesh = mLayer->mesh; + uint16_t* indices = mLayer->meshIndices; + + for (size_t i = 0; i < count; i++) { + const android::Rect* r = &rects[i]; + + const float u1 = r->left * texX; + const float v1 = (height - r->top) * texY; + const float u2 = r->right * texX; + const float v2 = (height - r->bottom) * texY; + + TextureVertex::set(mesh++, r->left, r->top, u1, v1); + TextureVertex::set(mesh++, r->right, r->top, u2, v1); + TextureVertex::set(mesh++, r->left, r->bottom, u1, v2); + TextureVertex::set(mesh++, r->right, r->bottom, u2, v2); + + if (rebuildIndices) { + uint16_t quad = i * 4; + int index = i * 6; + indices[index ] = quad; // top-left + indices[index + 1] = quad + 1; // top-right + indices[index + 2] = quad + 2; // bottom-left + indices[index + 3] = quad + 2; // bottom-left + indices[index + 4] = quad + 1; // top-right + indices[index + 5] = quad + 3; // bottom-right + } + } +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// Layers management +/////////////////////////////////////////////////////////////////////////////// + +Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) { + LAYER_RENDERER_LOGD("Creating new layer %dx%d", width, height); + + GLuint fbo = Caches::getInstance().fboCache.get(); + if (!fbo) { + LOGW("Could not obtain an FBO"); + return NULL; + } + + glActiveTexture(GL_TEXTURE0); + Layer* layer = Caches::getInstance().layerCache.get(width, height); + if (!layer) { + LOGW("Could not obtain a layer"); + return NULL; + } + + layer->fbo = fbo; + layer->layer.set(0.0f, 0.0f, width, height); + layer->texCoords.set(0.0f, height / float(layer->height), + width / float(layer->width), 0.0f); + layer->alpha = 255; + layer->mode = SkXfermode::kSrcOver_Mode; + layer->blend = !isOpaque; + layer->colorFilter = NULL; + layer->region.clear(); + + GLuint previousFbo; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); + + glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); + glBindTexture(GL_TEXTURE_2D, layer->texture); + + // Initialize the texture if needed + if (layer->empty) { + layer->empty = false; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + if (glGetError() != GL_NO_ERROR) { + LOGD("Could not allocate texture"); + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + glDeleteTextures(1, &layer->texture); + Caches::getInstance().fboCache.put(fbo); + delete layer; + return NULL; + } + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + layer->texture, 0); + + glDisable(GL_SCISSOR_TEST); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + glEnable(GL_SCISSOR_TEST); + + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + + return layer; +} + +bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { + if (layer) { + LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->fbo, width, height); + + if (Caches::getInstance().layerCache.resize(layer, width, height)) { + layer->layer.set(0.0f, 0.0f, width, height); + layer->texCoords.set(0.0f, height / float(layer->height), + width / float(layer->width), 0.0f); + } else { + if (layer->texture) glDeleteTextures(1, &layer->texture); + delete layer; + return false; + } + } + + return true; +} + +void LayerRenderer::destroyLayer(Layer* layer) { + if (layer) { + LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo); + + if (layer->fbo) { + Caches::getInstance().fboCache.put(layer->fbo); + } + + if (!Caches::getInstance().layerCache.put(layer)) { + if (layer->texture) glDeleteTextures(1, &layer->texture); + delete layer; + } else { + layer->region.clear(); + } + } +} + +void LayerRenderer::destroyLayerDeferred(Layer* layer) { + if (layer) { + LAYER_RENDERER_LOGD("Deferring layer destruction, fbo = %d", layer->fbo); + + Caches::getInstance().deleteLayerDeferred(layer); + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h new file mode 100644 index 000000000000..d2f565e4f307 --- /dev/null +++ b/libs/hwui/LayerRenderer.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_LAYER_RENDERER_H +#define ANDROID_HWUI_LAYER_RENDERER_H + +#include "OpenGLRenderer.h" +#include "Layer.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#if DEBUG_LAYER_RENDERER + #define LAYER_RENDERER_LOGD(...) LOGD(__VA_ARGS__) +#else + #define LAYER_RENDERER_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Renderer +/////////////////////////////////////////////////////////////////////////////// + +class LayerRenderer: public OpenGLRenderer { +public: + LayerRenderer(Layer* layer): mLayer(layer) { + } + + ~LayerRenderer() { + } + + void prepareDirty(float left, float top, float right, float bottom, bool opaque); + void finish(); + + bool hasLayer(); + Region* getRegion(); + GLint getTargetFbo(); + + static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false); + static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height); + static void destroyLayer(Layer* layer); + static void destroyLayerDeferred(Layer* layer); + +private: + void generateMesh(); + + Layer* mLayer; +}; // class LayerRenderer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_LAYER_RENDERER_H diff --git a/libs/hwui/MODULE_LICENSE_APACHE2 b/libs/hwui/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/libs/hwui/MODULE_LICENSE_APACHE2 diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp new file mode 100644 index 000000000000..e7c0fe35194a --- /dev/null +++ b/libs/hwui/Matrix.cpp @@ -0,0 +1,359 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include <utils/Log.h> + +#include <SkMatrix.h> + +#include "utils/Compare.h" +#include "Matrix.h" + +namespace android { +namespace uirenderer { + +void Matrix4::loadIdentity() { + data[kScaleX] = 1.0f; + data[kSkewY] = 0.0f; + data[2] = 0.0f; + data[kPerspective0] = 0.0f; + + data[kSkewX] = 0.0f; + data[kScaleY] = 1.0f; + data[6] = 0.0f; + data[kPerspective1] = 0.0f; + + data[8] = 0.0f; + data[9] = 0.0f; + data[kScaleZ] = 1.0f; + data[11] = 0.0f; + + data[kTranslateX] = 0.0f; + data[kTranslateY] = 0.0f; + data[kTranslateZ] = 0.0f; + data[kPerspective2] = 1.0f; + + mSimpleMatrix = true; +} + +bool Matrix4::changesBounds() { + return !(ALMOST_EQUAL(data[0], 1.0f) && ALMOST_EQUAL(data[1], 0.0f) && + ALMOST_EQUAL(data[2], 0.0f) && ALMOST_EQUAL(data[4], 0.0f) && + ALMOST_EQUAL(data[5], 1.0f) && ALMOST_EQUAL(data[6], 0.0f) && + ALMOST_EQUAL(data[8], 0.0f) && ALMOST_EQUAL(data[9], 0.0f) && + ALMOST_EQUAL(data[10], 1.0f)); +} + +bool Matrix4::isPureTranslate() { + return mSimpleMatrix && + ALMOST_EQUAL(data[kScaleX], 1.0f) && ALMOST_EQUAL(data[kScaleY], 1.0f); +} + +void Matrix4::load(const float* v) { + memcpy(data, v, sizeof(data)); + mSimpleMatrix = false; +} + +void Matrix4::load(const Matrix4& v) { + memcpy(data, v.data, sizeof(data)); + mSimpleMatrix = v.mSimpleMatrix; +} + +void Matrix4::load(const SkMatrix& v) { + memset(data, 0, sizeof(data)); + + data[kScaleX] = v[SkMatrix::kMScaleX]; + data[kSkewX] = v[SkMatrix::kMSkewX]; + data[kTranslateX] = v[SkMatrix::kMTransX]; + + data[kSkewY] = v[SkMatrix::kMSkewY]; + data[kScaleY] = v[SkMatrix::kMScaleY]; + data[kTranslateY] = v[SkMatrix::kMTransY]; + + data[kPerspective0] = v[SkMatrix::kMPersp0]; + data[kPerspective1] = v[SkMatrix::kMPersp1]; + data[kPerspective2] = v[SkMatrix::kMPersp2]; + + data[kScaleZ] = 1.0f; + + mSimpleMatrix = (v.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)); +} + +void Matrix4::copyTo(SkMatrix& v) const { + v.reset(); + + v.set(SkMatrix::kMScaleX, data[kScaleX]); + v.set(SkMatrix::kMSkewX, data[kSkewX]); + v.set(SkMatrix::kMTransX, data[kTranslateX]); + + v.set(SkMatrix::kMSkewY, data[kSkewY]); + v.set(SkMatrix::kMScaleY, data[kScaleY]); + v.set(SkMatrix::kMTransY, data[kTranslateY]); + + v.set(SkMatrix::kMPersp0, data[kPerspective0]); + v.set(SkMatrix::kMPersp1, data[kPerspective1]); + v.set(SkMatrix::kMPersp2, data[kPerspective2]); +} + +void Matrix4::loadInverse(const Matrix4& v) { + double scale = 1.0 / + (v.data[kScaleX] * ((double) v.data[kScaleY] * v.data[kPerspective2] - + (double) v.data[kTranslateY] * v.data[kPerspective1]) + + v.data[kSkewX] * ((double) v.data[kTranslateY] * v.data[kPerspective0] - + (double) v.data[kSkewY] * v.data[kPerspective2]) + + v.data[kTranslateX] * ((double) v.data[kSkewY] * v.data[kPerspective1] - + (double) v.data[kScaleY] * v.data[kPerspective0])); + + data[kScaleX] = (v.data[kScaleY] * v.data[kPerspective2] - + v.data[kTranslateY] * v.data[kPerspective1]) * scale; + data[kSkewX] = (v.data[kTranslateX] * v.data[kPerspective1] - + v.data[kSkewX] * v.data[kPerspective2]) * scale; + data[kTranslateX] = (v.data[kSkewX] * v.data[kTranslateY] - + v.data[kTranslateX] * v.data[kScaleY]) * scale; + + data[kSkewY] = (v.data[kTranslateY] * v.data[kPerspective0] - + v.data[kSkewY] * v.data[kPerspective2]) * scale; + data[kScaleY] = (v.data[kScaleX] * v.data[kPerspective2] - + v.data[kTranslateX] * v.data[kPerspective0]) * scale; + data[kTranslateY] = (v.data[kTranslateX] * v.data[kSkewY] - + v.data[kScaleX] * v.data[kTranslateY]) * scale; + + data[kPerspective0] = (v.data[kSkewY] * v.data[kPerspective1] - + v.data[kScaleY] * v.data[kPerspective0]) * scale; + data[kPerspective1] = (v.data[kSkewX] * v.data[kPerspective0] - + v.data[kScaleX] * v.data[kPerspective1]) * scale; + data[kPerspective2] = (v.data[kScaleX] * v.data[kScaleY] - + v.data[kSkewX] * v.data[kSkewY]) * scale; + + mSimpleMatrix = v.mSimpleMatrix; +} + +void Matrix4::copyTo(float* v) const { + memcpy(v, data, sizeof(data)); +} + +float Matrix4::getTranslateX() { + return data[kTranslateX]; +} + +float Matrix4::getTranslateY() { + return data[kTranslateY]; +} + +void Matrix4::multiply(float v) { + for (int i = 0; i < 16; i++) { + data[i] *= v; + } +} + +void Matrix4::loadTranslate(float x, float y, float z) { + loadIdentity(); + data[kTranslateX] = x; + data[kTranslateY] = y; + data[kTranslateZ] = z; +} + +void Matrix4::loadScale(float sx, float sy, float sz) { + loadIdentity(); + data[kScaleX] = sx; + data[kScaleY] = sy; + data[kScaleZ] = sz; +} + +void Matrix4::loadSkew(float sx, float sy) { + loadIdentity(); + + data[kScaleX] = 1.0f; + data[kSkewX] = sx; + data[kTranslateX] = 0.0f; + + data[kSkewY] = sy; + data[kScaleY] = 1.0f; + data[kTranslateY] = 0.0f; + + data[kPerspective0] = 0.0f; + data[kPerspective1] = 0.0f; + data[kPerspective2] = 1.0f; + + mSimpleMatrix = false; +} + +void Matrix4::loadRotate(float angle, float x, float y, float z) { + data[kPerspective0] = 0.0f; + data[kPerspective1] = 0.0f; + data[11] = 0.0f; + data[kTranslateX] = 0.0f; + data[kTranslateY] = 0.0f; + data[kTranslateZ] = 0.0f; + data[kPerspective2] = 1.0f; + + angle *= float(M_PI / 180.0f); + float c = cosf(angle); + float s = sinf(angle); + + const float length = sqrtf(x * x + y * y + z * z); + float recipLen = 1.0f / length; + x *= recipLen; + y *= recipLen; + z *= recipLen; + + const float nc = 1.0f - c; + const float xy = x * y; + const float yz = y * z; + const float zx = z * x; + const float xs = x * s; + const float ys = y * s; + const float zs = z * s; + + data[kScaleX] = x * x * nc + c; + data[kSkewX] = xy * nc - zs; + data[8] = zx * nc + ys; + data[kSkewY] = xy * nc + zs; + data[kScaleY] = y * y * nc + c; + data[9] = yz * nc - xs; + data[2] = zx * nc - ys; + data[6] = yz * nc + xs; + data[kScaleZ] = z * z * nc + c; + + mSimpleMatrix = false; +} + +void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) { + for (int i = 0 ; i < 4 ; i++) { + float x = 0; + float y = 0; + float z = 0; + float w = 0; + + for (int j = 0 ; j < 4 ; j++) { + const float e = v.get(i, j); + x += u.get(j, 0) * e; + y += u.get(j, 1) * e; + z += u.get(j, 2) * e; + w += u.get(j, 3) * e; + } + + set(i, 0, x); + set(i, 1, y); + set(i, 2, z); + set(i, 3, w); + } + + mSimpleMatrix = u.mSimpleMatrix && v.mSimpleMatrix; +} + +void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) { + loadIdentity(); + data[kScaleX] = 2.0f / (right - left); + data[kScaleY] = 2.0f / (top - bottom); + data[kScaleZ] = -2.0f / (far - near); + data[kTranslateX] = -(right + left) / (right - left); + data[kTranslateY] = -(top + bottom) / (top - bottom); + data[kTranslateZ] = -(far + near) / (far - near); +} + +#define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c) + +void Matrix4::mapPoint(float& x, float& y) const { + if (mSimpleMatrix) { + MUL_ADD_STORE(x, data[kScaleX], data[kTranslateX]); + MUL_ADD_STORE(y, data[kScaleY], data[kTranslateY]); + return; + } + + float dx = x * data[kScaleX] + y * data[kSkewX] + data[kTranslateX]; + float dy = x * data[kSkewY] + y * data[kScaleY] + data[kTranslateY]; + float dz = x * data[kPerspective0] + y * data[kPerspective1] + data[kPerspective2]; + if (dz) dz = 1.0f / dz; + + x = dx * dz; + y = dy * dz; +} + +void Matrix4::mapRect(Rect& r) const { + if (mSimpleMatrix) { + MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]); + MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]); + MUL_ADD_STORE(r.top, data[kScaleY], data[kTranslateY]); + MUL_ADD_STORE(r.bottom, data[kScaleY], data[kTranslateY]); + + if (r.left > r.right) { + float x = r.left; + r.left = r.right; + r.right = x; + } + + if (r.top > r.bottom) { + float y = r.top; + r.top = r.bottom; + r.bottom = y; + } + + return; + } + + float vertices[] = { + r.left, r.top, + r.right, r.top, + r.right, r.bottom, + r.left, r.bottom + }; + + float x, y, z; + + for (int i = 0; i < 8; i+= 2) { + float px = vertices[i]; + float py = vertices[i + 1]; + + x = px * data[kScaleX] + py * data[kSkewX] + data[kTranslateX]; + y = px * data[kSkewY] + py * data[kScaleY] + data[kTranslateY]; + z = px * data[kPerspective0] + py * data[kPerspective1] + data[kPerspective2]; + if (z) z = 1.0f / z; + + vertices[i] = x * z; + vertices[i + 1] = y * z; + } + + r.left = r.right = vertices[0]; + r.top = r.bottom = vertices[1]; + + for (int i = 2; i < 8; i += 2) { + x = vertices[i]; + y = vertices[i + 1]; + + if (x < r.left) r.left = x; + else if (x > r.right) r.right = x; + if (y < r.top) r.top = y; + else if (y > r.bottom) r.bottom = y; + } +} + +void Matrix4::dump() const { + LOGD("Matrix4[simple=%d", mSimpleMatrix); + LOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]); + LOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]); + LOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]); + LOGD(" %f %f %f %f", data[kPerspective0], data[kPerspective1], data[11], data[kPerspective2]); + LOGD("]"); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h new file mode 100644 index 000000000000..08f5d776d5f5 --- /dev/null +++ b/libs/hwui/Matrix.h @@ -0,0 +1,149 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_MATRIX_H +#define ANDROID_HWUI_MATRIX_H + +#include <SkMatrix.h> + +#include "Rect.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Classes +/////////////////////////////////////////////////////////////////////////////// + +class Matrix4 { +public: + float data[16]; + + enum Entry { + kScaleX = 0, + kSkewY = 1, + kPerspective0 = 3, + kSkewX = 4, + kScaleY = 5, + kPerspective1 = 7, + kScaleZ = 10, + kTranslateX = 12, + kTranslateY = 13, + kTranslateZ = 14, + kPerspective2 = 15 + }; + + Matrix4() { + loadIdentity(); + } + + Matrix4(const float* v) { + load(v); + } + + Matrix4(const Matrix4& v) { + load(v); + } + + Matrix4(const SkMatrix& v) { + load(v); + } + + void loadIdentity(); + + void load(const float* v); + void load(const Matrix4& v); + void load(const SkMatrix& v); + + void loadInverse(const Matrix4& v); + + void loadTranslate(float x, float y, float z); + void loadScale(float sx, float sy, float sz); + void loadSkew(float sx, float sy); + void loadRotate(float angle, float x, float y, float z); + void loadMultiply(const Matrix4& u, const Matrix4& v); + + void loadOrtho(float left, float right, float bottom, float top, float near, float far); + + void multiply(const Matrix4& v) { + Matrix4 u; + u.loadMultiply(*this, v); + load(u); + } + + void multiply(float v); + + void translate(float x, float y, float z) { + Matrix4 u; + u.loadTranslate(x, y, z); + multiply(u); + } + + void scale(float sx, float sy, float sz) { + Matrix4 u; + u.loadScale(sx, sy, sz); + multiply(u); + } + + void skew(float sx, float sy) { + Matrix4 u; + u.loadSkew(sx, sy); + multiply(u); + } + + void rotate(float angle, float x, float y, float z) { + Matrix4 u; + u.loadRotate(angle, x, y, z); + multiply(u); + } + + bool isPureTranslate(); + + bool changesBounds(); + + void copyTo(float* v) const; + void copyTo(SkMatrix& v) const; + + void mapRect(Rect& r) const; + void mapPoint(float& x, float& y) const; + + float getTranslateX(); + float getTranslateY(); + + void dump() const; + +private: + bool mSimpleMatrix; + + inline float get(int i, int j) const { + return data[i * 4 + j]; + } + + inline void set(int i, int j, float v) { + data[i * 4 + j] = v; + } +}; // class Matrix4 + +/////////////////////////////////////////////////////////////////////////////// +// Types +/////////////////////////////////////////////////////////////////////////////// + +typedef Matrix4 mat4; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_MATRIX_H diff --git a/libs/hwui/NOTICE b/libs/hwui/NOTICE new file mode 100644 index 000000000000..c5b1efa7aac7 --- /dev/null +++ b/libs/hwui/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp new file mode 100644 index 000000000000..d9d7d231a38f --- /dev/null +++ b/libs/hwui/OpenGLRenderer.cpp @@ -0,0 +1,2033 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <SkCanvas.h> +#include <SkTypeface.h> + +#include <utils/Log.h> +#include <utils/StopWatch.h> + +#include <private/hwui/DrawGlInfo.h> + +#include <ui/Rect.h> + +#include "OpenGLRenderer.h" +#include "DisplayListRenderer.h" +#include "Vector.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#define RAD_TO_DEG (180.0f / 3.14159265f) +#define MIN_ANGLE 0.001f + +// TODO: This should be set in properties +#define ALPHA_THRESHOLD (0x7f / PANEL_BIT_DEPTH) + +/////////////////////////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////////////////////////// + +/** + * Structure mapping Skia xfermodes to OpenGL blending factors. + */ +struct Blender { + SkXfermode::Mode mode; + GLenum src; + GLenum dst; +}; // struct Blender + +// In this array, the index of each Blender equals the value of the first +// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode] +static const Blender gBlends[] = { + { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO }, + { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO }, + { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE }, + { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, + { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO }, + { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA }, + { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, + { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, + { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA } +}; + +// This array contains the swapped version of each SkXfermode. For instance +// this array's SrcOver blending mode is actually DstOver. You can refer to +// createLayer() for more information on the purpose of this array. +static const Blender gBlendsSwap[] = { + { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO }, + { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE }, + { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO }, + { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, + { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA }, + { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, + { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA } +}; + +static const GLenum gTextureUnits[] = { + GL_TEXTURE0, + GL_TEXTURE1, + GL_TEXTURE2 +}; + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) { + mShader = NULL; + mColorFilter = NULL; + mHasShadow = false; + + memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices)); + + mFirstSnapshot = new Snapshot; +} + +OpenGLRenderer::~OpenGLRenderer() { + // The context has already been destroyed at this point, do not call + // GL APIs. All GL state should be kept in Caches.h +} + +/////////////////////////////////////////////////////////////////////////////// +// Setup +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::setViewport(int width, int height) { + glViewport(0, 0, width, height); + mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + + mWidth = width; + mHeight = height; + + mFirstSnapshot->height = height; + mFirstSnapshot->viewport.set(0, 0, width, height); + + mDirtyClip = false; +} + +void OpenGLRenderer::prepare(bool opaque) { + prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque); +} + +void OpenGLRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { + mCaches.clearGarbage(); + + mSnapshot = new Snapshot(mFirstSnapshot, + SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + mSnapshot->fbo = getTargetFbo(); + + mSaveCount = 1; + + glViewport(0, 0, mWidth, mHeight); + + glDisable(GL_DITHER); + + glEnable(GL_SCISSOR_TEST); + glScissor(left, mSnapshot->height - bottom, right - left, bottom - top); + mSnapshot->setClip(left, top, right, bottom); + + if (!opaque) { + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + } +} + +void OpenGLRenderer::finish() { +#if DEBUG_OPENGL + GLenum status = GL_NO_ERROR; + while ((status = glGetError()) != GL_NO_ERROR) { + LOGD("GL error from OpenGLRenderer: 0x%x", status); + switch (status) { + case GL_OUT_OF_MEMORY: + LOGE(" OpenGLRenderer is out of memory!"); + break; + } + } +#endif +#if DEBUG_MEMORY_USAGE + mCaches.dumpMemoryUsage(); +#else + if (mCaches.getDebugLevel() & kDebugMemory) { + mCaches.dumpMemoryUsage(); + } +#endif +} + +void OpenGLRenderer::interrupt() { + if (mCaches.currentProgram) { + if (mCaches.currentProgram->isInUse()) { + mCaches.currentProgram->remove(); + mCaches.currentProgram = NULL; + } + } + mCaches.unbindMeshBuffer(); +} + +void OpenGLRenderer::resume() { + glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight()); + + glEnable(GL_SCISSOR_TEST); + dirtyClip(); + + glDisable(GL_DITHER); + + glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo()); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + mCaches.blend = true; + glEnable(GL_BLEND); + glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode); + glBlendEquation(GL_FUNC_ADD); +} + +bool OpenGLRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) { + interrupt(); + if (mDirtyClip) { + setScissorFromClip(); + } + + Rect clip(*mSnapshot->clipRect); + clip.snapToPixelBoundaries(); + +#if RENDER_LAYERS_AS_REGIONS + // Since we don't know what the functor will draw, let's dirty + // tne entire clip region + if (hasLayer()) { + dirtyLayerUnchecked(clip, getRegion()); + } +#endif + + DrawGlInfo info; + info.clipLeft = clip.left; + info.clipTop = clip.top; + info.clipRight = clip.right; + info.clipBottom = clip.bottom; + info.isLayer = hasLayer(); + getSnapshot()->transform->copyTo(&info.transform[0]); + + status_t result = (*functor)(0, &info); + + if (result != 0) { + Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom); + dirty.unionWith(localDirty); + } + + resume(); + return result != 0; +} + +/////////////////////////////////////////////////////////////////////////////// +// State management +/////////////////////////////////////////////////////////////////////////////// + +int OpenGLRenderer::getSaveCount() const { + return mSaveCount; +} + +int OpenGLRenderer::save(int flags) { + return saveSnapshot(flags); +} + +void OpenGLRenderer::restore() { + if (mSaveCount > 1) { + restoreSnapshot(); + } +} + +void OpenGLRenderer::restoreToCount(int saveCount) { + if (saveCount < 1) saveCount = 1; + + while (mSaveCount > saveCount) { + restoreSnapshot(); + } +} + +int OpenGLRenderer::saveSnapshot(int flags) { + mSnapshot = new Snapshot(mSnapshot, flags); + return mSaveCount++; +} + +bool OpenGLRenderer::restoreSnapshot() { + bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet; + bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer; + bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho; + + sp<Snapshot> current = mSnapshot; + sp<Snapshot> previous = mSnapshot->previous; + + if (restoreOrtho) { + Rect& r = previous->viewport; + glViewport(r.left, r.top, r.right, r.bottom); + mOrthoMatrix.load(current->orthoMatrix); + } + + mSaveCount--; + mSnapshot = previous; + + if (restoreClip) { + dirtyClip(); + } + + if (restoreLayer) { + composeLayer(current, previous); + } + + return restoreClip; +} + +/////////////////////////////////////////////////////////////////////////////// +// Layers +/////////////////////////////////////////////////////////////////////////////// + +int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, + SkPaint* p, int flags) { + const GLuint previousFbo = mSnapshot->fbo; + const int count = saveSnapshot(flags); + + if (!mSnapshot->isIgnored()) { + int alpha = 255; + SkXfermode::Mode mode; + + if (p) { + alpha = p->getAlpha(); + if (!mCaches.extensions.hasFramebufferFetch()) { + const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode); + if (!isMode) { + // Assume SRC_OVER + mode = SkXfermode::kSrcOver_Mode; + } + } else { + mode = getXfermode(p->getXfermode()); + } + } else { + mode = SkXfermode::kSrcOver_Mode; + } + + createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags, previousFbo); + } + + return count; +} + +int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int flags) { + if (alpha >= 255 - ALPHA_THRESHOLD) { + return saveLayer(left, top, right, bottom, NULL, flags); + } else { + SkPaint paint; + paint.setAlpha(alpha); + return saveLayer(left, top, right, bottom, &paint, flags); + } +} + +/** + * Layers are viewed by Skia are slightly different than layers in image editing + * programs (for instance.) When a layer is created, previously created layers + * and the frame buffer still receive every drawing command. For instance, if a + * layer is created and a shape intersecting the bounds of the layers and the + * framebuffer is draw, the shape will be drawn on both (unless the layer was + * created with the SkCanvas::kClipToLayer_SaveFlag flag.) + * + * A way to implement layers is to create an FBO for each layer, backed by an RGBA + * texture. Unfortunately, this is inefficient as it requires every primitive to + * be drawn n + 1 times, where n is the number of active layers. In practice this + * means, for every primitive: + * - Switch active frame buffer + * - Change viewport, clip and projection matrix + * - Issue the drawing + * + * Switching rendering target n + 1 times per drawn primitive is extremely costly. + * To avoid this, layers are implemented in a different way here, at least in the + * general case. FBOs are used, as an optimization, when the "clip to layer" flag + * is set. When this flag is set we can redirect all drawing operations into a + * single FBO. + * + * This implementation relies on the frame buffer being at least RGBA 8888. When + * a layer is created, only a texture is created, not an FBO. The content of the + * frame buffer contained within the layer's bounds is copied into this texture + * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame + * buffer and drawing continues as normal. This technique therefore treats the + * frame buffer as a scratch buffer for the layers. + * + * To compose the layers back onto the frame buffer, each layer texture + * (containing the original frame buffer data) is drawn as a simple quad over + * the frame buffer. The trick is that the quad is set as the composition + * destination in the blending equation, and the frame buffer becomes the source + * of the composition. + * + * Drawing layers with an alpha value requires an extra step before composition. + * An empty quad is drawn over the layer's region in the frame buffer. This quad + * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the + * quad is used to multiply the colors in the frame buffer. This is achieved by + * changing the GL blend functions for the GL_FUNC_ADD blend equation to + * GL_ZERO, GL_SRC_ALPHA. + * + * Because glCopyTexImage2D() can be slow, an alternative implementation might + * be use to draw a single clipped layer. The implementation described above + * is correct in every case. + * + * (1) The frame buffer is actually not cleared right away. To allow the GPU + * to potentially optimize series of calls to glCopyTexImage2D, the frame + * buffer is left untouched until the first drawing operation. Only when + * something actually gets drawn are the layers regions cleared. + */ +bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, + float right, float bottom, int alpha, SkXfermode::Mode mode, + int flags, GLuint previousFbo) { + LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top); + LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); + + const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag; + + // Window coordinates of the layer + Rect bounds(left, top, right, bottom); + if (!fboLayer) { + mSnapshot->transform->mapRect(bounds); + + // Layers only make sense if they are in the framebuffer's bounds + if (bounds.intersect(*snapshot->clipRect)) { + // We cannot work with sub-pixels in this case + bounds.snapToPixelBoundaries(); + + // When the layer is not an FBO, we may use glCopyTexImage so we + // need to make sure the layer does not extend outside the bounds + // of the framebuffer + if (!bounds.intersect(snapshot->previous->viewport)) { + bounds.setEmpty(); + } + } else { + bounds.setEmpty(); + } + } + + if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize || + bounds.getHeight() > mCaches.maxTextureSize) { + snapshot->empty = fboLayer; + } else { + snapshot->invisible = snapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer); + } + + // Bail out if we won't draw in this snapshot + if (snapshot->invisible || snapshot->empty) { + return false; + } + + glActiveTexture(gTextureUnits[0]); + Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight()); + if (!layer) { + return false; + } + + layer->mode = mode; + layer->alpha = alpha; + layer->layer.set(bounds); + layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->height), + bounds.getWidth() / float(layer->width), 0.0f); + layer->colorFilter = mColorFilter; + + // Save the layer in the snapshot + snapshot->flags |= Snapshot::kFlagIsLayer; + snapshot->layer = layer; + + if (fboLayer) { + return createFboLayer(layer, bounds, snapshot, previousFbo); + } else { + // Copy the framebuffer into the layer + glBindTexture(GL_TEXTURE_2D, layer->texture); + if (!bounds.isEmpty()) { + if (layer->empty) { + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, + snapshot->height - bounds.bottom, layer->width, layer->height, 0); + layer->empty = false; + } else { + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, + snapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight()); + } + + // Clear the framebuffer where the layer will draw + glScissor(bounds.left, mSnapshot->height - bounds.bottom, + bounds.getWidth(), bounds.getHeight()); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + dirtyClip(); + } + } + + return true; +} + +bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot, + GLuint previousFbo) { + layer->fbo = mCaches.fboCache.get(); + +#if RENDER_LAYERS_AS_REGIONS + snapshot->region = &snapshot->layer->region; + snapshot->flags |= Snapshot::kFlagFboTarget; +#endif + + Rect clip(bounds); + snapshot->transform->mapRect(clip); + clip.intersect(*snapshot->clipRect); + clip.snapToPixelBoundaries(); + clip.intersect(snapshot->previous->viewport); + + mat4 inverse; + inverse.loadInverse(*mSnapshot->transform); + + inverse.mapRect(clip); + clip.snapToPixelBoundaries(); + clip.intersect(bounds); + clip.translate(-bounds.left, -bounds.top); + + snapshot->flags |= Snapshot::kFlagIsFboLayer; + snapshot->fbo = layer->fbo; + snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); + snapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); + snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); + snapshot->height = bounds.getHeight(); + snapshot->flags |= Snapshot::kFlagDirtyOrtho; + snapshot->orthoMatrix.load(mOrthoMatrix); + + // Bind texture to FBO + glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); + glBindTexture(GL_TEXTURE_2D, layer->texture); + + // Initialize the texture if needed + if (layer->empty) { + layer->empty = false; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + layer->texture, 0); + +#if DEBUG_LAYERS_AS_REGIONS + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LOGE("Framebuffer incomplete (GL error code 0x%x)", status); + + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + glDeleteTextures(1, &layer->texture); + mCaches.fboCache.put(layer->fbo); + + delete layer; + + return false; + } +#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, + clip.getWidth() + 2.0f, clip.getHeight() + 2.0f); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + dirtyClip(); + + // Change the ortho projection + glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); + mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + + return true; +} + +/** + * Read the documentation of createLayer() before doing anything in this method. + */ +void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { + if (!current->layer) { + LOGE("Attempting to compose a layer that does not exist"); + return; + } + + const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer; + + if (fboLayer) { + // Unbind current FBO and restore previous one + glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); + } + + Layer* layer = current->layer; + const Rect& rect = layer->layer; + + if (!fboLayer && layer->alpha < 255) { + drawColorRect(rect.left, rect.top, rect.right, rect.bottom, + layer->alpha << 24, SkXfermode::kDstIn_Mode, true); + // Required below, composeLayerRect() will divide by 255 + layer->alpha = 255; + } + + mCaches.unbindMeshBuffer(); + + glActiveTexture(gTextureUnits[0]); + + // When the layer is stored in an FBO, we can save a bit of fillrate by + // drawing only the dirty region + if (fboLayer) { + dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform); + if (layer->colorFilter) { + setupColorFilter(layer->colorFilter); + } + composeLayerRegion(layer, rect); + if (layer->colorFilter) { + resetColorFilter(); + } + } else { + if (!rect.isEmpty()) { + dirtyLayer(rect.left, rect.top, rect.right, rect.bottom); + composeLayerRect(layer, rect, true); + } + } + + if (fboLayer) { + // Detach the texture from the FBO + glBindFramebuffer(GL_FRAMEBUFFER, current->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); + + // Put the FBO name back in the cache, if it doesn't fit, it will be destroyed + mCaches.fboCache.put(current->fbo); + } + + dirtyClip(); + + // Failing to add the layer to the cache should happen only if the layer is too large + if (!mCaches.layerCache.put(layer)) { + LAYER_LOGD("Deleting layer"); + glDeleteTextures(1, &layer->texture); + delete layer; + } +} + +void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) { + const Rect& texCoords = layer->texCoords; + resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom); + + drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, + layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], + &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap); + + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); +} + +void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { +#if RENDER_LAYERS_AS_REGIONS +#if RENDER_LAYERS_RECT_AS_RECT + if (layer->region.isRect()) { + composeLayerRect(layer, rect); + layer->region.clear(); + return; + } +#endif + + if (!layer->region.isEmpty()) { + size_t count; + const android::Rect* rects = layer->region.getArray(&count); + + const float alpha = layer->alpha / 255.0f; + const float texX = 1.0f / float(layer->width); + const float texY = 1.0f / float(layer->height); + const float height = rect.getHeight(); + + TextureVertex* mesh = mCaches.getRegionMesh(); + GLsizei numQuads = 0; + + setupDraw(); + setupDrawWithTexture(); + setupDrawColor(alpha, alpha, alpha, alpha); + setupDrawColorFilter(); + setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode, false); + setupDrawProgram(); + setupDrawDirtyRegionsDisabled(); + setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawTexture(layer->texture); + setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom); + setupDrawMesh(&mesh[0].position[0], &mesh[0].texture[0]); + + for (size_t i = 0; i < count; i++) { + const android::Rect* r = &rects[i]; + + const float u1 = r->left * texX; + const float v1 = (height - r->top) * texY; + const float u2 = r->right * texX; + const float v2 = (height - r->bottom) * texY; + + // TODO: Reject quads outside of the clip + TextureVertex::set(mesh++, r->left, r->top, u1, v1); + TextureVertex::set(mesh++, r->right, r->top, u2, v1); + TextureVertex::set(mesh++, r->left, r->bottom, u1, v2); + TextureVertex::set(mesh++, r->right, r->bottom, u2, v2); + + numQuads++; + + if (numQuads >= REGION_MESH_QUAD_COUNT) { + glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL); + numQuads = 0; + mesh = mCaches.getRegionMesh(); + } + } + + if (numQuads > 0) { + glDrawElements(GL_TRIANGLES, numQuads * 6, GL_UNSIGNED_SHORT, NULL); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + finishDrawTexture(); + +#if DEBUG_LAYERS_AS_REGIONS + drawRegionRects(layer->region); +#endif + + layer->region.clear(); + } +#else + composeLayerRect(layer, rect); +#endif +} + +void OpenGLRenderer::drawRegionRects(const Region& region) { +#if DEBUG_LAYERS_AS_REGIONS + size_t count; + const android::Rect* rects = region.getArray(&count); + + uint32_t colors[] = { + 0x7fff0000, 0x7f00ff00, + 0x7f0000ff, 0x7fff00ff, + }; + + int offset = 0; + int32_t top = rects[0].top; + + for (size_t i = 0; i < count; i++) { + if (top != rects[i].top) { + offset ^= 0x2; + top = rects[i].top; + } + + Rect r(rects[i].left, rects[i].top, rects[i].right, rects[i].bottom); + drawColorRect(r.left, r.top, r.right, r.bottom, colors[offset + (i & 0x1)], + SkXfermode::kSrcOver_Mode); + } +#endif +} + +void OpenGLRenderer::dirtyLayer(const float left, const float top, + const float right, const float bottom, const mat4 transform) { +#if RENDER_LAYERS_AS_REGIONS + if (hasLayer()) { + Rect bounds(left, top, right, bottom); + transform.mapRect(bounds); + dirtyLayerUnchecked(bounds, getRegion()); + } +#endif +} + +void OpenGLRenderer::dirtyLayer(const float left, const float top, + const float right, const float bottom) { +#if RENDER_LAYERS_AS_REGIONS + if (hasLayer()) { + Rect bounds(left, top, right, bottom); + dirtyLayerUnchecked(bounds, getRegion()); + } +#endif +} + +void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) { +#if RENDER_LAYERS_AS_REGIONS + if (bounds.intersect(*mSnapshot->clipRect)) { + bounds.snapToPixelBoundaries(); + android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom); + if (!dirty.isEmpty()) { + region->orSelf(dirty); + } + } +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// Transforms +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::translate(float dx, float dy) { + mSnapshot->transform->translate(dx, dy, 0.0f); +} + +void OpenGLRenderer::rotate(float degrees) { + mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); +} + +void OpenGLRenderer::scale(float sx, float sy) { + mSnapshot->transform->scale(sx, sy, 1.0f); +} + +void OpenGLRenderer::skew(float sx, float sy) { + mSnapshot->transform->skew(sx, sy); +} + +void OpenGLRenderer::setMatrix(SkMatrix* matrix) { + mSnapshot->transform->load(*matrix); +} + +const float* OpenGLRenderer::getMatrix() const { + if (mSnapshot->fbo != 0) { + return &mSnapshot->transform->data[0]; + } + return &mIdentity.data[0]; +} + +void OpenGLRenderer::getMatrix(SkMatrix* matrix) { + mSnapshot->transform->copyTo(*matrix); +} + +void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { + SkMatrix transform; + mSnapshot->transform->copyTo(transform); + transform.preConcat(*matrix); + mSnapshot->transform->load(transform); +} + +/////////////////////////////////////////////////////////////////////////////// +// Clipping +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::setScissorFromClip() { + Rect clip(*mSnapshot->clipRect); + clip.snapToPixelBoundaries(); + glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight()); + mDirtyClip = false; +} + +const Rect& OpenGLRenderer::getClipBounds() { + return mSnapshot->getLocalClip(); +} + +bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) { + if (mSnapshot->isIgnored()) { + return true; + } + + Rect r(left, top, right, bottom); + mSnapshot->transform->mapRect(r); + r.snapToPixelBoundaries(); + + Rect clipRect(*mSnapshot->clipRect); + clipRect.snapToPixelBoundaries(); + + return !clipRect.intersects(r); +} + +bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { + bool clipped = mSnapshot->clip(left, top, right, bottom, op); + if (clipped) { + dirtyClip(); + } + return !mSnapshot->clipRect->isEmpty(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Drawing commands +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::setupDraw() { + if (mDirtyClip) { + setScissorFromClip(); + } + mDescription.reset(); + mSetShaderColor = false; + mColorSet = false; + mColorA = mColorR = mColorG = mColorB = 0.0f; + mTextureUnit = 0; + mTrackDirtyRegions = true; + mTexCoordsSlot = -1; +} + +void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) { + mDescription.hasTexture = true; + mDescription.hasAlpha8Texture = isAlpha8; +} + +void OpenGLRenderer::setupDrawColor(int color) { + setupDrawColor(color, (color >> 24) & 0xFF); +} + +void OpenGLRenderer::setupDrawColor(int color, int alpha) { + mColorA = alpha / 255.0f; + const float a = mColorA / 255.0f; + mColorR = a * ((color >> 16) & 0xFF); + mColorG = a * ((color >> 8) & 0xFF); + mColorB = a * ((color ) & 0xFF); + mColorSet = true; + mSetShaderColor = mDescription.setColor(mColorR, mColorG, mColorB, mColorA); +} + +void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) { + mColorA = alpha / 255.0f; + const float a = mColorA / 255.0f; + mColorR = a * ((color >> 16) & 0xFF); + mColorG = a * ((color >> 8) & 0xFF); + mColorB = a * ((color ) & 0xFF); + mColorSet = true; + mSetShaderColor = mDescription.setAlpha8Color(mColorR, mColorG, mColorB, mColorA); +} + +void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) { + mColorA = a; + mColorR = r; + mColorG = g; + mColorB = b; + mColorSet = true; + mSetShaderColor = mDescription.setColor(r, g, b, a); +} + +void OpenGLRenderer::setupDrawAlpha8Color(float r, float g, float b, float a) { + mColorA = a; + mColorR = r; + mColorG = g; + mColorB = b; + mColorSet = true; + mSetShaderColor = mDescription.setAlpha8Color(r, g, b, a); +} + +void OpenGLRenderer::setupDrawShader() { + if (mShader) { + mShader->describe(mDescription, mCaches.extensions); + } +} + +void OpenGLRenderer::setupDrawColorFilter() { + if (mColorFilter) { + mColorFilter->describe(mDescription, mCaches.extensions); + } +} + +void OpenGLRenderer::setupDrawBlending(SkXfermode::Mode mode, bool swapSrcDst) { + chooseBlending((mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode, + mDescription, swapSrcDst); +} + +void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool swapSrcDst) { + chooseBlending(blend || (mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode, + mDescription, swapSrcDst); +} + +void OpenGLRenderer::setupDrawProgram() { + useProgram(mCaches.programCache.get(mDescription)); +} + +void OpenGLRenderer::setupDrawDirtyRegionsDisabled() { + mTrackDirtyRegions = false; +} + +void OpenGLRenderer::setupDrawModelViewTranslate(float left, float top, float right, float bottom, + bool ignoreTransform) { + mModelView.loadTranslate(left, top, 0.0f); + if (!ignoreTransform) { + mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *mSnapshot->transform); + } else { + mCaches.currentProgram->set(mOrthoMatrix, mModelView, mIdentity); + if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom); + } +} + +void OpenGLRenderer::setupDrawModelViewIdentity() { + mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform); +} + +void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom, + bool ignoreTransform, bool ignoreModelView) { + if (!ignoreModelView) { + mModelView.loadTranslate(left, top, 0.0f); + mModelView.scale(right - left, bottom - top, 1.0f); + } else { + mModelView.loadIdentity(); + } + bool dirty = right - left > 0.0f && bottom - top > 0.0f; + if (!ignoreTransform) { + mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + if (mTrackDirtyRegions && dirty) { + dirtyLayer(left, top, right, bottom, *mSnapshot->transform); + } + } else { + mCaches.currentProgram->set(mOrthoMatrix, mModelView, mIdentity); + if (mTrackDirtyRegions && dirty) dirtyLayer(left, top, right, bottom); + } +} + +void OpenGLRenderer::setupDrawColorUniforms() { + if (mColorSet || (mShader && mSetShaderColor)) { + mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA); + } +} + +void OpenGLRenderer::setupDrawPureColorUniforms() { + if (mSetShaderColor) { + mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA); + } +} + +void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) { + if (mShader) { + if (ignoreTransform) { + mModelView.loadInverse(*mSnapshot->transform); + } + mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &mTextureUnit); + } +} + +void OpenGLRenderer::setupDrawShaderIdentityUniforms() { + if (mShader) { + mShader->setupProgram(mCaches.currentProgram, mIdentity, *mSnapshot, &mTextureUnit); + } +} + +void OpenGLRenderer::setupDrawColorFilterUniforms() { + if (mColorFilter) { + mColorFilter->setupProgram(mCaches.currentProgram); + } +} + +void OpenGLRenderer::setupDrawSimpleMesh() { + mCaches.bindMeshBuffer(); + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, + gMeshStride, 0); +} + +void OpenGLRenderer::setupDrawTexture(GLuint texture) { + bindTexture(texture); + glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++); + + mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); + glEnableVertexAttribArray(mTexCoordsSlot); +} + +void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { + if (!vertices) { + mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); + } else { + 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); + } +} + +void OpenGLRenderer::finishDrawTexture() { + glDisableVertexAttribArray(mTexCoordsSlot); +} + +/////////////////////////////////////////////////////////////////////////////// +// Drawing +/////////////////////////////////////////////////////////////////////////////// + +bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, + Rect& dirty, uint32_t level) { + if (quickReject(0.0f, 0.0f, width, height)) { + return false; + } + + // All the usual checks and setup operations (quickReject, setupDraw, etc.) + // will be performed by the display list itself + if (displayList) { + return displayList->replay(*this, dirty, level); + } + + return false; +} + +void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) { + const float right = left + bitmap->width(); + const float bottom = top + bitmap->height(); + + if (quickReject(left, top, right, bottom)) { + return; + } + + glActiveTexture(gTextureUnits[0]); + Texture* texture = mCaches.textureCache.get(bitmap); + if (!texture) return; + const AutoTexture autoCleanup(texture); + + drawTextureRect(left, top, right, bottom, texture, paint); +} + +void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) { + Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height()); + const mat4 transform(*matrix); + transform.mapRect(r); + + if (quickReject(r.left, r.top, r.right, r.bottom)) { + return; + } + + glActiveTexture(gTextureUnits[0]); + Texture* texture = mCaches.textureCache.get(bitmap); + if (!texture) return; + const AutoTexture autoCleanup(texture); + + // This could be done in a cheaper way, all we need is pass the matrix + // to the vertex shader. The save/restore is a bit overkill. + save(SkCanvas::kMatrix_SaveFlag); + concatMatrix(matrix); + drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint); + restore(); +} + +void OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, + float* vertices, int* colors, SkPaint* paint) { + // TODO: Do a quickReject + if (!vertices || mSnapshot->isIgnored()) { + return; + } + + glActiveTexture(gTextureUnits[0]); + Texture* texture = mCaches.textureCache.get(bitmap); + if (!texture) return; + const AutoTexture autoCleanup(texture); + setTextureWrapModes(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + const uint32_t count = meshWidth * meshHeight * 6; + + float left = FLT_MAX; + float top = FLT_MAX; + float right = FLT_MIN; + float bottom = FLT_MIN; + +#if RENDER_LAYERS_AS_REGIONS + bool hasActiveLayer = hasLayer(); +#else + bool hasActiveLayer = false; +#endif + + // TODO: Support the colors array + TextureVertex mesh[count]; + TextureVertex* vertex = mesh; + for (int32_t y = 0; y < meshHeight; y++) { + for (int32_t x = 0; x < meshWidth; x++) { + uint32_t i = (y * (meshWidth + 1) + x) * 2; + + float u1 = float(x) / meshWidth; + float u2 = float(x + 1) / meshWidth; + float v1 = float(y) / meshHeight; + float v2 = float(y + 1) / meshHeight; + + int ax = i + (meshWidth + 1) * 2; + int ay = ax + 1; + int bx = i; + int by = bx + 1; + int cx = i + 2; + int cy = cx + 1; + int dx = i + (meshWidth + 1) * 2 + 2; + int dy = dx + 1; + + TextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2); + TextureVertex::set(vertex++, vertices[bx], vertices[by], u1, v1); + TextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1); + + TextureVertex::set(vertex++, vertices[ax], vertices[ay], u1, v2); + TextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1); + TextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2); + +#if RENDER_LAYERS_AS_REGIONS + if (hasActiveLayer) { + // TODO: This could be optimized to avoid unnecessary ops + left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx]))); + top = fminf(top, fminf(vertices[ay], fminf(vertices[by], vertices[cy]))); + right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx]))); + bottom = fmaxf(bottom, fmaxf(vertices[ay], fmaxf(vertices[by], vertices[cy]))); + } +#endif + } + } + +#if RENDER_LAYERS_AS_REGIONS + if (hasActiveLayer) { + dirtyLayer(left, top, right, bottom, *mSnapshot->transform); + } +#endif + + drawTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f, + mode, texture->blend, &mesh[0].position[0], &mesh[0].texture[0], + GL_TRIANGLES, count, false, false, 0, false, false); +} + +void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, + float srcLeft, float srcTop, float srcRight, float srcBottom, + float dstLeft, float dstTop, float dstRight, float dstBottom, + SkPaint* paint) { + if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) { + return; + } + + glActiveTexture(gTextureUnits[0]); + Texture* texture = mCaches.textureCache.get(bitmap); + if (!texture) return; + const AutoTexture autoCleanup(texture); + setTextureWrapModes(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + + const float width = texture->width; + const float height = texture->height; + + const float u1 = srcLeft / width; + const float v1 = srcTop / height; + const float u2 = srcRight / width; + const float v2 = srcBottom / height; + + mCaches.unbindMeshBuffer(); + resetDrawTextureTexCoords(u1, v1, u2, v2); + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + if (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); + + 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 { + 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); + } + + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); +} + +void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, + const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + float left, float top, float right, float bottom, SkPaint* paint) { + if (quickReject(left, top, right, bottom)) { + return; + } + + glActiveTexture(gTextureUnits[0]); + Texture* texture = mCaches.textureCache.get(bitmap); + if (!texture) return; + const AutoTexture autoCleanup(texture); + setTextureWrapModes(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + 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) { + const bool pureTranslate = mSnapshot->transform->isPureTranslate(); +#if RENDER_LAYERS_AS_REGIONS + // Mark the current layer dirty where we are going to draw the patch + if (hasLayer() && mesh->hasEmptyQuads) { + const float offsetX = left + mSnapshot->transform->getTranslateX(); + const float offsetY = top + mSnapshot->transform->getTranslateY(); + const size_t count = mesh->quads.size(); + for (size_t i = 0; i < count; i++) { + const Rect& bounds = mesh->quads.itemAt(i); + if (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()); + } else { + dirtyLayer(left + bounds.left, top + bounds.top, + left + bounds.right, top + bounds.bottom, *mSnapshot->transform); + } + } + } +#endif + + if (pureTranslate) { + const float x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f); + const float y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f); + + drawTextureMesh(x, y, x + right - left, y + bottom - top, texture->id, alpha / 255.0f, + mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset, + GL_TRIANGLES, mesh->verticesCount, false, true, mesh->meshBuffer, + true, !mesh->hasEmptyQuads); + } else { + drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, + mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset, + GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer, + true, !mesh->hasEmptyQuads); + } + } +} + +void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { + if (mSnapshot->isIgnored()) return; + + const bool isAA = paint->isAntiAlias(); + const float strokeWidth = paint->getStrokeWidth() * 0.5f; + // A stroke width of 0 has a special meaningin Skia: + // it draws an unscaled 1px wide line + const bool isHairLine = paint->getStrokeWidth() == 0.0f; + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + int verticesCount = count >> 2; + int generatedVerticesCount = 0; + if (!isHairLine) { + // TODO: AA needs more vertices + verticesCount *= 6; + } else { + // TODO: AA will be different + verticesCount *= 2; + } + + TextureVertex lines[verticesCount]; + TextureVertex* vertex = &lines[0]; + + setupDraw(); + setupDrawColor(paint->getColor(), alpha); + setupDrawColorFilter(); + setupDrawShader(); + setupDrawBlending(mode); + setupDrawProgram(); + setupDrawModelViewIdentity(); + setupDrawColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawShaderIdentityUniforms(); + setupDrawMesh(vertex); + + if (!isHairLine) { + // TODO: Handle the AA case + for (int i = 0; i < count; i += 4) { + // a = start point, b = end point + vec2 a(points[i], points[i + 1]); + vec2 b(points[i + 2], points[i + 3]); + + // Bias to snap to the same pixels as Skia + a += 0.375; + b += 0.375; + + // Find the normal to the line + vec2 n = (b - a).copyNormalized() * strokeWidth; + float x = n.x; + n.x = -n.y; + n.y = x; + + // Four corners of the rectangle defining a thick line + vec2 p1 = a - n; + vec2 p2 = a + n; + vec2 p3 = b + n; + vec2 p4 = b - n; + + const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x))); + const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x))); + const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y))); + const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y))); + + if (!quickReject(left, top, right, bottom)) { + // Draw the line as 2 triangles, could be optimized + // by using only 4 vertices and the correct indices + // Also we should probably used non textured vertices + // when line AA is disabled to save on bandwidth + TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f); + TextureVertex::set(vertex++, p2.x, p2.y, 0.0f, 0.0f); + TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f); + TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f); + TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f); + TextureVertex::set(vertex++, p4.x, p4.y, 0.0f, 0.0f); + + generatedVerticesCount += 6; + + dirtyLayer(left, top, right, bottom, *mSnapshot->transform); + } + } + + if (generatedVerticesCount > 0) { + // GL_LINE does not give the result we want to match Skia + glDrawArrays(GL_TRIANGLES, 0, generatedVerticesCount); + } + } else { + // TODO: Handle the AA case + for (int i = 0; i < count; i += 4) { + const float left = fmin(points[i], points[i + 1]); + const float right = fmax(points[i], points[i + 1]); + const float top = fmin(points[i + 2], points[i + 3]); + const float bottom = fmax(points[i + 2], points[i + 3]); + + if (!quickReject(left, top, right, bottom)) { + TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f); + TextureVertex::set(vertex++, points[i + 2], points[i + 3], 0.0f, 0.0f); + + generatedVerticesCount += 2; + + dirtyLayer(left, top, right, bottom, *mSnapshot->transform); + } + } + + if (generatedVerticesCount > 0) { + glLineWidth(1.0f); + glDrawArrays(GL_LINES, 0, generatedVerticesCount); + } + } +} + +void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { + // No need to check against the clip, we fill the clip region + if (mSnapshot->isIgnored()) return; + + Rect& clip(*mSnapshot->clipRect); + clip.snapToPixelBoundaries(); + + drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true); +} + +void OpenGLRenderer::drawShape(float left, float top, const PathTexture* texture, SkPaint* paint) { + if (!texture) return; + const AutoTexture autoCleanup(texture); + + const float x = left + texture->left - texture->offset; + const float y = top + texture->top - texture->offset; + + drawPathTexture(texture, x, y, paint); +} + +void OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, SkPaint* paint) { + if (mSnapshot->isIgnored()) return; + + glActiveTexture(gTextureUnits[0]); + const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect( + right - left, bottom - top, rx, ry, paint); + drawShape(left, top, texture, paint); +} + +void OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) { + if (mSnapshot->isIgnored()) return; + + glActiveTexture(gTextureUnits[0]); + const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, paint); + drawShape(x - radius, y - radius, texture, paint); +} + +void OpenGLRenderer::drawOval(float left, float top, float right, float bottom, SkPaint* paint) { + if (mSnapshot->isIgnored()) return; + + glActiveTexture(gTextureUnits[0]); + const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, paint); + drawShape(left, top, texture, paint); +} + +void OpenGLRenderer::drawArc(float left, float top, float right, float bottom, + float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { + if (mSnapshot->isIgnored()) return; + + if (fabs(sweepAngle) >= 360.0f) { + drawOval(left, top, right, bottom, paint); + return; + } + + glActiveTexture(gTextureUnits[0]); + const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top, + startAngle, sweepAngle, useCenter, paint); + drawShape(left, top, texture, paint); +} + +void OpenGLRenderer::drawRectAsShape(float left, float top, float right, float bottom, + SkPaint* paint) { + if (mSnapshot->isIgnored()) return; + + glActiveTexture(gTextureUnits[0]); + const PathTexture* texture = mCaches.rectShapeCache.getRect(right - left, bottom - top, paint); + drawShape(left, top, texture, paint); +} + +void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) { + if (p->getStyle() != SkPaint::kFill_Style) { + drawRectAsShape(left, top, right, bottom, p); + return; + } + + if (quickReject(left, top, right, bottom)) { + return; + } + + SkXfermode::Mode mode; + if (!mCaches.extensions.hasFramebufferFetch()) { + const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode); + if (!isMode) { + // Assume SRC_OVER + mode = SkXfermode::kSrcOver_Mode; + } + } else { + mode = getXfermode(p->getXfermode()); + } + + int color = p->getColor(); + drawColorRect(left, top, right, bottom, color, mode); +} + +void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, + float x, float y, SkPaint* paint) { + if (text == NULL || count == 0) { + return; + } + if (mSnapshot->isIgnored()) return; + + paint->setAntiAlias(true); + + float length = -1.0f; + switch (paint->getTextAlign()) { + case SkPaint::kCenter_Align: + length = paint->measureText(text, bytesCount); + x -= length / 2.0f; + break; + case SkPaint::kRight_Align: + length = paint->measureText(text, bytesCount); + x -= length; + break; + default: + break; + } + + const float oldX = x; + const float oldY = y; + 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); + + if (mHasShadow) { + mCaches.dropShadowCache.setFontRenderer(fontRenderer); + const ShadowTexture* shadow = mCaches.dropShadowCache.get(paint, text, bytesCount, + count, mShadowRadius); + const AutoTexture autoCleanup(shadow); + + const float sx = x - shadow->left + mShadowDx; + const float sy = y - shadow->top + mShadowDy; + + const int shadowAlpha = ((mShadowColor >> 24) & 0xFF); + + glActiveTexture(gTextureUnits[0]); + setupDraw(); + setupDrawWithTexture(true); + setupDrawAlpha8Color(mShadowColor, shadowAlpha < 255 ? shadowAlpha : alpha); + setupDrawBlending(true, mode); + setupDrawProgram(); + setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height, pureTranslate); + setupDrawTexture(shadow->id); + setupDrawPureColorUniforms(); + setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + finishDrawTexture(); + } + + if (paint->getAlpha() == 0 && paint->getXfermode() == NULL) { + return; + } + + // 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; + } + + glActiveTexture(gTextureUnits[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 + bool hasActiveLayer = hasLayer(); +#else + 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 + if (hasActiveLayer) { + if (!pureTranslate) { + mSnapshot->transform->mapRect(bounds); + } + dirtyLayerUnchecked(bounds, getRegion()); + } +#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]); + + const PathTexture* texture = mCaches.pathCache.get(path, paint); + if (!texture) return; + const AutoTexture autoCleanup(texture); + + const float x = texture->left - texture->offset; + const float y = texture->top - texture->offset; + + drawPathTexture(texture, x, y, paint); +} + +void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { + if (!layer || quickReject(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight())) { + return; + } + + glActiveTexture(gTextureUnits[0]); + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + layer->alpha = alpha; + layer->mode = mode; + +#if RENDER_LAYERS_AS_REGIONS + if (!layer->region.isEmpty()) { +#if RENDER_LAYERS_RECT_AS_RECT + if (layer->region.isRect()) { + const Rect r(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight()); + composeLayerRect(layer, r); + } else if (layer->mesh) { +#else + if (layer->mesh) { +#endif + const float a = alpha / 255.0f; + const Rect& rect = layer->layer; + + setupDraw(); + setupDrawWithTexture(); + setupDrawColor(a, a, a, a); + setupDrawColorFilter(); + setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode, false); + setupDrawProgram(); + setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawTexture(layer->texture); + // TODO: The current layer, if any, will be dirtied with the bounding box + // of the layer we are drawing. Since the layer we are drawing has + // a mesh, we know the dirty region, we should use it instead + setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom); + setupDrawMesh(&layer->mesh[0].position[0], &layer->mesh[0].texture[0]); + + glDrawElements(GL_TRIANGLES, layer->meshElementCount, + GL_UNSIGNED_SHORT, layer->meshIndices); + + finishDrawTexture(); + +#if DEBUG_LAYERS_AS_REGIONS + drawRegionRects(layer->region); +#endif + } + } +#else + const Rect r(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight()); + composeLayerRect(layer, r); +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +// Shaders +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::resetShader() { + mShader = NULL; +} + +void OpenGLRenderer::setupShader(SkiaShader* shader) { + mShader = shader; + if (mShader) { + mShader->set(&mCaches.textureCache, &mCaches.gradientCache); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Color filters +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::resetColorFilter() { + mColorFilter = NULL; +} + +void OpenGLRenderer::setupColorFilter(SkiaColorFilter* filter) { + mColorFilter = filter; +} + +/////////////////////////////////////////////////////////////////////////////// +// Drop shadow +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::resetShadow() { + mHasShadow = false; +} + +void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) { + mHasShadow = true; + mShadowRadius = radius; + mShadowDx = dx; + mShadowDy = dy; + mShadowColor = color; +} + +/////////////////////////////////////////////////////////////////////////////// +// Drawing implementation +/////////////////////////////////////////////////////////////////////////////// + +void OpenGLRenderer::drawPathTexture(const PathTexture* texture, + float x, float y, SkPaint* paint) { + if (quickReject(x, y, x + texture->width, y + texture->height)) { + return; + } + + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + setupDraw(); + setupDrawWithTexture(true); + setupDrawAlpha8Color(paint->getColor(), alpha); + setupDrawColorFilter(); + setupDrawShader(); + setupDrawBlending(true, mode); + setupDrawProgram(); + setupDrawModelView(x, y, x + texture->width, y + texture->height); + setupDrawTexture(texture->id); + setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawShaderUniforms(); + setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + + finishDrawTexture(); +} + +// Same values used by Skia +#define kStdStrikeThru_Offset (-6.0f / 21.0f) +#define kStdUnderline_Offset (1.0f / 9.0f) +#define kStdUnderline_Thickness (1.0f / 18.0f) + +void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float length, + float x, float y, SkPaint* paint) { + // Handle underline and strike-through + uint32_t flags = paint->getFlags(); + if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { + float underlineWidth = length; + // If length is > 0.0f, we already measured the text for the text alignment + if (length <= 0.0f) { + underlineWidth = paint->measureText(text, bytesCount); + } + + float offsetX = 0; + switch (paint->getTextAlign()) { + case SkPaint::kCenter_Align: + offsetX = underlineWidth * 0.5f; + break; + case SkPaint::kRight_Align: + offsetX = underlineWidth; + break; + default: + break; + } + + if (underlineWidth > 0.0f) { + const float textSize = paint->getTextSize(); + // TODO: Support stroke width < 1.0f when we have AA lines + const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); + + const float left = x - offsetX; + float top = 0.0f; + + int linesCount = 0; + if (flags & SkPaint::kUnderlineText_Flag) linesCount++; + if (flags & SkPaint::kStrikeThruText_Flag) linesCount++; + + const int pointsCount = 4 * linesCount; + float points[pointsCount]; + int currentPoint = 0; + + if (flags & SkPaint::kUnderlineText_Flag) { + top = y + textSize * kStdUnderline_Offset; + points[currentPoint++] = left; + points[currentPoint++] = top; + points[currentPoint++] = left + underlineWidth; + points[currentPoint++] = top; + } + + if (flags & SkPaint::kStrikeThruText_Flag) { + top = y + textSize * kStdStrikeThru_Offset; + points[currentPoint++] = left; + points[currentPoint++] = top; + points[currentPoint++] = left + underlineWidth; + points[currentPoint++] = top; + } + + SkPaint linesPaint(*paint); + linesPaint.setStrokeWidth(strokeWidth); + + drawLines(&points[0], pointsCount, &linesPaint); + } + } +} + +void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, + int color, SkXfermode::Mode mode, bool ignoreTransform) { + // If a shader is set, preserve only the alpha + if (mShader) { + color |= 0x00ffffff; + } + + setupDraw(); + setupDrawColor(color); + setupDrawShader(); + setupDrawColorFilter(); + setupDrawBlending(mode); + setupDrawProgram(); + setupDrawModelView(left, top, right, bottom, ignoreTransform); + setupDrawColorUniforms(); + setupDrawShaderUniforms(ignoreTransform); + setupDrawColorFilterUniforms(); + setupDrawSimpleMesh(); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); +} + +void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, + Texture* texture, SkPaint* paint) { + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + setTextureWrapModes(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + + if (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); + + 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 { + drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, + texture->blend, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, + GL_TRIANGLE_STRIP, gMeshCount); + } +} + +void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, + GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) { + drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend, + (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount); +} + +void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom, + GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, + GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, + bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) { + + setupDraw(); + setupDrawWithTexture(); + setupDrawColor(alpha, alpha, alpha, alpha); + setupDrawColorFilter(); + setupDrawBlending(blend, mode, swapSrcDst); + setupDrawProgram(); + if (!dirty) { + setupDrawDirtyRegionsDisabled(); + } + if (!ignoreScale) { + setupDrawModelView(left, top, right, bottom, ignoreTransform); + } else { + setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform); + } + setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawTexture(texture); + setupDrawMesh(vertices, texCoords, vbo); + + glDrawArrays(drawMode, 0, elementsCount); + + finishDrawTexture(); +} + +void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, + ProgramDescription& description, bool swapSrcDst) { + blend = blend || mode != SkXfermode::kSrcOver_Mode; + if (blend) { + if (mode < SkXfermode::kPlus_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()) { + description.framebufferMode = mode; + description.swapSrcDst = swapSrcDst; + } + + if (mCaches.blend) { + glDisable(GL_BLEND); + } + blend = false; + } + } else if (mCaches.blend) { + glDisable(GL_BLEND); + } + mCaches.blend = blend; +} + +bool OpenGLRenderer::useProgram(Program* program) { + if (!program->isInUse()) { + if (mCaches.currentProgram != NULL) mCaches.currentProgram->remove(); + program->use(); + mCaches.currentProgram = program; + return false; + } + return true; +} + +void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) { + TextureVertex* v = &mMeshVertices[0]; + TextureVertex::setUV(v++, u1, v1); + TextureVertex::setUV(v++, u2, v1); + TextureVertex::setUV(v++, u1, v2); + TextureVertex::setUV(v++, u2, v2); +} + +void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) { + if (paint) { + if (!mCaches.extensions.hasFramebufferFetch()) { + const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode); + if (!isMode) { + // Assume SRC_OVER + *mode = SkXfermode::kSrcOver_Mode; + } + } else { + *mode = getXfermode(paint->getXfermode()); + } + + // Skia draws using the color's alpha channel if < 255 + // Otherwise, it uses the paint's alpha + int color = paint->getColor(); + *alpha = (color >> 24) & 0xFF; + if (*alpha == 255) { + *alpha = paint->getAlpha(); + } + } else { + *mode = SkXfermode::kSrcOver_Mode; + *alpha = 255; + } +} + +SkXfermode::Mode OpenGLRenderer::getXfermode(SkXfermode* mode) { + // In the future we should look at unifying the Porter-Duff modes and + // SkXferModes so that we can use SkXfermode::IsMode(xfer, &mode). + if (mode == NULL) { + return SkXfermode::kSrcOver_Mode; + } + return mode->fMode; +} + +void OpenGLRenderer::setTextureWrapModes(Texture* texture, GLenum wrapS, GLenum wrapT) { + bool bound = false; + if (wrapS != texture->wrapS) { + glBindTexture(GL_TEXTURE_2D, texture->id); + bound = true; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); + texture->wrapS = wrapS; + } + if (wrapT != texture->wrapT) { + if (!bound) { + glBindTexture(GL_TEXTURE_2D, texture->id); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); + texture->wrapT = wrapT; + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h new file mode 100644 index 000000000000..73624733df0a --- /dev/null +++ b/libs/hwui/OpenGLRenderer.h @@ -0,0 +1,525 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_OPENGL_RENDERER_H +#define ANDROID_HWUI_OPENGL_RENDERER_H + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <SkBitmap.h> +#include <SkMatrix.h> +#include <SkPaint.h> +#include <SkRegion.h> +#include <SkShader.h> +#include <SkXfermode.h> + +#include <utils/Functor.h> +#include <utils/RefBase.h> +#include <utils/Vector.h> + +#include "Debug.h" +#include "Extensions.h" +#include "Matrix.h" +#include "Program.h" +#include "Rect.h" +#include "Snapshot.h" +#include "Vertex.h" +#include "SkiaShader.h" +#include "SkiaColorFilter.h" +#include "Caches.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Renderer +/////////////////////////////////////////////////////////////////////////////// + +class DisplayList; + +/** + * OpenGL renderer used to draw accelerated 2D graphics. The API is a + * simplified version of Skia's Canvas API. + */ +class OpenGLRenderer { +public: + OpenGLRenderer(); + virtual ~OpenGLRenderer(); + + virtual void setViewport(int width, int height); + + void prepare(bool opaque); + virtual void prepareDirty(float left, float top, float right, float bottom, bool opaque); + virtual void finish(); + + // These two calls must not be recorded in display lists + virtual void interrupt(); + virtual void resume(); + + virtual bool callDrawGLFunction(Functor *functor, Rect& dirty); + + int getSaveCount() const; + virtual int save(int flags); + virtual void restore(); + virtual void restoreToCount(int saveCount); + + virtual int saveLayer(float left, float top, float right, float bottom, + SkPaint* p, int flags); + virtual int saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int flags); + + virtual void translate(float dx, float dy); + virtual void rotate(float degrees); + virtual void scale(float sx, float sy); + virtual void skew(float sx, float sy); + + const float* getMatrix() const; + void getMatrix(SkMatrix* matrix); + virtual void setMatrix(SkMatrix* matrix); + virtual void concatMatrix(SkMatrix* matrix); + + const Rect& getClipBounds(); + bool quickReject(float left, float top, float right, float bottom); + virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); + + virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, + Rect& dirty, uint32_t level = 0); + virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint); + virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint); + virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint); + virtual void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, SkPaint* paint); + virtual void drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, + float* vertices, int* colors, SkPaint* paint); + virtual void drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, + const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + float left, float top, float right, float bottom, SkPaint* paint); + virtual void drawColor(int color, SkXfermode::Mode mode); + virtual void drawRect(float left, float top, float right, float bottom, SkPaint* paint); + virtual void drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, SkPaint* paint); + virtual void drawCircle(float x, float y, float radius, SkPaint* paint); + virtual void drawOval(float left, float top, float right, float bottom, SkPaint* paint); + virtual void drawArc(float left, float top, float right, float bottom, + float startAngle, float sweepAngle, bool useCenter, SkPaint* paint); + virtual void drawPath(SkPath* path, SkPaint* paint); + virtual void drawLines(float* points, int count, SkPaint* paint); + virtual void drawText(const char* text, int bytesCount, int count, float x, float y, + SkPaint* paint); + + virtual void resetShader(); + virtual void setupShader(SkiaShader* shader); + + virtual void resetColorFilter(); + virtual void setupColorFilter(SkiaColorFilter* filter); + + virtual void resetShadow(); + virtual void setupShadow(float radius, float dx, float dy, int color); + +protected: + /** + * Compose the layer defined in the current snapshot with the layer + * defined by the previous snapshot. + * + * The current snapshot *must* be a layer (flag kFlagIsLayer set.) + * + * @param curent The current snapshot containing the layer to compose + * @param previous The previous snapshot to compose the current layer with + */ + virtual void composeLayer(sp<Snapshot> current, sp<Snapshot> previous); + + /** + * Marks the specified region as dirty at the specified bounds. + */ + void dirtyLayerUnchecked(Rect& bounds, Region* region); + + /** + * Returns the current snapshot. + */ + sp<Snapshot> getSnapshot() { + return mSnapshot; + } + + /** + * Returns the region of the current layer. + */ + virtual Region* getRegion() { + return mSnapshot->region; + } + + /** + * Indicates whether rendering is currently targeted at a layer. + */ + virtual bool hasLayer() { + return (mSnapshot->flags & Snapshot::kFlagFboTarget) && mSnapshot->region; + } + + /** + * Returns the name of the FBO this renderer is rendering into. + */ + virtual GLint getTargetFbo() { + return 0; + } + +private: + /** + * Saves the current state of the renderer as a new snapshot. + * The new snapshot is saved in mSnapshot and the previous snapshot + * is linked from mSnapshot->previous. + * + * @param flags The save flags; see SkCanvas for more information + * + * @return The new save count. This value can be passed to #restoreToCount() + */ + int saveSnapshot(int flags); + + /** + * Restores the current snapshot; mSnapshot becomes mSnapshot->previous. + * + * @return True if the clip was modified. + */ + bool restoreSnapshot(); + + /** + * Sets the clipping rectangle using glScissor. The clip is defined by + * the current snapshot's clipRect member. + */ + void setScissorFromClip(); + + /** + * Creates a new layer stored in the specified snapshot. + * + * @param snapshot The snapshot associated with the new layer + * @param left The left coordinate of the layer + * @param top The top coordinate of the layer + * @param right The right coordinate of the layer + * @param bottom The bottom coordinate of the layer + * @param alpha The translucency of the layer + * @param mode The blending mode of the layer + * @param flags The layer save flags + * @param previousFbo The name of the current framebuffer + * + * @return True if the layer was successfully created, false otherwise + */ + bool createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom, + int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo); + + /** + * Creates a new layer stored in the specified snapshot as an FBO. + * + * @param layer The layer to store as an FBO + * @param snapshot The snapshot associated with the new layer + * @param bounds The bounds of the layer + * @param previousFbo The name of the current framebuffer + */ + bool createFboLayer(Layer* layer, Rect& bounds, sp<Snapshot> snapshot, + GLuint previousFbo); + + /** + * Compose the specified layer as a region. + * + * @param layer The layer to compose + * @param rect The layer's bounds + */ + void composeLayerRegion(Layer* layer, const Rect& rect); + + /** + * Compose the specified layer as a simple rectangle. + * + * @param layer The layer to compose + * @param rect The layer's bounds + * @param swap If true, the source and destination are swapped + */ + void composeLayerRect(Layer* layer, const Rect& rect, bool swap = false); + + /** + * Mark the layer as dirty at the specified coordinates. The coordinates + * are transformed with the supplied matrix. + */ + void dirtyLayer(const float left, const float top, + const float right, const float bottom, const mat4 transform); + + /** + * Mark the layer as dirty at the specified coordinates. + */ + void dirtyLayer(const float left, const float top, + const float right, const float bottom); + + /** + * Draws a colored rectangle with the specified color. The specified coordinates + * are transformed by the current snapshot's transform matrix. + * + * @param left The left coordinate of the rectangle + * @param top The top coordinate of the rectangle + * @param right The right coordinate of the rectangle + * @param bottom The bottom coordinate of the rectangle + * @param color The rectangle's ARGB color, defined as a packed 32 bits word + * @param mode The Skia xfermode to use + * @param ignoreTransform True if the current transform should be ignored + * @param ignoreBlending True if the blending is set by the caller + */ + void drawColorRect(float left, float top, float right, float bottom, + int color, SkXfermode::Mode mode, bool ignoreTransform = false); + + void drawShape(float left, float top, const PathTexture* texture, SkPaint* paint); + void drawRectAsShape(float left, float top, float right, float bottom, SkPaint* p); + + /** + * Draws a textured rectangle with the specified texture. The specified coordinates + * are transformed by the current snapshot's transform matrix. + * + * @param left The left coordinate of the rectangle + * @param top The top coordinate of the rectangle + * @param right The right coordinate of the rectangle + * @param bottom The bottom coordinate of the rectangle + * @param texture The texture name to map onto the rectangle + * @param alpha An additional translucency parameter, between 0.0f and 1.0f + * @param mode The blending mode + * @param blend True if the texture contains an alpha channel + */ + void drawTextureRect(float left, float top, float right, float bottom, GLuint texture, + float alpha, SkXfermode::Mode mode, bool blend); + + /** + * Draws a textured rectangle with the specified texture. The specified coordinates + * are transformed by the current snapshot's transform matrix. + * + * @param left The left coordinate of the rectangle + * @param top The top coordinate of the rectangle + * @param right The right coordinate of the rectangle + * @param bottom The bottom coordinate of the rectangle + * @param texture The texture to use + * @param paint The paint containing the alpha, blending mode, etc. + */ + void drawTextureRect(float left, float top, float right, float bottom, + Texture* texture, SkPaint* paint); + + /** + * Draws a textured mesh with the specified texture. If the indices are omitted, + * the mesh is drawn as a simple quad. The mesh pointers become offsets when a + * VBO is bound. + * + * @param left The left coordinate of the rectangle + * @param top The top coordinate of the rectangle + * @param right The right coordinate of the rectangle + * @param bottom The bottom coordinate of the rectangle + * @param texture The texture name to map onto the rectangle + * @param alpha An additional translucency parameter, between 0.0f and 1.0f + * @param mode The blending mode + * @param blend True if the texture contains an alpha channel + * @param vertices The vertices that define the mesh + * @param texCoords The texture coordinates of each vertex + * @param elementsCount The number of elements in the mesh, required by indices + * @param swapSrcDst Whether or not the src and dst blending operations should be swapped + * @param ignoreTransform True if the current transform should be ignored + * @param vbo The VBO used to draw the mesh + * @param ignoreScale True if the model view matrix should not be scaled + * @param dirty True if calling this method should dirty the current layer + */ + void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture, + float alpha, SkXfermode::Mode mode, bool blend, + GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, + bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0, + bool ignoreScale = false, bool dirty = true); + + /** + * Draws text underline and strike-through if needed. + * + * @param text The text to decor + * @param bytesCount The number of bytes in the text + * @param length The length in pixels of the text, can be <= 0.0f to force a measurement + * @param x The x coordinate where the text will be drawn + * @param y The y coordinate where the text will be drawn + * @param paint The paint to draw the text with + */ + void drawTextDecorations(const char* text, int bytesCount, float length, + float x, float y, SkPaint* paint); + + void drawPathTexture(const PathTexture* texture, float x, float y, SkPaint* paint); + + /** + * Resets the texture coordinates stored in mMeshVertices. Setting the values + * back to default is achieved by calling: + * + * resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + * + * @param u1 The left coordinate of the texture + * @param v1 The bottom coordinate of the texture + * @param u2 The right coordinate of the texture + * @param v2 The top coordinate of the texture + */ + void resetDrawTextureTexCoords(float u1, float v1, float u2, float v2); + + /** + * Gets the alpha and xfermode out of a paint object. If the paint is null + * alpha will be 255 and the xfermode will be SRC_OVER. + * + * @param paint The paint to extract values from + * @param alpha Where to store the resulting alpha + * @param mode Where to store the resulting xfermode + */ + inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode); + + /** + * Binds the specified texture. The texture unit must have been selected + * prior to calling this method. + */ + inline void bindTexture(GLuint texture) { + glBindTexture(GL_TEXTURE_2D, texture); + } + + /** + * Sets the wrap modes for the specified texture. The wrap modes are modified + * only when needed. + */ + inline void setTextureWrapModes(Texture* texture, GLenum wrapS, GLenum wrapT); + + /** + * Enable or disable blending as necessary. This function sets the appropriate + * blend function based on the specified xfermode. + */ + inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description, + bool swapSrcDst = false); + + /** + * Safely retrieves the mode from the specified xfermode. If the specified + * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode. + */ + inline SkXfermode::Mode getXfermode(SkXfermode* mode); + + /** + * Use the specified program with the current GL context. If the program is already + * in use, it will not be bound again. If it is not in use, the current program is + * marked unused and the specified program becomes used and becomes the new + * current program. + * + * @param program The program to use + * + * @return true If the specified program was already in use, false otherwise. + */ + inline bool useProgram(Program* program); + + /** + * Invoked before any drawing operation. This sets required state. + */ + void setupDraw(); + /** + * Various methods to setup OpenGL rendering. + */ + void setupDrawWithTexture(bool isAlpha8 = false); + void setupDrawColor(int color); + void setupDrawColor(int color, int alpha); + void setupDrawColor(float r, float g, float b, float a); + void setupDrawAlpha8Color(int color, int alpha); + void setupDrawAlpha8Color(float r, float g, float b, float a); + void setupDrawShader(); + void setupDrawColorFilter(); + void setupDrawBlending(SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode, + bool swapSrcDst = false); + void setupDrawBlending(bool blend = true, SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode, + bool swapSrcDst = false); + void setupDrawProgram(); + void setupDrawDirtyRegionsDisabled(); + void setupDrawModelViewIdentity(); + void setupDrawModelView(float left, float top, float right, float bottom, + bool ignoreTransform = false, bool ignoreModelView = false); + void setupDrawModelViewTranslate(float left, float top, float right, float bottom, + bool ignoreTransform = false); + void setupDrawColorUniforms(); + void setupDrawPureColorUniforms(); + void setupDrawShaderIdentityUniforms(); + void setupDrawShaderUniforms(bool ignoreTransform = false); + void setupDrawColorFilterUniforms(); + void setupDrawSimpleMesh(); + void setupDrawTexture(GLuint texture); + void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0); + void finishDrawTexture(); + + void drawRegionRects(const Region& region); + + /** + * Should be invoked every time the glScissor is modified. + */ + inline void dirtyClip() { + mDirtyClip = true; + } + + // Dimensions of the drawing surface + int mWidth, mHeight; + + // Matrix used for ortho projection in shaders + mat4 mOrthoMatrix; + + // Model-view matrix used to position/size objects + mat4 mModelView; + + // Number of saved states + int mSaveCount; + // Base state + sp<Snapshot> mFirstSnapshot; + // Current state + sp<Snapshot> mSnapshot; + + // Shaders + SkiaShader* mShader; + + // Color filters + SkiaColorFilter* mColorFilter; + + // Used to draw textured quads + TextureVertex mMeshVertices[4]; + + // Drop shadow + bool mHasShadow; + float mShadowRadius; + float mShadowDx; + float mShadowDy; + int mShadowColor; + + // Various caches + Caches& mCaches; + + // Indentity matrix + const mat4 mIdentity; + + // Indicates whether the clip must be restored + bool mDirtyClip; + + // The following fields are used to setup drawing + // Used to describe the shaders to generate + ProgramDescription mDescription; + // Color description + bool mColorSet; + float mColorA, mColorR, mColorG, mColorB; + // Indicates that the shader should get a color + bool mSetShaderColor; + // Current texture unit + GLuint mTextureUnit; + // Track dirty regions, true by default + bool mTrackDirtyRegions; + // Texture coordinates slot + int mTexCoordsSlot; + + friend class DisplayListRenderer; + +}; // class OpenGLRenderer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_OPENGL_RENDERER_H diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp new file mode 100644 index 000000000000..11eb953907ca --- /dev/null +++ b/libs/hwui/Patch.cpp @@ -0,0 +1,279 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <cmath> + +#include <utils/Log.h> + +#include "Patch.h" +#include "Caches.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads): + mXCount(xCount), mYCount(yCount), mEmptyQuads(emptyQuads) { + // Initialized with the maximum number of vertices we will need + // 2 triangles per patch, 3 vertices per triangle + uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3; + mVertices = new TextureVertex[maxVertices]; + mUploaded = false; + + verticesCount = 0; + hasEmptyQuads = emptyQuads > 0; + + mColorKey = 0; + mXDivs = new int32_t[mXCount]; + mYDivs = new int32_t[mYCount]; + + PATCH_LOGD(" patch: xCount = %d, yCount = %d, emptyQuads = %d, max vertices = %d", + xCount, yCount, emptyQuads, maxVertices); + + glGenBuffers(1, &meshBuffer); +} + +Patch::~Patch() { + delete[] mVertices; + delete[] mXDivs; + delete[] mYDivs; + glDeleteBuffers(1, &meshBuffer); +} + +/////////////////////////////////////////////////////////////////////////////// +// Patch management +/////////////////////////////////////////////////////////////////////////////// + +void Patch::copy(const int32_t* xDivs, const int32_t* yDivs) { + memcpy(mXDivs, xDivs, mXCount * sizeof(int32_t)); + memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t)); +} + +void Patch::copy(const int32_t* yDivs) { + memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t)); +} + +void Patch::updateColorKey(const uint32_t colorKey) { + mColorKey = colorKey; +} + +bool Patch::matches(const int32_t* xDivs, const int32_t* yDivs, const uint32_t colorKey) { + if (mColorKey != colorKey) { + updateColorKey(colorKey); + copy(xDivs, yDivs); + return false; + } + + for (uint32_t i = 0; i < mXCount; i++) { + if (mXDivs[i] != xDivs[i]) { + // The Y divs may or may not match, copy everything + copy(xDivs, yDivs); + return false; + } + } + + for (uint32_t i = 0; i < mYCount; i++) { + if (mYDivs[i] != yDivs[i]) { + // We know all the X divs match, copy only Y divs + copy(yDivs); + return false; + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// Vertices management +/////////////////////////////////////////////////////////////////////////////// + +void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, + float left, float top, float right, float bottom) { +#if RENDER_LAYERS_AS_REGIONS + if (hasEmptyQuads) quads.clear(); +#endif + + // Reset the vertices count here, we will count exactly how many + // vertices we actually need when generating the quads + verticesCount = 0; + + const uint32_t xStretchCount = (mXCount + 1) >> 1; + const uint32_t yStretchCount = (mYCount + 1) >> 1; + + float stretchX = 0.0f; + float stretchY = 0.0; + + const float meshWidth = right - left; + + if (xStretchCount > 0) { + uint32_t stretchSize = 0; + for (uint32_t i = 1; i < mXCount; i += 2) { + stretchSize += mXDivs[i] - mXDivs[i - 1]; + } + const float xStretchTex = stretchSize; + const float fixed = bitmapWidth - stretchSize; + const float xStretch = right - left - fixed; + stretchX = xStretch / xStretchTex; + } + + if (yStretchCount > 0) { + uint32_t stretchSize = 0; + for (uint32_t i = 1; i < mYCount; i += 2) { + stretchSize += mYDivs[i] - mYDivs[i - 1]; + } + const float yStretchTex = stretchSize; + const float fixed = bitmapHeight - stretchSize; + const float yStretch = bottom - top - fixed; + stretchY = yStretch / yStretchTex; + } + + TextureVertex* vertex = mVertices; + uint32_t quadCount = 0; + + float previousStepY = 0.0f; + + float y1 = 0.0f; + float v1 = 0.0f; + + for (uint32_t i = 0; i < mYCount; i++) { + float stepY = mYDivs[i]; + + float y2 = 0.0f; + if (i & 1) { + const float segment = stepY - previousStepY; + y2 = y1 + floorf(segment * stretchY + 0.5f); + } else { + y2 = y1 + stepY - previousStepY; + } + float v2 = fmax(0.0f, stepY - 0.5f) / bitmapHeight; + + if (stepY > 0.0f) { + generateRow(vertex, y1, y2, v1, v2, stretchX, right - left, + bitmapWidth, quadCount); + } + + y1 = y2; + v1 = (stepY + 0.5f) / bitmapHeight; + + previousStepY = stepY; + } + + if (previousStepY != bitmapHeight) { + generateRow(vertex, y1, bottom - top, v1, 1.0f, stretchX, right - left, + bitmapWidth, quadCount); + } + + if (verticesCount > 0) { + Caches::getInstance().bindMeshBuffer(meshBuffer); + if (!mUploaded) { + glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount, + mVertices, GL_DYNAMIC_DRAW); + mUploaded = true; + } else { + glBufferSubData(GL_ARRAY_BUFFER, 0, + sizeof(TextureVertex) * verticesCount, mVertices); + } + } + + PATCH_LOGD(" patch: new vertices count = %d", verticesCount); +} + +void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2, + float stretchX, float width, float bitmapWidth, uint32_t& quadCount) { + float previousStepX = 0.0f; + + float x1 = 0.0f; + float u1 = 0.0f; + + // Generate the row quad by quad + for (uint32_t i = 0; i < mXCount; i++) { + float stepX = mXDivs[i]; + + float x2 = 0.0f; + if (i & 1) { + const float segment = stepX - previousStepX; + x2 = x1 + floorf(segment * stretchX + 0.5f); + } else { + x2 = x1 + stepX - previousStepX; + } + float u2 = fmax(0.0f, stepX - 0.5f) / bitmapWidth; + + if (stepX > 0.0f) { + generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount); + } + + x1 = x2; + u1 = (stepX + 0.5f) / bitmapWidth; + + previousStepX = stepX; + } + + if (previousStepX != bitmapWidth) { + generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2, quadCount); + } +} + +void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, + float u1, float v1, float u2, float v2, uint32_t& quadCount) { + const uint32_t oldQuadCount = quadCount; + quadCount++; + + // Skip degenerate and transparent (empty) quads + if ((mColorKey >> oldQuadCount) & 0x1) { +#if DEBUG_PATCHES_EMPTY_VERTICES + PATCH_LOGD(" quad %d (empty)", oldQuadCount); + PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.2f, %.2f", x1, y1, u1, v1); + PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.2f, %.2f", x2, y2, u2, v2); +#endif + return; + } + +#if RENDER_LAYERS_AS_REGIONS + // Record all non empty quads + if (hasEmptyQuads) { + Rect bounds(x1, y1, x2, y2); + quads.add(bounds); + } +#endif + + // Left triangle + TextureVertex::set(vertex++, x1, y1, u1, v1); + TextureVertex::set(vertex++, x2, y1, u2, v1); + TextureVertex::set(vertex++, x1, y2, u1, v2); + + // Right triangle + TextureVertex::set(vertex++, x1, y2, u1, v2); + TextureVertex::set(vertex++, x2, y1, u2, v1); + TextureVertex::set(vertex++, x2, y2, u2, v2); + + // A quad is made of 2 triangles, 6 vertices + verticesCount += 6; + +#if DEBUG_PATCHES_VERTICES + PATCH_LOGD(" quad %d", oldQuadCount); + PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.2f, %.2f", x1, y1, u1, v1); + PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.2f, %.2f", x2, y2, u2, v2); +#endif +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h new file mode 100644 index 000000000000..0f0ffa22594e --- /dev/null +++ b/libs/hwui/Patch.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_PATCH_H +#define ANDROID_HWUI_PATCH_H + +#include <sys/types.h> + +#include <GLES2/gl2.h> + +#include <utils/Vector.h> + +#include "Rect.h" +#include "Vertex.h" +#include "utils/Compare.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// 9-patch structures +/////////////////////////////////////////////////////////////////////////////// + +/** + * An OpenGL patch. This contains an array of vertices and an array of + * indices to render the vertices. + */ +struct Patch { + Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads = 0); + ~Patch(); + + void updateVertices(const float bitmapWidth, const float bitmapHeight, + float left, float top, float right, float bottom); + + void updateColorKey(const uint32_t colorKey); + void copy(const int32_t* xDivs, const int32_t* yDivs); + bool matches(const int32_t* xDivs, const int32_t* yDivs, const uint32_t colorKey); + + GLuint meshBuffer; + uint32_t verticesCount; + bool hasEmptyQuads; + Vector<Rect> quads; + +private: + TextureVertex* mVertices; + bool mUploaded; + + int32_t* mXDivs; + int32_t* mYDivs; + uint32_t mColorKey; + + uint32_t mXCount; + uint32_t mYCount; + int8_t mEmptyQuads; + + void copy(const int32_t* yDivs); + + void generateRow(TextureVertex*& vertex, float y1, float y2, + float v1, float v2, float stretchX, float width, float bitmapWidth, + uint32_t& quadCount); + void generateQuad(TextureVertex*& vertex, + float x1, float y1, float x2, float y2, + float u1, float v1, float u2, float v2, + uint32_t& quadCount); +}; // struct Patch + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_PATCH_H diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp new file mode 100644 index 000000000000..9702c3d50c1f --- /dev/null +++ b/libs/hwui/PatchCache.cpp @@ -0,0 +1,109 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <utils/Log.h> + +#include "PatchCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +PatchCache::PatchCache(): mMaxEntries(DEFAULT_PATCH_CACHE_SIZE) { +} + +PatchCache::PatchCache(uint32_t maxEntries): mMaxEntries(maxEntries) { +} + +PatchCache::~PatchCache() { + clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void PatchCache::clear() { + size_t count = mCache.size(); + for (size_t i = 0; i < count; i++) { + delete mCache.valueAt(i); + } + mCache.clear(); +} + +Patch* PatchCache::get(const float bitmapWidth, const float bitmapHeight, + const float pixelWidth, const float pixelHeight, + const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors, + const uint32_t width, const uint32_t height, const int8_t numColors) { + + int8_t transparentQuads = 0; + uint32_t colorKey = 0; + + if (uint8_t(numColors) < sizeof(uint32_t) * 4) { + for (int8_t i = 0; i < numColors; i++) { + if (colors[i] == 0x0) { + transparentQuads++; + colorKey |= 0x1 << i; + } + } + } + + // If the 9patch is made of only transparent quads + if (transparentQuads == int8_t((width + 1) * (height + 1))) { + return NULL; + } + + const PatchDescription description(bitmapWidth, bitmapHeight, + pixelWidth, pixelHeight, width, height, transparentQuads, colorKey); + + ssize_t index = mCache.indexOfKey(description); + Patch* mesh = NULL; + if (index >= 0) { + mesh = mCache.valueAt(index); + } + + if (!mesh) { + PATCH_LOGD("New patch mesh " + "xCount=%d yCount=%d, w=%.2f h=%.2f, bw=%.2f bh=%.2f", + width, height, pixelWidth, pixelHeight, bitmapWidth, bitmapHeight); + + mesh = new Patch(width, height, transparentQuads); + mesh->updateColorKey(colorKey); + mesh->copy(xDivs, yDivs); + mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight); + + if (mCache.size() >= mMaxEntries) { + delete mCache.valueAt(mCache.size() - 1); + mCache.removeItemsAt(mCache.size() - 1, 1); + } + + mCache.add(description, mesh); + } else if (!mesh->matches(xDivs, yDivs, colorKey)) { + PATCH_LOGD("Patch mesh does not match, refreshing vertices"); + mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight); + } + + return mesh; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h new file mode 100644 index 000000000000..62d0ce190aec --- /dev/null +++ b/libs/hwui/PatchCache.h @@ -0,0 +1,129 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_PATCH_CACHE_H +#define ANDROID_HWUI_PATCH_CACHE_H + +#include <utils/KeyedVector.h> + +#include "utils/Compare.h" +#include "Debug.h" +#include "Patch.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#if DEBUG_PATCHES + #define PATCH_LOGD(...) LOGD(__VA_ARGS__) +#else + #define PATCH_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Cache +/////////////////////////////////////////////////////////////////////////////// + +class PatchCache { +public: + PatchCache(); + PatchCache(uint32_t maxCapacity); + ~PatchCache(); + + Patch* get(const float bitmapWidth, const float bitmapHeight, + const float pixelWidth, const float pixelHeight, + const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors, + const uint32_t width, const uint32_t height, const int8_t numColors); + void clear(); + + uint32_t getSize() const { + return mCache.size(); + } + + uint32_t getMaxSize() const { + return mMaxEntries; + } + +private: + /** + * Description of a patch. + */ + struct PatchDescription { + PatchDescription(): bitmapWidth(0), bitmapHeight(0), pixelWidth(0), pixelHeight(0), + xCount(0), yCount(0), emptyCount(0), colorKey(0) { + } + + PatchDescription(const float bitmapWidth, const float bitmapHeight, + const float pixelWidth, const float pixelHeight, + const uint32_t xCount, const uint32_t yCount, + const int8_t emptyCount, const uint32_t colorKey): + bitmapWidth(bitmapWidth), bitmapHeight(bitmapHeight), + pixelWidth(pixelWidth), pixelHeight(pixelHeight), + xCount(xCount), yCount(yCount), + emptyCount(emptyCount), colorKey(colorKey) { + } + + PatchDescription(const PatchDescription& description): + bitmapWidth(description.bitmapWidth), bitmapHeight(description.bitmapHeight), + pixelWidth(description.pixelWidth), pixelHeight(description.pixelHeight), + xCount(description.xCount), yCount(description.yCount), + emptyCount(description.emptyCount), colorKey(description.colorKey) { + } + + bool operator<(const PatchDescription& rhs) const { + LTE_FLOAT(bitmapWidth) { + LTE_FLOAT(bitmapHeight) { + LTE_FLOAT(pixelWidth) { + LTE_FLOAT(pixelHeight) { + LTE_INT(xCount) { + LTE_INT(yCount) { + LTE_INT(emptyCount) { + LTE_INT(colorKey) return false; + } + } + } + } + } + } + } + return false; + } + + private: + float bitmapWidth; + float bitmapHeight; + float pixelWidth; + float pixelHeight; + uint32_t xCount; + uint32_t yCount; + int8_t emptyCount; + uint32_t colorKey; + + }; // struct PatchDescription + + uint32_t mMaxEntries; + KeyedVector<PatchDescription, Patch*> mCache; + +}; // class PatchCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_PATCH_CACHE_H diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp new file mode 100644 index 000000000000..367c6275c711 --- /dev/null +++ b/libs/hwui/PathCache.cpp @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <utils/threads.h> + +#include "PathCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Path cache +/////////////////////////////////////////////////////////////////////////////// + +PathCache::PathCache(): ShapeCache<PathCacheEntry>("path", + PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) { +} + +void PathCache::remove(SkPath* path) { + // TODO: Linear search... + Vector<uint32_t> pathsToRemove; + for (uint32_t i = 0; i < mCache.size(); i++) { + if (mCache.getKeyAt(i).path == path) { + pathsToRemove.push(i); + removeTexture(mCache.getValueAt(i)); + } + } + + mCache.setOnEntryRemovedListener(NULL); + for (size_t i = 0; i < pathsToRemove.size(); i++) { + // This will work because pathsToRemove is sorted + // and because the cache is a sorted keyed vector + mCache.removeAt(pathsToRemove.itemAt(i) - i); + } + mCache.setOnEntryRemovedListener(this); +} + +void PathCache::removeDeferred(SkPath* path) { + Mutex::Autolock _l(mLock); + mGarbage.push(path); +} + +void PathCache::clearGarbage() { + Mutex::Autolock _l(mLock); + size_t count = mGarbage.size(); + for (size_t i = 0; i < count; i++) { + remove(mGarbage.itemAt(i)); + } + mGarbage.clear(); +} + +PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { + PathCacheEntry entry(path, paint); + PathTexture* texture = mCache.get(entry); + + if (!texture) { + texture = addTexture(entry, path, paint); + } else if (path->getGenerationID() != texture->generation) { + mCache.remove(entry); + texture = addTexture(entry, path, paint); + } + + return texture; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h new file mode 100644 index 000000000000..dc67e160fabf --- /dev/null +++ b/libs/hwui/PathCache.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_PATH_CACHE_H +#define ANDROID_HWUI_PATH_CACHE_H + +#include <utils/Vector.h> + +#include "Debug.h" +#include "ShapeCache.h" + +#include "utils/Compare.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Classes +/////////////////////////////////////////////////////////////////////////////// + +struct PathCacheEntry: public ShapeCacheEntry { + PathCacheEntry(SkPath* path, SkPaint* paint): + ShapeCacheEntry(ShapeCacheEntry::kShapePath, paint) { + this->path = path; + } + + PathCacheEntry(): ShapeCacheEntry() { + path = NULL; + } + + PathCacheEntry(const PathCacheEntry& entry): + ShapeCacheEntry(entry) { + path = entry.path; + } + + bool lessThan(const ShapeCacheEntry& r) const { + const PathCacheEntry& rhs = (const PathCacheEntry&) r; + LTE_INT(path) { + return false; + } + return false; + } + + SkPath* path; +}; // PathCacheEntry + +/** + * A simple LRU path cache. The cache has a maximum size expressed in bytes. + * Any texture added to the cache causing the cache to grow beyond the maximum + * allowed size will also cause the oldest texture to be kicked out. + */ +class PathCache: public ShapeCache<PathCacheEntry> { +public: + PathCache(); + + /** + * Returns the texture associated with the specified path. If the texture + * cannot be found in the cache, a new texture is generated. + */ + PathTexture* get(SkPath* path, SkPaint* paint); + /** + * Removes an entry. + */ + void remove(SkPath* path); + /** + * Removes the specified path. This is meant to be called from threads + * that are not the EGL context thread. + */ + void removeDeferred(SkPath* path); + /** + * Process deferred removals. + */ + void clearGarbage(); + +private: + Vector<SkPath*> mGarbage; + mutable Mutex mLock; +}; // class PathCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_PATH_CACHE_H diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp new file mode 100644 index 000000000000..2187f24e2c83 --- /dev/null +++ b/libs/hwui/Program.cpp @@ -0,0 +1,153 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "Program.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Base program +/////////////////////////////////////////////////////////////////////////////// + +Program::Program(const char* vertex, const char* fragment) { + mInitialized = false; + + vertexShader = buildShader(vertex, GL_VERTEX_SHADER); + if (vertexShader) { + + fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER); + if (fragmentShader) { + + id = glCreateProgram(); + glAttachShader(id, vertexShader); + glAttachShader(id, fragmentShader); + glLinkProgram(id); + + GLint status; + glGetProgramiv(id, GL_LINK_STATUS, &status); + if (status != GL_TRUE) { + LOGE("Error while linking shaders:"); + GLint infoLen = 0; + glGetProgramiv(id, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen > 1) { + GLchar log[infoLen]; + glGetProgramInfoLog(id, infoLen, 0, &log[0]); + LOGE("%s", log); + } + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + glDeleteProgram(id); + } else { + mInitialized = true; + } + } + } + + mUse = false; + + if (mInitialized) { + position = addAttrib("position"); + transform = addUniform("transform"); + } +} + +Program::~Program() { + if (mInitialized) { + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + glDeleteProgram(id); + } +} + +int Program::addAttrib(const char* name) { + int slot = glGetAttribLocation(id, name); + attributes.add(name, slot); + return slot; +} + +int Program::getAttrib(const char* name) { + ssize_t index = attributes.indexOfKey(name); + if (index >= 0) { + return attributes.valueAt(index); + } + return addAttrib(name); +} + +int Program::addUniform(const char* name) { + int slot = glGetUniformLocation(id, name); + uniforms.add(name, slot); + return slot; +} + +int Program::getUniform(const char* name) { + ssize_t index = uniforms.indexOfKey(name); + if (index >= 0) { + return uniforms.valueAt(index); + } + return addUniform(name); +} + +GLuint Program::buildShader(const char* source, GLenum type) { + GLuint shader = glCreateShader(type); + glShaderSource(shader, 1, &source, 0); + glCompileShader(shader); + + GLint status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) { + // Some drivers return wrong values for GL_INFO_LOG_LENGTH + // use a fixed size instead + GLchar log[512]; + glGetShaderInfoLog(shader, sizeof(log), 0, &log[0]); + LOGE("Error while compiling shader: %s", log); + glDeleteShader(shader); + return 0; + } + + return shader; +} + +void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, + const mat4& transformMatrix) { + mat4 t(projectionMatrix); + t.multiply(transformMatrix); + t.multiply(modelViewMatrix); + + glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]); +} + +void Program::setColor(const float r, const float g, const float b, const float a) { + glUniform4f(getUniform("color"), r, g, b, a); +} + +void Program::use() { + glUseProgram(id); + mUse = true; + + glEnableVertexAttribArray(position); +} + +void Program::remove() { + mUse = false; + + glDisableVertexAttribArray(position); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h new file mode 100644 index 000000000000..afc6f3d4a368 --- /dev/null +++ b/libs/hwui/Program.h @@ -0,0 +1,142 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_PROGRAM_H +#define ANDROID_HWUI_PROGRAM_H + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <utils/KeyedVector.h> + +#include "Matrix.h" + +namespace android { +namespace uirenderer { + +/** + * A program holds a vertex and a fragment shader. It offers several utility + * methods to query attributes and uniforms. + */ +class Program { +public: + /** + * Creates a new program with the specified vertex and fragment + * shaders sources. + */ + Program(const char* vertex, const char* fragment); + virtual ~Program(); + + /** + * Binds this program to the GL context. + */ + virtual void use(); + + /** + * Marks this program as unused. This will not unbind + * the program from the GL context. + */ + virtual void remove(); + + /** + * Returns the OpenGL name of the specified attribute. + */ + int getAttrib(const char* name); + + /** + * Returns the OpenGL name of the specified uniform. + */ + int getUniform(const char* name); + + /** + * Indicates whether this program is currently in use with + * the GL context. + */ + inline bool isInUse() const { + return mUse; + } + + /** + * Indicates whether this program was correctly compiled and linked. + */ + inline bool isInitialized() const { + return mInitialized; + } + + /** + * Binds the program with the specified projection, modelView and + * transform matrices. + */ + void set(const mat4& projectionMatrix, const mat4& modelViewMatrix, + const mat4& transformMatrix); + + /** + * Sets the color associated with this shader. + */ + void setColor(const float r, const float g, const float b, const float a); + + /** + * Name of the position attribute. + */ + int position; + + /** + * Name of the transform uniform. + */ + int transform; + +protected: + /** + * Adds an attribute with the specified name. + * + * @return The OpenGL name of the attribute. + */ + int addAttrib(const char* name); + + /** + * Adds a uniform with the specified name. + * + * @return The OpenGL name of the uniform. + */ + int addUniform(const char* name); + +private: + /** + * Compiles the specified shader of the specified type. + * + * @return The name of the compiled shader. + */ + GLuint buildShader(const char* source, GLenum type); + + // Name of the OpenGL program + GLuint id; + + // Name of the shaders + GLuint vertexShader; + GLuint fragmentShader; + + // Keeps track of attributes and uniforms slots + KeyedVector<const char*, int> attributes; + KeyedVector<const char*, int> uniforms; + + bool mUse; + bool mInitialized; +}; // class Program + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_PROGRAM_H diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp new file mode 100644 index 000000000000..0b6c7b5c64d2 --- /dev/null +++ b/libs/hwui/ProgramCache.cpp @@ -0,0 +1,617 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <utils/String8.h> + +#include "ProgramCache.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#define MODULATE_OP_NO_MODULATE 0 +#define MODULATE_OP_MODULATE 1 +#define MODULATE_OP_MODULATE_A8 2 + +/////////////////////////////////////////////////////////////////////////////// +// Vertex shaders snippets +/////////////////////////////////////////////////////////////////////////////// + +const char* gVS_Header_Attributes = + "attribute vec4 position;\n"; +const char* gVS_Header_Attributes_TexCoords = + "attribute vec2 texCoords;\n"; +const char* gVS_Header_Uniforms = + "uniform mat4 transform;\n"; +const char* gVS_Header_Uniforms_HasGradient[3] = { + // Linear + "uniform mat4 screenSpace;\n", + // Circular + "uniform mat4 screenSpace;\n", + // Sweep + "uniform mat4 screenSpace;\n" +}; +const char* gVS_Header_Uniforms_HasBitmap = + "uniform mat4 textureTransform;\n" + "uniform vec2 textureDimension;\n"; +const char* gVS_Header_Varyings_HasTexture = + "varying vec2 outTexCoords;\n"; +const char* gVS_Header_Varyings_HasBitmap = + "varying vec2 outBitmapTexCoords;\n"; +const char* gVS_Header_Varyings_HasGradient[3] = { + // Linear + "varying vec2 linear;\n", + // Circular + "varying vec2 circular;\n", + // Sweep + "varying vec2 sweep;\n" +}; +const char* gVS_Main = + "\nvoid main(void) {\n"; +const char* gVS_Main_OutTexCoords = + " outTexCoords = texCoords;\n"; +const char* gVS_Main_OutGradient[3] = { + // Linear + " linear = vec2((screenSpace * position).x, 0.5);\n", + // Circular + " circular = (screenSpace * position).xy;\n", + // Sweep + " sweep = (screenSpace * position).xy;\n" +}; +const char* gVS_Main_OutBitmapTexCoords = + " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; +const char* gVS_Main_Position = + " gl_Position = transform * position;\n"; +const char* gVS_Footer = + "}\n\n"; + +/////////////////////////////////////////////////////////////////////////////// +// Fragment shaders snippets +/////////////////////////////////////////////////////////////////////////////// + +const char* gFS_Header_Extension_FramebufferFetch = + "#extension GL_NV_shader_framebuffer_fetch : enable\n\n"; +const char* gFS_Header = + "precision mediump float;\n\n"; +const char* gFS_Uniforms_Color = + "uniform vec4 color;\n"; +const char* gFS_Uniforms_TextureSampler = + "uniform sampler2D sampler;\n"; +const char* gFS_Uniforms_GradientSampler[3] = { + // Linear + "uniform sampler2D gradientSampler;\n", + // Circular + "uniform sampler2D gradientSampler;\n", + // Sweep + "uniform sampler2D gradientSampler;\n" +}; +const char* gFS_Uniforms_BitmapSampler = + "uniform sampler2D bitmapSampler;\n"; +const char* gFS_Uniforms_ColorOp[4] = { + // None + "", + // Matrix + "uniform mat4 colorMatrix;\n" + "uniform vec4 colorMatrixVector;\n", + // Lighting + "uniform vec4 lightingMul;\n" + "uniform vec4 lightingAdd;\n", + // PorterDuff + "uniform vec4 colorBlend;\n" +}; +const char* gFS_Main = + "\nvoid main(void) {\n" + " lowp vec4 fragColor;\n"; + +// Fast cases +const char* gFS_Fast_SingleColor = + "\nvoid main(void) {\n" + " gl_FragColor = color;\n" + "}\n\n"; +const char* gFS_Fast_SingleTexture = + "\nvoid main(void) {\n" + " gl_FragColor = texture2D(sampler, outTexCoords);\n" + "}\n\n"; +const char* gFS_Fast_SingleModulateTexture = + "\nvoid main(void) {\n" + " gl_FragColor = color.a * texture2D(sampler, outTexCoords);\n" + "}\n\n"; +const char* gFS_Fast_SingleA8Texture = + "\nvoid main(void) {\n" + " gl_FragColor = texture2D(sampler, outTexCoords);\n" + "}\n\n"; +const char* gFS_Fast_SingleModulateA8Texture = + "\nvoid main(void) {\n" + " gl_FragColor = color * texture2D(sampler, outTexCoords).a;\n" + "}\n\n"; +const char* gFS_Fast_SingleGradient = + "\nvoid main(void) {\n" + " gl_FragColor = texture2D(gradientSampler, linear);\n" + "}\n\n"; +const char* gFS_Fast_SingleModulateGradient = + "\nvoid main(void) {\n" + " gl_FragColor = color.a * texture2D(gradientSampler, linear);\n" + "}\n\n"; + +// General case +const char* gFS_Main_FetchColor = + " fragColor = color;\n"; +const char* gFS_Main_FetchTexture[2] = { + // Don't modulate + " fragColor = texture2D(sampler, outTexCoords);\n", + // Modulate + " fragColor = color * texture2D(sampler, outTexCoords);\n" +}; +const char* gFS_Main_FetchA8Texture[2] = { + // Don't modulate + " fragColor = texture2D(sampler, outTexCoords);\n", + // Modulate + " fragColor = color * texture2D(sampler, outTexCoords).a;\n" +}; +const char* gFS_Main_FetchGradient[3] = { + // Linear + " vec4 gradientColor = texture2D(gradientSampler, linear);\n", + // Circular + " float index = length(circular);\n" + " vec4 gradientColor = texture2D(gradientSampler, vec2(index, 0.5));\n", + // Sweep + " float index = atan(sweep.y, sweep.x) * 0.15915494309; // inv(2 * PI)\n" + " vec4 gradientColor = texture2D(gradientSampler, vec2(index - floor(index), 0.5));\n" +}; +const char* gFS_Main_FetchBitmap = + " vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n"; +const char* gFS_Main_FetchBitmapNpot = + " vec4 bitmapColor = texture2D(bitmapSampler, wrap(outBitmapTexCoords));\n"; +const char* gFS_Main_BlendShadersBG = + " fragColor = blendShaders(gradientColor, bitmapColor)"; +const char* gFS_Main_BlendShadersGB = + " fragColor = blendShaders(bitmapColor, gradientColor)"; +const char* gFS_Main_BlendShaders_Modulate[3] = { + // Don't modulate + ";\n", + // Modulate + " * fragColor.a;\n", + // Modulate with alpha 8 texture + " * texture2D(sampler, outTexCoords).a;\n" +}; +const char* gFS_Main_GradientShader_Modulate[3] = { + // Don't modulate + " fragColor = gradientColor;\n", + // Modulate + " fragColor = gradientColor * fragColor.a;\n", + // Modulate with alpha 8 texture + " fragColor = gradientColor * texture2D(sampler, outTexCoords).a;\n" + }; +const char* gFS_Main_BitmapShader_Modulate[3] = { + // Don't modulate + " fragColor = bitmapColor;\n", + // Modulate + " fragColor = bitmapColor * fragColor.a;\n", + // Modulate with alpha 8 texture + " fragColor = bitmapColor * texture2D(sampler, outTexCoords).a;\n" + }; +const char* gFS_Main_FragColor = + " gl_FragColor = fragColor;\n"; +const char* gFS_Main_FragColor_Blend = + " gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n"; +const char* gFS_Main_FragColor_Blend_Swap = + " gl_FragColor = blendFramebuffer(gl_LastFragColor, fragColor);\n"; +const char* gFS_Main_ApplyColorOp[4] = { + // None + "", + // Matrix + // TODO: Fix premultiplied alpha computations for color matrix + " fragColor *= colorMatrix;\n" + " fragColor += colorMatrixVector;\n" + " fragColor.rgb *= fragColor.a;\n", + // Lighting + " float lightingAlpha = fragColor.a;\n" + " fragColor = min(fragColor * lightingMul + (lightingAdd * lightingAlpha), lightingAlpha);\n" + " fragColor.a = lightingAlpha;\n", + // PorterDuff + " fragColor = blendColors(colorBlend, fragColor);\n" +}; +const char* gFS_Footer = + "}\n\n"; + +/////////////////////////////////////////////////////////////////////////////// +// PorterDuff snippets +/////////////////////////////////////////////////////////////////////////////// + +const char* gBlendOps[18] = { + // Clear + "return vec4(0.0, 0.0, 0.0, 0.0);\n", + // Src + "return src;\n", + // Dst + "return dst;\n", + // SrcOver + "return src + dst * (1.0 - src.a);\n", + // DstOver + "return dst + src * (1.0 - dst.a);\n", + // SrcIn + "return src * dst.a;\n", + // DstIn + "return dst * src.a;\n", + // SrcOut + "return src * (1.0 - dst.a);\n", + // DstOut + "return dst * (1.0 - src.a);\n", + // SrcAtop + "return vec4(src.rgb * dst.a + (1.0 - src.a) * dst.rgb, dst.a);\n", + // DstAtop + "return vec4(dst.rgb * src.a + (1.0 - dst.a) * src.rgb, src.a);\n", + // Xor + "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb, " + "src.a + dst.a - 2.0 * src.a * dst.a);\n", + // Add + "return min(src + dst, 1.0);\n", + // Multiply + "return src * dst;\n", + // Screen + "return src + dst - src * dst;\n", + // Overlay + "return clamp(vec4(mix(" + "2.0 * src.rgb * dst.rgb + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), " + "src.a * dst.a - 2.0 * (dst.a - dst.rgb) * (src.a - src.rgb) + src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a), " + "step(dst.a, 2.0 * dst.rgb)), " + "src.a + dst.a - src.a * dst.a), 0.0, 1.0);\n", + // Darken + "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + " + "min(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n", + // Lighten + "return vec4(src.rgb * (1.0 - dst.a) + (1.0 - src.a) * dst.rgb + " + "max(src.rgb * dst.a, dst.rgb * src.a), src.a + dst.a - src.a * dst.a);\n", +}; + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructors +/////////////////////////////////////////////////////////////////////////////// + +ProgramCache::ProgramCache() { +} + +ProgramCache::~ProgramCache() { + clear(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Cache management +/////////////////////////////////////////////////////////////////////////////// + +void ProgramCache::clear() { + PROGRAM_LOGD("Clearing program cache"); + + size_t count = mCache.size(); + for (size_t i = 0; i < count; i++) { + delete mCache.valueAt(i); + } + mCache.clear(); +} + +Program* ProgramCache::get(const ProgramDescription& description) { + programid key = description.key(); + ssize_t index = mCache.indexOfKey(key); + Program* program = NULL; + if (index < 0) { + description.log("Could not find program"); + program = generateProgram(description, key); + mCache.add(key, program); + } else { + program = mCache.valueAt(index); + } + return program; +} + +/////////////////////////////////////////////////////////////////////////////// +// Program generation +/////////////////////////////////////////////////////////////////////////////// + +Program* ProgramCache::generateProgram(const ProgramDescription& description, programid key) { + String8 vertexShader = generateVertexShader(description); + String8 fragmentShader = generateFragmentShader(description); + + Program* program = new Program(vertexShader.string(), fragmentShader.string()); + return program; +} + +String8 ProgramCache::generateVertexShader(const ProgramDescription& description) { + // Add attributes + String8 shader(gVS_Header_Attributes); + if (description.hasTexture) { + shader.append(gVS_Header_Attributes_TexCoords); + } + // Uniforms + shader.append(gVS_Header_Uniforms); + if (description.hasGradient) { + shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]); + } + if (description.hasBitmap) { + shader.append(gVS_Header_Uniforms_HasBitmap); + } + // Varyings + if (description.hasTexture) { + shader.append(gVS_Header_Varyings_HasTexture); + } + if (description.hasGradient) { + shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); + } + if (description.hasBitmap) { + shader.append(gVS_Header_Varyings_HasBitmap); + } + + // Begin the shader + shader.append(gVS_Main); { + if (description.hasTexture) { + shader.append(gVS_Main_OutTexCoords); + } + if (description.hasGradient) { + shader.append(gVS_Main_OutGradient[description.gradientType]); + } + if (description.hasBitmap) { + shader.append(gVS_Main_OutBitmapTexCoords); + } + // Output transformed position + shader.append(gVS_Main_Position); + } + // End the shader + shader.append(gVS_Footer); + + PROGRAM_LOGD("*** Generated vertex shader:\n\n%s", shader.string()); + + return shader; +} + +String8 ProgramCache::generateFragmentShader(const ProgramDescription& description) { + // Set the default precision + String8 shader; + + const bool blendFramebuffer = description.framebufferMode >= SkXfermode::kPlus_Mode; + if (blendFramebuffer) { + shader.append(gFS_Header_Extension_FramebufferFetch); + } + + shader.append(gFS_Header); + + // Varyings + if (description.hasTexture) { + shader.append(gVS_Header_Varyings_HasTexture); + } + if (description.hasGradient) { + shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); + } + if (description.hasBitmap) { + shader.append(gVS_Header_Varyings_HasBitmap); + } + + // Uniforms + int modulateOp = MODULATE_OP_NO_MODULATE; + const bool singleColor = !description.hasTexture && + !description.hasGradient && !description.hasBitmap; + + if (description.modulate || singleColor) { + shader.append(gFS_Uniforms_Color); + if (!singleColor) modulateOp = MODULATE_OP_MODULATE; + } + if (description.hasTexture) { + shader.append(gFS_Uniforms_TextureSampler); + } + if (description.hasGradient) { + shader.append(gFS_Uniforms_GradientSampler[description.gradientType]); + } + + // Optimization for common cases + if (!blendFramebuffer && description.colorOp == ProgramDescription::kColorNone) { + bool fast = false; + + const bool noShader = !description.hasGradient && !description.hasBitmap; + const bool singleTexture = description.hasTexture && + !description.hasAlpha8Texture && noShader; + const bool singleA8Texture = description.hasTexture && + description.hasAlpha8Texture && noShader; + const bool singleGradient = !description.hasTexture && + description.hasGradient && !description.hasBitmap && + description.gradientType == ProgramDescription::kGradientLinear; + + if (singleColor) { + shader.append(gFS_Fast_SingleColor); + fast = true; + } else if (singleTexture) { + if (!description.modulate) { + shader.append(gFS_Fast_SingleTexture); + } else { + shader.append(gFS_Fast_SingleModulateTexture); + } + fast = true; + } else if (singleA8Texture) { + if (!description.modulate) { + shader.append(gFS_Fast_SingleA8Texture); + } else { + shader.append(gFS_Fast_SingleModulateA8Texture); + } + fast = true; + } else if (singleGradient) { + if (!description.modulate) { + shader.append(gFS_Fast_SingleGradient); + } else { + shader.append(gFS_Fast_SingleModulateGradient); + } + fast = true; + } + + if (fast) { +#if DEBUG_PROGRAMS + PROGRAM_LOGD("*** Fast case:\n"); + PROGRAM_LOGD("*** Generated fragment shader:\n\n"); + printLongString(shader); +#endif + + return shader; + } + } + + if (description.hasBitmap) { + shader.append(gFS_Uniforms_BitmapSampler); + } + shader.append(gFS_Uniforms_ColorOp[description.colorOp]); + + // Generate required functions + if (description.hasGradient && description.hasBitmap) { + generateBlend(shader, "blendShaders", description.shadersMode); + } + if (description.colorOp == ProgramDescription::kColorBlend) { + generateBlend(shader, "blendColors", description.colorMode); + } + if (blendFramebuffer) { + generateBlend(shader, "blendFramebuffer", description.framebufferMode); + } + if (description.isBitmapNpot) { + generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT); + } + + // Begin the shader + shader.append(gFS_Main); { + // Stores the result in fragColor directly + if (description.hasTexture) { + if (description.hasAlpha8Texture) { + if (!description.hasGradient && !description.hasBitmap) { + shader.append(gFS_Main_FetchA8Texture[modulateOp]); + } + } else { + shader.append(gFS_Main_FetchTexture[modulateOp]); + } + } else { + if ((!description.hasGradient && !description.hasBitmap) || description.modulate) { + shader.append(gFS_Main_FetchColor); + } + } + if (description.hasGradient) { + shader.append(gFS_Main_FetchGradient[description.gradientType]); + } + if (description.hasBitmap) { + if (!description.isBitmapNpot) { + shader.append(gFS_Main_FetchBitmap); + } else { + shader.append(gFS_Main_FetchBitmapNpot); + } + } + // Case when we have two shaders set + if (description.hasGradient && description.hasBitmap) { + int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; + if (description.isBitmapFirst) { + shader.append(gFS_Main_BlendShadersBG); + } else { + shader.append(gFS_Main_BlendShadersGB); + } + shader.append(gFS_Main_BlendShaders_Modulate[op]); + } else { + if (description.hasGradient) { + int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; + shader.append(gFS_Main_GradientShader_Modulate[op]); + } else if (description.hasBitmap) { + int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; + shader.append(gFS_Main_BitmapShader_Modulate[op]); + } + } + // Apply the color op if needed + shader.append(gFS_Main_ApplyColorOp[description.colorOp]); + // Output the fragment + if (!blendFramebuffer) { + shader.append(gFS_Main_FragColor); + } else { + shader.append(!description.swapSrcDst ? + gFS_Main_FragColor_Blend : gFS_Main_FragColor_Blend_Swap); + } + } + // End the shader + shader.append(gFS_Footer); + +#if DEBUG_PROGRAMS + PROGRAM_LOGD("*** Generated fragment shader:\n\n"); + printLongString(shader); +#endif + + return shader; +} + +void ProgramCache::generateBlend(String8& shader, const char* name, SkXfermode::Mode mode) { + shader.append("\nvec4 "); + shader.append(name); + shader.append("(vec4 src, vec4 dst) {\n"); + shader.append(" "); + shader.append(gBlendOps[mode]); + shader.append("}\n"); +} + +void ProgramCache::generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT) { + shader.append("\nvec2 wrap(vec2 texCoords) {\n"); + if (wrapS == GL_MIRRORED_REPEAT) { + shader.append(" float xMod2 = mod(texCoords.x, 2.0);\n"); + shader.append(" if (xMod2 > 1.0) xMod2 = 2.0 - xMod2;\n"); + } + if (wrapT == GL_MIRRORED_REPEAT) { + shader.append(" float yMod2 = mod(texCoords.y, 2.0);\n"); + shader.append(" if (yMod2 > 1.0) yMod2 = 2.0 - yMod2;\n"); + } + shader.append(" return vec2("); + switch (wrapS) { + case GL_CLAMP_TO_EDGE: + shader.append("texCoords.x"); + break; + case GL_REPEAT: + shader.append("mod(texCoords.x, 1.0)"); + break; + case GL_MIRRORED_REPEAT: + shader.append("xMod2"); + break; + } + shader.append(", "); + switch (wrapT) { + case GL_CLAMP_TO_EDGE: + shader.append("texCoords.y"); + break; + case GL_REPEAT: + shader.append("mod(texCoords.y, 1.0)"); + break; + case GL_MIRRORED_REPEAT: + shader.append("yMod2"); + break; + } + shader.append(");\n"); + shader.append("}\n"); +} + +void ProgramCache::printLongString(const String8& shader) const { + ssize_t index = 0; + ssize_t lastIndex = 0; + const char* str = shader.string(); + while ((index = shader.find("\n", index)) > -1) { + String8 line(str, index - lastIndex); + if (line.length() == 0) line.append("\n"); + PROGRAM_LOGD("%s", line.string()); + index++; + str += (index - lastIndex); + lastIndex = index; + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h new file mode 100644 index 000000000000..ead5b920c1dc --- /dev/null +++ b/libs/hwui/ProgramCache.h @@ -0,0 +1,284 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_PROGRAM_CACHE_H +#define ANDROID_HWUI_PROGRAM_CACHE_H + +#include <utils/KeyedVector.h> +#include <utils/Log.h> +#include <utils/String8.h> + +#include <GLES2/gl2.h> + +#include <SkXfermode.h> + +#include "Debug.h" +#include "Program.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#if DEBUG_PROGRAMS + #define PROGRAM_LOGD(...) LOGD(__VA_ARGS__) +#else + #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 35 + +/////////////////////////////////////////////////////////////////////////////// +// 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; + + // Modulate, this should only be set when setColor() return true + bool modulate; + + // Shaders + bool hasBitmap; + bool isBitmapNpot; + + 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; + + /** + * 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; + + 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; + } + + /** + * 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; + 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. + */ +class ProgramCache { +public: + ProgramCache(); + ~ProgramCache(); + + Program* get(const ProgramDescription& description); + + void clear(); + +private: + Program* generateProgram(const ProgramDescription& description, programid key); + String8 generateVertexShader(const ProgramDescription& description); + String8 generateFragmentShader(const ProgramDescription& description); + void generateBlend(String8& shader, const char* name, SkXfermode::Mode mode); + void generateTextureWrap(String8& shader, GLenum wrapS, GLenum wrapT); + + void printLongString(const String8& shader) const; + + KeyedVector<programid, Program*> mCache; +}; // class ProgramCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_PROGRAM_CACHE_H diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h new file mode 100644 index 000000000000..1aef99b53f68 --- /dev/null +++ b/libs/hwui/Properties.h @@ -0,0 +1,90 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_PROPERTIES_H +#define ANDROID_HWUI_PROPERTIES_H + +#include <cutils/properties.h> +#include <stdlib.h> + +/** + * This file contains the list of system properties used to configure + * the OpenGLRenderer. + */ + +// If turned on, layers drawn inside FBOs are optimized with regions +#define RENDER_LAYERS_AS_REGIONS 1 +// If turned on, layers that map to a single rect are drawn as a rect +#define RENDER_LAYERS_RECT_AS_RECT 0 + +/** + * Debug level for app developers. + */ +#define PROPERTY_DEBUG "hwui.debug_level" + +/** + * Debug levels. Debug levels are used as flags. + */ +enum DebugLevel { + kDebugDisabled = 0, + kDebugMemory = 1, + kDebugCaches = 2, + kDebugMoreCaches = 3 +}; + +// These properties are defined in mega-bytes +#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size" +#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size" +#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size" +#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size" +#define PROPERTY_SHAPE_CACHE_SIZE "ro.hwui.shape_cache_size" +#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size" +#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size" + +// These properties are defined in pixels +#define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width" +#define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height" + +// Gamma (>= 1.0, <= 10.0) +#define PROPERTY_TEXT_GAMMA "ro.text_gamma" +#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "ro.text_gamma.black_threshold" +#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "ro.text_gamma.white_threshold" + +// Converts a number of mega-bytes into bytes +#define MB(s) s * 1024 * 1024 + +#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f +#define DEFAULT_LAYER_CACHE_SIZE 24.0f +#define DEFAULT_PATH_CACHE_SIZE 4.0f +#define DEFAULT_SHAPE_CACHE_SIZE 1.0f +#define DEFAULT_PATCH_CACHE_SIZE 512 +#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f +#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f +#define DEFAULT_FBO_CACHE_SIZE 16 + +#define DEFAULT_TEXT_GAMMA 1.4f +#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64 +#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192 + +static DebugLevel readDebugLevel() { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_DEBUG, property, NULL) > 0) { + return (DebugLevel) atoi(property); + } + return kDebugDisabled; +} + +#endif // ANDROID_HWUI_PROPERTIES_H diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h new file mode 100644 index 000000000000..71951b7a3cc8 --- /dev/null +++ b/libs/hwui/Rect.h @@ -0,0 +1,190 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_RECT_H +#define ANDROID_HWUI_RECT_H + +#include <cmath> + +#include <utils/Log.h> + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Structs +/////////////////////////////////////////////////////////////////////////////// + +struct Rect { + float left; + float top; + float right; + float bottom; + + // Used by Region + typedef float value_type; + + inline Rect(): + left(0), + top(0), + right(0), + bottom(0) { + } + + inline Rect(float left, float top, float right, float bottom): + left(left), + top(top), + right(right), + bottom(bottom) { + } + + inline Rect(float width, float height): + left(0.0f), + top(0.0f), + right(width), + bottom(height) { + } + + inline Rect(const Rect& r) { + set(r); + } + + inline Rect(Rect& r) { + set(r); + } + + Rect& operator=(const Rect& r) { + set(r); + return *this; + } + + Rect& operator=(Rect& r) { + set(r); + return *this; + } + + friend int operator==(const Rect& a, const Rect& b) { + return !memcmp(&a, &b, sizeof(a)); + } + + friend int operator!=(const Rect& a, const Rect& b) { + return memcmp(&a, &b, sizeof(a)); + } + + inline void clear() { + left = top = right = bottom = 0.0f; + } + + inline bool isEmpty() const { + return left >= right || top >= bottom; + } + + inline void setEmpty() { + left = top = right = bottom = 0.0f; + } + + inline void set(float left, float top, float right, float bottom) { + this->left = left; + this->right = right; + this->top = top; + this->bottom = bottom; + } + + inline void set(const Rect& r) { + set(r.left, r.top, r.right, r.bottom); + } + + inline float getWidth() const { + return right - left; + } + + inline float getHeight() const { + return bottom - top; + } + + bool intersects(float left, float top, float right, float bottom) const { + return left < right && top < bottom && + this->left < this->right && this->top < this->bottom && + this->left < right && left < this->right && + this->top < bottom && top < this->bottom; + } + + bool intersects(const Rect& r) const { + return intersects(r.left, r.top, r.right, r.bottom); + } + + bool intersect(float left, float top, float right, float bottom) { + if (left < right && top < bottom && !this->isEmpty() && + this->left < right && left < this->right && + this->top < bottom && top < this->bottom) { + + if (this->left < left) this->left = left; + if (this->top < top) this->top = top; + if (this->right > right) this->right = right; + if (this->bottom > bottom) this->bottom = bottom; + + return true; + } + return false; + } + + bool intersect(const Rect& r) { + return intersect(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) { + if (left > r.left) left = r.left; + if (top > r.top) top = r.top; + if (right < r.right) right = r.right; + if (bottom < r.bottom) bottom = r.bottom; + return true; + } else { + left = r.left; + top = r.top; + right = r.right; + bottom = r.bottom; + return true; + } + } + return false; + } + + void translate(float dx, float dy) { + left += dx; + right += dx; + top += dy; + bottom += dy; + } + + void snapToPixelBoundaries() { + left = floorf(left + 0.5f); + top = floorf(top + 0.5f); + right = floorf(right + 0.5f); + bottom = floorf(bottom + 0.5f); + } + + void dump() const { + LOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom); + } + +}; // struct Rect + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_RECT_H diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp new file mode 100644 index 000000000000..9aade5175c56 --- /dev/null +++ b/libs/hwui/ResourceCache.cpp @@ -0,0 +1,252 @@ +/* + * 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 <SkPixelRef.h> +#include "ResourceCache.h" +#include "Caches.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Resource cache +/////////////////////////////////////////////////////////////////////////////// + +void ResourceCache::logCache() { + LOGD("ResourceCache: cacheReport:"); + for (size_t i = 0; i < mCache->size(); ++i) { + ResourceReference* ref = mCache->valueAt(i); + LOGD(" ResourceCache: mCache(%d): resource, ref = 0x%p, 0x%p", + i, mCache->keyAt(i), mCache->valueAt(i)); + LOGD(" ResourceCache: mCache(%d): refCount, recycled, destroyed, type = %d, %d, %d, %d", + i, ref->refCount, ref->recycled, ref->destroyed, ref->resourceType); + } +} + +ResourceCache::ResourceCache() { + Mutex::Autolock _l(mLock); + mCache = new KeyedVector<void *, ResourceReference *>(); +} + +ResourceCache::~ResourceCache() { + Mutex::Autolock _l(mLock); + delete mCache; +} + +void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) { + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mCache->size(); ++i) { + void* ref = mCache->valueAt(i); + } + ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL; + if (ref == NULL || mCache->size() == 0) { + ref = new ResourceReference(resourceType); + mCache->add(resource, ref); + } + ref->refCount++; +} + +void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) { + SkSafeRef(bitmapResource->pixelRef()); + SkSafeRef(bitmapResource->getColorTable()); + incrementRefcount((void*)bitmapResource, kBitmap); +} + +void ResourceCache::incrementRefcount(SkPath* pathResource) { + incrementRefcount((void*)pathResource, kPath); +} + +void ResourceCache::incrementRefcount(SkiaShader* shaderResource) { + SkSafeRef(shaderResource->getSkShader()); + incrementRefcount((void*) shaderResource, kShader); +} + +void ResourceCache::incrementRefcount(SkiaColorFilter* filterResource) { + SkSafeRef(filterResource->getSkColorFilter()); + incrementRefcount((void*) filterResource, kColorFilter); +} + +void ResourceCache::decrementRefcount(void* resource) { + Mutex::Autolock _l(mLock); + ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL; + if (ref == NULL) { + // Should not get here - shouldn't get a call to decrement if we're not yet tracking it + return; + } + ref->refCount--; + if (ref->refCount == 0) { + deleteResourceReference(resource, ref); + } +} + +void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) { + SkSafeUnref(bitmapResource->pixelRef()); + SkSafeUnref(bitmapResource->getColorTable()); + decrementRefcount((void*) bitmapResource); +} + +void ResourceCache::decrementRefcount(SkPath* pathResource) { + decrementRefcount((void*) pathResource); +} + +void ResourceCache::decrementRefcount(SkiaShader* shaderResource) { + SkSafeUnref(shaderResource->getSkShader()); + decrementRefcount((void*) shaderResource); +} + +void ResourceCache::decrementRefcount(SkiaColorFilter* filterResource) { + SkSafeUnref(filterResource->getSkColorFilter()); + decrementRefcount((void*) filterResource); +} + +void ResourceCache::recycle(SkBitmap* resource) { + Mutex::Autolock _l(mLock); + if (mCache->indexOfKey(resource) < 0) { + // not tracking this resource; just recycle the pixel data + resource->setPixels(NULL, NULL); + return; + } + ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL; + if (ref == NULL) { + // Should not get here - shouldn't get a call to recycle if we're not yet tracking it + return; + } + ref->recycled = true; + if (ref->refCount == 0) { + deleteResourceReference(resource, ref); + } +} + +void ResourceCache::destructor(SkPath* resource) { + Mutex::Autolock _l(mLock); + ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL; + if (ref == NULL) { + // If we're not tracking this resource, just delete it + if (Caches::hasInstance()) { + Caches::getInstance().pathCache.removeDeferred(resource); + } + delete resource; + return; + } + ref->destroyed = true; + if (ref->refCount == 0) { + deleteResourceReference(resource, ref); + return; + } +} + +void ResourceCache::destructor(SkBitmap* resource) { + Mutex::Autolock _l(mLock); + ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL; + if (ref == NULL) { + // If we're not tracking this resource, just delete it + if (Caches::hasInstance()) { + Caches::getInstance().textureCache.removeDeferred(resource); + } + delete resource; + return; + } + ref->destroyed = true; + if (ref->refCount == 0) { + deleteResourceReference(resource, ref); + return; + } +} + +void ResourceCache::destructor(SkiaShader* resource) { + Mutex::Autolock _l(mLock); + ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL; + if (ref == NULL) { + // If we're not tracking this resource, just delete it + if (Caches::hasInstance()) { + Caches::getInstance().gradientCache.removeDeferred(resource->getSkShader()); + } + delete resource; + return; + } + ref->destroyed = true; + if (ref->refCount == 0) { + deleteResourceReference(resource, ref); + return; + } +} + +void ResourceCache::destructor(SkiaColorFilter* resource) { + Mutex::Autolock _l(mLock); + ResourceReference* ref = mCache->indexOfKey(resource) >= 0 ? mCache->valueFor(resource) : NULL; + if (ref == NULL) { + // If we're not tracking this resource, just delete it + delete resource; + return; + } + ref->destroyed = true; + if (ref->refCount == 0) { + deleteResourceReference(resource, ref); + return; + } +} + +/** + * This method should only be called while the mLock mutex is held (that mutex is grabbed + * by the various destructor() and recycle() methods which call this method). + */ +void ResourceCache::deleteResourceReference(void* resource, ResourceReference* ref) { + if (ref->recycled && ref->resourceType == kBitmap) { + ((SkBitmap*) resource)->setPixels(NULL, NULL); + } + if (ref->destroyed) { + switch (ref->resourceType) { + case kBitmap: + { + SkBitmap* bitmap = (SkBitmap*)resource; + if (Caches::hasInstance()) { + Caches::getInstance().textureCache.removeDeferred(bitmap); + } + delete bitmap; + } + break; + case kPath: + { + SkPath* path = (SkPath*)resource; + if (Caches::hasInstance()) { + Caches::getInstance().pathCache.removeDeferred(path); + } + delete path; + } + break; + case kShader: + { + SkiaShader* shader = (SkiaShader*)resource; + if (Caches::hasInstance()) { + Caches::getInstance().gradientCache.removeDeferred(shader->getSkShader()); + } + delete shader; + } + break; + case kColorFilter: + { + SkiaColorFilter* filter = (SkiaColorFilter*)resource; + delete filter; + } + break; + } + } + mCache->removeItem(resource); + delete ref; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h new file mode 100644 index 000000000000..2a38910951c4 --- /dev/null +++ b/libs/hwui/ResourceCache.h @@ -0,0 +1,88 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_RESOURCE_CACHE_H +#define ANDROID_HWUI_RESOURCE_CACHE_H + +#include <SkBitmap.h> +#include <SkiaColorFilter.h> +#include <SkiaShader.h> +#include <utils/KeyedVector.h> + +namespace android { +namespace uirenderer { + +/** + * Type of Resource being cached + */ +enum ResourceType { + kBitmap, + kShader, + kColorFilter, + kPath, +}; + +class ResourceReference { +public: + + ResourceReference() { refCount = 0; recycled = false; destroyed = false;} + ResourceReference(ResourceType type) { + refCount = 0; recycled = false; destroyed = false; resourceType = type; + } + + int refCount; + bool recycled; + bool destroyed; + ResourceType resourceType; +}; + +class ResourceCache { + KeyedVector<void *, ResourceReference *>* mCache; +public: + ResourceCache(); + ~ResourceCache(); + void incrementRefcount(SkPath* resource); + void incrementRefcount(SkBitmap* resource); + void incrementRefcount(SkiaShader* resource); + void incrementRefcount(SkiaColorFilter* resource); + void incrementRefcount(const void* resource, ResourceType resourceType); + void decrementRefcount(void* resource); + void decrementRefcount(SkBitmap* resource); + void decrementRefcount(SkPath* resource); + void decrementRefcount(SkiaShader* resource); + void decrementRefcount(SkiaColorFilter* resource); + void recycle(SkBitmap* resource); + void destructor(SkPath* resource); + void destructor(SkBitmap* resource); + void destructor(SkiaShader* resource); + void destructor(SkiaColorFilter* resource); +private: + void deleteResourceReference(void* resource, ResourceReference* ref); + void incrementRefcount(void* resource, ResourceType resourceType); + void logCache(); + + /** + * Used to increment, decrement, and destroy. Incrementing is generally accessed on the UI + * thread, but destroying resources may be called from the GC thread, the finalizer thread, + * or a reference queue finalization thread. + */ + mutable Mutex mLock; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_RESOURCE_CACHE_H diff --git a/libs/hwui/ShapeCache.cpp b/libs/hwui/ShapeCache.cpp new file mode 100644 index 000000000000..0d7cd9cb9a72 --- /dev/null +++ b/libs/hwui/ShapeCache.cpp @@ -0,0 +1,149 @@ +/* + * 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 "OpenGLRenderer" + +#include "ShapeCache.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Rounded rects +/////////////////////////////////////////////////////////////////////////////// + +RoundRectShapeCache::RoundRectShapeCache(): ShapeCache<RoundRectShapeCacheEntry>( + "round rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) { +} + +PathTexture* RoundRectShapeCache::getRoundRect(float width, float height, + float rx, float ry, SkPaint* paint) { + RoundRectShapeCacheEntry entry(width, height, rx, ry, paint); + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + path.addRoundRect(r, rx, ry, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Circles +/////////////////////////////////////////////////////////////////////////////// + +CircleShapeCache::CircleShapeCache(): ShapeCache<CircleShapeCacheEntry>( + "circle", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) { +} + +PathTexture* CircleShapeCache::getCircle(float radius, SkPaint* paint) { + CircleShapeCacheEntry entry(radius, paint); + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + path.addCircle(radius, radius, radius, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Ovals +/////////////////////////////////////////////////////////////////////////////// + +OvalShapeCache::OvalShapeCache(): ShapeCache<OvalShapeCacheEntry>( + "oval", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) { +} + +PathTexture* OvalShapeCache::getOval(float width, float height, SkPaint* paint) { + OvalShapeCacheEntry entry(width, height, paint); + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + path.addOval(r, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Rects +/////////////////////////////////////////////////////////////////////////////// + +RectShapeCache::RectShapeCache(): ShapeCache<RectShapeCacheEntry>( + "rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) { +} + +PathTexture* RectShapeCache::getRect(float width, float height, SkPaint* paint) { + RectShapeCacheEntry entry(width, height, paint); + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + path.addRect(0.0f, 0.0f, width, height, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Arcs +/////////////////////////////////////////////////////////////////////////////// + +ArcShapeCache::ArcShapeCache(): ShapeCache<ArcShapeCacheEntry>( + "arc", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) { +} + +PathTexture* ArcShapeCache::getArc(float width, float height, + float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { + ArcShapeCacheEntry entry(width, height, startAngle, sweepAngle, useCenter, paint); + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + if (useCenter) { + path.moveTo(r.centerX(), r.centerY()); + } + path.arcTo(r, startAngle, sweepAngle, !useCenter); + if (useCenter) { + path.close(); + } + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h new file mode 100644 index 000000000000..4c626dd49303 --- /dev/null +++ b/libs/hwui/ShapeCache.h @@ -0,0 +1,630 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_SHAPE_CACHE_H +#define ANDROID_HWUI_SHAPE_CACHE_H + +#include <GLES2/gl2.h> + +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkPaint.h> +#include <SkPath.h> +#include <SkRect.h> + +#include "Debug.h" +#include "Properties.h" +#include "Texture.h" +#include "utils/Compare.h" +#include "utils/GenerationCache.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#if DEBUG_SHAPES + #define SHAPE_LOGD(...) LOGD(__VA_ARGS__) +#else + #define SHAPE_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Classes +/////////////////////////////////////////////////////////////////////////////// + +/** + * Alpha texture used to represent a path. + */ +struct PathTexture: public Texture { + PathTexture(): Texture() { + } + + /** + * Left coordinate of the path bounds. + */ + float left; + /** + * Top coordinate of the path bounds. + */ + float top; + /** + * Offset to draw the path at the correct origin. + */ + float offset; +}; // struct PathTexture + +/** + * Describe a shape in the shape cache. + */ +struct ShapeCacheEntry { + enum ShapeType { + kShapeNone, + kShapeRect, + kShapeRoundRect, + kShapeCircle, + kShapeOval, + kShapeArc, + kShapePath + }; + + ShapeCacheEntry() { + shapeType = kShapeNone; + join = SkPaint::kDefault_Join; + cap = SkPaint::kDefault_Cap; + style = SkPaint::kFill_Style; + miter = 4.0f; + strokeWidth = 1.0f; + } + + ShapeCacheEntry(const ShapeCacheEntry& entry): + shapeType(entry.shapeType), join(entry.join), cap(entry.cap), + style(entry.style), miter(entry.miter), + strokeWidth(entry.strokeWidth) { + } + + ShapeCacheEntry(ShapeType type, SkPaint* paint) { + shapeType = type; + join = paint->getStrokeJoin(); + cap = paint->getStrokeCap(); + float v = paint->getStrokeMiter(); + miter = *(uint32_t*) &v; + v = paint->getStrokeWidth(); + strokeWidth = *(uint32_t*) &v; + style = paint->getStyle(); + } + + virtual ~ShapeCacheEntry() { + } + + // shapeType must be checked in subclasses operator< + ShapeType shapeType; + SkPaint::Join join; + SkPaint::Cap cap; + SkPaint::Style style; + uint32_t miter; + uint32_t strokeWidth; + + bool operator<(const ShapeCacheEntry& rhs) const { + LTE_INT(shapeType) { + LTE_INT(join) { + LTE_INT(cap) { + LTE_INT(style) { + LTE_INT(miter) { + LTE_INT(strokeWidth) { + return lessThan(rhs); + } + } + } + } + } + } + return false; + } + +protected: + virtual bool lessThan(const ShapeCacheEntry& rhs) const { + return false; + } +}; // struct ShapeCacheEntry + + +struct RoundRectShapeCacheEntry: public ShapeCacheEntry { + RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint): + ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) { + mWidth = *(uint32_t*) &width; + mHeight = *(uint32_t*) &height; + mRx = *(uint32_t*) ℞ + mRy = *(uint32_t*) &ry; + } + + RoundRectShapeCacheEntry(): ShapeCacheEntry() { + mWidth = 0; + mHeight = 0; + mRx = 0; + mRy = 0; + } + + RoundRectShapeCacheEntry(const RoundRectShapeCacheEntry& entry): + ShapeCacheEntry(entry) { + mWidth = entry.mWidth; + mHeight = entry.mHeight; + mRx = entry.mRx; + mRy = entry.mRy; + } + + bool lessThan(const ShapeCacheEntry& r) const { + const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r; + LTE_INT(mWidth) { + LTE_INT(mHeight) { + LTE_INT(mRx) { + LTE_INT(mRy) { + return false; + } + } + } + } + return false; + } + +private: + uint32_t mWidth; + uint32_t mHeight; + uint32_t mRx; + uint32_t mRy; +}; // RoundRectShapeCacheEntry + +struct CircleShapeCacheEntry: public ShapeCacheEntry { + CircleShapeCacheEntry(float radius, SkPaint* paint): + ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) { + mRadius = *(uint32_t*) &radius; + } + + CircleShapeCacheEntry(): ShapeCacheEntry() { + mRadius = 0; + } + + CircleShapeCacheEntry(const CircleShapeCacheEntry& entry): + ShapeCacheEntry(entry) { + mRadius = entry.mRadius; + } + + bool lessThan(const ShapeCacheEntry& r) const { + const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r; + LTE_INT(mRadius) { + return false; + } + return false; + } + +private: + uint32_t mRadius; +}; // CircleShapeCacheEntry + +struct OvalShapeCacheEntry: public ShapeCacheEntry { + OvalShapeCacheEntry(float width, float height, SkPaint* paint): + ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) { + mWidth = *(uint32_t*) &width; + mHeight = *(uint32_t*) &height; + } + + OvalShapeCacheEntry(): ShapeCacheEntry() { + mWidth = mHeight = 0; + } + + OvalShapeCacheEntry(const OvalShapeCacheEntry& entry): + ShapeCacheEntry(entry) { + mWidth = entry.mWidth; + mHeight = entry.mHeight; + } + + bool lessThan(const ShapeCacheEntry& r) const { + const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r; + LTE_INT(mWidth) { + LTE_INT(mHeight) { + return false; + } + } + return false; + } + +private: + uint32_t mWidth; + uint32_t mHeight; +}; // OvalShapeCacheEntry + +struct RectShapeCacheEntry: public ShapeCacheEntry { + RectShapeCacheEntry(float width, float height, SkPaint* paint): + ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) { + mWidth = *(uint32_t*) &width; + mHeight = *(uint32_t*) &height; + } + + RectShapeCacheEntry(): ShapeCacheEntry() { + mWidth = mHeight = 0; + } + + RectShapeCacheEntry(const RectShapeCacheEntry& entry): + ShapeCacheEntry(entry) { + mWidth = entry.mWidth; + mHeight = entry.mHeight; + } + + bool lessThan(const ShapeCacheEntry& r) const { + const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r; + LTE_INT(mWidth) { + LTE_INT(mHeight) { + return false; + } + } + return false; + } + +private: + uint32_t mWidth; + uint32_t mHeight; +}; // RectShapeCacheEntry + +struct ArcShapeCacheEntry: public ShapeCacheEntry { + ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle, + bool useCenter, SkPaint* paint): + ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) { + mWidth = *(uint32_t*) &width; + mHeight = *(uint32_t*) &height; + mStartAngle = *(uint32_t*) &startAngle; + mSweepAngle = *(uint32_t*) &sweepAngle; + mUseCenter = useCenter ? 1 : 0; + } + + ArcShapeCacheEntry(): ShapeCacheEntry() { + mWidth = 0; + mHeight = 0; + mStartAngle = 0; + mSweepAngle = 0; + mUseCenter = 0; + } + + ArcShapeCacheEntry(const ArcShapeCacheEntry& entry): + ShapeCacheEntry(entry) { + mWidth = entry.mWidth; + mHeight = entry.mHeight; + mStartAngle = entry.mStartAngle; + mSweepAngle = entry.mSweepAngle; + mUseCenter = entry.mUseCenter; + } + + bool lessThan(const ShapeCacheEntry& r) const { + const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r; + LTE_INT(mWidth) { + LTE_INT(mHeight) { + LTE_INT(mStartAngle) { + LTE_INT(mSweepAngle) { + LTE_INT(mUseCenter) { + return false; + } + } + } + } + } + return false; + } + +private: + uint32_t mWidth; + uint32_t mHeight; + uint32_t mStartAngle; + uint32_t mSweepAngle; + uint32_t mUseCenter; +}; // ArcShapeCacheEntry + +/** + * A simple LRU shape cache. The cache has a maximum size expressed in bytes. + * Any texture added to the cache causing the cache to grow beyond the maximum + * allowed size will also cause the oldest texture to be kicked out. + */ +template<typename Entry> +class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> { +public: + ShapeCache(const char* name, const char* propertyName, float defaultSize); + ~ShapeCache(); + + /** + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. + */ + void operator()(Entry& path, PathTexture*& texture); + + /** + * Clears the cache. This causes all textures to be deleted. + */ + void clear(); + + /** + * Sets the maximum size of the cache in bytes. + */ + void setMaxSize(uint32_t maxSize); + /** + * Returns the maximum size of the cache in bytes. + */ + uint32_t getMaxSize(); + /** + * Returns the current size of the cache in bytes. + */ + uint32_t getSize(); + +protected: + PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint); + + PathTexture* get(Entry entry) { + return mCache.get(entry); + } + + void removeTexture(PathTexture* texture); + + GenerationCache<Entry, PathTexture*> mCache; + uint32_t mSize; + uint32_t mMaxSize; + GLuint mMaxTextureSize; + + char* mName; + bool mDebugEnabled; + +private: + /** + * Generates the texture from a bitmap into the specified texture structure. + */ + void generateTexture(SkBitmap& bitmap, Texture* texture); + + void init(); +}; // class ShapeCache + +class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> { +public: + RoundRectShapeCache(); + + PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint); +}; // class RoundRectShapeCache + +class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> { +public: + CircleShapeCache(); + + PathTexture* getCircle(float radius, SkPaint* paint); +}; // class CircleShapeCache + +class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> { +public: + OvalShapeCache(); + + PathTexture* getOval(float width, float height, SkPaint* paint); +}; // class OvalShapeCache + +class RectShapeCache: public ShapeCache<RectShapeCacheEntry> { +public: + RectShapeCache(); + + PathTexture* getRect(float width, float height, SkPaint* paint); +}; // class RectShapeCache + +class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> { +public: + ArcShapeCache(); + + PathTexture* getArc(float width, float height, float startAngle, float sweepAngle, + bool useCenter, SkPaint* paint); +}; // class ArcShapeCache + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +template<class Entry> +ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize): + mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(defaultSize)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(propertyName, property, NULL) > 0) { + INIT_LOGD(" Setting %s cache size to %sMB", name, property); + setMaxSize(MB(atof(property))); + } else { + INIT_LOGD(" Using default %s cache size of %.2fMB", name, defaultSize); + } + + size_t len = strlen(name); + mName = new char[len + 1]; + strcpy(mName, name); + mName[len] = '\0'; + + init(); +} + +template<class Entry> +ShapeCache<Entry>::~ShapeCache() { + mCache.clear(); + delete[] mName; +} + +template<class Entry> +void ShapeCache<Entry>::init() { + mCache.setOnEntryRemovedListener(this); + + GLint maxTextureSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + mMaxTextureSize = maxTextureSize; + + mDebugEnabled = readDebugLevel() & kDebugCaches; +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +template<class Entry> +uint32_t ShapeCache<Entry>::getSize() { + return mSize; +} + +template<class Entry> +uint32_t ShapeCache<Entry>::getMaxSize() { + return mMaxSize; +} + +template<class Entry> +void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) { + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +template<class Entry> +void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) { + removeTexture(texture); +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +template<class Entry> +void ShapeCache<Entry>::removeTexture(PathTexture* texture) { + if (texture) { + const uint32_t size = texture->width * texture->height; + mSize -= size; + + SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d", + mName, texture->id, size, mSize); + if (mDebugEnabled) { + LOGD("Shape %s deleted, size = %d", mName, size); + } + + glDeleteTextures(1, &texture->id); + delete texture; + } +} + +template<class Entry> +PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path, + const SkPaint* paint) { + const SkRect& bounds = path->getBounds(); + + const float pathWidth = fmax(bounds.width(), 1.0f); + const float pathHeight = fmax(bounds.height(), 1.0f); + + if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) { + LOGW("Shape %s too large to be rendered into a texture", mName); + return NULL; + } + + const float offset = paint->getStrokeWidth() * 1.5f; + const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5); + const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5); + + const uint32_t size = width * height; + // Don't even try to cache a bitmap that's bigger than the cache + if (size < mMaxSize) { + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + } + + PathTexture* texture = new PathTexture; + texture->left = bounds.fLeft; + texture->top = bounds.fTop; + texture->offset = offset; + texture->width = width; + texture->height = height; + texture->generation = path->getGenerationID(); + + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kA8_Config, width, height); + bitmap.allocPixels(); + bitmap.eraseColor(0); + + SkPaint pathPaint(*paint); + + // Make sure the paint is opaque, color, alpha, filter, etc. + // will be applied later when compositing the alpha8 texture + pathPaint.setColor(0xff000000); + pathPaint.setAlpha(255); + pathPaint.setColorFilter(NULL); + pathPaint.setMaskFilter(NULL); + pathPaint.setShader(NULL); + SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode); + SkSafeUnref(pathPaint.setXfermode(mode)); + + SkCanvas canvas(bitmap); + canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset); + canvas.drawPath(*path, pathPaint); + + generateTexture(bitmap, texture); + + if (size < mMaxSize) { + mSize += size; + SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d", + mName, texture->id, size, mSize); + if (mDebugEnabled) { + LOGD("Shape %s created, size = %d", mName, size); + } + mCache.put(entry, texture); + } else { + texture->cleanup = true; + } + + return texture; +} + +template<class Entry> +void ShapeCache<Entry>::clear() { + mCache.clear(); +} + +template<class Entry> +void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) { + SkAutoLockPixels alp(bitmap); + if (!bitmap.readyToDraw()) { + LOGE("Cannot generate texture from bitmap"); + return; + } + + glGenTextures(1, &texture->id); + + glBindTexture(GL_TEXTURE_2D, texture->id); + // Textures are Alpha8 + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + texture->blend = true; + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SHAPE_CACHE_H diff --git a/libs/hwui/SkiaColorFilter.cpp b/libs/hwui/SkiaColorFilter.cpp new file mode 100644 index 000000000000..b86bbc5f8f4c --- /dev/null +++ b/libs/hwui/SkiaColorFilter.cpp @@ -0,0 +1,108 @@ +/* + * 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 "SkiaColorFilter.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Base color filter +/////////////////////////////////////////////////////////////////////////////// + +SkiaColorFilter::SkiaColorFilter(SkColorFilter *skFilter, Type type, bool blend): + mType(type), mBlend(blend), mSkFilter(skFilter) { +} + +SkiaColorFilter::~SkiaColorFilter() { +} + +/////////////////////////////////////////////////////////////////////////////// +// Color matrix filter +/////////////////////////////////////////////////////////////////////////////// + +SkiaColorMatrixFilter::SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector): + SkiaColorFilter(skFilter, kColorMatrix, true), mMatrix(matrix), mVector(vector) { + // Skia uses the range [0..255] for the addition vector, but we need + // the [0..1] range to apply the vector in GLSL + for (int i = 0; i < 4; i++) { + mVector[i] /= 255.0f; + } +} + +SkiaColorMatrixFilter::~SkiaColorMatrixFilter() { + delete[] mMatrix; + delete[] mVector; +} + +void SkiaColorMatrixFilter::describe(ProgramDescription& description, + const Extensions& extensions) { + description.colorOp = ProgramDescription::kColorMatrix; +} + +void SkiaColorMatrixFilter::setupProgram(Program* program) { + glUniformMatrix4fv(program->getUniform("colorMatrix"), 1, GL_FALSE, &mMatrix[0]); + glUniform4fv(program->getUniform("colorMatrixVector"), 1, mVector); +} + +/////////////////////////////////////////////////////////////////////////////// +// Lighting color filter +/////////////////////////////////////////////////////////////////////////////// + +SkiaLightingFilter::SkiaLightingFilter(SkColorFilter *skFilter, int multiply, int add): + SkiaColorFilter(skFilter, kLighting, true) { + mMulR = ((multiply >> 16) & 0xFF) / 255.0f; + mMulG = ((multiply >> 8) & 0xFF) / 255.0f; + mMulB = ((multiply ) & 0xFF) / 255.0f; + + mAddR = ((add >> 16) & 0xFF) / 255.0f; + mAddG = ((add >> 8) & 0xFF) / 255.0f; + mAddB = ((add ) & 0xFF) / 255.0f; +} + +void SkiaLightingFilter::describe(ProgramDescription& description, const Extensions& extensions) { + description.colorOp = ProgramDescription::kColorLighting; +} + +void SkiaLightingFilter::setupProgram(Program* program) { + glUniform4f(program->getUniform("lightingMul"), mMulR, mMulG, mMulB, 1.0f); + glUniform4f(program->getUniform("lightingAdd"), mAddR, mAddG, mAddB, 0.0f); +} + +/////////////////////////////////////////////////////////////////////////////// +// Blend color filter +/////////////////////////////////////////////////////////////////////////////// + +SkiaBlendFilter::SkiaBlendFilter(SkColorFilter *skFilter, int color, SkXfermode::Mode mode): + SkiaColorFilter(skFilter, kBlend, true), mMode(mode) { + const int alpha = (color >> 24) & 0xFF; + mA = alpha / 255.0f; + mR = mA * ((color >> 16) & 0xFF) / 255.0f; + mG = mA * ((color >> 8) & 0xFF) / 255.0f; + mB = mA * ((color ) & 0xFF) / 255.0f; +} + +void SkiaBlendFilter::describe(ProgramDescription& description, const Extensions& extensions) { + description.colorOp = ProgramDescription::kColorBlend; + description.colorMode = mMode; +} + +void SkiaBlendFilter::setupProgram(Program* program) { + glUniform4f(program->getUniform("colorBlend"), mR, mG, mB, mA); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/SkiaColorFilter.h b/libs/hwui/SkiaColorFilter.h new file mode 100644 index 000000000000..bf45e13bc0f2 --- /dev/null +++ b/libs/hwui/SkiaColorFilter.h @@ -0,0 +1,126 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_SKIA_COLOR_FILTER_H +#define ANDROID_HWUI_SKIA_COLOR_FILTER_H + +#include <GLES2/gl2.h> +#include <SkColorFilter.h> + +#include "ProgramCache.h" +#include "Extensions.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Base color filter +/////////////////////////////////////////////////////////////////////////////// + +/** + * Represents a Skia color filter. A color filter modifies a ProgramDescription + * and sets uniforms on the resulting shaders. + */ +struct SkiaColorFilter { + /** + * Type of Skia color filter in use. + */ + enum Type { + kNone, + kColorMatrix, + kLighting, + kBlend, + }; + + SkiaColorFilter(SkColorFilter *skFilter, Type type, bool blend); + virtual ~SkiaColorFilter(); + + virtual void describe(ProgramDescription& description, const Extensions& extensions) = 0; + virtual void setupProgram(Program* program) = 0; + + inline bool blend() const { + return mBlend; + } + + Type type() const { + return mType; + } + + SkColorFilter *getSkColorFilter() { + return mSkFilter; + } + +protected: + Type mType; + bool mBlend; + +private: + SkColorFilter *mSkFilter; +}; // struct SkiaColorFilter + +/////////////////////////////////////////////////////////////////////////////// +// Implementations +/////////////////////////////////////////////////////////////////////////////// + +/** + * A color filter that multiplies the source color with a matrix and adds a vector. + */ +struct SkiaColorMatrixFilter: public SkiaColorFilter { + SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector); + ~SkiaColorMatrixFilter(); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program); + +private: + float* mMatrix; + float* mVector; +}; // struct SkiaColorMatrixFilter + +/** + * A color filters that multiplies the source color with a fixed value and adds + * another fixed value. Ignores the alpha channel of both arguments. + */ +struct SkiaLightingFilter: public SkiaColorFilter { + SkiaLightingFilter(SkColorFilter *skFilter, int multiply, int add); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program); + +private: + GLfloat mMulR, mMulG, mMulB; + GLfloat mAddR, mAddG, mAddB; +}; // struct SkiaLightingFilter + +/** + * A color filters that blends the source color with a specified destination color + * and PorterDuff blending mode. + */ +struct SkiaBlendFilter: public SkiaColorFilter { + SkiaBlendFilter(SkColorFilter *skFilter, int color, SkXfermode::Mode mode); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program); + +private: + SkXfermode::Mode mMode; + GLfloat mR, mG, mB, mA; +}; // struct SkiaBlendFilter + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SKIA_COLOR_FILTER_H diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp new file mode 100644 index 000000000000..8878c709809e --- /dev/null +++ b/libs/hwui/SkiaShader.cpp @@ -0,0 +1,415 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <utils/Log.h> + +#include <SkMatrix.h> + +#include "SkiaShader.h" +#include "Texture.h" +#include "Matrix.h" + +namespace android { +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 + GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode +}; + +/////////////////////////////////////////////////////////////////////////////// +// Base shader +/////////////////////////////////////////////////////////////////////////////// + +void SkiaShader::copyFrom(const SkiaShader& shader) { + mType = shader.mType; + mKey = shader.mKey; + mTileX = shader.mTileX; + mTileY = shader.mTileY; + mBlend = shader.mBlend; + mUnitMatrix = shader.mUnitMatrix; + mShaderMatrix = shader.mShaderMatrix; + mGenerationId = shader.mGenerationId; +} + +SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, + SkShader::TileMode tileY, SkMatrix* matrix, bool blend): + mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend) { + setMatrix(matrix); + mGenerationId = 0; +} + +SkiaShader::~SkiaShader() { +} + +void SkiaShader::describe(ProgramDescription& description, const Extensions& extensions) { +} + +void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit) { +} + +void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) { + glBindTexture(GL_TEXTURE_2D, texture->id); + if (wrapS != texture->wrapS) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); + texture->wrapS = wrapS; + } + if (wrapT != texture->wrapT) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT); + texture->wrapT = wrapT; + } +} + +void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView) { + screenSpace.loadMultiply(mUnitMatrix, mShaderMatrix); + screenSpace.multiply(modelView); +} + +/////////////////////////////////////////////////////////////////////////////// +// Bitmap shader +/////////////////////////////////////////////////////////////////////////////// + +SkiaBitmapShader::SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX, + SkShader::TileMode tileY, SkMatrix* matrix, bool blend): + SkiaShader(kBitmap, key, tileX, tileY, matrix, blend), mBitmap(bitmap), mTexture(NULL) { + updateLocalMatrix(matrix); +} + +SkiaShader* SkiaBitmapShader::copy() { + SkiaBitmapShader* copy = new SkiaBitmapShader(); + copy->copyFrom(*this); + copy->mBitmap = mBitmap; + return copy; +} + +void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) { + Texture* texture = mTextureCache->get(mBitmap); + if (!texture) return; + mTexture = texture; + + const float width = texture->width; + const float height = texture->height; + + description.hasBitmap = true; + // The driver does not support non-power of two mirrored/repeated + // textures, so do it ourselves + if (!extensions.hasNPot() && (!isPowerOfTwo(width) || !isPowerOfTwo(height)) && + (mTileX != SkShader::kClamp_TileMode || mTileY != SkShader::kClamp_TileMode)) { + description.isBitmapNpot = true; + description.bitmapWrapS = gTileModes[mTileX]; + description.bitmapWrapT = gTileModes[mTileY]; + mWrapS = GL_CLAMP_TO_EDGE; + mWrapT = GL_CLAMP_TO_EDGE; + } else { + mWrapS = gTileModes[mTileX]; + mWrapT = gTileModes[mTileY]; + } +} + +void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView, + const Snapshot& snapshot, GLuint* textureUnit) { + GLuint textureSlot = (*textureUnit)++; + glActiveTexture(gTextureUnitsMap[textureSlot]); + + Texture* texture = mTexture; + mTexture = NULL; + if (!texture) return; + const AutoTexture autoCleanup(texture); + + const float width = texture->width; + const float height = texture->height; + + mat4 textureTransform; + computeScreenSpaceMatrix(textureTransform, modelView); + + // Uniforms + bindTexture(texture, mWrapS, mWrapT); + glUniform1i(program->getUniform("bitmapSampler"), textureSlot); + glUniformMatrix4fv(program->getUniform("textureTransform"), 1, + GL_FALSE, &textureTransform.data[0]); + glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height); +} + +void SkiaBitmapShader::updateTransforms(Program* program, const mat4& modelView, + const Snapshot& snapshot) { + mat4 textureTransform; + computeScreenSpaceMatrix(textureTransform, modelView); + glUniformMatrix4fv(program->getUniform("textureTransform"), 1, + GL_FALSE, &textureTransform.data[0]); +} + +/////////////////////////////////////////////////////////////////////////////// +// Linear gradient shader +/////////////////////////////////////////////////////////////////////////////// + +static void toUnitMatrix(const SkPoint pts[2], SkMatrix* matrix) { + SkVector vec = pts[1] - pts[0]; + const float mag = vec.length(); + const float inv = mag ? 1.0f / mag : 0; + + vec.scale(inv); + matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); + matrix->postTranslate(-pts[0].fX, -pts[0].fY); + matrix->postScale(inv, inv); +} + +SkiaLinearGradientShader::SkiaLinearGradientShader(float* bounds, uint32_t* colors, + float* positions, int count, SkShader* key, SkShader::TileMode tileMode, + SkMatrix* matrix, bool blend): + SkiaShader(kLinearGradient, key, tileMode, tileMode, matrix, blend), + mBounds(bounds), mColors(colors), mPositions(positions), mCount(count) { + SkPoint points[2]; + points[0].set(bounds[0], bounds[1]); + points[1].set(bounds[2], bounds[3]); + + SkMatrix unitMatrix; + toUnitMatrix(points, &unitMatrix); + mUnitMatrix.load(unitMatrix); + + updateLocalMatrix(matrix); +} + +SkiaLinearGradientShader::~SkiaLinearGradientShader() { + delete[] mBounds; + delete[] mColors; + delete[] mPositions; +} + +SkiaShader* SkiaLinearGradientShader::copy() { + SkiaLinearGradientShader* copy = new SkiaLinearGradientShader(); + copy->copyFrom(*this); + copy->mBounds = new float[4]; + memcpy(copy->mBounds, mBounds, sizeof(float) * 4); + copy->mColors = new uint32_t[mCount]; + memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount); + copy->mPositions = new float[mCount]; + memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); + copy->mCount = mCount; + return copy; +} + +void SkiaLinearGradientShader::describe(ProgramDescription& description, + const Extensions& extensions) { + description.hasGradient = true; + description.gradientType = ProgramDescription::kGradientLinear; +} + +void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView, + const Snapshot& snapshot, GLuint* textureUnit) { + GLuint textureSlot = (*textureUnit)++; + glActiveTexture(gTextureUnitsMap[textureSlot]); + + Texture* texture = mGradientCache->get(mKey); + if (!texture) { + texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount, mTileX); + } + + mat4 screenSpace; + computeScreenSpaceMatrix(screenSpace, modelView); + + // Uniforms + bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]); + glUniform1i(program->getUniform("gradientSampler"), textureSlot); + glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); +} + +void SkiaLinearGradientShader::updateTransforms(Program* program, const mat4& modelView, + const Snapshot& snapshot) { + mat4 screenSpace; + computeScreenSpaceMatrix(screenSpace, modelView); + glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); +} + +/////////////////////////////////////////////////////////////////////////////// +// Circular gradient shader +/////////////////////////////////////////////////////////////////////////////// + +static void toCircularUnitMatrix(const float x, const float y, const float radius, + SkMatrix* matrix) { + const float inv = 1.0f / radius; + matrix->setTranslate(-x, -y); + matrix->postScale(inv, inv); +} + +SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius, + uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, + SkMatrix* matrix, bool blend): + SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key, + tileMode, matrix, blend) { + SkMatrix unitMatrix; + toCircularUnitMatrix(x, y, radius, &unitMatrix); + mUnitMatrix.load(unitMatrix); + + updateLocalMatrix(matrix); +} + +SkiaShader* SkiaCircularGradientShader::copy() { + SkiaCircularGradientShader* copy = new SkiaCircularGradientShader(); + copy->copyFrom(*this); + copy->mColors = new uint32_t[mCount]; + memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount); + copy->mPositions = new float[mCount]; + memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); + copy->mCount = mCount; + return copy; +} + +void SkiaCircularGradientShader::describe(ProgramDescription& description, + const Extensions& extensions) { + description.hasGradient = true; + description.gradientType = ProgramDescription::kGradientCircular; +} + +/////////////////////////////////////////////////////////////////////////////// +// Sweep gradient shader +/////////////////////////////////////////////////////////////////////////////// + +static void toSweepUnitMatrix(const float x, const float y, SkMatrix* matrix) { + matrix->setTranslate(-x, -y); +} + +SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* colors, + float* positions, int count, SkShader* key, SkMatrix* matrix, bool blend): + SkiaShader(kSweepGradient, key, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, matrix, blend), + mColors(colors), mPositions(positions), mCount(count) { + SkMatrix unitMatrix; + toSweepUnitMatrix(x, y, &unitMatrix); + mUnitMatrix.load(unitMatrix); + + updateLocalMatrix(matrix); +} + +SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, + float* positions, int count, SkShader* key, SkShader::TileMode tileMode, + SkMatrix* matrix, bool blend): + SkiaShader(type, key, tileMode, tileMode, matrix, blend), + mColors(colors), mPositions(positions), mCount(count) { +} + +SkiaSweepGradientShader::~SkiaSweepGradientShader() { + delete[] mColors; + delete[] mPositions; +} + +SkiaShader* SkiaSweepGradientShader::copy() { + SkiaSweepGradientShader* copy = new SkiaSweepGradientShader(); + copy->copyFrom(*this); + copy->mColors = new uint32_t[mCount]; + memcpy(copy->mColors, mColors, sizeof(uint32_t) * mCount); + copy->mPositions = new float[mCount]; + memcpy(copy->mPositions, mPositions, sizeof(float) * mCount); + copy->mCount = mCount; + return copy; +} + +void SkiaSweepGradientShader::describe(ProgramDescription& description, + const Extensions& extensions) { + description.hasGradient = true; + description.gradientType = ProgramDescription::kGradientSweep; +} + +void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelView, + const Snapshot& snapshot, GLuint* textureUnit) { + GLuint textureSlot = (*textureUnit)++; + glActiveTexture(gTextureUnitsMap[textureSlot]); + + Texture* texture = mGradientCache->get(mKey); + if (!texture) { + texture = mGradientCache->addLinearGradient(mKey, mColors, mPositions, mCount); + } + + mat4 screenSpace; + computeScreenSpaceMatrix(screenSpace, modelView); + + // Uniforms + bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]); + glUniform1i(program->getUniform("gradientSampler"), textureSlot); + glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); +} + +void SkiaSweepGradientShader::updateTransforms(Program* program, const mat4& modelView, + const Snapshot& snapshot) { + mat4 screenSpace; + computeScreenSpaceMatrix(screenSpace, modelView); + glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); +} + +/////////////////////////////////////////////////////////////////////////////// +// Compose shader +/////////////////////////////////////////////////////////////////////////////// + +SkiaComposeShader::SkiaComposeShader(SkiaShader* first, SkiaShader* second, + SkXfermode::Mode mode, SkShader* key): + SkiaShader(kCompose, key, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, + NULL, first->blend() || second->blend()), + mFirst(first), mSecond(second), mMode(mode), mCleanup(false) { +} + +SkiaComposeShader::~SkiaComposeShader() { + if (mCleanup) { + delete mFirst; + delete mSecond; + } +} + +SkiaShader* SkiaComposeShader::copy() { + SkiaComposeShader* copy = new SkiaComposeShader(); + copy->copyFrom(*this); + copy->mFirst = mFirst->copy(); + copy->mSecond = mSecond->copy(); + copy->mMode = mMode; + copy->cleanup(); + return copy; +} + +void SkiaComposeShader::set(TextureCache* textureCache, GradientCache* gradientCache) { + SkiaShader::set(textureCache, gradientCache); + mFirst->set(textureCache, gradientCache); + mSecond->set(textureCache, gradientCache); +} + +void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) { + mFirst->describe(description, extensions); + mSecond->describe(description, extensions); + if (mFirst->type() == kBitmap) { + description.isBitmapFirst = true; + } + description.shadersMode = mMode; +} + +void SkiaComposeShader::setupProgram(Program* program, const mat4& modelView, + const Snapshot& snapshot, GLuint* textureUnit) { + mFirst->setupProgram(program, modelView, snapshot, textureUnit); + mSecond->setupProgram(program, modelView, snapshot, textureUnit); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h new file mode 100644 index 000000000000..89dd131f8ab2 --- /dev/null +++ b/libs/hwui/SkiaShader.h @@ -0,0 +1,264 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_SKIA_SHADER_H +#define ANDROID_HWUI_SKIA_SHADER_H + +#include <SkShader.h> +#include <SkXfermode.h> + +#include <GLES2/gl2.h> + +#include "Extensions.h" +#include "ProgramCache.h" +#include "TextureCache.h" +#include "GradientCache.h" +#include "Snapshot.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Base shader +/////////////////////////////////////////////////////////////////////////////// + +/** + * Represents a Skia shader. A shader will modify the GL context and active + * program to recreate the original effect. + */ +struct SkiaShader { + /** + * Type of Skia shader in use. + */ + enum Type { + kNone, + kBitmap, + kLinearGradient, + kCircularGradient, + kSweepGradient, + kCompose + }; + + SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, SkShader::TileMode tileY, + SkMatrix* matrix, bool blend); + virtual ~SkiaShader(); + + virtual SkiaShader* copy() = 0; + void copyFrom(const SkiaShader& shader); + + virtual void describe(ProgramDescription& description, const Extensions& extensions); + virtual void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + + inline SkShader *getSkShader() { + return mKey; + } + + inline bool blend() const { + return mBlend; + } + + Type type() const { + return mType; + } + + virtual void set(TextureCache* textureCache, GradientCache* gradientCache) { + mTextureCache = textureCache; + mGradientCache = gradientCache; + } + + virtual void updateTransforms(Program* program, const mat4& modelView, + const Snapshot& snapshot) { + } + + uint32_t getGenerationId() { + return mGenerationId; + } + + void setMatrix(SkMatrix* matrix) { + updateLocalMatrix(matrix); + mGenerationId++; + } + + void updateLocalMatrix(const SkMatrix* matrix) { + if (matrix) { + mat4 localMatrix(*matrix); + mShaderMatrix.loadInverse(localMatrix); + } else { + mShaderMatrix.loadIdentity(); + } + } + + void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView); + +protected: + SkiaShader() { + } + + /** + * The appropriate texture unit must have been activated prior to invoking + * this method. + */ + inline void bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT); + + Type mType; + SkShader* mKey; + SkShader::TileMode mTileX; + SkShader::TileMode mTileY; + bool mBlend; + + TextureCache* mTextureCache; + GradientCache* mGradientCache; + + mat4 mUnitMatrix; + mat4 mShaderMatrix; + +private: + uint32_t mGenerationId; +}; // struct SkiaShader + + +/////////////////////////////////////////////////////////////////////////////// +// Implementations +/////////////////////////////////////////////////////////////////////////////// + +/** + * A shader that draws a bitmap. + */ +struct SkiaBitmapShader: public SkiaShader { + SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX, + SkShader::TileMode tileY, SkMatrix* matrix, bool blend); + SkiaShader* copy(); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); + +private: + SkiaBitmapShader() { + } + + /** + * This method does not work for n == 0. + */ + inline bool isPowerOfTwo(unsigned int n) { + return !(n & (n - 1)); + } + + SkBitmap* mBitmap; + Texture* mTexture; + GLenum mWrapS; + GLenum mWrapT; +}; // struct SkiaBitmapShader + +/** + * A shader that draws a linear gradient. + */ +struct SkiaLinearGradientShader: public SkiaShader { + SkiaLinearGradientShader(float* bounds, uint32_t* colors, float* positions, int count, + SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); + ~SkiaLinearGradientShader(); + SkiaShader* copy(); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); + +private: + SkiaLinearGradientShader() { + } + + float* mBounds; + uint32_t* mColors; + float* mPositions; + int mCount; +}; // struct SkiaLinearGradientShader + +/** + * A shader that draws a sweep gradient. + */ +struct SkiaSweepGradientShader: public SkiaShader { + SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions, int count, + SkShader* key, SkMatrix* matrix, bool blend); + ~SkiaSweepGradientShader(); + SkiaShader* copy(); + + virtual void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + void updateTransforms(Program* program, const mat4& modelView, const Snapshot& snapshot); + +protected: + SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions, + int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); + SkiaSweepGradientShader() { + } + + uint32_t* mColors; + float* mPositions; + int mCount; +}; // struct SkiaSweepGradientShader + +/** + * A shader that draws a circular gradient. + */ +struct SkiaCircularGradientShader: public SkiaSweepGradientShader { + SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors, float* positions, + int count, SkShader* key,SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); + SkiaShader* copy(); + + void describe(ProgramDescription& description, const Extensions& extensions); + +private: + SkiaCircularGradientShader() { + } +}; // struct SkiaCircularGradientShader + +/** + * A shader that draws two shaders, composited with an xfermode. + */ +struct SkiaComposeShader: public SkiaShader { + SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode, SkShader* key); + ~SkiaComposeShader(); + SkiaShader* copy(); + + void set(TextureCache* textureCache, GradientCache* gradientCache); + + void describe(ProgramDescription& description, const Extensions& extensions); + void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot, + GLuint* textureUnit); + +private: + SkiaComposeShader(): mCleanup(false) { + } + + void cleanup() { + mCleanup = true; + } + + SkiaShader* mFirst; + SkiaShader* mSecond; + SkXfermode::Mode mMode; + + bool mCleanup; +}; // struct SkiaComposeShader + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SKIA_SHADER_H diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h new file mode 100644 index 000000000000..bd7031927b5e --- /dev/null +++ b/libs/hwui/Snapshot.h @@ -0,0 +1,289 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_SNAPSHOT_H +#define ANDROID_HWUI_SNAPSHOT_H + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <utils/RefBase.h> +#include <ui/Region.h> + +#include <SkCanvas.h> + +#include "Layer.h" +#include "Matrix.h" +#include "Rect.h" + +namespace android { +namespace uirenderer { + +/** + * A snapshot holds information about the current state of the rendering + * surface. A snapshot is usually created whenever the user calls save() + * and discarded when the user calls restore(). Once a snapshot is created, + * it can hold information for deferred rendering. + * + * Each snapshot has a link to a previous snapshot, indicating the previous + * state of the renderer. + */ +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::kFlagClipSet) && + !(s->flags & Snapshot::kFlagDirtyLocalClip)) { + mLocalClip.set(s->mLocalClip); + } else { + flags |= Snapshot::kFlagDirtyLocalClip; + } + + if (s->flags & Snapshot::kFlagFboTarget) { + flags |= Snapshot::kFlagFboTarget; + region = s->region; + } else { + region = NULL; + } + } + + /** + * Various flags set on #flags. + */ + enum Flags { + /** + * Indicates that the clip region was modified. When this + * snapshot is restored so must the clip. + */ + kFlagClipSet = 0x1, + /** + * Indicates that this snapshot was created when saving + * a new layer. + */ + kFlagIsLayer = 0x2, + /** + * Indicates that this snapshot is a special type of layer + * backed by an FBO. This flag only makes sense when the + * flag kFlagIsLayer is also set. + */ + kFlagIsFboLayer = 0x4, + /** + * Indicates that the local clip should be recomputed. + */ + kFlagDirtyLocalClip = 0x8, + /** + * Indicates that this snapshot has changed the ortho matrix. + */ + kFlagDirtyOrtho = 0x10, + /** + * Indicates that this snapshot or an ancestor snapshot is + * an FBO layer. + */ + kFlagFboTarget = 0x20 + }; + + /** + * Modifies the current clip with the new clip rectangle and + * the specified operation. The specified rectangle is transformed + * 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); + } + + /** + * 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 | Snapshot::kFlagDirtyLocalClip; + } + + return clipped; + } + + /** + * Sets the current clip. + */ + void setClip(float left, float top, float right, float bottom) { + clipRect->set(left, top, right, bottom); + flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip; + } + + const Rect& getLocalClip() { + if (flags & Snapshot::kFlagDirtyLocalClip) { + mat4 inverse; + inverse.loadInverse(*transform); + + mLocalClip.set(*clipRect); + inverse.mapRect(mLocalClip); + + flags &= ~Snapshot::kFlagDirtyLocalClip; + } + return mLocalClip; + } + + void resetTransform(float x, float y, float z) { + transform = &mTransformRoot; + transform->loadTranslate(x, y, z); + } + + void resetClip(float left, float top, float right, float bottom) { + clipRect = &mClipRectRoot; + clipRect->set(left, top, right, bottom); + flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip; + } + + bool isIgnored() const { + return invisible || empty; + } + + /** + * Dirty flags. + */ + int flags; + + /** + * Previous snapshot. + */ + sp<Snapshot> previous; + + /** + * Only set when the flag kFlagIsLayer is set. + */ + Layer* layer; + + /** + * Only set when the flag kFlagIsFboLayer is set. + */ + GLuint fbo; + + /** + * Indicates that this snapshot is invisible and nothing should be drawn + * inside it. This flag is set only when the layer clips drawing to its + * bounds and is passed to subsequent snapshots. + */ + bool invisible; + + /** + * If set to true, the layer will not be composited. This is similar to + * invisible but this flag is not passed to subsequent snapshots. + */ + bool empty; + + /** + * Current viewport. + */ + Rect viewport; + + /** + * Height of the framebuffer the snapshot is rendering into. + */ + int height; + + /** + * Contains the previous ortho matrix. + */ + mat4 orthoMatrix; + + /** + * Local transformation. Holds the current translation, scale and + * rotation values. + */ + mat4* transform; + + /** + * Current clip region. The clip is stored in canvas-space coordinates, + * (screen-space coordinates in the regular case.) + */ + Rect* clipRect; + + /** + * The ancestor layer's dirty region. + */ + Region* region; + +private: + mat4 mTransformRoot; + Rect mClipRectRoot; + Rect mLocalClip; + +}; // class Snapshot + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SNAPSHOT_H diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp new file mode 100644 index 000000000000..8f6f860ff14e --- /dev/null +++ b/libs/hwui/TextDropShadowCache.cpp @@ -0,0 +1,167 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include "Debug.h" +#include "TextDropShadowCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +TextDropShadowCache::TextDropShadowCache(): + mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) { + INIT_LOGD(" Setting drop shadow cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + INIT_LOGD(" Using default drop shadow cache size of %.2fMB", + DEFAULT_DROP_SHADOW_CACHE_SIZE); + } + + init(); +} + +TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize): + mCache(GenerationCache<ShadowText, ShadowTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize) { + init(); +} + +TextDropShadowCache::~TextDropShadowCache() { + mCache.clear(); +} + +void TextDropShadowCache::init() { + mCache.setOnEntryRemovedListener(this); + mDebugEnabled = readDebugLevel() & kDebugMoreCaches; +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t TextDropShadowCache::getSize() { + return mSize; +} + +uint32_t TextDropShadowCache::getMaxSize() { + return mMaxSize; +} + +void TextDropShadowCache::setMaxSize(uint32_t maxSize) { + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture) { + if (texture) { + mSize -= texture->bitmapSize; + + if (mDebugEnabled) { + LOGD("Shadow texture deleted, size = %d", texture->bitmapSize); + } + + glDeleteTextures(1, &texture->id); + delete texture; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void TextDropShadowCache::clear() { + mCache.clear(); +} + +ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len, + int numGlyphs, uint32_t radius) { + ShadowText entry(paint, radius, len, text); + ShadowTexture* texture = mCache.get(entry); + + if (!texture) { + FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(paint, text, 0, + len, numGlyphs, radius); + + texture = new ShadowTexture; + texture->left = shadow.penX; + texture->top = shadow.penY; + texture->width = shadow.width; + texture->height = shadow.height; + texture->generation = 0; + texture->blend = true; + + const uint32_t size = shadow.width * shadow.height; + texture->bitmapSize = size; + + // Don't even try to cache a bitmap that's bigger than the cache + if (size < mMaxSize) { + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + } + + glGenTextures(1, &texture->id); + + glBindTexture(GL_TEXTURE_2D, texture->id); + // Textures are Alpha8 + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + if (size < mMaxSize) { + if (mDebugEnabled) { + LOGD("Shadow texture created, size = %d", texture->bitmapSize); + } + + entry.copyTextLocally(); + + mSize += size; + mCache.put(entry, texture); + } else { + texture->cleanup = true; + } + + // Cleanup shadow + delete[] shadow.image; + } + + return texture; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h new file mode 100644 index 000000000000..d46686d90845 --- /dev/null +++ b/libs/hwui/TextDropShadowCache.h @@ -0,0 +1,159 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H +#define ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H + +#include <GLES2/gl2.h> + +#include <SkPaint.h> + +#include <utils/String16.h> + +#include "utils/Compare.h" +#include "utils/GenerationCache.h" +#include "FontRenderer.h" +#include "Texture.h" + +namespace android { +namespace uirenderer { + +struct ShadowText { + ShadowText(): radius(0), len(0), textSize(0.0f), typeface(NULL) { + } + + ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText): + radius(radius), len(len) { + // TODO: Propagate this through the API, we should not cast here + text = (const char16_t*) srcText; + + textSize = paint->getTextSize(); + typeface = paint->getTypeface(); + + flags = 0; + if (paint->isFakeBoldText()) { + flags |= Font::kFakeBold; + } + + const float skewX = paint->getTextSkewX(); + italicStyle = *(uint32_t*) &skewX; + + const float scaleXFloat = paint->getTextScaleX(); + scaleX = *(uint32_t*) &scaleXFloat; + } + + ~ShadowText() { + } + + uint32_t radius; + uint32_t len; + float textSize; + SkTypeface* typeface; + uint32_t flags; + uint32_t italicStyle; + uint32_t scaleX; + const char16_t* text; + String16 str; + + void copyTextLocally() { + str.setTo((const char16_t*) text, len >> 1); + text = str.string(); + } + + // TODO: Should take into account fake bold and text skew + bool operator<(const ShadowText& rhs) const { + LTE_INT(len) { + LTE_INT(radius) { + LTE_FLOAT(textSize) { + LTE_INT(typeface) { + LTE_INT(flags) { + LTE_INT(italicStyle) { + LTE_INT(scaleX) { + return strncmp16(text, rhs.text, len >> 1) < 0; + } + } + } + } + } + } + } + return false; + } +}; // struct ShadowText + +/** + * Alpha texture used to represent a shadow. + */ +struct ShadowTexture: public Texture { + ShadowTexture(): Texture() { + } + + float left; + float top; +}; // struct ShadowTexture + +class TextDropShadowCache: public OnEntryRemoved<ShadowText, ShadowTexture*> { +public: + TextDropShadowCache(); + TextDropShadowCache(uint32_t maxByteSize); + ~TextDropShadowCache(); + + /** + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. + */ + void operator()(ShadowText& text, ShadowTexture*& texture); + + ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len, + int numGlyphs, uint32_t radius); + + /** + * Clears the cache. This causes all textures to be deleted. + */ + void clear(); + + void setFontRenderer(FontRenderer& fontRenderer) { + mRenderer = &fontRenderer; + } + + /** + * Sets the maximum size of the cache in bytes. + */ + void setMaxSize(uint32_t maxSize); + /** + * Returns the maximum size of the cache in bytes. + */ + uint32_t getMaxSize(); + /** + * Returns the current size of the cache in bytes. + */ + uint32_t getSize(); + +private: + void init(); + + GenerationCache<ShadowText, ShadowTexture*> mCache; + + uint32_t mSize; + uint32_t mMaxSize; + FontRenderer* mRenderer; + bool mDebugEnabled; +}; // class TextDropShadowCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_TEXT_DROP_SHADOW_CACHE_H diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h new file mode 100644 index 000000000000..4922bb311878 --- /dev/null +++ b/libs/hwui/Texture.h @@ -0,0 +1,89 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_TEXTURE_H +#define ANDROID_HWUI_TEXTURE_H + +#include <GLES2/gl2.h> + +namespace android { +namespace uirenderer { + +/** + * Represents an OpenGL texture. + */ +struct Texture { + Texture() { + cleanup = false; + bitmapSize = 0; + wrapS = GL_CLAMP_TO_EDGE; + wrapT = GL_CLAMP_TO_EDGE; + } + + /** + * Name of the texture. + */ + GLuint id; + /** + * Generation of the backing bitmap, + */ + uint32_t generation; + /** + * Indicates whether the texture requires blending. + */ + bool blend; + /** + * Width of the backing bitmap. + */ + uint32_t width; + /** + * Height of the backing bitmap. + */ + uint32_t height; + /** + * Indicates whether this texture should be cleaned up after use. + */ + bool cleanup; + /** + * Optional, size of the original bitmap. + */ + uint32_t bitmapSize; + + /** + * Last wrap modes set on this texture. Defaults to GL_CLAMP_TO_EDGE. + */ + GLenum wrapS; + GLenum wrapT; +}; // struct Texture + +class AutoTexture { +public: + AutoTexture(const Texture* texture): mTexture(texture) { } + ~AutoTexture() { + if (mTexture && mTexture->cleanup) { + glDeleteTextures(1, &mTexture->id); + delete mTexture; + } + } + +private: + const Texture* mTexture; +}; // class AutoTexture + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_TEXTURE_H diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp new file mode 100644 index 000000000000..3752874053a6 --- /dev/null +++ b/libs/hwui/TextureCache.cpp @@ -0,0 +1,254 @@ +/* + * 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. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <GLES2/gl2.h> + +#include <SkCanvas.h> + +#include <utils/threads.h> + +#include "TextureCache.h" +#include "Properties.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Constructors/destructor +/////////////////////////////////////////////////////////////////////////////// + +TextureCache::TextureCache(): + mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) { + INIT_LOGD(" Setting texture cache size to %sMB", property); + setMaxSize(MB(atof(property))); + } else { + INIT_LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE); + } + + init(); +} + +TextureCache::TextureCache(uint32_t maxByteSize): + mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize) { + init(); +} + +TextureCache::~TextureCache() { + mCache.clear(); +} + +void TextureCache::init() { + mCache.setOnEntryRemovedListener(this); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + INIT_LOGD(" Maximum texture dimension is %d pixels", mMaxTextureSize); + + mDebugEnabled = readDebugLevel() & kDebugCaches; +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t TextureCache::getSize() { + return mSize; +} + +uint32_t TextureCache::getMaxSize() { + return mMaxSize; +} + +void TextureCache::setMaxSize(uint32_t maxSize) { + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) { + // This will be called already locked + if (texture) { + mSize -= texture->bitmapSize; + TEXTURE_LOGD("TextureCache::callback: name, removed size, mSize = %d, %d, %d", + texture->id, texture->bitmapSize, mSize); + if (mDebugEnabled) { + LOGD("Texture deleted, size = %d", texture->bitmapSize); + } + glDeleteTextures(1, &texture->id); + delete texture; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +Texture* TextureCache::get(SkBitmap* bitmap) { + Texture* texture = mCache.get(bitmap); + + if (!texture) { + if (bitmap->width() > mMaxTextureSize || bitmap->height() > mMaxTextureSize) { + LOGW("Bitmap too large to be uploaded into a texture"); + return NULL; + } + + const uint32_t size = bitmap->rowBytes() * bitmap->height(); + // Don't even try to cache a bitmap that's bigger than the cache + if (size < mMaxSize) { + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + } + + texture = new Texture; + texture->bitmapSize = size; + generateTexture(bitmap, texture, false); + + if (size < mMaxSize) { + mSize += size; + TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d", + bitmap, texture->id, size, mSize); + if (mDebugEnabled) { + LOGD("Texture created, size = %d", size); + } + mCache.put(bitmap, texture); + } else { + texture->cleanup = true; + } + } else if (bitmap->getGenerationID() != texture->generation) { + generateTexture(bitmap, texture, true); + } + + return texture; +} + +void TextureCache::remove(SkBitmap* bitmap) { + mCache.remove(bitmap); +} + +void TextureCache::removeDeferred(SkBitmap* bitmap) { + Mutex::Autolock _l(mLock); + mGarbage.push(bitmap); +} + +void TextureCache::clearGarbage() { + Mutex::Autolock _l(mLock); + size_t count = mGarbage.size(); + for (size_t i = 0; i < count; i++) { + mCache.remove(mGarbage.itemAt(i)); + } + mGarbage.clear(); +} + +void TextureCache::clear() { + mCache.clear(); + TEXTURE_LOGD("TextureCache:clear(), miSize = %d", mSize); +} + +void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) { + SkAutoLockPixels alp(*bitmap); + + if (!bitmap->readyToDraw()) { + LOGE("Cannot generate texture from bitmap"); + return; + } + + const bool resize = !regenerate || bitmap->width() != int(texture->width) || + bitmap->height() != int(texture->height); + + if (!regenerate) { + glGenTextures(1, &texture->id); + } + + texture->generation = bitmap->getGenerationID(); + texture->width = bitmap->width(); + texture->height = bitmap->height(); + + glBindTexture(GL_TEXTURE_2D, texture->id); + glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); + + switch (bitmap->getConfig()) { + case SkBitmap::kA8_Config: + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height, + GL_UNSIGNED_BYTE, bitmap->getPixels()); + texture->blend = true; + break; + case SkBitmap::kRGB_565_Config: + uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height, + GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels()); + texture->blend = false; + break; + case SkBitmap::kARGB_8888_Config: + uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, + GL_UNSIGNED_BYTE, bitmap->getPixels()); + // Do this after calling getPixels() to make sure Skia's deferred + // decoding happened + texture->blend = !bitmap->isOpaque(); + break; + case SkBitmap::kARGB_4444_Config: + case SkBitmap::kIndex8_Config: + uploadLoFiTexture(resize, bitmap, texture->width, texture->height); + texture->blend = !bitmap->isOpaque(); + break; + default: + LOGW("Unsupported bitmap config: %d", bitmap->getConfig()); + break; + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + 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 TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap, + uint32_t width, uint32_t height) { + SkBitmap rgbaBitmap; + rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + rgbaBitmap.allocPixels(); + rgbaBitmap.eraseColor(0); + rgbaBitmap.setIsOpaque(bitmap->isOpaque()); + + SkCanvas canvas(rgbaBitmap); + canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); + + uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height, + GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); +} + +void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height, + GLenum type, const GLvoid * data) { + if (resize) { + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h new file mode 100644 index 000000000000..f7707f7ae8e8 --- /dev/null +++ b/libs/hwui/TextureCache.h @@ -0,0 +1,131 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_TEXTURE_CACHE_H +#define ANDROID_HWUI_TEXTURE_CACHE_H + +#include <SkBitmap.h> + +#include <utils/Vector.h> + +#include "Debug.h" +#include "Texture.h" +#include "utils/GenerationCache.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#if DEBUG_TEXTURES + #define TEXTURE_LOGD(...) LOGD(__VA_ARGS__) +#else + #define TEXTURE_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Classes +/////////////////////////////////////////////////////////////////////////////// + +/** + * A simple LRU texture cache. The cache has a maximum size expressed in bytes. + * Any texture added to the cache causing the cache to grow beyond the maximum + * allowed size will also cause the oldest texture to be kicked out. + */ +class TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> { +public: + TextureCache(); + TextureCache(uint32_t maxByteSize); + ~TextureCache(); + + /** + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. + */ + void operator()(SkBitmap*& bitmap, Texture*& texture); + + /** + * Returns the texture associated with the specified bitmap. If the texture + * cannot be found in the cache, a new texture is generated. + */ + Texture* get(SkBitmap* bitmap); + /** + * Removes the texture associated with the specified bitmap. + * Upon remove the texture is freed. + */ + void remove(SkBitmap* bitmap); + /** + * Removes the texture associated with the specified bitmap. This is meant + * to be called from threads that are not the EGL context thread. + */ + void removeDeferred(SkBitmap* bitmap); + /** + * Process deferred removals. + */ + void clearGarbage(); + + /** + * Clears the cache. This causes all textures to be deleted. + */ + void clear(); + + /** + * Sets the maximum size of the cache in bytes. + */ + void setMaxSize(uint32_t maxSize); + /** + * Returns the maximum size of the cache in bytes. + */ + uint32_t getMaxSize(); + /** + * Returns the current size of the cache in bytes. + */ + uint32_t getSize(); + +private: + /** + * Generates the texture from a bitmap into the specified texture structure. + * + * @param regenerate If true, the bitmap data is reuploaded into the texture, but + * no new texture is generated. + */ + void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false); + + void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height); + void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height, + GLenum type, const GLvoid * data); + + void init(); + + GenerationCache<SkBitmap*, Texture*> mCache; + + uint32_t mSize; + uint32_t mMaxSize; + GLint mMaxTextureSize; + + bool mDebugEnabled; + + Vector<SkBitmap*> mGarbage; + mutable Mutex mLock; +}; // class TextureCache + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_TEXTURE_CACHE_H diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h new file mode 100644 index 000000000000..46dded5ade19 --- /dev/null +++ b/libs/hwui/Vector.h @@ -0,0 +1,119 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_VECTOR_H +#define ANDROID_HWUI_VECTOR_H + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Classes +/////////////////////////////////////////////////////////////////////////////// + +struct Vector2 { + float x; + float y; + + Vector2() : + x(0.0f), y(0.0f) { + } + + Vector2(float px, float py) : + x(px), y(py) { + } + + float length() const { + return sqrt(x * x + y * y); + } + + void operator+=(const Vector2& v) { + x += v.x; + y += v.y; + } + + void operator-=(const Vector2& v) { + x -= v.x; + y -= v.y; + } + + void operator+=(const float v) { + x += v; + y += v; + } + + void operator-=(const float v) { + x -= v; + y -= v; + } + + void operator/=(float s) { + x /= s; + y /= s; + } + + void operator*=(float s) { + x *= s; + y *= s; + } + + Vector2 operator+(const Vector2& v) const { + return Vector2(x + v.x, y + v.y); + } + + Vector2 operator-(const Vector2& v) const { + return Vector2(x - v.x, y - v.y); + } + + Vector2 operator/(float s) const { + return Vector2(x / s, y / s); + } + + Vector2 operator*(float s) const { + return Vector2(x * s, y * s); + } + + void normalize() { + float s = 1.0f / length(); + x *= s; + y *= s; + } + + Vector2 copyNormalized() const { + Vector2 v(x, y); + v.normalize(); + return v; + } + + float dot(const Vector2& v) const { + return x * v.x + y * v.y; + } + + void dump() { + LOGD("Vector2[%.2f, %.2f]", x, y); + } +}; // class Vector2 + +/////////////////////////////////////////////////////////////////////////////// +// Types +/////////////////////////////////////////////////////////////////////////////// + +typedef Vector2 vec2; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_VECTOR_H diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h new file mode 100644 index 000000000000..bbf4d4af579c --- /dev/null +++ b/libs/hwui/Vertex.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_VERTEX_H +#define ANDROID_HWUI_VERTEX_H + +namespace android { +namespace uirenderer { + +/** + * Simple structure to describe a vertex with a position and a texture. + */ +struct TextureVertex { + float position[2]; + float texture[2]; + + static inline void set(TextureVertex* vertex, float x, float y, float u, float v) { + vertex[0].position[0] = x; + vertex[0].position[1] = y; + vertex[0].texture[0] = u; + vertex[0].texture[1] = v; + } + + static inline void setUV(TextureVertex* vertex, float u, float v) { + vertex[0].texture[0] = u; + vertex[0].texture[1] = v; + } +}; // struct TextureVertex + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_VERTEX_H diff --git a/libs/hwui/utils/Compare.h b/libs/hwui/utils/Compare.h new file mode 100644 index 000000000000..6531e78ea108 --- /dev/null +++ b/libs/hwui/utils/Compare.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_COMPARE_H +#define ANDROID_HWUI_COMPARE_H + +#include <cmath> + +#define EPSILON 0.00001f + +#define ALMOST_EQUAL(u, v) (fabs((u) - (v)) < EPSILON) + +/** + * Compare floats. + */ +#define LTE_FLOAT(a) \ + if (a < rhs.a) return true; \ + if (ALMOST_EQUAL(a, rhs.a)) + +/** + * Compare integers. + */ +#define LTE_INT(a) \ + if (a < rhs.a) return true; \ + if (a == rhs.a) + +#endif // ANDROID_HWUI_COMPARE_H diff --git a/libs/hwui/utils/GenerationCache.h b/libs/hwui/utils/GenerationCache.h new file mode 100644 index 000000000000..42e6d9bb8cda --- /dev/null +++ b/libs/hwui/utils/GenerationCache.h @@ -0,0 +1,245 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_GENERATION_CACHE_H +#define ANDROID_HWUI_GENERATION_CACHE_H + +#include <utils/KeyedVector.h> +#include <utils/RefBase.h> + +namespace android { +namespace uirenderer { + +template<typename EntryKey, typename EntryValue> +class OnEntryRemoved { +public: + virtual ~OnEntryRemoved() { }; + virtual void operator()(EntryKey& key, EntryValue& value) = 0; +}; // class OnEntryRemoved + +template<typename EntryKey, typename EntryValue> +struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > { + Entry() { } + Entry(const Entry<EntryKey, EntryValue>& e): + key(e.key), value(e.value), parent(e.parent), child(e.child) { } + Entry(sp<Entry<EntryKey, EntryValue> > e): + key(e->key), value(e->value), parent(e->parent), child(e->child) { } + + EntryKey key; + EntryValue value; + + sp<Entry<EntryKey, EntryValue> > parent; + sp<Entry<EntryKey, EntryValue> > child; +}; // struct Entry + +template<typename K, typename V> +class GenerationCache { +public: + GenerationCache(uint32_t maxCapacity); + virtual ~GenerationCache(); + + enum Capacity { + kUnlimitedCapacity, + }; + + void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener); + + void clear(); + + bool contains(K key) const; + V get(K key); + K getKeyAt(uint32_t index) const; + bool put(K key, V value); + V remove(K key); + V removeOldest(); + V getValueAt(uint32_t index) const; + + uint32_t size() const; + + void addToCache(sp<Entry<K, V> > entry, K key, V value); + void attachToCache(sp<Entry<K, V> > entry); + void detachFromCache(sp<Entry<K, V> > entry); + + V removeAt(ssize_t index); + + KeyedVector<K, sp<Entry<K, V> > > mCache; + uint32_t mMaxCapacity; + + OnEntryRemoved<K, V>* mListener; + + sp<Entry<K, V> > mOldest; + sp<Entry<K, V> > mYoungest; +}; // class GenerationCache + +template<typename K, typename V> +GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) { +}; + +template<typename K, typename V> +GenerationCache<K, V>::~GenerationCache() { + clear(); +}; + +template<typename K, typename V> +uint32_t GenerationCache<K, V>::size() const { + return mCache.size(); +} + +template<typename K, typename V> +void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) { + mListener = listener; +} + +template<typename K, typename V> +void GenerationCache<K, V>::clear() { + if (mListener) { + for (uint32_t i = 0; i < mCache.size(); i++) { + sp<Entry<K, V> > entry = mCache.valueAt(i); + if (mListener) { + (*mListener)(entry->key, entry->value); + } + } + } + mCache.clear(); + mYoungest.clear(); + mOldest.clear(); +} + +template<typename K, typename V> +bool GenerationCache<K, V>::contains(K key) const { + return mCache.indexOfKey(key) >= 0; +} + +template<typename K, typename V> +K GenerationCache<K, V>::getKeyAt(uint32_t index) const { + return mCache.keyAt(index); +} + +template<typename K, typename V> +V GenerationCache<K, V>::getValueAt(uint32_t index) const { + return mCache.valueAt(index)->value; +} + +template<typename K, typename V> +V GenerationCache<K, V>::get(K key) { + ssize_t index = mCache.indexOfKey(key); + if (index >= 0) { + sp<Entry<K, V> > entry = mCache.valueAt(index); + if (entry.get()) { + detachFromCache(entry); + attachToCache(entry); + return entry->value; + } + } + + return NULL; +} + +template<typename K, typename V> +bool GenerationCache<K, V>::put(K key, V value) { + if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { + removeOldest(); + } + + ssize_t index = mCache.indexOfKey(key); + if (index < 0) { + sp<Entry<K, V> > entry = new Entry<K, V>; + addToCache(entry, key, value); + return true; + } + + return false; +} + +template<typename K, typename V> +void GenerationCache<K, V>::addToCache(sp<Entry<K, V> > entry, K key, V value) { + entry->key = key; + entry->value = value; + mCache.add(key, entry); + attachToCache(entry); +} + +template<typename K, typename V> +V GenerationCache<K, V>::remove(K key) { + ssize_t index = mCache.indexOfKey(key); + if (index >= 0) { + return removeAt(index); + } + + return NULL; +} + +template<typename K, typename V> +V GenerationCache<K, V>::removeAt(ssize_t index) { + sp<Entry<K, V> > entry = mCache.valueAt(index); + if (mListener) { + (*mListener)(entry->key, entry->value); + } + mCache.removeItemsAt(index, 1); + detachFromCache(entry); + + return entry->value; +} + +template<typename K, typename V> +V GenerationCache<K, V>::removeOldest() { + if (mOldest.get()) { + ssize_t index = mCache.indexOfKey(mOldest->key); + if (index >= 0) { + return removeAt(index); + } + } + + return NULL; +} + +template<typename K, typename V> +void GenerationCache<K, V>::attachToCache(sp<Entry<K, V> > entry) { + if (!mYoungest.get()) { + mYoungest = mOldest = entry; + } else { + entry->parent = mYoungest; + mYoungest->child = entry; + mYoungest = entry; + } +} + +template<typename K, typename V> +void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) { + if (entry->parent.get()) { + entry->parent->child = entry->child; + } + + if (entry->child.get()) { + entry->child->parent = entry->parent; + } + + if (mOldest == entry) { + mOldest = entry->child; + } + + if (mYoungest == entry) { + mYoungest = entry->parent; + } + + entry->parent.clear(); + entry->child.clear(); +} + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_GENERATION_CACHE_H diff --git a/libs/hwui/utils/SortedList.h b/libs/hwui/utils/SortedList.h new file mode 100644 index 000000000000..2fa890a39f73 --- /dev/null +++ b/libs/hwui/utils/SortedList.h @@ -0,0 +1,242 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_SORTED_LIST_H +#define ANDROID_HWUI_SORTED_LIST_H + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Vector.h> +#include <utils/TypeHelpers.h> + +#include "SortedListImpl.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Sorted list +/////////////////////////////////////////////////////////////////////////////// + +template<class TYPE> +class SortedList: private SortedListImpl { +public: + typedef TYPE value_type; + + SortedList(); + SortedList(const SortedList<TYPE>& rhs); + virtual ~SortedList(); + + const SortedList<TYPE>& operator =(const SortedList<TYPE>& rhs) const; + SortedList<TYPE>& operator =(const SortedList<TYPE>& rhs); + + inline void clear() { + VectorImpl::clear(); + } + + inline size_t size() const { + return VectorImpl::size(); + } + + inline bool isEmpty() const { + return VectorImpl::isEmpty(); + } + + inline size_t capacity() const { + return VectorImpl::capacity(); + } + + inline ssize_t setCapacity(size_t size) { + return VectorImpl::setCapacity(size); + } + + inline const TYPE* array() const; + + TYPE* editArray(); + + ssize_t indexOf(const TYPE& item) const; + size_t orderOf(const TYPE& item) const; + + inline const TYPE& operator [](size_t index) const; + inline const TYPE& itemAt(size_t index) const; + const TYPE& top() const; + const TYPE& mirrorItemAt(ssize_t index) const; + + ssize_t add(const TYPE& item); + + TYPE& editItemAt(size_t index) { + return *(static_cast<TYPE *> (VectorImpl::editItemLocation(index))); + } + + ssize_t merge(const Vector<TYPE>& vector); + ssize_t merge(const SortedList<TYPE>& vector); + + ssize_t remove(const TYPE&); + + inline ssize_t removeItemsAt(size_t index, size_t count = 1); + inline ssize_t removeAt(size_t index) { + return removeItemsAt(index); + } + +protected: + virtual void do_construct(void* storage, size_t num) const; + virtual void do_destroy(void* storage, size_t num) const; + virtual void do_copy(void* dest, const void* from, size_t num) const; + virtual void do_splat(void* dest, const void* item, size_t num) const; + virtual void do_move_forward(void* dest, const void* from, size_t num) const; + virtual void do_move_backward(void* dest, const void* from, size_t num) const; + virtual int do_compare(const void* lhs, const void* rhs) const; +}; // class SortedList + +/////////////////////////////////////////////////////////////////////////////// +// Implementation +/////////////////////////////////////////////////////////////////////////////// + +template<class TYPE> +inline SortedList<TYPE>::SortedList(): + SortedListImpl(sizeof(TYPE), ((traits<TYPE>::has_trivial_ctor ? HAS_TRIVIAL_CTOR : 0) + | (traits<TYPE>::has_trivial_dtor ? HAS_TRIVIAL_DTOR : 0) + | (traits<TYPE>::has_trivial_copy ? HAS_TRIVIAL_COPY : 0))) { +} + +template<class TYPE> +inline SortedList<TYPE>::SortedList(const SortedList<TYPE>& rhs): SortedListImpl(rhs) { +} + +template<class TYPE> inline SortedList<TYPE>::~SortedList() { + finish_vector(); +} + +template<class TYPE> +inline SortedList<TYPE>& SortedList<TYPE>::operator =(const SortedList<TYPE>& rhs) { + SortedListImpl::operator =(rhs); + return *this; +} + +template<class TYPE> +inline const SortedList<TYPE>& SortedList<TYPE>::operator =( + const SortedList<TYPE>& rhs) const { + SortedListImpl::operator =(rhs); + return *this; +} + +template<class TYPE> +inline const TYPE* SortedList<TYPE>::array() const { + return static_cast<const TYPE *> (arrayImpl()); +} + +template<class TYPE> +inline TYPE* SortedList<TYPE>::editArray() { + return static_cast<TYPE *> (editArrayImpl()); +} + +template<class TYPE> +inline const TYPE& SortedList<TYPE>::operator[](size_t index) const { + assert( index<size() ); + return *(array() + index); +} + +template<class TYPE> +inline const TYPE& SortedList<TYPE>::itemAt(size_t index) const { + return operator[](index); +} + +template<class TYPE> +inline const TYPE& SortedList<TYPE>::mirrorItemAt(ssize_t index) const { + assert( (index>0 ? index : -index)<size() ); + return *(array() + ((index < 0) ? (size() - index) : index)); +} + +template<class TYPE> +inline const TYPE& SortedList<TYPE>::top() const { + return *(array() + size() - 1); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::add(const TYPE& item) { + return SortedListImpl::add(&item); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::indexOf(const TYPE& item) const { + return SortedListImpl::indexOf(&item); +} + +template<class TYPE> +inline size_t SortedList<TYPE>::orderOf(const TYPE& item) const { + return SortedListImpl::orderOf(&item); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::merge(const Vector<TYPE>& vector) { + return SortedListImpl::merge(reinterpret_cast<const VectorImpl&> (vector)); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::merge(const SortedList<TYPE>& vector) { + return SortedListImpl::merge(reinterpret_cast<const SortedListImpl&> (vector)); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::remove(const TYPE& item) { + return SortedListImpl::remove(&item); +} + +template<class TYPE> +inline ssize_t SortedList<TYPE>::removeItemsAt(size_t index, size_t count) { + return VectorImpl::removeItemsAt(index, count); +} + +template<class TYPE> +void SortedList<TYPE>::do_construct(void* storage, size_t num) const { + construct_type(reinterpret_cast<TYPE*> (storage), num); +} + +template<class TYPE> +void SortedList<TYPE>::do_destroy(void* storage, size_t num) const { + destroy_type(reinterpret_cast<TYPE*> (storage), num); +} + +template<class TYPE> +void SortedList<TYPE>::do_copy(void* dest, const void* from, size_t num) const { + copy_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num); +} + +template<class TYPE> +void SortedList<TYPE>::do_splat(void* dest, const void* item, size_t num) const { + splat_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (item), num); +} + +template<class TYPE> +void SortedList<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const { + move_forward_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num); +} + +template<class TYPE> +void SortedList<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const { + move_backward_type(reinterpret_cast<TYPE*> (dest), reinterpret_cast<const TYPE*> (from), num); +} + +template<class TYPE> +int SortedList<TYPE>::do_compare(const void* lhs, const void* rhs) const { + return compare_type(*reinterpret_cast<const TYPE*> (lhs), *reinterpret_cast<const TYPE*> (rhs)); +} + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SORTED_LIST_H diff --git a/libs/hwui/utils/SortedListImpl.cpp b/libs/hwui/utils/SortedListImpl.cpp new file mode 100644 index 000000000000..35171d5b1a5b --- /dev/null +++ b/libs/hwui/utils/SortedListImpl.cpp @@ -0,0 +1,126 @@ +/* + * 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 "SortedListImpl.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Sorted list implementation, not for direct use +/////////////////////////////////////////////////////////////////////////////// + +SortedListImpl::SortedListImpl(size_t itemSize, uint32_t flags): VectorImpl(itemSize, flags) { +} + +SortedListImpl::SortedListImpl(const VectorImpl& rhs): VectorImpl(rhs) { +} + +SortedListImpl::~SortedListImpl() { +} + +SortedListImpl& SortedListImpl::operator =(const SortedListImpl& rhs) { + return static_cast<SortedListImpl&> + (VectorImpl::operator =(static_cast<const VectorImpl&> (rhs))); +} + +ssize_t SortedListImpl::indexOf(const void* item) const { + return _indexOrderOf(item); +} + +size_t SortedListImpl::orderOf(const void* item) const { + size_t o; + _indexOrderOf(item, &o); + return o; +} + +ssize_t SortedListImpl::_indexOrderOf(const void* item, size_t* order) const { + // binary search + ssize_t err = NAME_NOT_FOUND; + ssize_t l = 0; + ssize_t h = size() - 1; + ssize_t mid; + const void* a = arrayImpl(); + const size_t s = itemSize(); + while (l <= h) { + mid = l + (h - l) / 2; + const void* const curr = reinterpret_cast<const char *> (a) + (mid * s); + const int c = do_compare(curr, item); + if (c == 0) { + err = l = mid; + break; + } else if (c < 0) { + l = mid + 1; + } else { + h = mid - 1; + } + } + if (order) { + *order = l; + } + return err; +} + +ssize_t SortedListImpl::add(const void* item) { + size_t order; + ssize_t index = _indexOrderOf(item, &order); + index = VectorImpl::insertAt(item, order, 1); + return index; +} + +ssize_t SortedListImpl::merge(const VectorImpl& vector) { + // naive merge... + if (!vector.isEmpty()) { + const void* buffer = vector.arrayImpl(); + const size_t is = itemSize(); + size_t s = vector.size(); + for (size_t i = 0; i < s; i++) { + ssize_t err = add(reinterpret_cast<const char*> (buffer) + i * is); + if (err < 0) { + return err; + } + } + } + return NO_ERROR; +} + +ssize_t SortedListImpl::merge(const SortedListImpl& vector) { + // we've merging a sorted vector... nice! + ssize_t err = NO_ERROR; + if (!vector.isEmpty()) { + // first take care of the case where the vectors are sorted together + if (do_compare(vector.itemLocation(vector.size() - 1), arrayImpl()) <= 0) { + err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&> (vector), 0); + } else if (do_compare(vector.arrayImpl(), itemLocation(size() - 1)) >= 0) { + err = VectorImpl::appendVector(static_cast<const VectorImpl&> (vector)); + } else { + // this could be made a little better + err = merge(static_cast<const VectorImpl&> (vector)); + } + } + return err; +} + +ssize_t SortedListImpl::remove(const void* item) { + ssize_t i = indexOf(item); + if (i >= 0) { + VectorImpl::removeItemsAt(i, 1); + } + return i; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/utils/SortedListImpl.h b/libs/hwui/utils/SortedListImpl.h new file mode 100644 index 000000000000..dc385b5f7b3a --- /dev/null +++ b/libs/hwui/utils/SortedListImpl.h @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#ifndef ANDROID_HWUI_SORTED_LIST_IMPL_H +#define ANDROID_HWUI_SORTED_LIST_IMPL_H + +#include <utils/VectorImpl.h> + +namespace android { +namespace uirenderer { + +class SortedListImpl: public VectorImpl { +public: + SortedListImpl(size_t itemSize, uint32_t flags); + SortedListImpl(const VectorImpl& rhs); + virtual ~SortedListImpl(); + + SortedListImpl& operator =(const SortedListImpl& rhs); + + ssize_t indexOf(const void* item) const; + size_t orderOf(const void* item) const; + ssize_t add(const void* item); + ssize_t merge(const VectorImpl& vector); + ssize_t merge(const SortedListImpl& vector); + ssize_t remove(const void* item); + +protected: + virtual int do_compare(const void* lhs, const void* rhs) const = 0; + +private: + ssize_t _indexOrderOf(const void* item, size_t* order = 0) const; + + // these are made private, because they can't be used on a SortedVector + // (they don't have an implementation either) + ssize_t add(); + void pop(); + void push(); + void push(const void* item); + ssize_t insertVectorAt(const VectorImpl& vector, size_t index); + ssize_t appendVector(const VectorImpl& vector); + ssize_t insertArrayAt(const void* array, size_t index, size_t length); + ssize_t appendArray(const void* array, size_t length); + ssize_t insertAt(size_t where, size_t numItems = 1); + ssize_t insertAt(const void* item, size_t where, size_t numItems = 1); + ssize_t replaceAt(size_t index); + ssize_t replaceAt(const void* item, size_t index); +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SORTED_LIST_IMPL_H diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk index 6f2cd075f72e..083688771dac 100644 --- a/libs/rs/Android.mk +++ b/libs/rs/Android.mk @@ -23,7 +23,17 @@ include $(BUILD_HOST_EXECUTABLE) # TODO: This should go into build/core/config.mk RSG_GENERATOR:=$(LOCAL_BUILT_MODULE) +# include $(CLEAR_VARS) +# input_data_file := $(LOCAL_PATH)/rslib.bc +# slangdata_output_var_name := rs_runtime_lib_bc +# LOCAL_MODULE := librslib_rt +# LOCAL_PRELINK_MODULE := false +# LOCAL_MODULE_CLASS := STATIC_LIBRARIES + +# LOCAL_MODULE_TAGS := optional +# include frameworks/compile/slang/SlangData.mk +# include $(BUILD_STATIC_LIBRARY) # Build render script lib ==================== @@ -76,46 +86,73 @@ ifneq ($(TARGET_SIMULATOR),true) LOCAL_SRC_FILES:= \ rsAdapter.cpp \ rsAllocation.cpp \ + rsAnimation.cpp \ rsComponent.cpp \ rsContext.cpp \ rsDevice.cpp \ rsElement.cpp \ - rsFileA3D.cpp \ - rsLight.cpp \ + rsFileA3D.cpp \ + rsFont.cpp \ rsLocklessFifo.cpp \ rsObjectBase.cpp \ rsMatrix.cpp \ - rsMesh.cpp \ - rsNoise.cpp \ + rsMesh.cpp \ + rsMutex.cpp \ rsProgram.cpp \ rsProgramFragment.cpp \ - rsProgramFragmentStore.cpp \ + rsProgramStore.cpp \ rsProgramRaster.cpp \ rsProgramVertex.cpp \ rsSampler.cpp \ rsScript.cpp \ rsScriptC.cpp \ rsScriptC_Lib.cpp \ - rsShaderCache.cpp \ - rsSimpleMesh.cpp \ + rsScriptC_LibCL.cpp \ + rsScriptC_LibGL.cpp \ + rsShaderCache.cpp \ + rsSignal.cpp \ + rsStream.cpp \ rsThreadIO.cpp \ rsType.cpp \ rsVertexArray.cpp -ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) - LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -endif -LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libGLESv2 libui libacc +LOCAL_SHARED_LIBRARIES += libz libcutils libutils libEGL libGLESv1_CM libGLESv2 libui libbcc + +LOCAL_STATIC_LIBRARIES := libdex libft2 + +LOCAL_C_INCLUDES += external/freetype/include external/zlib dalvik +LOCAL_C_INCLUDES += frameworks/compile/libbcc/include + +LOCAL_CFLAGS += -Werror -Wall -Wno-unused-parameter -Wno-unused-variable + LOCAL_LDLIBS := -lpthread -ldl LOCAL_MODULE:= libRS LOCAL_MODULE_TAGS := optional include $(BUILD_SHARED_LIBRARY) -# include the java examples -include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk,\ - java \ - )) +# Now build a host version for serialization +include $(CLEAR_VARS) +LOCAL_CFLAGS += -Werror -Wall -Wno-unused-parameter -Wno-unused-variable +LOCAL_CFLAGS += -DANDROID_RS_SERIALIZE + +LOCAL_SRC_FILES:= \ + rsAllocation.cpp \ + rsComponent.cpp \ + rsElement.cpp \ + rsFileA3D.cpp \ + rsObjectBase.cpp \ + rsMesh.cpp \ + rsStream.cpp \ + rsType.cpp + +LOCAL_STATIC_LIBRARIES := libcutils libutils + +LOCAL_LDLIBS := -lpthread +LOCAL_MODULE:= libRSserialize +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_STATIC_LIBRARY) endif #simulator diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h index d280f5053b03..ffa9a8c8b74b 100644 --- a/libs/rs/RenderScript.h +++ b/libs/rs/RenderScript.h @@ -27,23 +27,27 @@ extern "C" { ////////////////////////////////////////////////////// // +typedef void * RsAsyncVoidPtr; + typedef void * RsAdapter1D; typedef void * RsAdapter2D; typedef void * RsAllocation; +typedef void * RsAnimation; typedef void * RsContext; typedef void * RsDevice; typedef void * RsElement; typedef void * RsFile; +typedef void * RsFont; typedef void * RsSampler; typedef void * RsScript; -typedef void * RsSimpleMesh; +typedef void * RsMesh; typedef void * RsType; -typedef void * RsLight; +typedef void * RsObjectBase; typedef void * RsProgram; typedef void * RsProgramVertex; typedef void * RsProgramFragment; -typedef void * RsProgramFragmentStore; +typedef void * RsProgramStore; typedef void * RsProgramRaster; typedef void (* RsBitmapCallback_t)(void *); @@ -53,22 +57,70 @@ enum RsDeviceParam { RS_DEVICE_PARAM_COUNT }; +typedef struct { + uint32_t colorMin; + uint32_t colorPref; + uint32_t alphaMin; + uint32_t alphaPref; + uint32_t depthMin; + uint32_t depthPref; + uint32_t stencilMin; + uint32_t stencilPref; + uint32_t samplesMin; + uint32_t samplesPref; + float samplesQ; +} RsSurfaceConfig; + RsDevice rsDeviceCreate(); void rsDeviceDestroy(RsDevice); void rsDeviceSetConfig(RsDevice, RsDeviceParam, int32_t value); RsContext rsContextCreate(RsDevice, uint32_t version); -RsContext rsContextCreateGL(RsDevice, uint32_t version, bool useDepth); +RsContext rsContextCreateGL(RsDevice, uint32_t version, + RsSurfaceConfig sc, uint32_t dpi); void rsContextDestroy(RsContext); -void rsObjDestroyOOB(RsContext, void *); -uint32_t rsContextGetMessage(RsContext, void *data, size_t *receiveLen, size_t bufferLen, bool wait); +enum RsMessageToClientType { + RS_MESSAGE_TO_CLIENT_NONE = 0, + RS_MESSAGE_TO_CLIENT_EXCEPTION = 1, + RS_MESSAGE_TO_CLIENT_RESIZE = 2, + RS_MESSAGE_TO_CLIENT_ERROR = 3, + RS_MESSAGE_TO_CLIENT_USER = 4 +}; + +RsMessageToClientType rsContextGetMessage(RsContext vrsc, void *data, size_t *receiveLen, uint32_t *subID, size_t bufferLen, bool wait); +RsMessageToClientType rsContextPeekMessage(RsContext vrsc, size_t *receiveLen, uint32_t *subID, bool wait); void rsContextInitToClient(RsContext); void rsContextDeinitToClient(RsContext); #define RS_MAX_TEXTURE 2 #define RS_MAX_ATTRIBS 16 + +enum RsAllocationUsageType { + RS_ALLOCATION_USAGE_SCRIPT = 0x0001, + RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE = 0x0002, + RS_ALLOCATION_USAGE_GRAPHICS_VERTEX = 0x0004, + RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS = 0x0008, + + RS_ALLOCATION_USAGE_ALL = 0x000F +}; + +enum RsAllocationMipmapControl { + RS_ALLOCATION_MIPMAP_NONE = 0, + RS_ALLOCATION_MIPMAP_FULL = 1, + RS_ALLOCATION_MIPMAP_ON_SYNC_TO_TEXTURE = 2 +}; + +enum RsAllocationCubemapFace { + RS_ALLOCATION_CUBMAP_FACE_POSITVE_X = 0, + RS_ALLOCATION_CUBMAP_FACE_NEGATIVE_X = 1, + RS_ALLOCATION_CUBMAP_FACE_POSITVE_Y = 2, + RS_ALLOCATION_CUBMAP_FACE_NEGATIVE_Y = 3, + RS_ALLOCATION_CUBMAP_FACE_POSITVE_Z = 4, + RS_ALLOCATION_CUBMAP_FACE_NEGATIVE_Z = 5 +}; + enum RsDataType { RS_TYPE_NONE, RS_TYPE_FLOAT_16, @@ -83,11 +135,17 @@ enum RsDataType { RS_TYPE_UNSIGNED_32, RS_TYPE_UNSIGNED_64, + RS_TYPE_BOOLEAN, + RS_TYPE_UNSIGNED_5_6_5, RS_TYPE_UNSIGNED_5_5_5_1, RS_TYPE_UNSIGNED_4_4_4_4, - RS_TYPE_ELEMENT, + RS_TYPE_MATRIX_4X4, + RS_TYPE_MATRIX_3X3, + RS_TYPE_MATRIX_2X2, + + RS_TYPE_ELEMENT = 1000, RS_TYPE_TYPE, RS_TYPE_ALLOCATION, RS_TYPE_SAMPLER, @@ -96,24 +154,17 @@ enum RsDataType { RS_TYPE_PROGRAM_FRAGMENT, RS_TYPE_PROGRAM_VERTEX, RS_TYPE_PROGRAM_RASTER, - RS_TYPE_PROGRAM_STORE + RS_TYPE_PROGRAM_STORE, }; enum RsDataKind { RS_KIND_USER, - RS_KIND_COLOR, - RS_KIND_POSITION, - RS_KIND_TEXTURE, - RS_KIND_NORMAL, - RS_KIND_INDEX, - RS_KIND_POINT_SIZE, - - RS_KIND_PIXEL_L, + + RS_KIND_PIXEL_L = 7, RS_KIND_PIXEL_A, RS_KIND_PIXEL_LA, RS_KIND_PIXEL_RGB, RS_KIND_PIXEL_RGBA, - }; enum RsSamplerParam { @@ -121,7 +172,8 @@ enum RsSamplerParam { RS_SAMPLER_MAG_FILTER, RS_SAMPLER_WRAP_S, RS_SAMPLER_WRAP_T, - RS_SAMPLER_WRAP_R + RS_SAMPLER_WRAP_R, + RS_SAMPLER_ANISO }; enum RsSamplerValue { @@ -129,7 +181,13 @@ enum RsSamplerValue { RS_SAMPLER_LINEAR, RS_SAMPLER_LINEAR_MIP_LINEAR, RS_SAMPLER_WRAP, - RS_SAMPLER_CLAMP + RS_SAMPLER_CLAMP, + RS_SAMPLER_LINEAR_MIP_NEAREST, +}; + +enum RsTextureTarget { + RS_TEXTURE_2D, + RS_TEXTURE_CUBE }; enum RsDimension { @@ -190,7 +248,7 @@ enum RsProgramParam { RS_PROGRAM_PARAM_INPUT, RS_PROGRAM_PARAM_OUTPUT, RS_PROGRAM_PARAM_CONSTANT, - RS_PROGRAM_PARAM_TEXTURE_COUNT, + RS_PROGRAM_PARAM_TEXTURE_TYPE, }; enum RsPrimitive { @@ -203,11 +261,115 @@ enum RsPrimitive { }; enum RsError { - RS_ERROR_NONE, - RS_ERROR_BAD_SHADER, - RS_ERROR_BAD_SCRIPT + RS_ERROR_NONE = 0, + RS_ERROR_BAD_SHADER = 1, + RS_ERROR_BAD_SCRIPT = 2, + RS_ERROR_BAD_VALUE = 3, + RS_ERROR_OUT_OF_MEMORY = 4, + RS_ERROR_DRIVER = 5, + + RS_ERROR_FATAL_UNKNOWN = 0x1000, + RS_ERROR_FATAL_DRIVER = 0x1001, + RS_ERROR_FATAL_PROGRAM_LINK = 0x1002 +}; + +enum RsAnimationInterpolation { + RS_ANIMATION_INTERPOLATION_STEP, + RS_ANIMATION_INTERPOLATION_LINEAR, + RS_ANIMATION_INTERPOLATION_BEZIER, + RS_ANIMATION_INTERPOLATION_CARDINAL, + RS_ANIMATION_INTERPOLATION_HERMITE, + RS_ANIMATION_INTERPOLATION_BSPLINE +}; + +enum RsAnimationEdge { + RS_ANIMATION_EDGE_UNDEFINED, + RS_ANIMATION_EDGE_CONSTANT, + RS_ANIMATION_EDGE_GRADIENT, + RS_ANIMATION_EDGE_CYCLE, + RS_ANIMATION_EDGE_OSCILLATE, + RS_ANIMATION_EDGE_CYLE_RELATIVE +}; + +enum RsA3DClassID { + RS_A3D_CLASS_ID_UNKNOWN, + RS_A3D_CLASS_ID_MESH, + RS_A3D_CLASS_ID_TYPE, + RS_A3D_CLASS_ID_ELEMENT, + RS_A3D_CLASS_ID_ALLOCATION, + RS_A3D_CLASS_ID_PROGRAM_VERTEX, + RS_A3D_CLASS_ID_PROGRAM_RASTER, + RS_A3D_CLASS_ID_PROGRAM_FRAGMENT, + RS_A3D_CLASS_ID_PROGRAM_STORE, + RS_A3D_CLASS_ID_SAMPLER, + RS_A3D_CLASS_ID_ANIMATION, + RS_A3D_CLASS_ID_ADAPTER_1D, + RS_A3D_CLASS_ID_ADAPTER_2D, + RS_A3D_CLASS_ID_SCRIPT_C }; +enum RsCullMode { + RS_CULL_BACK, + RS_CULL_FRONT, + RS_CULL_NONE +}; + +typedef struct { + RsA3DClassID classID; + const char* objectName; +} RsFileIndexEntry; + +// Script to Script +typedef struct { + uint32_t xStart; + uint32_t xEnd; + uint32_t yStart; + uint32_t yEnd; + uint32_t zStart; + uint32_t zEnd; + uint32_t arrayStart; + uint32_t arrayEnd; + +} RsScriptCall; + +// A3D loading and object update code. +// Should only be called at object creation, not thread safe +RsObjectBase rsaFileA3DGetEntryByIndex(RsContext, uint32_t idx, RsFile); +RsFile rsaFileA3DCreateFromMemory(RsContext, const void *data, uint32_t len); +RsFile rsaFileA3DCreateFromAsset(RsContext, void *asset); +RsFile rsaFileA3DCreateFromFile(RsContext, const char *path); +void rsaFileA3DGetNumIndexEntries(RsContext, int32_t *numEntries, RsFile); +void rsaFileA3DGetIndexEntries(RsContext, RsFileIndexEntry *fileEntries,uint32_t numEntries, RsFile); +void rsaGetName(RsContext, void * obj, const char **name); +// Mesh update functions +void rsaMeshGetVertexBufferCount(RsContext, RsMesh, int32_t *vtxCount); +void rsaMeshGetIndexCount(RsContext, RsMesh, int32_t *idxCount); +void rsaMeshGetVertices(RsContext, RsMesh, RsAllocation *vtxData, uint32_t vtxDataCount); +void rsaMeshGetIndices(RsContext, RsMesh, RsAllocation *va, uint32_t *primType, uint32_t idxDataCount); +// Allocation update +const void* rsaAllocationGetType(RsContext con, RsAllocation va); +// Type update +void rsaTypeGetNativeData(RsContext, RsType, uint32_t *typeData, uint32_t typeDataSize); +// Element update +void rsaElementGetNativeData(RsContext, RsElement, uint32_t *elemData, uint32_t elemDataSize); +void rsaElementGetSubElements(RsContext, RsElement, uint32_t *ids, const char **names, uint32_t dataSize); + +// Async commands for returning new IDS +RsType rsaTypeCreate(RsContext, RsElement, uint32_t dimX, uint32_t dimY, + uint32_t dimZ, bool mips, bool faces); +RsAllocation rsaAllocationCreateTyped(RsContext rsc, RsType vtype, + RsAllocationMipmapControl mips, + uint32_t usages); +RsAllocation rsaAllocationCreateFromBitmap(RsContext con, RsType vtype, + RsAllocationMipmapControl mips, + const void *data, uint32_t usages); +RsAllocation rsaAllocationCubeCreateFromBitmap(RsContext con, RsType vtype, + RsAllocationMipmapControl mips, + const void *data, uint32_t usages); +#ifdef ANDROID_RS_SERIALIZE +#define NO_RS_FUNCS +#endif + #ifndef NO_RS_FUNCS #include "rsgApiFuncDecl.h" #endif diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h index 99b8c0456f04..b82eaf1d6c59 100644 --- a/libs/rs/RenderScriptEnv.h +++ b/libs/rs/RenderScriptEnv.h @@ -9,12 +9,10 @@ typedef void * RsDevice; typedef void * RsElement; typedef void * RsSampler; typedef void * RsScript; -typedef void * RsSimpleMesh; +typedef void * RsMesh; typedef void * RsType; typedef void * RsProgramFragment; -typedef void * RsProgramFragmentStore; -typedef void * RsLight; - +typedef void * RsProgramStore; typedef struct { float m[16]; @@ -28,4 +26,4 @@ typedef struct { #define RS_PROGRAM_VERTEX_MODELVIEW_OFFSET 0 #define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16 #define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32 - +#define RS_PROGRAM_VERTEX_MVP_OFFSET 48 diff --git a/libs/rs/java/Android.mk b/libs/rs/java/Android.mk deleted file mode 100644 index 5053e7d64389..000000000000 --- a/libs/rs/java/Android.mk +++ /dev/null @@ -1 +0,0 @@ -include $(call all-subdir-makefiles) diff --git a/libs/rs/java/Film/AndroidManifest.xml b/libs/rs/java/Film/AndroidManifest.xml deleted file mode 100644 index a5ce8a18d427..000000000000 --- a/libs/rs/java/Film/AndroidManifest.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.film"> - <application android:label="Film"> - <activity android:name="Film" - android:screenOrientation="portrait" - android:theme="@android:style/Theme.Black.NoTitleBar"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/libs/rs/java/Film/res/drawable/p01.png b/libs/rs/java/Film/res/drawable/p01.png Binary files differdeleted file mode 100644 index a9b9bdbe5686..000000000000 --- a/libs/rs/java/Film/res/drawable/p01.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p02.png b/libs/rs/java/Film/res/drawable/p02.png Binary files differdeleted file mode 100644 index 8162c82b629a..000000000000 --- a/libs/rs/java/Film/res/drawable/p02.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p03.png b/libs/rs/java/Film/res/drawable/p03.png Binary files differdeleted file mode 100644 index e3e26c0b3f1d..000000000000 --- a/libs/rs/java/Film/res/drawable/p03.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p04.png b/libs/rs/java/Film/res/drawable/p04.png Binary files differdeleted file mode 100644 index daee603048ec..000000000000 --- a/libs/rs/java/Film/res/drawable/p04.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p05.png b/libs/rs/java/Film/res/drawable/p05.png Binary files differdeleted file mode 100644 index fac5248fb36b..000000000000 --- a/libs/rs/java/Film/res/drawable/p05.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p06.png b/libs/rs/java/Film/res/drawable/p06.png Binary files differdeleted file mode 100644 index 3b5126164ab2..000000000000 --- a/libs/rs/java/Film/res/drawable/p06.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p07.png b/libs/rs/java/Film/res/drawable/p07.png Binary files differdeleted file mode 100644 index d8bd9383fbd3..000000000000 --- a/libs/rs/java/Film/res/drawable/p07.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p08.png b/libs/rs/java/Film/res/drawable/p08.png Binary files differdeleted file mode 100644 index ef175e8f1127..000000000000 --- a/libs/rs/java/Film/res/drawable/p08.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p09.png b/libs/rs/java/Film/res/drawable/p09.png Binary files differdeleted file mode 100644 index 7bf387440b6d..000000000000 --- a/libs/rs/java/Film/res/drawable/p09.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p10.png b/libs/rs/java/Film/res/drawable/p10.png Binary files differdeleted file mode 100644 index 908827d243f4..000000000000 --- a/libs/rs/java/Film/res/drawable/p10.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p11.png b/libs/rs/java/Film/res/drawable/p11.png Binary files differdeleted file mode 100644 index 1289f7117f88..000000000000 --- a/libs/rs/java/Film/res/drawable/p11.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p12.png b/libs/rs/java/Film/res/drawable/p12.png Binary files differdeleted file mode 100644 index e1af16a43b63..000000000000 --- a/libs/rs/java/Film/res/drawable/p12.png +++ /dev/null diff --git a/libs/rs/java/Film/res/drawable/p13.png b/libs/rs/java/Film/res/drawable/p13.png Binary files differdeleted file mode 100644 index d08bcbee295c..000000000000 --- a/libs/rs/java/Film/res/drawable/p13.png +++ /dev/null diff --git a/libs/rs/java/Film/res/raw/filmimage.c b/libs/rs/java/Film/res/raw/filmimage.c deleted file mode 100644 index d154c68bfa00..000000000000 --- a/libs/rs/java/Film/res/raw/filmimage.c +++ /dev/null @@ -1,110 +0,0 @@ -// Fountain test script - -#pragma version(1) -#pragma stateVertex(orthoWindow) -#pragma stateRaster(flat) -#pragma stateFragment(PgmFragBackground) -#pragma stateStore(MyBlend) - - -int main(void* con, int ft, int launchID) { - int count, touch, x, y, rate, maxLife, lifeShift; - int life; - int ct, ct2; - int newPart; - int drawCount; - int dx, dy, idx; - int posx,posy; - int c; - int srcIdx; - int dstIdx; - - count = loadI32(con, 0, 1); - touch = loadI32(con, 0, 2); - x = loadI32(con, 0, 3); - y = loadI32(con, 0, 4); - - rate = 4; - maxLife = (count / rate) - 1; - lifeShift = 0; - { - life = maxLife; - while (life > 255) { - life = life >> 1; - lifeShift ++; - } - } - - drawRect(con, 0, 256, 0, 512); - contextBindProgramFragment(con, NAMED_PgmFragParts); - - if (touch) { - newPart = loadI32(con, 2, 0); - for (ct2=0; ct2<rate; ct2++) { - dx = scriptRand(con, 0x10000) - 0x8000; - dy = scriptRand(con, 0x10000) - 0x8000; - - idx = newPart * 5 + 1; - storeI32(con, 2, idx, dx); - storeI32(con, 2, idx + 1, dy); - storeI32(con, 2, idx + 2, maxLife); - storeI32(con, 2, idx + 3, x << 16); - storeI32(con, 2, idx + 4, y << 16); - - newPart++; - if (newPart >= count) { - newPart = 0; - } - } - storeI32(con, 2, 0, newPart); - } - - drawCount = 0; - for (ct=0; ct < count; ct++) { - srcIdx = ct * 5 + 1; - - dx = loadI32(con, 2, srcIdx); - dy = loadI32(con, 2, srcIdx + 1); - life = loadI32(con, 2, srcIdx + 2); - posx = loadI32(con, 2, srcIdx + 3); - posy = loadI32(con, 2, srcIdx + 4); - - if (life) { - if (posy < (480 << 16)) { - dstIdx = drawCount * 9; - c = 0xffafcf | ((life >> lifeShift) << 24); - - storeU32(con, 1, dstIdx, c); - storeI32(con, 1, dstIdx + 1, posx); - storeI32(con, 1, dstIdx + 2, posy); - - storeU32(con, 1, dstIdx + 3, c); - storeI32(con, 1, dstIdx + 4, posx + 0x10000); - storeI32(con, 1, dstIdx + 5, posy + dy * 4); - - storeU32(con, 1, dstIdx + 6, c); - storeI32(con, 1, dstIdx + 7, posx - 0x10000); - storeI32(con, 1, dstIdx + 8, posy + dy * 4); - drawCount ++; - } else { - if (dy > 0) { - dy = (-dy) >> 1; - } - } - - posx = posx + dx; - posy = posy + dy; - dy = dy + 0x400; - life --; - - //storeI32(con, 2, srcIdx, dx); - storeI32(con, 2, srcIdx + 1, dy); - storeI32(con, 2, srcIdx + 2, life); - storeI32(con, 2, srcIdx + 3, posx); - storeI32(con, 2, srcIdx + 4, posy); - } - } - - drawTriangleArray(con, NAMED_PartBuffer, drawCount); - return 1; -} diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c deleted file mode 100644 index bf75675743a1..000000000000 --- a/libs/rs/java/Film/res/raw/filmstrip.c +++ /dev/null @@ -1,94 +0,0 @@ -// Fountain test script - -#pragma version(1) -#pragma stateVertex(PVBackground) -#pragma stateFragment(PFBackground) -#pragma stateStore(PSBackground) - -#define STATE_TRIANGLE_OFFSET_COUNT 0 -#define STATE_LAST_FOCUS 1 - - -// The script enviroment has 3 env allocations. -// bank0: (r) The enviroment structure -// bank1: (r) The position information -// bank2: (rw) The temporary texture state - -int lastFocus; - -int main(int index) -{ - float mat1[16]; - - float trans = Pos->translate; - float rot = Pos->rotate; - - matrixLoadScale(mat1, 2.f, 2.f, 2.f); - matrixTranslate(mat1, 0.f, 0.f, trans); - matrixRotate(mat1, 90.f, 0.f, 0.f, 1.f); - matrixRotate(mat1, rot, 1.f, 0.f, 0.f); - vpLoadModelMatrix(mat1); - - // Draw the lighting effect in the strip and fill the Z buffer. - drawSimpleMesh(NAMED_mesh); - - // Start of images. - bindProgramStore(NAMED_PSImages); - bindProgramFragment(NAMED_PFImages); - bindProgramVertex(NAMED_PVImages); - - float focusPos = Pos->focus; - int focusID = 0; - int lastFocusID = loadI32(2, STATE_LAST_FOCUS); - int imgCount = 13; - - if (trans > (-.3f)) { - focusID = -1.0f - focusPos; - if (focusID >= imgCount) { - focusID = -1; - } - } else { - focusID = -1; - } - - /* - if (focusID != lastFocusID) { - if (lastFocusID >= 0) { - uploadToTexture(con, env->tex[lastFocusID], 1); - } - if (focusID >= 0) { - uploadToTexture(con, env->tex[focusID], 0); - } - } - */ - lastFocus = focusID; - - int triangleOffsetsCount = Pos->triangleOffsetCount; - - int imgId = 0; - for (imgId=1; imgId <= imgCount; imgId++) { - float pos = focusPos + imgId + 0.4f; - int offset = (int)floorf(pos * 2.f); - pos = pos - 0.75f; - - offset = offset + triangleOffsetsCount / 2; - if (!((offset < 0) || (offset >= triangleOffsetsCount))) { - int start = offset -2; - int end = offset + 2; - - if (start < 0) { - start = 0; - } - if (end >= triangleOffsetsCount) { - end = triangleOffsetsCount-1; - } - - bindTexture(NAMED_PFImages, 0, loadI32(0, imgId - 1)); - matrixLoadTranslate(mat1, -pos - loadF(5, triangleOffsetsCount / 2), 0, 0); - vpLoadTextureMatrix(mat1); - drawSimpleMeshRange(NAMED_mesh, loadI32(4, start), (loadI32(4, end) - loadI32(4, start))); - } - } - return 0; -} - diff --git a/libs/rs/java/Film/src/com/android/film/Film.java b/libs/rs/java/Film/src/com/android/film/Film.java deleted file mode 100644 index 6e99816889da..000000000000 --- a/libs/rs/java/Film/src/com/android/film/Film.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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. - */ - -package com.android.film; - -import android.renderscript.RSSurfaceView; -import android.renderscript.RenderScript; - -import android.app.Activity; -import android.content.res.Configuration; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.provider.Settings.System; -import android.util.Config; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.Window; -import android.widget.Button; -import android.widget.ListView; - -import java.lang.Runtime; - -public class Film extends Activity { - //EventListener mListener = new EventListener(); - - private static final String LOG_TAG = "libRS_jni"; - private static final boolean DEBUG = false; - private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; - - private FilmView mView; - - // get the current looper (from your Activity UI thread for instance - - - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - // Create our Preview view and set it as the content of our - // Activity - mView = new FilmView(this); - setContentView(mView); - } - - @Override - protected void onResume() { - // Ideally a game should implement onResume() and onPause() - // to take appropriate action when the activity looses focus - super.onResume(); - mView.onResume(); - } - - @Override - protected void onPause() { - // Ideally a game should implement onResume() and onPause() - // to take appropriate action when the activity looses focus - super.onPause(); - mView.onPause(); - - Runtime.getRuntime().exit(0); - } - - - static void log(String message) { - if (LOG_ENABLED) { - Log.v(LOG_TAG, message); - } - } - - -} - diff --git a/libs/rs/java/Film/src/com/android/film/FilmRS.java b/libs/rs/java/Film/src/com/android/film/FilmRS.java deleted file mode 100644 index 7d04502256be..000000000000 --- a/libs/rs/java/Film/src/com/android/film/FilmRS.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 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. - */ - -package com.android.film; - -import java.io.Writer; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.util.Log; - -import android.renderscript.*; - -public class FilmRS { - class StripPosition { - public float translate; - public float rotate; - public float focus; - public int triangleOffsetCount; - } - StripPosition mPos = new StripPosition(); - - - private final int STATE_LAST_FOCUS = 1; - - public FilmRS() { - } - - public void init(RenderScriptGL rs, Resources res, int width, int height) { - mRS = rs; - mRes = res; - initRS(); - } - - public void setFilmStripPosition(int x, int y) - { - if (x < 50) { - x = 50; - } - if (x > 270) { - x = 270; - } - - float anim = ((float)x-50) / 270.f; - mPos.translate = 2f * anim + 0.5f; // translation - mPos.rotate = (anim * 40); // rotation - mPos.focus = ((float)y) / 16.f - 10.f; // focusPos - mPos.triangleOffsetCount = mFSM.mTriangleOffsetsCount; - mAllocPos.data(mPos); - } - - - private Resources mRes; - private RenderScriptGL mRS; - private Script mScriptStrip; - private Script mScriptImage; - private Sampler mSampler; - private ProgramStore mPSBackground; - private ProgramStore mPSImages; - private ProgramFragment mPFBackground; - private ProgramFragment mPFImages; - private ProgramVertex mPVBackground; - private ProgramVertex mPVImages; - private ProgramVertex.MatrixAllocation mPVA; - private Type mStripPositionType; - - private Allocation mImages[]; - private Allocation mAllocIDs; - private Allocation mAllocPos; - private Allocation mAllocState; - private Allocation mAllocPV; - private Allocation mAllocOffsetsTex; - private Allocation mAllocOffsets; - - private SimpleMesh mMesh; - private Light mLight; - - private FilmStripMesh mFSM; - - private int[] mBufferIDs; - private float[] mBufferPos = new float[3]; - private int[] mBufferState; - - private void initPFS() { - ProgramStore.Builder b = new ProgramStore.Builder(mRS, null, null); - - b.setDepthFunc(ProgramStore.DepthFunc.LESS); - b.setDitherEnable(true); - b.setDepthMask(true); - mPSBackground = b.create(); - mPSBackground.setName("PSBackground"); - - b.setDepthFunc(ProgramStore.DepthFunc.EQUAL); - b.setDitherEnable(false); - b.setDepthMask(false); - b.setBlendFunc(ProgramStore.BlendSrcFunc.ONE, - ProgramStore.BlendDstFunc.ONE); - mPSImages = b.create(); - mPSImages.setName("PSImages"); - } - - private void initPF() { - Sampler.Builder bs = new Sampler.Builder(mRS); - bs.setMin(Sampler.Value.LINEAR);//_MIP_LINEAR); - bs.setMag(Sampler.Value.LINEAR); - bs.setWrapS(Sampler.Value.CLAMP); - bs.setWrapT(Sampler.Value.WRAP); - mSampler = bs.create(); - - ProgramFragment.Builder b = new ProgramFragment.Builder(mRS); - mPFBackground = b.create(); - mPFBackground.setName("PFBackground"); - - b = new ProgramFragment.Builder(mRS); - b.setTexture(ProgramFragment.Builder.EnvMode.REPLACE, - ProgramFragment.Builder.Format.RGBA, 0); - mPFImages = b.create(); - mPFImages.bindSampler(mSampler, 0); - mPFImages.setName("PFImages"); - } - - private void initPV() { - mLight = (new Light.Builder(mRS)).create(); - mLight.setPosition(0, -0.5f, -1.0f); - - ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null); - //pvb.addLight(mLight); - mPVBackground = pvb.create(); - mPVBackground.setName("PVBackground"); - - pvb = new ProgramVertex.Builder(mRS, null, null); - pvb.setTextureMatrixEnable(true); - mPVImages = pvb.create(); - mPVImages.setName("PVImages"); - } - - private void loadImages() { - mBufferIDs = new int[13]; - mImages = new Allocation[13]; - mAllocIDs = Allocation.createSized(mRS, - Element.createUser(mRS, Element.DataType.FLOAT_32), - mBufferIDs.length); - - Element ie = Element.createPixel(mRS, Element.DataType.UNSIGNED_5_6_5, Element.DataKind.PIXEL_RGB); - mImages[0] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p01, ie, true); - mImages[1] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p02, ie, true); - mImages[2] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p03, ie, true); - mImages[3] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p04, ie, true); - mImages[4] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p05, ie, true); - mImages[5] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p06, ie, true); - mImages[6] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p07, ie, true); - mImages[7] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p08, ie, true); - mImages[8] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p09, ie, true); - mImages[9] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p10, ie, true); - mImages[10] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p11, ie, true); - mImages[11] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p12, ie, true); - mImages[12] = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.p13, ie, true); - - int black[] = new int[1024]; - for(int ct=0; ct < mImages.length; ct++) { - Allocation.Adapter2D a = mImages[ct].createAdapter2D(); - - int size = 512; - int mip = 0; - while(size >= 2) { - a.subData(0, 0, 2, size, black); - a.subData(size-2, 0, 2, size, black); - a.subData(0, 0, size, 2, black); - a.subData(0, size-2, size, 2, black); - size >>= 1; - mip++; - a.setConstraint(Dimension.LOD, mip); - } - - mImages[ct].uploadToTexture(1); - mBufferIDs[ct] = mImages[ct].getID(); - } - mAllocIDs.data(mBufferIDs); - } - - private void initState() - { - mBufferState = new int[10]; - mAllocState = Allocation.createSized(mRS, - Element.createUser(mRS, Element.DataType.FLOAT_32), - mBufferState.length); - mBufferState[STATE_LAST_FOCUS] = -1; - mAllocState.data(mBufferState); - } - - private void initRS() { - mFSM = new FilmStripMesh(); - mMesh = mFSM.init(mRS); - mMesh.setName("mesh"); - - initPFS(); - initPF(); - initPV(); - - Log.e("rs", "Done loading named"); - - mStripPositionType = Type.createFromClass(mRS, StripPosition.class, 1); - - ScriptC.Builder sb = new ScriptC.Builder(mRS); - sb.setScript(mRes, R.raw.filmstrip); - sb.setRoot(true); - sb.setType(mStripPositionType, "Pos", 1); - mScriptStrip = sb.create(); - mScriptStrip.setClearColor(0.0f, 0.0f, 0.0f, 1.0f); - - mAllocPos = Allocation.createTyped(mRS, mStripPositionType); - - loadImages(); - initState(); - - mPVA = new ProgramVertex.MatrixAllocation(mRS); - mPVBackground.bindAllocation(mPVA); - mPVImages.bindAllocation(mPVA); - mPVA.setupProjectionNormalized(320, 480); - - - mScriptStrip.bindAllocation(mAllocIDs, 0); - mScriptStrip.bindAllocation(mAllocPos, 1); - mScriptStrip.bindAllocation(mAllocState, 2); - mScriptStrip.bindAllocation(mPVA.mAlloc, 3); - - - mAllocOffsets = Allocation.createSized(mRS, - Element.createUser(mRS, Element.DataType.SIGNED_32), mFSM.mTriangleOffsets.length); - mAllocOffsets.data(mFSM.mTriangleOffsets); - mScriptStrip.bindAllocation(mAllocOffsets, 4); - - mAllocOffsetsTex = Allocation.createSized(mRS, - Element.createUser(mRS, Element.DataType.FLOAT_32), mFSM.mTriangleOffsetsTex.length); - mAllocOffsetsTex.data(mFSM.mTriangleOffsetsTex); - mScriptStrip.bindAllocation(mAllocOffsetsTex, 5); - - setFilmStripPosition(0, 0); - mRS.contextBindRootScript(mScriptStrip); - } -} - - - diff --git a/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java b/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java deleted file mode 100644 index 448cce02745d..000000000000 --- a/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * 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.film; - -import java.io.Writer; -import java.lang.Math; -import android.util.Log; - -import android.renderscript.RenderScript; -import android.renderscript.SimpleMesh; - - -class FilmStripMesh { - - class Vertex { - float nx; - float ny; - float nz; - float s; - float t; - float x; - float y; - float z; - - Vertex() { - nx = 0; - ny = 0; - nz = 0; - s = 0; - t = 0; - x = 0; - y = 0; - z = 0; - } - - void xyz(float _x, float _y, float _z) { - x = _x; - y = _y; - z = _z; - } - - void nxyz(float _x, float _y, float _z) { - nx = _x; - ny = _y; - nz = _z; - } - - void st(float _s, float _t) { - s = _s; - t = _t; - } - - void computeNorm(Vertex v1, Vertex v2) { - float dx = v1.x - v2.x; - float dy = v1.y - v2.y; - float dz = v1.z - v2.z; - float len = (float)java.lang.Math.sqrt(dx*dx + dy*dy + dz*dz); - dx /= len; - dy /= len; - dz /= len; - - nx = dx * dz; - ny = dy * dz; - nz = (float)java.lang.Math.sqrt(dx*dx + dy*dy); - - len = (float)java.lang.Math.sqrt(nx*nx + ny*ny + nz*nz); - nx /= len; - ny /= len; - nz /= len; - } - } - - int[] mTriangleOffsets; - float[] mTriangleOffsetsTex; - int mTriangleOffsetsCount; - - SimpleMesh init(RenderScript rs) - { - float vtx[] = new float[] { - 60.431003f, 124.482050f, - 60.862074f, 120.872604f, - 61.705303f, 117.336662f, - 62.949505f, 113.921127f, - 64.578177f, 110.671304f, - 66.569716f, 107.630302f, - 68.897703f, 104.838457f, - 71.531259f, 102.332803f, - 74.435452f, 100.146577f, - 77.571757f, 98.308777f, - 80.898574f, 96.843781f, - 84.371773f, 95.771023f, - 87.945283f, 95.104731f, - 98.958994f, 95.267098f, - 109.489523f, 98.497596f, - 118.699582f, 104.539366f, - 125.856872f, 112.912022f, - 130.392311f, 122.949849f, - 131.945283f, 133.854731f, - 130.392311f, 144.759613f, - 125.856872f, 154.797439f, - 118.699582f, 163.170096f, - 109.489523f, 169.211866f, - 98.958994f, 172.442364f, - 87.945283f, 172.604731f, - 72.507313f, 172.672927f, - 57.678920f, 168.377071f, - 44.668135f, 160.067134f, - 34.534908f, 148.420104f, - 28.104767f, 134.384831f, - 25.901557f, 119.104731f, - 28.104767f, 103.824631f, - 34.534908f, 89.789358f, - 44.668135f, 78.142327f, - 57.678920f, 69.832390f, - 72.507313f, 65.536534f, - 87.945283f, 65.604731f, - 106.918117f, 65.688542f, - 125.141795f, 60.409056f, - 141.131686f, 50.196376f, - 153.585137f, 35.882502f, - 161.487600f, 18.633545f, - 164.195283f, -0.145269f, - 161.487600f, -18.924084f, - 153.585137f, -36.173040f, - 141.131686f, -50.486914f, - 125.141795f, -60.699594f, - 106.918117f, -65.979081f, - 87.945283f, -65.895269f, - 80f, -65.895269f, - 60f, -65.895269f, - 40f, -65.895269f, - 20f, -65.895269f, - 0f, -65.895269f, - -20f, -65.895269f, - -40f, -65.895269f, - -60f, -65.895269f, - -80f, -65.895269f, - -87.945283f, -65.895269f, - -106.918117f, -65.979081f, - -125.141795f, -60.699594f, - -141.131686f, -50.486914f, - -153.585137f, -36.173040f, - -161.487600f, -18.924084f, - -164.195283f, -0.145269f, - -161.487600f, 18.633545f, - -153.585137f, 35.882502f, - -141.131686f, 50.196376f, - -125.141795f, 60.409056f, - -106.918117f, 65.688542f, - -87.945283f, 65.604731f, - -72.507313f, 65.536534f, - -57.678920f, 69.832390f, - -44.668135f, 78.142327f, - -34.534908f, 89.789358f, - -28.104767f, 103.824631f, - -25.901557f, 119.104731f, - -28.104767f, 134.384831f, - -34.534908f, 148.420104f, - -44.668135f, 160.067134f, - -57.678920f, 168.377071f, - -72.507313f, 172.672927f, - -87.945283f, 172.604731f, - -98.958994f, 172.442364f, - -109.489523f, 169.211866f, - -118.699582f, 163.170096f, - -125.856872f, 154.797439f, - -130.392311f, 144.759613f, - -131.945283f, 133.854731f, - -130.392311f, 122.949849f, - -125.856872f, 112.912022f, - -118.699582f, 104.539366f, - -109.489523f, 98.497596f, - -98.958994f, 95.267098f, - -87.945283f, 95.104731f, - -84.371773f, 95.771023f, - -80.898574f, 96.843781f, - -77.571757f, 98.308777f, - -74.435452f, 100.146577f, - -71.531259f, 102.332803f, - -68.897703f, 104.838457f, - -66.569716f, 107.630302f, - -64.578177f, 110.671304f, - -62.949505f, 113.921127f, - -61.705303f, 117.336662f, - -60.862074f, 120.872604f, - -60.431003f, 124.482050f - }; - - - mTriangleOffsets = new int[64]; - mTriangleOffsetsTex = new float[64]; - - mTriangleOffsets[0] = 0; - mTriangleOffsetsCount = 1; - - Vertex t = new Vertex(); - t.nxyz(1, 0, 0); - int count = vtx.length / 2; - - SimpleMesh.TriangleMeshBuilder tm = new SimpleMesh.TriangleMeshBuilder( - rs, 3, - SimpleMesh.TriangleMeshBuilder.NORMAL | SimpleMesh.TriangleMeshBuilder.TEXTURE_0); - - float runningS = 0; - for (int ct=0; ct < (count-1); ct++) { - t.x = -vtx[ct*2] / 100.f; - t.z = vtx[ct*2+1] / 100.f; - t.s = runningS; - t.nx = (vtx[ct*2+3] - vtx[ct*2 +1]); - t.ny = (vtx[ct*2+2] - vtx[ct*2 ]); - float len = (float)java.lang.Math.sqrt(t.nx * t.nx + t.ny * t.ny); - runningS += len / 100; - t.nx /= len; - t.ny /= len; - t.y = -0.5f; - t.t = 0; - tm.setNormal(t.nx, t.ny, t.nz); - tm.setTexture(t.s, t.t); - tm.addVertex(t.x, t.y, t.z); - //android.util.Log.e("rs", "vtx x="+t.x+" y="+t.y+" z="+t.z+" s="+t.s+" t="+t.t); - t.y = .5f; - t.t = 1; - tm.setTexture(t.s, t.t); - tm.addVertex(t.x, t.y, t.z); - //android.util.Log.e("rs", "vtx x="+t.x+" y="+t.y+" z="+t.z+" s="+t.s+" t="+t.t); - - if((runningS*2) > mTriangleOffsetsCount) { - mTriangleOffsets[mTriangleOffsetsCount] = ct*2 * 3; - mTriangleOffsetsTex[mTriangleOffsetsCount] = t.s; - mTriangleOffsetsCount ++; - } - } - - count = (count * 2 - 2); - for (int ct=0; ct < (count-2); ct+= 2) { - tm.addTriangle(ct, ct+1, ct+2); - tm.addTriangle(ct+1, ct+3, ct+2); - } - return tm.create(); - } - - -} - diff --git a/libs/rs/java/Film/src/com/android/film/FilmView.java b/libs/rs/java/Film/src/com/android/film/FilmView.java deleted file mode 100644 index 5bc281158f3f..000000000000 --- a/libs/rs/java/Film/src/com/android/film/FilmView.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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. - */ - -package com.android.film; - -import java.io.Writer; -import java.util.ArrayList; -import java.util.concurrent.Semaphore; - -import android.renderscript.RSSurfaceView; -import android.renderscript.RenderScript; -import android.renderscript.RenderScriptGL; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.Message; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.KeyEvent; -import android.view.MotionEvent; - -public class FilmView extends RSSurfaceView { - - public FilmView(Context context) { - super(context); - //setFocusable(true); - } - - private RenderScriptGL mRS; - private FilmRS mRender; - - - public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { - super.surfaceChanged(holder, format, w, h); - if (mRS == null) { - mRS = createRenderScript(true); - mRS.contextSetSurface(w, h, holder.getSurface()); - mRender = new FilmRS(); - mRender.init(mRS, getResources(), w, h); - } - } - - @Override - protected void onDetachedFromWindow() { - if(mRS != null) { - mRS = null; - destroyRenderScript(); - } - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) - { - // break point at here - // this method doesn't work when 'extends View' include 'extends ScrollView'. - return super.onKeyDown(keyCode, event); - } - - - @Override - public boolean onTouchEvent(MotionEvent ev) - { - boolean ret = true; - int act = ev.getAction(); - if (act == ev.ACTION_UP) { - ret = false; - } - mRender.setFilmStripPosition((int)ev.getX(), (int)ev.getY() / 5); - return ret; - } -} - - diff --git a/libs/rs/java/Fountain/Android.mk b/libs/rs/java/Fountain/Android.mk deleted file mode 100644 index f7e53a8f1244..000000000000 --- a/libs/rs/java/Fountain/Android.mk +++ /dev/null @@ -1,27 +0,0 @@ -# -# 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. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) -#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript - -LOCAL_PACKAGE_NAME := Fountain - -include $(BUILD_PACKAGE) diff --git a/libs/rs/java/Fountain/AndroidManifest.xml b/libs/rs/java/Fountain/AndroidManifest.xml deleted file mode 100644 index 951c45139269..000000000000 --- a/libs/rs/java/Fountain/AndroidManifest.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.fountain"> - <application - android:label="Fountain" - android:icon="@drawable/test_pattern"> - <activity android:name="Fountain"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png b/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png Binary files differdeleted file mode 100755 index e91bfb418a8e..000000000000 --- a/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png +++ /dev/null diff --git a/libs/rs/java/Fountain/res/drawable/test_pattern.png b/libs/rs/java/Fountain/res/drawable/test_pattern.png Binary files differdeleted file mode 100644 index e7d145554c00..000000000000 --- a/libs/rs/java/Fountain/res/drawable/test_pattern.png +++ /dev/null diff --git a/libs/rs/java/Fountain/res/raw/fountain.c b/libs/rs/java/Fountain/res/raw/fountain.c deleted file mode 100644 index 73b819b3df86..000000000000 --- a/libs/rs/java/Fountain/res/raw/fountain.c +++ /dev/null @@ -1,52 +0,0 @@ -// Fountain test script -#pragma version(1) - -int newPart = 0; - -int main(int launchID) { - int ct; - int count = Control->count; - int rate = Control->rate; - float height = getHeight(); - struct point_s * p = (struct point_s *)point; - - if (rate) { - float rMax = ((float)rate) * 0.005f; - int x = Control->x; - int y = Control->y; - int color = ((int)(Control->r * 255.f)) | - ((int)(Control->g * 255.f)) << 8 | - ((int)(Control->b * 255.f)) << 16 | - (0xf0 << 24); - struct point_s * np = &p[newPart]; - - while (rate--) { - vec2Rand((float *)&np->delta.x, rMax); - np->position.x = x; - np->position.y = y; - np->color = color; - newPart++; - np++; - if (newPart >= count) { - newPart = 0; - np = &p[newPart]; - } - } - } - - for (ct=0; ct < count; ct++) { - float dy = p->delta.y + 0.15f; - float posy = p->position.y + dy; - if ((posy > height) && (dy > 0)) { - dy *= -0.3f; - } - p->delta.y = dy; - p->position.x += p->delta.x; - p->position.y = posy; - p++; - } - - uploadToBufferObject(NAMED_PartBuffer); - drawSimpleMesh(NAMED_PartMesh); - return 1; -} diff --git a/libs/rs/java/Fountain/res/raw/fountain2.rs b/libs/rs/java/Fountain/res/raw/fountain2.rs deleted file mode 100644 index 33011403f15a..000000000000 --- a/libs/rs/java/Fountain/res/raw/fountain2.rs +++ /dev/null @@ -1,73 +0,0 @@ -// Fountain test script -#pragma version(1) - -#include "rs_types.rsh" -#include "rs_math.rsh" -#include "rs_graphics.rsh" - -static int newPart = 0; - -typedef struct Control_s { - int x, y; - int rate; - int count; - float r, g, b; - rs_allocation partBuffer; - rs_mesh partMesh; -} Control_t; -Control_t *Control; - -typedef struct Point_s{ - float2 delta; - float2 position; - unsigned int color; -} Point_t; -Point_t *point; - -int main(int launchID) { - int ct; - int count = Control->count; - int rate = Control->rate; - float height = getHeight(); - Point_t * p = point; - - if (rate) { - float rMax = ((float)rate) * 0.005f; - int x = Control->x; - int y = Control->y; - int color = ((int)(Control->r * 255.f)) | - ((int)(Control->g * 255.f)) << 8 | - ((int)(Control->b * 255.f)) << 16 | - (0xf0 << 24); - Point_t * np = &p[newPart]; - - while (rate--) { - np->delta = vec2Rand(rMax); - np->position.x = x; - np->position.y = y; - np->color = color; - newPart++; - np++; - if (newPart >= count) { - newPart = 0; - np = &p[newPart]; - } - } - } - - for (ct=0; ct < count; ct++) { - float dy = p->delta.y + 0.15f; - float posy = p->position.y + dy; - if ((posy > height) && (dy > 0)) { - dy *= -0.3f; - } - p->delta.y = dy; - p->position.x += p->delta.x; - p->position.y = posy; - p++; - } - - uploadToBufferObject(Control->partBuffer); - drawSimpleMesh(Control->partMesh); - return 1; -} diff --git a/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java b/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java deleted file mode 100644 index 9ae3e6703471..000000000000 --- a/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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. - */ - -package com.android.fountain; - -import android.renderscript.RSSurfaceView; -import android.renderscript.RenderScript; - -import android.app.Activity; -import android.content.res.Configuration; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; -import android.provider.Settings.System; -import android.util.Config; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.Window; -import android.widget.Button; -import android.widget.ListView; - -import java.lang.Runtime; - -public class Fountain extends Activity { - //EventListener mListener = new EventListener(); - - private static final String LOG_TAG = "libRS_jni"; - private static final boolean DEBUG = false; - private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; - - private FountainView mView; - - // get the current looper (from your Activity UI thread for instance - - - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - // Create our Preview view and set it as the content of our - // Activity - mView = new FountainView(this); - setContentView(mView); - } - - @Override - protected void onResume() { - Log.e("rs", "onResume"); - - // Ideally a game should implement onResume() and onPause() - // to take appropriate action when the activity looses focus - super.onResume(); - mView.onResume(); - } - - @Override - protected void onPause() { - Log.e("rs", "onPause"); - - // Ideally a game should implement onResume() and onPause() - // to take appropriate action when the activity looses focus - super.onPause(); - mView.onPause(); - - - - //Runtime.getRuntime().exit(0); - } - - - static void log(String message) { - if (LOG_ENABLED) { - Log.v(LOG_TAG, message); - } - } - - -} - diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java deleted file mode 100644 index 935657965e11..000000000000 --- a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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. - */ - -package com.android.fountain; - -import android.content.res.Resources; -import android.renderscript.*; -import android.util.Log; - - -public class FountainRS { - public static final int PART_COUNT = 20000; - - static class SomeData { - public int x; - public int y; - public int rate; - public int count; - public float r; - public float g; - public float b; - } - - public FountainRS() { - } - - public void init(RenderScriptGL rs, Resources res, int width, int height) { - mRS = rs; - mRes = res; - initRS(); - } - - public void newTouchPosition(int x, int y, int rate) { - if (mSD.rate == 0) { - mSD.r = ((x & 0x1) != 0) ? 0.f : 1.f; - mSD.g = ((x & 0x2) != 0) ? 0.f : 1.f; - mSD.b = ((x & 0x4) != 0) ? 0.f : 1.f; - if ((mSD.r + mSD.g + mSD.b) < 0.9f) { - mSD.r = 0.8f; - mSD.g = 0.5f; - mSD.b = 1.f; - } - } - mSD.rate = rate; - mSD.x = x; - mSD.y = y; - mIntAlloc.data(mSD); - } - - - ///////////////////////////////////////// - - private Resources mRes; - - private RenderScriptGL mRS; - private Allocation mIntAlloc; - private SimpleMesh mSM; - private SomeData mSD; - private Type mSDType; - - private void initRS() { - mSD = new SomeData(); - mSDType = Type.createFromClass(mRS, SomeData.class, 1, "SomeData"); - mIntAlloc = Allocation.createTyped(mRS, mSDType); - mSD.count = PART_COUNT; - mIntAlloc.data(mSD); - - Element.Builder eb = new Element.Builder(mRS); - eb.add(Element.createVector(mRS, Element.DataType.FLOAT_32, 2), "delta"); - eb.add(Element.createAttrib(mRS, Element.DataType.FLOAT_32, Element.DataKind.POSITION, 2), "position"); - eb.add(Element.createAttrib(mRS, Element.DataType.UNSIGNED_8, Element.DataKind.COLOR, 4), "color"); - Element primElement = eb.create(); - - - SimpleMesh.Builder smb = new SimpleMesh.Builder(mRS); - int vtxSlot = smb.addVertexType(primElement, PART_COUNT); - smb.setPrimitive(Primitive.POINT); - mSM = smb.create(); - mSM.setName("PartMesh"); - - Allocation partAlloc = mSM.createVertexAllocation(vtxSlot); - partAlloc.setName("PartBuffer"); - mSM.bindVertexAllocation(partAlloc, 0); - - // All setup of named objects should be done by this point - // because we are about to compile the script. - ScriptC.Builder sb = new ScriptC.Builder(mRS); - sb.setScript(mRes, R.raw.fountain); - sb.setRoot(true); - sb.setType(mSDType, "Control", 0); - sb.setType(mSM.getVertexType(0), "point", 1); - Script script = sb.create(); - script.setClearColor(0.0f, 0.0f, 0.0f, 1.0f); - - script.bindAllocation(mIntAlloc, 0); - script.bindAllocation(partAlloc, 1); - mRS.contextBindRootScript(script); - } - -} - - diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java deleted file mode 100644 index dfd6a49d09a1..000000000000 --- a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * 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. - */ - -package com.android.fountain; - -import java.io.Writer; -import java.util.ArrayList; -import java.util.concurrent.Semaphore; - -import android.renderscript.RSSurfaceView; -import android.renderscript.RenderScript; -import android.renderscript.RenderScriptGL; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.Message; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.KeyEvent; -import android.view.MotionEvent; - -public class FountainView extends RSSurfaceView { - - public FountainView(Context context) { - super(context); - //setFocusable(true); - } - - private RenderScriptGL mRS; - private FountainRS mRender; - - public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { - super.surfaceChanged(holder, format, w, h); - if (mRS == null) { - mRS = createRenderScript(false); - mRS.contextSetSurface(w, h, holder.getSurface()); - mRender = new FountainRS(); - mRender.init(mRS, getResources(), w, h); - } - } - - @Override - protected void onDetachedFromWindow() { - if(mRS != null) { - mRS = null; - destroyRenderScript(); - } - } - - - @Override - public boolean onTouchEvent(MotionEvent ev) - { - int act = ev.getAction(); - if (act == ev.ACTION_UP) { - mRender.newTouchPosition(0, 0, 0); - return false; - } - float rate = (ev.getPressure() * 50.f); - rate *= rate; - if(rate > 2000.f) { - rate = 2000.f; - } - mRender.newTouchPosition((int)ev.getX(), (int)ev.getY(), (int)rate); - return true; - } -} - - diff --git a/libs/rs/java/ImageProcessing/Android.mk b/libs/rs/java/ImageProcessing/Android.mk deleted file mode 100644 index 833427b269ae..000000000000 --- a/libs/rs/java/ImageProcessing/Android.mk +++ /dev/null @@ -1,27 +0,0 @@ -# -# 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. -# - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, src) -#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript - -LOCAL_PACKAGE_NAME := ImageProcessing - -include $(BUILD_PACKAGE) diff --git a/libs/rs/java/ImageProcessing/AndroidManifest.xml b/libs/rs/java/ImageProcessing/AndroidManifest.xml deleted file mode 100644 index b48d2082ae38..000000000000 --- a/libs/rs/java/ImageProcessing/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.rs.image"> - - <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - - <application android:label="Image Processing"> - <activity android:name="ImageProcessingActivity"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/libs/rs/java/ImageProcessing/res/drawable-hdpi/data.jpg b/libs/rs/java/ImageProcessing/res/drawable-hdpi/data.jpg Binary files differdeleted file mode 100644 index 81a87b1726c2..000000000000 --- a/libs/rs/java/ImageProcessing/res/drawable-hdpi/data.jpg +++ /dev/null diff --git a/libs/rs/java/ImageProcessing/res/layout/main.xml b/libs/rs/java/ImageProcessing/res/layout/main.xml deleted file mode 100644 index 6770c18838c9..000000000000 --- a/libs/rs/java/ImageProcessing/res/layout/main.xml +++ /dev/null @@ -1,38 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> - -<merge xmlns:android="http://schemas.android.com/apk/res/android"> - - <SurfaceView - android:id="@+id/surface" - android:layout_width="1dip" - android:layout_height="1dip" /> - - <ImageView - android:id="@+id/display" - android:layout_width="320dip" - android:layout_height="266dip" /> - - <SeekBar - android:id="@+id/threshold" - android:layout_marginBottom="10dip" - android:layout_marginLeft="10dip" - android:layout_marginRight="10dip" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="bottom" /> - -</merge>
\ No newline at end of file diff --git a/libs/rs/java/ImageProcessing/res/raw/threshold.rs b/libs/rs/java/ImageProcessing/res/raw/threshold.rs deleted file mode 100644 index 888f0cdb029e..000000000000 --- a/libs/rs/java/ImageProcessing/res/raw/threshold.rs +++ /dev/null @@ -1,62 +0,0 @@ -/* -// block of defines matching what RS will insert at runtime. -struct Params_s{ - int inHeight; - int inWidth; - int outHeight; - int outWidth; - float threshold; -}; -struct Params_s * Params; -struct InPixel_s{ - char a; - char b; - char g; - char r; -}; -struct InPixel_s * InPixel; -struct OutPixel_s{ - char a; - char b; - char g; - char r; -}; -struct OutPixel_s * OutPixel; -*/ - -struct color_s { - char b; - char g; - char r; - char a; -}; - -void main() { - int t = uptimeMillis(); - - struct color_s *in = (struct color_s *) InPixel; - struct color_s *out = (struct color_s *) OutPixel; - - int count = Params->inWidth * Params->inHeight; - int i; - float threshold = (Params->threshold * 255.f); - - for (i = 0; i < count; i++) { - float luminance = 0.2125f * in->r + - 0.7154f * in->g + - 0.0721f * in->b; - if (luminance > threshold) { - *out = *in; - } else { - *((int *)out) = *((int *)in) & 0xff000000; - } - - in++; - out++; - } - - t= uptimeMillis() - t; - debugI32("Filter time", t); - - sendToClient(&count, 1, 4, 0); -} diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java b/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java deleted file mode 100644 index 9ce53d814b1b..000000000000 --- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/ImageProcessingActivity.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * 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.rs.image; - -import android.app.Activity; -import android.os.Bundle; -import android.graphics.BitmapFactory; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.renderscript.ScriptC; -import android.renderscript.RenderScript; -import android.renderscript.Type; -import android.renderscript.Allocation; -import android.renderscript.Element; -import android.renderscript.Script; -import android.view.SurfaceView; -import android.view.SurfaceHolder; -import android.widget.ImageView; -import android.widget.SeekBar; -import java.lang.Math; - -public class ImageProcessingActivity extends Activity implements SurfaceHolder.Callback { - private Bitmap mBitmap; - private Params mParams; - private Script.Invokable mInvokable; - private int[] mInData; - private int[] mOutData; - - @SuppressWarnings({"FieldCanBeLocal"}) - private RenderScript mRS; - @SuppressWarnings({"FieldCanBeLocal"}) - private Type mParamsType; - @SuppressWarnings({"FieldCanBeLocal"}) - private Allocation mParamsAllocation; - @SuppressWarnings({"FieldCanBeLocal"}) - private Type mPixelType; - @SuppressWarnings({"FieldCanBeLocal"}) - private Allocation mInPixelsAllocation; - @SuppressWarnings({"FieldCanBeLocal"}) - private Allocation mOutPixelsAllocation; - - private SurfaceView mSurfaceView; - private ImageView mDisplayView; - - static class Params { - public int inWidth; - public int outWidth; - public int inHeight; - public int outHeight; - - public float threshold; - } - - static class Pixel { - public byte a; - public byte r; - public byte g; - public byte b; - } - - class FilterCallback extends RenderScript.RSMessage { - private Runnable mAction = new Runnable() { - public void run() { - mOutPixelsAllocation.readData(mOutData); - mBitmap.setPixels(mOutData, 0, mParams.outWidth, 0, 0, - mParams.outWidth, mParams.outHeight); - mDisplayView.invalidate(); - } - }; - - @Override - public void run() { - mSurfaceView.removeCallbacks(mAction); - mSurfaceView.post(mAction); - } - } - - private void javaFilter() { - long t = java.lang.System.currentTimeMillis(); - int count = mParams.inWidth * mParams.inHeight; - float threshold = mParams.threshold * 255.f; - - for (int i = 0; i < count; i++) { - final float r = (float)((mInData[i] >> 0) & 0xff); - final float g = (float)((mInData[i] >> 8) & 0xff); - final float b = (float)((mInData[i] >> 16) & 0xff); - - final float luminance = 0.2125f * r + - 0.7154f * g + - 0.0721f * b; - if (luminance > threshold) { - mOutData[i] = mInData[i]; - } else { - mOutData[i] = mInData[i] & 0xff000000; - } - } - - t = java.lang.System.currentTimeMillis() - t; - - android.util.Log.v("Img", "frame time ms " + t); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); - - mBitmap = loadBitmap(R.drawable.data); - - mSurfaceView = (SurfaceView) findViewById(R.id.surface); - mSurfaceView.getHolder().addCallback(this); - - mDisplayView = (ImageView) findViewById(R.id.display); - mDisplayView.setImageBitmap(mBitmap); - - ((SeekBar) findViewById(R.id.threshold)).setOnSeekBarChangeListener( - new SeekBar.OnSeekBarChangeListener() { - public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { - if (fromUser) { - mParams.threshold = progress / 100.0f; - mParamsAllocation.data(mParams); - - if (true) { - mInvokable.execute(); - } else { - javaFilter(); - mBitmap.setPixels(mOutData, 0, mParams.outWidth, 0, 0, - mParams.outWidth, mParams.outHeight); - mDisplayView.invalidate(); - } - } - } - - public void onStartTrackingTouch(SeekBar seekBar) { - } - - public void onStopTrackingTouch(SeekBar seekBar) { - } - }); - } - - public void surfaceCreated(SurfaceHolder holder) { - mParams = createParams(); - mInvokable = createScript(); - - mInvokable.execute(); - } - - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - } - - public void surfaceDestroyed(SurfaceHolder holder) { - } - - private Script.Invokable createScript() { - mRS = RenderScript.create(); - mRS.mMessageCallback = new FilterCallback(); - - mParamsType = Type.createFromClass(mRS, Params.class, 1, "Parameters"); - mParamsAllocation = Allocation.createTyped(mRS, mParamsType); - mParamsAllocation.data(mParams); - - final int pixelCount = mParams.inWidth * mParams.inHeight; - - mPixelType = Type.createFromClass(mRS, Pixel.class, 1, "Pixel"); - mInPixelsAllocation = Allocation.createSized(mRS, - Element.createUser(mRS, Element.DataType.SIGNED_32), - pixelCount); - mOutPixelsAllocation = Allocation.createSized(mRS, - Element.createUser(mRS, Element.DataType.SIGNED_32), - pixelCount); - - mInData = new int[pixelCount]; - mBitmap.getPixels(mInData, 0, mParams.inWidth, 0, 0, mParams.inWidth, mParams.inHeight); - mInPixelsAllocation.data(mInData); - - mOutData = new int[pixelCount]; - mOutPixelsAllocation.data(mOutData); - - ScriptC.Builder sb = new ScriptC.Builder(mRS); - sb.setType(mParamsType, "Params", 0); - sb.setType(mPixelType, "InPixel", 1); - sb.setType(mPixelType, "OutPixel", 2); - sb.setType(true, 2); - Script.Invokable invokable = sb.addInvokable("main"); - sb.setScript(getResources(), R.raw.threshold); - //sb.setRoot(true); - - ScriptC script = sb.create(); - script.bindAllocation(mParamsAllocation, 0); - script.bindAllocation(mInPixelsAllocation, 1); - script.bindAllocation(mOutPixelsAllocation, 2); - - return invokable; - } - - private Params createParams() { - final Params params = new Params(); - params.inWidth = params.outWidth = mBitmap.getWidth(); - params.inHeight = params.outHeight = mBitmap.getHeight(); - params.threshold = 0.5f; - return params; - } - - private Bitmap loadBitmap(int resource) { - final BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - return copyBitmap(BitmapFactory.decodeResource(getResources(), resource, options)); - } - - private static Bitmap copyBitmap(Bitmap source) { - Bitmap b = Bitmap.createBitmap(source.getWidth(), source.getHeight(), source.getConfig()); - Canvas c = new Canvas(b); - c.drawBitmap(source, 0, 0, null); - source.recycle(); - return b; - } -} diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 5ae8d0120e39..7e23cec90805 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -1,11 +1,14 @@ +ContextFinish { + handcodeApi + } ContextBindRootScript { param RsScript sampler } -ContextBindProgramFragmentStore { - param RsProgramFragmentStore pgm +ContextBindProgramStore { + param RsProgramStore pgm } ContextBindProgramFragment { @@ -20,6 +23,10 @@ ContextBindProgramRaster { param RsProgramRaster pgm } +ContextBindFont { + param RsFont pgm + } + ContextPause { } @@ -36,15 +43,13 @@ ContextDump { param int32_t bits } -ContextGetError { - param RsError *err - ret const char * - } - ContextSetPriority { param int32_t priority } +ContextDestroyWorker { +} + AssignName { param void *obj param const char *name @@ -52,7 +57,7 @@ AssignName { } ObjDestroy { - param void *obj + param RsAsyncVoidPtr objPtr } ElementCreate { @@ -68,160 +73,86 @@ ElementCreate2 { param const RsElement * elements param const char ** names param const size_t * nameLengths + param const uint32_t * arraySize ret RsElement } -TypeBegin { - param RsElement type - } - -TypeAdd { - param RsDimension dim - param size_t value - } - -TypeCreate { - ret RsType - } - -AllocationCreateTyped { - param RsType type - ret RsAllocation - } - -AllocationCreateSized { - param RsElement e - param size_t count - ret RsAllocation - } - -AllocationCreateBitmapRef { - param RsType type - param void * bmpPtr - param void * callbackData - param RsBitmapCallback_t callback - ret RsAllocation - } - -AllocationCreateFromBitmap { - param uint32_t width - param uint32_t height - param RsElement dstFmt - param RsElement srcFmt - param bool genMips - param const void * data - ret RsAllocation - } - -AllocationCreateFromBitmapBoxed { - param uint32_t width - param uint32_t height - param RsElement dstFmt - param RsElement srcFmt - param bool genMips - param const void * data - ret RsAllocation - } - - -AllocationUploadToTexture { - param RsAllocation alloc - param bool genMipMaps - param uint32_t baseMipLevel - } - -AllocationUploadToBufferObject { +AllocationCopyToBitmap { param RsAllocation alloc + param void * data + param size_t dataLen } -AllocationData { +Allocation1DData { param RsAllocation va - param const void * data + param uint32_t xoff + param uint32_t lod + param uint32_t count + param const void *data param uint32_t bytes handcodeApi togglePlay } -Allocation1DSubData { +Allocation1DElementData { param RsAllocation va - param uint32_t xoff - param uint32_t count + param uint32_t x + param uint32_t lod param const void *data + param uint32_t comp_offset param uint32_t bytes handcodeApi togglePlay } -Allocation2DSubData { +Allocation2DData { param RsAllocation va param uint32_t xoff param uint32_t yoff + param uint32_t lod + param RsAllocationCubemapFace face param uint32_t w param uint32_t h param const void *data param uint32_t bytes } -AllocationRead { +Allocation2DElementData { param RsAllocation va - param void * data - } - -Adapter1DCreate { - ret RsAdapter1D - } - -Adapter1DBindAllocation { - param RsAdapter1D adapt - param RsAllocation alloc - } - -Adapter1DSetConstraint { - param RsAdapter1D adapter - param RsDimension dim - param uint32_t value - } - -Adapter1DData { - param RsAdapter1D adapter - param const void * data - } - -Adapter1DSubData { - param RsAdapter1D adapter - param uint32_t xoff - param uint32_t count + param uint32_t x + param uint32_t y + param uint32_t lod + param RsAllocationCubemapFace face param const void *data + param uint32_t element_offset + param uint32_t bytes } -Adapter2DCreate { - ret RsAdapter2D - } +AllocationGenerateMipmaps { + param RsAllocation va +} -Adapter2DBindAllocation { - param RsAdapter2D adapt - param RsAllocation alloc +AllocationRead { + param RsAllocation va + param void * data } -Adapter2DSetConstraint { - param RsAdapter2D adapter - param RsDimension dim - param uint32_t value - } +AllocationSyncAll { + param RsAllocation va + param RsAllocationUsageType src +} -Adapter2DData { - param RsAdapter2D adapter - param const void *data + +AllocationResize1D { + param RsAllocation va + param uint32_t dimX } -Adapter2DSubData { - param RsAdapter2D adapter - param uint32_t xoff - param uint32_t yoff - param uint32_t w - param uint32_t h - param const void *data +AllocationResize2D { + param RsAllocation va + param uint32_t dimX + param uint32_t dimY } SamplerBegin { @@ -232,6 +163,11 @@ SamplerSet { param RsSamplerValue value } +SamplerSet2 { + param RsSamplerParam p + param float value + } + SamplerCreate { ret RsSampler } @@ -248,13 +184,6 @@ ScriptBindAllocation { ScriptCBegin { } -ScriptSetClearColor { - param RsScript s - param float r - param float g - param float b - param float a - } ScriptSetTimeZone { param RsScript s @@ -262,98 +191,108 @@ ScriptSetTimeZone { param uint32_t length } -ScriptSetClearDepth { + +ScriptInvoke { param RsScript s - param float depth + param uint32_t slot } -ScriptSetClearStencil { +ScriptInvokeV { param RsScript s - param uint32_t stencil + param uint32_t slot + param const void * data + param uint32_t dataLen + handcodeApi + togglePlay } -ScriptSetType { - param RsType type +ScriptSetVarI { + param RsScript s param uint32_t slot - param bool isWritable - param const char * name + param int value } -ScriptSetInvoke { - param const char * name +ScriptSetVarObj { + param RsScript s param uint32_t slot + param RsObjectBase value } -ScriptInvoke { +ScriptSetVarJ { param RsScript s param uint32_t slot + param int64_t value } -ScriptSetRoot { - param bool isRoot +ScriptSetVarF { + param RsScript s + param uint32_t slot + param float value } +ScriptSetVarD { + param RsScript s + param uint32_t slot + param double value + } - -ScriptCSetScript { - param void * codePtr +ScriptSetVarV { + param RsScript s + param uint32_t slot + param const void * data + param uint32_t dataLen + handcodeApi + togglePlay } + ScriptCSetText { param const char * text param uint32_t length } ScriptCCreate { + param const char * packageName + param const char * resName + param const char * cacheDir ret RsScript } -ScriptCSetDefineF { - param const char* name - param float value - } - -ScriptCSetDefineI32 { - param const char* name - param int32_t value - } -ProgramFragmentStoreBegin { +ProgramStoreBegin { param RsElement in param RsElement out } -ProgramFragmentStoreColorMask { +ProgramStoreColorMask { param bool r param bool g param bool b param bool a } -ProgramFragmentStoreBlendFunc { +ProgramStoreBlendFunc { param RsBlendSrcFunc srcFunc param RsBlendDstFunc destFunc } -ProgramFragmentStoreDepthMask { +ProgramStoreDepthMask { param bool enable } -ProgramFragmentStoreDither { +ProgramStoreDither { param bool enable } -ProgramFragmentStoreDepthFunc { +ProgramStoreDepthFunc { param RsDepthFunc func } -ProgramFragmentStoreCreate { - ret RsProgramFragmentStore +ProgramStoreCreate { + ret RsProgramStore } ProgramRasterCreate { - param RsElement in - param RsElement out param bool pointSmooth param bool lineSmooth param bool pointSprite @@ -365,12 +304,11 @@ ProgramRasterSetLineWidth { param float lw } -ProgramRasterSetPointSize{ +ProgramRasterSetCullMode { param RsProgramRaster pr - param float ps + param RsCullMode mode } - ProgramBindConstants { param RsProgram vp param uint32_t slot @@ -391,12 +329,6 @@ ProgramBindSampler { } ProgramFragmentCreate { - param const uint32_t * params - param uint32_t paramLength - ret RsProgramFragment - } - -ProgramFragmentCreate2 { param const char * shaderText param uint32_t shaderLength param const uint32_t * params @@ -405,11 +337,6 @@ ProgramFragmentCreate2 { } ProgramVertexCreate { - param bool texMat - ret RsProgramVertex - } - -ProgramVertexCreate2 { param const char * shaderText param uint32_t shaderLength param const uint32_t * params @@ -417,66 +344,52 @@ ProgramVertexCreate2 { ret RsProgramVertex } -LightBegin { - } - -LightSetLocal { - param bool isLocal - } - -LightSetMonochromatic { - param bool isMono - } - -LightCreate { - ret RsLight light - } - - -LightSetPosition { - param RsLight light - param float x - param float y - param float z - } - -LightSetColor { - param RsLight light - param float r - param float g - param float b +FontCreateFromFile { + param const char *name + param float fontSize + param uint32_t dpi + ret RsFont } -FileOpen { - ret RsFile +FontCreateFromMemory { param const char *name - param size_t len + param float fontSize + param uint32_t dpi + param const void *data + param uint32_t dataLen + ret RsFont } - -SimpleMeshCreate { - ret RsSimpleMesh - param RsAllocation prim - param RsAllocation index - param RsAllocation *vtx +MeshCreate { + ret RsMesh param uint32_t vtxCount - param uint32_t primType + param uint32_t idxCount } - -SimpleMeshBindIndex { - param RsSimpleMesh mesh +MeshBindIndex { + param RsMesh mesh param RsAllocation idx + param uint32_t primType + param uint32_t slot } -SimpleMeshBindPrimitive { - param RsSimpleMesh mesh - param RsAllocation prim - } - -SimpleMeshBindVertex { - param RsSimpleMesh mesh +MeshBindVertex { + param RsMesh mesh param RsAllocation vtx param uint32_t slot } +MeshInitVertexAttribs { + param RsMesh mesh + } + +AnimationCreate { + param const float *inValues + param const float *outValues + param uint32_t valueCount + param RsAnimationInterpolation interp + param RsAnimationEdge pre + param RsAnimationEdge post + ret RsAnimation + } + diff --git a/libs/rs/rsAdapter.cpp b/libs/rs/rsAdapter.cpp index 0d31facba902..6e8ca705110e 100644 --- a/libs/rs/rsAdapter.cpp +++ b/libs/rs/rsAdapter.cpp @@ -20,32 +20,23 @@ using namespace android; using namespace android::renderscript; - -Adapter1D::Adapter1D(Context *rsc) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; +Adapter1D::Adapter1D(Context *rsc) : ObjectBase(rsc) { reset(); } -Adapter1D::Adapter1D(Context *rsc, Allocation *a) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; +Adapter1D::Adapter1D(Context *rsc, Allocation *a) : ObjectBase(rsc) { reset(); setAllocation(a); } -void Adapter1D::reset() -{ +void Adapter1D::reset() { mY = 0; mZ = 0; mLOD = 0; mFace = 0; } -void * Adapter1D::getElement(uint32_t x) -{ +void * Adapter1D::getElement(uint32_t x) { rsAssert(mAllocation.get()); rsAssert(mAllocation->getPtr()); rsAssert(mAllocation->getType()); @@ -54,8 +45,7 @@ void * Adapter1D::getElement(uint32_t x) return ptr; } -void Adapter1D::subData(uint32_t xoff, uint32_t count, const void *data) -{ +void Adapter1D::subData(uint32_t xoff, uint32_t count, const void *data) { if (mAllocation.get() && mAllocation.get()->getType()) { void *ptr = getElement(xoff); count *= mAllocation.get()->getType()->getElementSizeBytes(); @@ -63,34 +53,37 @@ void Adapter1D::subData(uint32_t xoff, uint32_t count, const void *data) } } -void Adapter1D::data(const void *data) -{ +void Adapter1D::data(const void *data) { memcpy(getElement(0), data, mAllocation.get()->getType()->getSizeBytes()); } +void Adapter1D::serialize(OStream *stream) const { +} + +Adapter1D *Adapter1D::createFromStream(Context *rsc, IStream *stream) { + return NULL; +} + namespace android { namespace renderscript { -RsAdapter1D rsi_Adapter1DCreate(Context *rsc) -{ +RsAdapter1D rsi_Adapter1DCreate(Context *rsc) { Adapter1D *a = new Adapter1D(rsc); a->incUserRef(); return a; } -void rsi_Adapter1DBindAllocation(Context *rsc, RsAdapter1D va, RsAllocation valloc) -{ +void rsi_Adapter1DBindAllocation(Context *rsc, RsAdapter1D va, RsAllocation valloc) { Adapter1D * a = static_cast<Adapter1D *>(va); Allocation * alloc = static_cast<Allocation *>(valloc); a->setAllocation(alloc); } -void rsi_Adapter1DSetConstraint(Context *rsc, RsAdapter1D va, RsDimension dim, uint32_t value) -{ +void rsi_Adapter1DSetConstraint(Context *rsc, RsAdapter1D va, RsDimension dim, uint32_t value) { Adapter1D * a = static_cast<Adapter1D *>(va); - switch(dim) { + switch (dim) { case RS_DIMENSION_X: rsAssert(!"Cannot contrain X in an 1D adapter"); return; @@ -112,14 +105,12 @@ void rsi_Adapter1DSetConstraint(Context *rsc, RsAdapter1D va, RsDimension dim, u } } -void rsi_Adapter1DSubData(Context *rsc, RsAdapter1D va, uint32_t xoff, uint32_t count, const void *data) -{ +void rsi_Adapter1DSubData(Context *rsc, RsAdapter1D va, uint32_t xoff, uint32_t count, const void *data) { Adapter1D * a = static_cast<Adapter1D *>(va); a->subData(xoff, count, data); } -void rsi_Adapter1DData(Context *rsc, RsAdapter1D va, const void *data) -{ +void rsi_Adapter1DData(Context *rsc, RsAdapter1D va, const void *data) { Adapter1D * a = static_cast<Adapter1D *>(va); a->data(data); } @@ -129,47 +120,48 @@ void rsi_Adapter1DData(Context *rsc, RsAdapter1D va, const void *data) ////////////////////////// -Adapter2D::Adapter2D(Context *rsc) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; +Adapter2D::Adapter2D(Context *rsc) : ObjectBase(rsc) { reset(); } -Adapter2D::Adapter2D(Context *rsc, Allocation *a) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; +Adapter2D::Adapter2D(Context *rsc, Allocation *a) : ObjectBase(rsc) { reset(); setAllocation(a); } -void Adapter2D::reset() -{ +void Adapter2D::reset() { mZ = 0; mLOD = 0; mFace = 0; } -void * Adapter2D::getElement(uint32_t x, uint32_t y) const -{ +void * Adapter2D::getElement(uint32_t x, uint32_t y) const { rsAssert(mAllocation.get()); rsAssert(mAllocation->getPtr()); rsAssert(mAllocation->getType()); + if (mFace != 0 && !mAllocation->getType()->getDimFaces()) { + LOGE("Adapter wants cubemap face, but allocation has none"); + return NULL; + } + uint8_t * ptr = static_cast<uint8_t *>(mAllocation->getPtr()); ptr += mAllocation->getType()->getLODOffset(mLOD, x, y); + + if (mFace != 0) { + uint32_t totalSizeBytes = mAllocation->getType()->getSizeBytes(); + uint32_t faceOffset = totalSizeBytes / 6; + ptr += faceOffset * mFace; + } return ptr; } -void Adapter2D::subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data) -{ +void Adapter2D::subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data) { rsAssert(mAllocation.get()); rsAssert(mAllocation->getPtr()); rsAssert(mAllocation->getType()); uint32_t eSize = mAllocation.get()->getType()->getElementSizeBytes(); uint32_t lineSize = eSize * w; - uint32_t destW = getDimX(); const uint8_t *src = static_cast<const uint8_t *>(data); for (uint32_t line=yoff; line < (yoff+h); line++) { @@ -178,36 +170,38 @@ void Adapter2D::subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, co } } -void Adapter2D::data(const void *data) -{ +void Adapter2D::data(const void *data) { memcpy(getElement(0,0), data, mAllocation.get()->getType()->getSizeBytes()); } +void Adapter2D::serialize(OStream *stream) const { +} + +Adapter2D *Adapter2D::createFromStream(Context *rsc, IStream *stream) { + return NULL; +} namespace android { namespace renderscript { -RsAdapter2D rsi_Adapter2DCreate(Context *rsc) -{ +RsAdapter2D rsi_Adapter2DCreate(Context *rsc) { Adapter2D *a = new Adapter2D(rsc); a->incUserRef(); return a; } -void rsi_Adapter2DBindAllocation(Context *rsc, RsAdapter2D va, RsAllocation valloc) -{ +void rsi_Adapter2DBindAllocation(Context *rsc, RsAdapter2D va, RsAllocation valloc) { Adapter2D * a = static_cast<Adapter2D *>(va); Allocation * alloc = static_cast<Allocation *>(valloc); a->setAllocation(alloc); } -void rsi_Adapter2DSetConstraint(Context *rsc, RsAdapter2D va, RsDimension dim, uint32_t value) -{ +void rsi_Adapter2DSetConstraint(Context *rsc, RsAdapter2D va, RsDimension dim, uint32_t value) { Adapter2D * a = static_cast<Adapter2D *>(va); - switch(dim) { + switch (dim) { case RS_DIMENSION_X: rsAssert(!"Cannot contrain X in an 2D adapter"); return; @@ -229,14 +223,12 @@ void rsi_Adapter2DSetConstraint(Context *rsc, RsAdapter2D va, RsDimension dim, u } } -void rsi_Adapter2DData(Context *rsc, RsAdapter2D va, const void *data) -{ +void rsi_Adapter2DData(Context *rsc, RsAdapter2D va, const void *data) { Adapter2D * a = static_cast<Adapter2D *>(va); a->data(data); } -void rsi_Adapter2DSubData(Context *rsc, RsAdapter2D va, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data) -{ +void rsi_Adapter2DSubData(Context *rsc, RsAdapter2D va, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data) { Adapter2D * a = static_cast<Adapter2D *>(va); a->subData(xoff, yoff, w, h, data); } diff --git a/libs/rs/rsAdapter.h b/libs/rs/rsAdapter.h index cb2872e4399c..d150789c4a19 100644 --- a/libs/rs/rsAdapter.h +++ b/libs/rs/rsAdapter.h @@ -24,8 +24,7 @@ namespace android { namespace renderscript { -class Adapter1D : public ObjectBase -{ +class Adapter1D : public ObjectBase { public: // By policy this allocation will hold a pointer to the type @@ -50,6 +49,10 @@ public: void subData(uint32_t xoff, uint32_t count, const void *data); void data(const void *data); + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_ADAPTER_1D; } + static Adapter1D *createFromStream(Context *rsc, IStream *stream); + protected: ObjectBaseRef<Allocation> mAllocation; uint32_t mY; @@ -58,8 +61,7 @@ protected: uint32_t mFace; }; -class Adapter2D : public ObjectBase -{ +class Adapter2D : public ObjectBase { public: // By policy this allocation will hold a pointer to the type @@ -82,6 +84,10 @@ public: void data(const void *data); void subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data); + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_ADAPTER_2D; } + static Adapter2D *createFromStream(Context *rsc, IStream *stream); + protected: ObjectBaseRef<Allocation> mAllocation; uint32_t mZ; @@ -89,7 +95,6 @@ protected: uint32_t mFace; }; - } } #endif diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp index 4e8278d79840..ec03a158871f 100644 --- a/libs/rs/rsAllocation.cpp +++ b/libs/rs/rsAllocation.cpp @@ -15,39 +15,34 @@ */ #include "rsContext.h" - +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES2/gl2.h> #include <GLES/glext.h> +#endif //ANDROID_RS_SERIALIZE using namespace android; using namespace android::renderscript; -Allocation::Allocation(Context *rsc, const Type *type) : ObjectBase(rsc) -{ +Allocation::Allocation(Context *rsc, const Type *type, uint32_t usages, + RsAllocationMipmapControl mc) + : ObjectBase(rsc) { init(rsc, type); - mPtr = malloc(mType->getSizeBytes()); + mUsageFlags = usages; + mMipmapControl = mc; + + allocScriptMemory(); + if (mType->getElement()->getHasReferences()) { + memset(mPtr, 0, mType->getSizeBytes()); + } if (!mPtr) { LOGE("Allocation::Allocation, alloc failure"); } } -Allocation::Allocation(Context *rsc, const Type *type, void *bmp, - void *callbackData, RsBitmapCallback_t callback) -: ObjectBase(rsc) -{ - init(rsc, type); - - mPtr = bmp; - mUserBitmapCallback = callback; - mUserBitmapCallbackData = callbackData; -} -void Allocation::init(Context *rsc, const Type *type) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; +void Allocation::init(Context *rsc, const Type *type) { mPtr = NULL; mCpuWrite = false; @@ -57,10 +52,10 @@ void Allocation::init(Context *rsc, const Type *type) mReadWriteRatio = 0; mUpdateSize = 0; + mUsageFlags = 0; + mMipmapControl = RS_ALLOCATION_MIPMAP_NONE; - mIsTexture = false; mTextureID = 0; - mIsVertexBuffer = false; mBufferID = 0; mUploadDefered = false; @@ -73,15 +68,13 @@ void Allocation::init(Context *rsc, const Type *type) mPtr = NULL; } -Allocation::~Allocation() -{ +Allocation::~Allocation() { if (mUserBitmapCallback != NULL) { mUserBitmapCallback(mUserBitmapCallbackData); - } else { - free(mPtr); + mPtr = NULL; } - mPtr = NULL; - + freeScriptMemory(); +#ifndef ANDROID_RS_SERIALIZE if (mBufferID) { // Causes a SW crash.... //LOGV(" mBufferID %i", mBufferID); @@ -92,48 +85,75 @@ Allocation::~Allocation() glDeleteTextures(1, &mTextureID); mTextureID = 0; } +#endif //ANDROID_RS_SERIALIZE } -void Allocation::setCpuWritable(bool) -{ +void Allocation::setCpuWritable(bool) { } -void Allocation::setGpuWritable(bool) -{ +void Allocation::setGpuWritable(bool) { } -void Allocation::setCpuReadable(bool) -{ +void Allocation::setCpuReadable(bool) { } -void Allocation::setGpuReadable(bool) -{ +void Allocation::setGpuReadable(bool) { } -bool Allocation::fixAllocation() -{ +bool Allocation::fixAllocation() { return false; } -void Allocation::deferedUploadToTexture(const Context *rsc, bool genMipmap, uint32_t lodOffset) -{ - rsAssert(lodOffset < mType->getLODCount()); - mIsTexture = true; - mTextureLOD = lodOffset; +void Allocation::deferedUploadToTexture(const Context *rsc) { + mUsageFlags |= RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE; mUploadDefered = true; - mTextureGenMipmap = !mType->getDimLOD() && genMipmap; } -void Allocation::uploadToTexture(const Context *rsc) -{ - //rsAssert(!mTextureId); +uint32_t Allocation::getGLTarget() const { +#ifndef ANDROID_RS_SERIALIZE + if (getIsTexture()) { + if (mType->getDimFaces()) { + return GL_TEXTURE_CUBE_MAP; + } else { + return GL_TEXTURE_2D; + } + } + if (getIsBufferObject()) { + return GL_ARRAY_BUFFER; + } +#endif //ANDROID_RS_SERIALIZE + return 0; +} - mIsTexture = true; - if (!rsc->checkDriver()) { - mUploadDefered = true; - return; +void Allocation::allocScriptMemory() { + rsAssert(!mPtr); + mPtr = malloc(mType->getSizeBytes()); +} + +void Allocation::freeScriptMemory() { + if (mPtr) { + free(mPtr); + mPtr = NULL; } +} + + +void Allocation::syncAll(Context *rsc, RsAllocationUsageType src) { + rsAssert(src == RS_ALLOCATION_USAGE_SCRIPT); + + if (getIsTexture()) { + uploadToTexture(rsc); + } + if (getIsBufferObject()) { + uploadToBufferObject(rsc); + } + + mUploadDefered = false; +} +void Allocation::uploadToTexture(const Context *rsc) { +#ifndef ANDROID_RS_SERIALIZE + mUsageFlags |= RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE; GLenum type = mType->getElement()->getComponent().getGLType(); GLenum format = mType->getElement()->getComponent().getGLFormat(); @@ -141,6 +161,12 @@ void Allocation::uploadToTexture(const Context *rsc) return; } + if (!mPtr) { + return; + } + + bool isFirstUpload = false; + if (!mTextureID) { glGenTextures(1, &mTextureID); @@ -153,41 +179,101 @@ void Allocation::uploadToTexture(const Context *rsc) mUploadDefered = true; return; } + isFirstUpload = true; } - glBindTexture(GL_TEXTURE_2D, mTextureID); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - Adapter2D adapt(getContext(), this); - for(uint32_t lod = 0; (lod + mTextureLOD) < mType->getLODCount(); lod++) { - adapt.setLOD(lod+mTextureLOD); + upload2DTexture(isFirstUpload); - uint16_t * ptr = static_cast<uint16_t *>(adapt.getElement(0,0)); - glTexImage2D(GL_TEXTURE_2D, lod, format, - adapt.getDimX(), adapt.getDimY(), - 0, format, type, ptr); + if (!(mUsageFlags & RS_ALLOCATION_USAGE_SCRIPT)) { + freeScriptMemory(); } - if (mTextureGenMipmap) { - glGenerateMipmap(GL_TEXTURE_2D); + + rsc->checkError("Allocation::uploadToTexture"); +#endif //ANDROID_RS_SERIALIZE +} + +#ifndef ANDROID_RS_SERIALIZE +const static GLenum gFaceOrder[] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; +#endif //ANDROID_RS_SERIALIZE + +void Allocation::update2DTexture(const void *ptr, uint32_t xoff, uint32_t yoff, + uint32_t lod, RsAllocationCubemapFace face, + uint32_t w, uint32_t h) { +#ifndef ANDROID_RS_SERIALIZE + GLenum type = mType->getElement()->getComponent().getGLType(); + GLenum format = mType->getElement()->getComponent().getGLFormat(); + GLenum target = (GLenum)getGLTarget(); + rsAssert(mTextureID); + glBindTexture(target, mTextureID); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + GLenum t = GL_TEXTURE_2D; + if (mType->getDimFaces()) { + t = gFaceOrder[face]; + } + glTexSubImage2D(t, lod, xoff, yoff, w, h, format, type, ptr); +#endif //ANDROID_RS_SERIALIZE +} + +void Allocation::upload2DTexture(bool isFirstUpload) { +#ifndef ANDROID_RS_SERIALIZE + GLenum type = mType->getElement()->getComponent().getGLType(); + GLenum format = mType->getElement()->getComponent().getGLFormat(); + + GLenum target = (GLenum)getGLTarget(); + glBindTexture(target, mTextureID); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + uint32_t faceCount = 1; + if (mType->getDimFaces()) { + faceCount = 6; + } + + for (uint32_t face = 0; face < faceCount; face ++) { + for (uint32_t lod = 0; lod < mType->getLODCount(); lod++) { + const uint8_t *p = (const uint8_t *)mPtr; + p += mType->getLODFaceOffset(lod, (RsAllocationCubemapFace)face, 0, 0); + + GLenum t = GL_TEXTURE_2D; + if (mType->getDimFaces()) { + t = gFaceOrder[face]; + } + + if (isFirstUpload) { + glTexImage2D(t, lod, format, + mType->getLODDimX(lod), mType->getLODDimY(lod), + 0, format, type, p); + } else { + glTexSubImage2D(t, lod, 0, 0, + mType->getLODDimX(lod), mType->getLODDimY(lod), + format, type, p); + } + } } + if (mMipmapControl == RS_ALLOCATION_MIPMAP_ON_SYNC_TO_TEXTURE) { + glGenerateMipmap(target); + } +#endif //ANDROID_RS_SERIALIZE } -void Allocation::deferedUploadToBufferObject(const Context *rsc) -{ - mIsVertexBuffer = true; +void Allocation::deferedUploadToBufferObject(const Context *rsc) { + mUsageFlags |= RS_ALLOCATION_USAGE_GRAPHICS_VERTEX; mUploadDefered = true; } -void Allocation::uploadToBufferObject(const Context *rsc) -{ +void Allocation::uploadToBufferObject(const Context *rsc) { +#ifndef ANDROID_RS_SERIALIZE rsAssert(!mType->getDimY()); rsAssert(!mType->getDimZ()); - mIsVertexBuffer = true; - if (!rsc->checkDriver()) { - mUploadDefered = true; - return; - } + mUsageFlags |= RS_ALLOCATION_USAGE_GRAPHICS_VERTEX; if (!mBufferID) { glGenBuffers(1, &mBufferID); @@ -197,45 +283,26 @@ void Allocation::uploadToBufferObject(const Context *rsc) mUploadDefered = true; return; } - - glBindBuffer(GL_ARRAY_BUFFER, mBufferID); - glBufferData(GL_ARRAY_BUFFER, mType->getSizeBytes(), getPtr(), GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); + GLenum target = (GLenum)getGLTarget(); + glBindBuffer(target, mBufferID); + glBufferData(target, mType->getSizeBytes(), getPtr(), GL_DYNAMIC_DRAW); + glBindBuffer(target, 0); + rsc->checkError("Allocation::uploadToBufferObject"); +#endif //ANDROID_RS_SERIALIZE } -void Allocation::uploadCheck(const Context *rsc) -{ +void Allocation::uploadCheck(Context *rsc) { if (mUploadDefered) { - mUploadDefered = false; - if (mIsVertexBuffer) { - uploadToBufferObject(rsc); - } - if (mIsTexture) { - uploadToTexture(rsc); - } + syncAll(rsc, RS_ALLOCATION_USAGE_SCRIPT); } } - -void Allocation::data(const void *data, uint32_t sizeBytes) -{ - uint32_t size = mType->getSizeBytes(); - if (size != sizeBytes) { - LOGE("Allocation::data called with mismatched size expected %i, got %i", size, sizeBytes); - return; - } - memcpy(mPtr, data, size); - sendDirty(); - mUploadDefered = true; -} - -void Allocation::read(void *data) -{ +void Allocation::read(void *data) { memcpy(data, mPtr, mType->getSizeBytes()); } -void Allocation::subData(uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes) -{ +void Allocation::data(Context *rsc, uint32_t xoff, uint32_t lod, + uint32_t count, const void *data, uint32_t sizeBytes) { uint32_t eSize = mType->getElementSizeBytes(); uint8_t * ptr = static_cast<uint8_t *>(mPtr); ptr += eSize * xoff; @@ -246,49 +313,146 @@ void Allocation::subData(uint32_t xoff, uint32_t count, const void *data, uint32 mType->dumpLOGV("type info"); return; } + + if (mType->getElement()->getHasReferences()) { + incRefs(data, count); + decRefs(ptr, count); + } + memcpy(ptr, data, size); sendDirty(); mUploadDefered = true; } -void Allocation::subData(uint32_t xoff, uint32_t yoff, - uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes) -{ +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) { uint32_t eSize = mType->getElementSizeBytes(); uint32_t lineSize = eSize * w; uint32_t destW = mType->getDimX(); - const uint8_t *src = static_cast<const uint8_t *>(data); - uint8_t *dst = static_cast<uint8_t *>(mPtr); - dst += eSize * (xoff + yoff * destW); + //LOGE("data2d %p, %i %i %i %i %i %i %p %i", this, xoff, yoff, lod, face, w, h, data, sizeBytes); - if ((lineSize * eSize * h) != sizeBytes) { + if ((lineSize * h) != sizeBytes) { + LOGE("Allocation size mismatch, expected %i, got %i", (lineSize * h), sizeBytes); rsAssert(!"Allocation::subData called with mismatched size"); return; } - for (uint32_t line=yoff; line < (yoff+h); line++) { - uint8_t * ptr = static_cast<uint8_t *>(mPtr); - memcpy(dst, src, lineSize); - src += lineSize; - dst += destW * eSize; + if (mPtr) { + const uint8_t *src = static_cast<const uint8_t *>(data); + uint8_t *dst = static_cast<uint8_t *>(mPtr); + dst += mType->getLODFaceOffset(lod, face, xoff, yoff); + + //LOGE(" %p %p %i ", dst, src, eSize); + for (uint32_t line=yoff; line < (yoff+h); line++) { + if (mType->getElement()->getHasReferences()) { + incRefs(src, w); + decRefs(dst, w); + } + memcpy(dst, src, lineSize); + src += lineSize; + dst += destW * eSize; + } + sendDirty(); + mUploadDefered = true; + } else { + update2DTexture(data, xoff, yoff, lod, face, w, h); + } +} + +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) { +} + +void Allocation::elementData(Context *rsc, uint32_t x, const void *data, + uint32_t cIdx, uint32_t sizeBytes) { + uint32_t eSize = mType->getElementSizeBytes(); + uint8_t * ptr = static_cast<uint8_t *>(mPtr); + ptr += eSize * x; + + if (cIdx >= mType->getElement()->getFieldCount()) { + LOGE("Error Allocation::subElementData component %i out of range.", cIdx); + rsc->setError(RS_ERROR_BAD_VALUE, "subElementData component out of range."); + return; } + + if (x >= mType->getDimX()) { + LOGE("Error Allocation::subElementData X offset %i out of range.", x); + rsc->setError(RS_ERROR_BAD_VALUE, "subElementData X offset out of range."); + return; + } + + const Element * e = mType->getElement()->getField(cIdx); + ptr += mType->getElement()->getFieldOffsetBytes(cIdx); + + if (sizeBytes != e->getSizeBytes()) { + LOGE("Error Allocation::subElementData data size %i does not match field size %zu.", sizeBytes, e->getSizeBytes()); + rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size."); + return; + } + + if (e->getHasReferences()) { + e->incRefs(data); + e->decRefs(ptr); + } + + memcpy(ptr, data, sizeBytes); sendDirty(); mUploadDefered = true; } -void Allocation::subData(uint32_t xoff, uint32_t yoff, uint32_t zoff, - uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes) -{ +void Allocation::elementData(Context *rsc, uint32_t x, uint32_t y, + const void *data, uint32_t cIdx, uint32_t sizeBytes) { + uint32_t eSize = mType->getElementSizeBytes(); + uint8_t * ptr = static_cast<uint8_t *>(mPtr); + ptr += eSize * (x + y * mType->getDimX()); + + if (x >= mType->getDimX()) { + LOGE("Error Allocation::subElementData X offset %i out of range.", x); + rsc->setError(RS_ERROR_BAD_VALUE, "subElementData X offset out of range."); + return; + } + + if (y >= mType->getDimY()) { + LOGE("Error Allocation::subElementData X offset %i out of range.", x); + rsc->setError(RS_ERROR_BAD_VALUE, "subElementData X offset out of range."); + return; + } + + if (cIdx >= mType->getElement()->getFieldCount()) { + LOGE("Error Allocation::subElementData component %i out of range.", cIdx); + rsc->setError(RS_ERROR_BAD_VALUE, "subElementData component out of range."); + return; + } + + const Element * e = mType->getElement()->getField(cIdx); + ptr += mType->getElement()->getFieldOffsetBytes(cIdx); + + if (sizeBytes != e->getSizeBytes()) { + LOGE("Error Allocation::subElementData data size %i does not match field size %zu.", sizeBytes, e->getSizeBytes()); + rsc->setError(RS_ERROR_BAD_VALUE, "subElementData bad size."); + return; + } + + if (e->getHasReferences()) { + e->incRefs(data); + e->decRefs(ptr); + } + + memcpy(ptr, data, sizeBytes); + sendDirty(); + mUploadDefered = true; } -void Allocation::addProgramToDirty(const Program *p) -{ - mToDirtyList.add(p); +void Allocation::addProgramToDirty(const Program *p) { +#ifndef ANDROID_RS_SERIALIZE + mToDirtyList.push(p); +#endif //ANDROID_RS_SERIALIZE } -void Allocation::removeProgramToDirty(const Program *p) -{ +void Allocation::removeProgramToDirty(const Program *p) { +#ifndef ANDROID_RS_SERIALIZE for (size_t ct=0; ct < mToDirtyList.size(); ct++) { if (mToDirtyList[ct] == p) { mToDirtyList.removeAt(ct); @@ -296,10 +460,10 @@ void Allocation::removeProgramToDirty(const Program *p) } } rsAssert(0); +#endif //ANDROID_RS_SERIALIZE } -void Allocation::dumpLOGV(const char *prefix) const -{ +void Allocation::dumpLOGV(const char *prefix) const { ObjectBase::dumpLOGV(prefix); String8 s(prefix); @@ -311,57 +475,147 @@ void Allocation::dumpLOGV(const char *prefix) const LOGV("%s allocation ptr=%p mCpuWrite=%i, mCpuRead=%i, mGpuWrite=%i, mGpuRead=%i", prefix, mPtr, mCpuWrite, mCpuRead, mGpuWrite, mGpuRead); - LOGV("%s allocation mIsTexture=%i mTextureID=%i, mIsVertexBuffer=%i, mBufferID=%i", - prefix, mIsTexture, mTextureID, mIsVertexBuffer, mBufferID); + LOGV("%s allocation mUsageFlags=0x04%x, mMipmapControl=0x%04x, mTextureID=%i, mBufferID=%i", + prefix, mUsageFlags, mMipmapControl, mTextureID, mBufferID); +} + +void Allocation::serialize(OStream *stream) const { + // Need to identify ourselves + stream->addU32((uint32_t)getClassId()); + String8 name(getName()); + stream->addString(&name); + + // First thing we need to serialize is the type object since it will be needed + // to initialize the class + mType->serialize(stream); + + uint32_t dataSize = mType->getSizeBytes(); + // Write how much data we are storing + stream->addU32(dataSize); + // Now write the data + stream->addByteArray(mPtr, dataSize); } -void Allocation::sendDirty() const -{ +Allocation *Allocation::createFromStream(Context *rsc, IStream *stream) { + // First make sure we are reading the correct object + RsA3DClassID classID = (RsA3DClassID)stream->loadU32(); + if (classID != RS_A3D_CLASS_ID_ALLOCATION) { + LOGE("allocation loading skipped due to invalid class id\n"); + return NULL; + } + + String8 name; + stream->loadString(&name); + + Type *type = Type::createFromStream(rsc, stream); + if (!type) { + return NULL; + } + type->compute(); + + // Number of bytes we wrote out for this allocation + uint32_t dataSize = stream->loadU32(); + if (dataSize != type->getSizeBytes()) { + LOGE("failed to read allocation because numbytes written is not the same loaded type wants\n"); + ObjectBase::checkDelete(type); + return NULL; + } + + Allocation *alloc = new Allocation(rsc, type, RS_ALLOCATION_USAGE_SCRIPT); + alloc->setName(name.string(), name.size()); + + uint32_t count = dataSize / type->getElementSizeBytes(); + + // Read in all of our allocation data + alloc->data(rsc, 0, 0, count, stream->getPtr() + stream->getPos(), dataSize); + stream->reset(stream->getPos() + dataSize); + + return alloc; +} + +void Allocation::sendDirty() const { +#ifndef ANDROID_RS_SERIALIZE for (size_t ct=0; ct < mToDirtyList.size(); ct++) { mToDirtyList[ct]->forceDirty(); } +#endif //ANDROID_RS_SERIALIZE } -///////////////// -// +void Allocation::incRefs(const void *ptr, size_t ct, size_t startOff) const { + const uint8_t *p = static_cast<const uint8_t *>(ptr); + const Element *e = mType->getElement(); + uint32_t stride = e->getSizeBytes(); + p += stride * startOff; + while (ct > 0) { + e->incRefs(p); + ct --; + p += stride; + } +} -namespace android { -namespace renderscript { +void Allocation::decRefs(const void *ptr, size_t ct, size_t startOff) const { + const uint8_t *p = static_cast<const uint8_t *>(ptr); + const Element *e = mType->getElement(); + uint32_t stride = e->getSizeBytes(); -RsAllocation rsi_AllocationCreateTyped(Context *rsc, RsType vtype) -{ - const Type * type = static_cast<const Type *>(vtype); + p += stride * startOff; + while (ct > 0) { + e->decRefs(p); + ct --; + p += stride; + } +} - Allocation * alloc = new Allocation(rsc, type); - alloc->incUserRef(); - return alloc; +void Allocation::copyRange1D(Context *rsc, const Allocation *src, int32_t srcOff, int32_t destOff, int32_t len) { } -RsAllocation rsi_AllocationCreateSized(Context *rsc, RsElement e, size_t count) -{ - Type * type = new Type(rsc); - type->setDimX(count); - type->setElement(static_cast<Element *>(e)); - type->compute(); - return rsi_AllocationCreateTyped(rsc, type); +void Allocation::resize1D(Context *rsc, uint32_t dimX) { + Type *t = mType->cloneAndResize1D(rsc, dimX); + + uint32_t oldDimX = mType->getDimX(); + if (dimX == oldDimX) { + return; + } + + if (dimX < oldDimX) { + decRefs(mPtr, oldDimX - dimX, dimX); + } + mPtr = realloc(mPtr, t->getSizeBytes()); + + if (dimX > oldDimX) { + const Element *e = mType->getElement(); + uint32_t stride = e->getSizeBytes(); + memset(((uint8_t *)mPtr) + stride * oldDimX, 0, stride * (dimX - oldDimX)); + } + mType.set(t); } -void rsi_AllocationUploadToTexture(Context *rsc, RsAllocation va, bool genmip, uint32_t baseMipLevel) -{ +void Allocation::resize2D(Context *rsc, uint32_t dimX, uint32_t dimY) { + LOGE("not implemented"); +} + +///////////////// +// +#ifndef ANDROID_RS_SERIALIZE + +static void rsaAllocationGenerateScriptMips(RsContext con, RsAllocation va); + +namespace android { +namespace renderscript { + +void rsi_AllocationUploadToTexture(Context *rsc, RsAllocation va, bool genmip, uint32_t baseMipLevel) { Allocation *alloc = static_cast<Allocation *>(va); - alloc->deferedUploadToTexture(rsc, genmip, baseMipLevel); + alloc->deferedUploadToTexture(rsc); } -void rsi_AllocationUploadToBufferObject(Context *rsc, RsAllocation va) -{ +void rsi_AllocationUploadToBufferObject(Context *rsc, RsAllocation va) { Allocation *alloc = static_cast<Allocation *>(va); alloc->deferedUploadToBufferObject(rsc); } -static void mip565(const Adapter2D &out, const Adapter2D &in) -{ +static void mip565(const Adapter2D &out, const Adapter2D &in) { uint32_t w = out.getDimX(); uint32_t h = out.getDimY(); @@ -379,8 +633,7 @@ static void mip565(const Adapter2D &out, const Adapter2D &in) } } -static void mip8888(const Adapter2D &out, const Adapter2D &in) -{ +static void mip8888(const Adapter2D &out, const Adapter2D &in) { uint32_t w = out.getDimX(); uint32_t h = out.getDimY(); @@ -398,8 +651,7 @@ static void mip8888(const Adapter2D &out, const Adapter2D &in) } } -static void mip8(const Adapter2D &out, const Adapter2D &in) -{ +static void mip8(const Adapter2D &out, const Adapter2D &in) { uint32_t w = out.getDimX(); uint32_t h = out.getDimY(); @@ -417,9 +669,8 @@ static void mip8(const Adapter2D &out, const Adapter2D &in) } } -static void mip(const Adapter2D &out, const Adapter2D &in) -{ - switch(out.getBaseType()->getElement()->getSizeBits()) { +static void mip(const Adapter2D &out, const Adapter2D &in) { + switch (out.getBaseType()->getElement()->getSizeBits()) { case 32: mip8888(out, in); break; @@ -429,190 +680,170 @@ static void mip(const Adapter2D &out, const Adapter2D &in) case 8: mip8(out, in); break; - } +} + +void rsi_AllocationSyncAll(Context *rsc, RsAllocation va, RsAllocationUsageType src) { + Allocation *a = static_cast<Allocation *>(va); + a->syncAll(rsc, src); + a->sendDirty(); +} +void rsi_AllocationGenerateMipmaps(Context *rsc, RsAllocation va) { + Allocation *texAlloc = static_cast<Allocation *>(va); + rsaAllocationGenerateScriptMips(rsc, texAlloc); } -typedef void (*ElementConverter_t)(void *dst, const void *src, uint32_t count); +void rsi_AllocationCopyToBitmap(Context *rsc, RsAllocation va, void *data, size_t dataLen) { + Allocation *texAlloc = static_cast<Allocation *>(va); + const Type * t = texAlloc->getType(); -static void elementConverter_cpy_16(void *dst, const void *src, uint32_t count) -{ - memcpy(dst, src, count * 2); + size_t s = t->getDimX() * t->getDimY() * t->getElementSizeBytes(); + if (s != dataLen) { + rsc->setError(RS_ERROR_BAD_VALUE, "Bitmap size didn't match allocation size"); + return; + } + + memcpy(data, texAlloc->getPtr(), s); } -static void elementConverter_cpy_8(void *dst, const void *src, uint32_t count) -{ - memcpy(dst, src, count); + +void rsi_Allocation1DData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t lod, + uint32_t count, const void *data, uint32_t sizeBytes) { + Allocation *a = static_cast<Allocation *>(va); + a->data(rsc, xoff, lod, count, data, sizeBytes); } -static void elementConverter_cpy_32(void *dst, const void *src, uint32_t count) -{ - memcpy(dst, src, count * 4); + +void rsi_Allocation2DElementData(Context *rsc, RsAllocation va, uint32_t x, uint32_t y, uint32_t lod, RsAllocationCubemapFace face, + const void *data, uint32_t eoff, uint32_t sizeBytes) { + 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, uint32_t eoff, uint32_t sizeBytes) { + Allocation *a = static_cast<Allocation *>(va); + a->elementData(rsc, x, data, eoff, sizeBytes); +} -static void elementConverter_888_to_565(void *dst, const void *src, uint32_t count) -{ - uint16_t *d = static_cast<uint16_t *>(dst); - const uint8_t *s = static_cast<const uint8_t *>(src); +void rsi_Allocation2DData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face, + uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes) { + Allocation *a = static_cast<Allocation *>(va); + a->data(rsc, xoff, yoff, lod, face, w, h, data, sizeBytes); +} - while(count--) { - *d = rs888to565(s[0], s[1], s[2]); - d++; - s+= 3; - } +void rsi_AllocationRead(Context *rsc, RsAllocation va, void *data) { + Allocation *a = static_cast<Allocation *>(va); + a->read(data); } -static void elementConverter_8888_to_565(void *dst, const void *src, uint32_t count) -{ - uint16_t *d = static_cast<uint16_t *>(dst); - const uint8_t *s = static_cast<const uint8_t *>(src); +void rsi_AllocationResize1D(Context *rsc, RsAllocation va, uint32_t dimX) { + Allocation *a = static_cast<Allocation *>(va); + a->resize1D(rsc, dimX); +} - while(count--) { - *d = rs888to565(s[0], s[1], s[2]); - d++; - s+= 4; - } +void rsi_AllocationResize2D(Context *rsc, RsAllocation va, uint32_t dimX, uint32_t dimY) { + Allocation *a = static_cast<Allocation *>(va); + a->resize2D(rsc, dimX, dimY); } -static ElementConverter_t pickConverter(const Element *dst, const Element *src) -{ - GLenum srcGLType = src->getComponent().getGLType(); - GLenum srcGLFmt = src->getComponent().getGLFormat(); - GLenum dstGLType = dst->getComponent().getGLType(); - GLenum dstGLFmt = dst->getComponent().getGLFormat(); +} +} - if (srcGLFmt == dstGLFmt && srcGLType == dstGLType) { - switch(dst->getSizeBytes()) { - case 4: - return elementConverter_cpy_32; - case 2: - return elementConverter_cpy_16; - case 1: - return elementConverter_cpy_8; +static void rsaAllocationGenerateScriptMips(RsContext con, RsAllocation va) { + Context *rsc = static_cast<Context *>(con); + Allocation *texAlloc = static_cast<Allocation *>(va); + uint32_t numFaces = texAlloc->getType()->getDimFaces() ? 6 : 1; + for (uint32_t face = 0; face < numFaces; face ++) { + Adapter2D adapt(rsc, texAlloc); + Adapter2D adapt2(rsc, texAlloc); + adapt.setFace(face); + adapt2.setFace(face); + for (uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) { + adapt.setLOD(lod); + adapt2.setLOD(lod + 1); + mip(adapt2, adapt); } } +} - if (srcGLType == GL_UNSIGNED_BYTE && - srcGLFmt == GL_RGB && - dstGLType == GL_UNSIGNED_SHORT_5_6_5 && - dstGLType == GL_RGB) { - - return elementConverter_888_to_565; - } - - if (srcGLType == GL_UNSIGNED_BYTE && - srcGLFmt == GL_RGBA && - dstGLType == GL_UNSIGNED_SHORT_5_6_5 && - dstGLType == GL_RGB) { - - return elementConverter_8888_to_565; - } +const void * rsaAllocationGetType(RsContext con, RsAllocation va) { + Allocation *a = static_cast<Allocation *>(va); + a->getType()->incUserRef(); - LOGE("pickConverter, unsuported combo, src %p, dst %p", src, dst); - return 0; + return a->getType(); } -RsAllocation rsi_AllocationCreateBitmapRef(Context *rsc, RsType vtype, - void *bmp, void *callbackData, RsBitmapCallback_t callback) -{ - const Type * type = static_cast<const Type *>(vtype); - Allocation * alloc = new Allocation(rsc, type, bmp, callbackData, callback); +RsAllocation rsaAllocationCreateTyped(RsContext con, RsType vtype, + RsAllocationMipmapControl mips, + uint32_t usages) { + Context *rsc = static_cast<Context *>(con); + Allocation * alloc = new Allocation(rsc, static_cast<Type *>(vtype), usages, mips); alloc->incUserRef(); return alloc; } -RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, uint32_t w, uint32_t h, RsElement _dst, RsElement _src, bool genMips, const void *data) -{ - const Element *src = static_cast<const Element *>(_src); - const Element *dst = static_cast<const Element *>(_dst); - // Check for pow2 on pre es 2.0 versions. - rsAssert(rsc->checkVersion2_0() || (!(w & (w-1)) && !(h & (h-1)))); +RsAllocation rsaAllocationCreateFromBitmap(RsContext con, RsType vtype, + RsAllocationMipmapControl mips, + const void *data, uint32_t usages) { + Context *rsc = static_cast<Context *>(con); + Type *t = static_cast<Type *>(vtype); - //LOGE("rsi_AllocationCreateFromBitmap %i %i %i %i %i", w, h, dstFmt, srcFmt, genMips); - rsi_TypeBegin(rsc, _dst); - rsi_TypeAdd(rsc, RS_DIMENSION_X, w); - rsi_TypeAdd(rsc, RS_DIMENSION_Y, h); - if (genMips) { - rsi_TypeAdd(rsc, RS_DIMENSION_LOD, 1); - } - RsType type = rsi_TypeCreate(rsc); - - RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, type); + RsAllocation vTexAlloc = rsaAllocationCreateTyped(rsc, vtype, mips, usages); Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc); if (texAlloc == NULL) { LOGE("Memory allocation failure"); return NULL; } - ElementConverter_t cvt = pickConverter(dst, src); - cvt(texAlloc->getPtr(), data, w * h); - - if (genMips) { - Adapter2D adapt(rsc, texAlloc); - Adapter2D adapt2(rsc, texAlloc); - for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) { - adapt.setLOD(lod); - adapt2.setLOD(lod + 1); - mip(adapt2, adapt); - } + memcpy(texAlloc->getPtr(), data, t->getDimX() * t->getDimY() * t->getElementSizeBytes()); + if (mips == RS_ALLOCATION_MIPMAP_FULL) { + rsaAllocationGenerateScriptMips(rsc, texAlloc); } + texAlloc->deferedUploadToTexture(rsc); return texAlloc; } -RsAllocation rsi_AllocationCreateFromBitmapBoxed(Context *rsc, uint32_t w, uint32_t h, RsElement _dst, RsElement _src, bool genMips, const void *data) -{ - const Element *srcE = static_cast<const Element *>(_src); - const Element *dstE = static_cast<const Element *>(_dst); - uint32_t w2 = rsHigherPow2(w); - uint32_t h2 = rsHigherPow2(h); +RsAllocation rsaAllocationCubeCreateFromBitmap(RsContext con, RsType vtype, + RsAllocationMipmapControl mips, + const void *data, uint32_t usages) { + Context *rsc = static_cast<Context *>(con); + Type *t = static_cast<Type *>(vtype); - if ((w2 == w) && (h2 == h)) { - return rsi_AllocationCreateFromBitmap(rsc, w, h, _dst, _src, genMips, data); + // Cubemap allocation's faces should be Width by Width each. + // Source data should have 6 * Width by Width pixels + // Error checking is done in the java layer + RsAllocation vTexAlloc = rsaAllocationCreateTyped(rsc, t, mips, usages); + Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc); + if (texAlloc == NULL) { + LOGE("Memory allocation failure"); + return NULL; } - uint32_t bpp = srcE->getSizeBytes(); - size_t size = w2 * h2 * bpp; - uint8_t *tmp = static_cast<uint8_t *>(malloc(size)); - memset(tmp, 0, size); + uint32_t faceSize = t->getDimX(); + uint32_t strideBytes = faceSize * 6 * t->getElementSizeBytes(); + uint32_t copySize = faceSize * t->getElementSizeBytes(); - const uint8_t * src = static_cast<const uint8_t *>(data); - for (uint32_t y = 0; y < h; y++) { - uint8_t * ydst = &tmp[(y + ((h2 - h) >> 1)) * w2 * bpp]; - memcpy(&ydst[((w2 - w) >> 1) * bpp], src, w * bpp); - src += w * bpp; - } + uint8_t *sourcePtr = (uint8_t*)data; + for (uint32_t face = 0; face < 6; face ++) { + Adapter2D faceAdapter(rsc, texAlloc); + faceAdapter.setFace(face); - RsAllocation ret = rsi_AllocationCreateFromBitmap(rsc, w2, h2, _dst, _src, genMips, tmp); - free(tmp); - return ret; -} - -void rsi_AllocationData(Context *rsc, RsAllocation va, const void *data, uint32_t sizeBytes) -{ - Allocation *a = static_cast<Allocation *>(va); - a->data(data, sizeBytes); -} + for (uint32_t dI = 0; dI < faceSize; dI ++) { + memcpy(faceAdapter.getElement(0, dI), sourcePtr + strideBytes * dI, copySize); + } -void rsi_Allocation1DSubData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes) -{ - Allocation *a = static_cast<Allocation *>(va); - a->subData(xoff, count, data, sizeBytes); -} + // Move the data pointer to the next cube face + sourcePtr += copySize; + } -void rsi_Allocation2DSubData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes) -{ - Allocation *a = static_cast<Allocation *>(va); - a->subData(xoff, yoff, w, h, data, sizeBytes); -} + if (mips == RS_ALLOCATION_MIPMAP_FULL) { + rsaAllocationGenerateScriptMips(rsc, texAlloc); + } -void rsi_AllocationRead(Context *rsc, RsAllocation va, void *data) -{ - Allocation *a = static_cast<Allocation *>(va); - a->read(data); + texAlloc->deferedUploadToTexture(rsc); + return texAlloc; } - -} -} +#endif //ANDROID_RS_SERIALIZE diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h index 516f8b798208..4f5d5a8e351f 100644 --- a/libs/rs/rsAllocation.h +++ b/libs/rs/rsAllocation.h @@ -25,15 +25,12 @@ namespace renderscript { class Program; -class Allocation : public ObjectBase -{ +class Allocation : public ObjectBase { // The graphics equilivent of malloc. The allocation contains a structure of elements. public: - // By policy this allocation will hold a pointer to the type - // but will not destroy it on destruction. - Allocation(Context *rsc, const Type *); - Allocation(Context *rsc, const Type *, void *bmp, void *callbackData, RsBitmapCallback_t callback); + Allocation(Context *rsc, const Type *, uint32_t usages, + RsAllocationMipmapControl mc = RS_ALLOCATION_MIPMAP_NONE); virtual ~Allocation(); @@ -47,22 +44,34 @@ public: void * getPtr() const {return mPtr;} const Type * getType() const {return mType.get();} - void deferedUploadToTexture(const Context *rsc, bool genMipmap, uint32_t lodOffset); + void syncAll(Context *rsc, RsAllocationUsageType src); + + void deferedUploadToTexture(const Context *rsc); void uploadToTexture(const Context *rsc); uint32_t getTextureID() const {return mTextureID;} + uint32_t getGLTarget() const; + void deferedUploadToBufferObject(const Context *rsc); void uploadToBufferObject(const Context *rsc); uint32_t getBufferObjectID() const {return mBufferID;} + void copyRange1D(Context *rsc, const Allocation *src, int32_t srcOff, int32_t destOff, int32_t len); + + void resize1D(Context *rsc, uint32_t dimX); + void resize2D(Context *rsc, uint32_t dimX, uint32_t dimY); - void data(const void *data, uint32_t sizeBytes); - void subData(uint32_t xoff, uint32_t count, const void *data, uint32_t sizeBytes); - void subData(uint32_t xoff, uint32_t yoff, + 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 yoff, uint32_t lod, RsAllocationCubemapFace face, uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes); - void subData(uint32_t xoff, uint32_t yoff, uint32_t zoff, + 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); + void elementData(Context *rsc, uint32_t x, + const void *data, uint32_t elementOff, uint32_t sizeBytes); + void elementData(Context *rsc, uint32_t x, uint32_t y, + const void *data, uint32_t elementOff, uint32_t sizeBytes); + void read(void *data); void enableGLVertexBuffers() const; @@ -72,12 +81,32 @@ public: void removeProgramToDirty(const Program *); virtual void dumpLOGV(const char *prefix) const; + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_ALLOCATION; } + static Allocation *createFromStream(Context *rsc, IStream *stream); - virtual void uploadCheck(const Context *rsc); + virtual void uploadCheck(Context *rsc); + + bool getIsScript() const { + return (mUsageFlags & RS_ALLOCATION_USAGE_SCRIPT) != 0; + } + bool getIsTexture() const { + return (mUsageFlags & RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE) != 0; + } + bool getIsBufferObject() const { + return (mUsageFlags & RS_ALLOCATION_USAGE_GRAPHICS_VERTEX) != 0; + } + + void incRefs(const void *ptr, size_t ct, size_t startOff = 0) const; + void decRefs(const void *ptr, size_t ct, size_t startOff = 0) const; -protected: void sendDirty() const; + bool getHasGraphicsMipmaps() const { + return mMipmapControl != RS_ALLOCATION_MIPMAP_NONE; + } + +protected: ObjectBaseRef<const Type> mType; void * mPtr; @@ -94,6 +123,9 @@ protected: bool mGpuWrite; bool mGpuRead; + uint32_t mUsageFlags; + RsAllocationMipmapControl mMipmapControl; + // more usage hint data from the application // which can be used by a driver to pick the best memory type. // Likely ignored for now @@ -103,21 +135,23 @@ protected: // Is this a legal structure to be used as a texture source. // Initially this will require 1D or 2D and color data - bool mIsTexture; - bool mTextureGenMipmap; - uint32_t mTextureLOD; uint32_t mTextureID; // Is this a legal structure to be used as a vertex source. // Initially this will require 1D and x(yzw). Additional per element data // is allowed. - bool mIsVertexBuffer; uint32_t mBufferID; bool mUploadDefered; private: void init(Context *rsc, const Type *); + void upload2DTexture(bool isFirstUpload); + void update2DTexture(const void *ptr, uint32_t xoff, uint32_t yoff, + uint32_t lod, RsAllocationCubemapFace face, uint32_t w, uint32_t h); + + void allocScriptMemory(); + void freeScriptMemory(); }; diff --git a/libs/rs/rsAnimation.cpp b/libs/rs/rsAnimation.cpp new file mode 100644 index 000000000000..48b4f029c6d0 --- /dev/null +++ b/libs/rs/rsAnimation.cpp @@ -0,0 +1,140 @@ +/* + * 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. + */ + +#include "rsContext.h" +#include "rsAnimation.h" + + +using namespace android; +using namespace android::renderscript; + +void Animation::serialize(OStream *stream) const { +} + +Animation *Animation::createFromStream(Context *rsc, IStream *stream) { + return NULL; +} + +/* +Animation::Animation(Context *rsc) : ObjectBase(rsc) +{ + mAllocFile = __FILE__; + mAllocLine = __LINE__; + + mValuesInput = NULL; + mValuesOutput = NULL; + mValueCount = 0; + mInterpolation = RS_ANIMATION_INTERPOLATION_STEP; + mEdgePre = RS_ANIMATION_EDGE_UNDEFINED; + mEdgePost = RS_ANIMATION_EDGE_UNDEFINED; + mInputMin = 0; + mInputMax = 0; +} + +Animation * Animation::create(Context *rsc, + const float *inValues, const float *outValues, + uint32_t valueCount, RsAnimationInterpolation interp, + RsAnimationEdge pre, RsAnimationEdge post) +{ + if (valueCount < 2) { + rsc->setError(RS_ERROR_BAD_VALUE, "Animations require more than 2 values."); + return NULL; + } + Animation *a = new Animation(rsc); + if (!a) { + rsc->setError(RS_ERROR_OUT_OF_MEMORY); + return NULL; + } + + float *vin = (float *)malloc(valueCount * sizeof(float)); + float *vout = (float *)malloc(valueCount * sizeof(float)); + a->mValuesInput = vin; + a->mValuesOutput = vout; + if (a->mValuesInput == NULL || a->mValuesOutput == NULL) { + delete a; + rsc->setError(RS_ERROR_OUT_OF_MEMORY); + return NULL; + } + + a->mEdgePre = pre; + a->mEdgePost = post; + a->mInterpolation = interp; + a->mValueCount = valueCount; + + memcpy(vin, inValues, valueCount * sizeof(float)); + memcpy(vout, outValues, valueCount * sizeof(float)); + a->mInputMin = inValues[0]; + a->mInputMax = inValues[0]; + + bool needSort = false; + for (uint32_t ct=1; ct < valueCount; ct++) { + if (a->mInputMin > vin[ct]) { + needSort = true; + a->mInputMin = vin[ct]; + } + if (a->mInputMax < vin[ct]) { + a->mInputMax = vin[ct]; + } else { + needSort = true; + } + } + + while (1) { + bool changed = false; + for (uint32_t ct=1; ct < valueCount; ct++) { + if (vin[ct-1] > vin[ct]) { + float t = vin[ct-1]; + vin[ct-1] = vin[ct]; + vin[ct] = t; + t = vout[ct-1]; + vout[ct-1] = vout[ct]; + vout[ct] = t; + changed = true; + } + } + if (!changed) break; + } + + return a; +} +*/ + + +///////////////////////////////////////// +// + +namespace android { +namespace renderscript { + +RsAnimation rsi_AnimationCreate(Context *rsc, + const float *inValues, + const float *outValues, + uint32_t valueCount, + RsAnimationInterpolation interp, + RsAnimationEdge pre, + RsAnimationEdge post) { + //LOGE("rsi_ElementCreate %i %i %i %i", dt, dk, norm, vecSize); + Animation *a = NULL;//Animation::create(rsc, inValues, outValues, valueCount, interp, pre, post); + if (a != NULL) { + a->incUserRef(); + } + return (RsAnimation)a; +} + + +} +} + diff --git a/libs/rs/rsAnimation.h b/libs/rs/rsAnimation.h new file mode 100644 index 000000000000..bff8d6f4c57f --- /dev/null +++ b/libs/rs/rsAnimation.h @@ -0,0 +1,67 @@ +/* + * 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. + */ + +#ifndef ANDROID_RS_ANIMATION_H +#define ANDROID_RS_ANIMATION_H + +#include "rsUtils.h" +#include "rsObjectBase.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + +class Animation : public ObjectBase { +public: + ~Animation(); + + static Animation * create(Context *rsc, + const float *inValues, const float *outValues, + uint32_t valueCount, RsAnimationInterpolation, + RsAnimationEdge pre, RsAnimationEdge post); + + float eval(float) const; + + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_ANIMATION; } + static Animation *createFromStream(Context *rsc, IStream *stream); + +protected: + Animation(Context *rsc); + + + + float evalInRange(float) const; + + + + const float *mValuesInput; + const float *mValuesOutput; + uint32_t mValueCount; + RsAnimationInterpolation mInterpolation; + RsAnimationEdge mEdgePre; + RsAnimationEdge mEdgePost; + + // derived + float mInputMin; + float mInputMax; +}; + +} +} +#endif //ANDROID_STRUCTURED_ELEMENT_H + diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp index 15a56f7fe48e..4c4987aaf7a4 100644 --- a/libs/rs/rsComponent.cpp +++ b/libs/rs/rsComponent.cpp @@ -16,22 +16,21 @@ #include "rsComponent.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> +#endif using namespace android; using namespace android::renderscript; -Component::Component() -{ +Component::Component() { set(RS_TYPE_NONE, RS_KIND_USER, false, 1); } -Component::~Component() -{ +Component::~Component() { } -void Component::set(RsDataType dt, RsDataKind dk, bool norm, uint32_t vecSize) -{ +void Component::set(RsDataType dt, RsDataKind dk, bool norm, uint32_t vecSize) { mType = dt; mKind = dk; mNormalized = norm; @@ -44,7 +43,7 @@ void Component::set(RsDataType dt, RsDataKind dk, bool norm, uint32_t vecSize) mIsSigned = false; mIsPixel = false; - switch(mKind) { + switch (mKind) { case RS_KIND_PIXEL_L: case RS_KIND_PIXEL_A: mIsPixel = true; @@ -70,7 +69,7 @@ void Component::set(RsDataType dt, RsDataKind dk, bool norm, uint32_t vecSize) break; } - switch(mType) { + switch (mType) { case RS_TYPE_NONE: return; case RS_TYPE_UNSIGNED_5_6_5: @@ -91,6 +90,26 @@ void Component::set(RsDataType dt, RsDataKind dk, bool norm, uint32_t vecSize) mNormalized = true; rsAssert(mKind == RS_KIND_PIXEL_RGBA); return; + + case RS_TYPE_MATRIX_4X4: + mTypeBits = 16 * 32; + rsAssert(mVectorSize == 1); + rsAssert(mNormalized == false); + rsAssert(mKind == RS_KIND_USER); + break; + case RS_TYPE_MATRIX_3X3: + mTypeBits = 9 * 32; + rsAssert(mVectorSize == 1); + rsAssert(mNormalized == false); + rsAssert(mKind == RS_KIND_USER); + break; + case RS_TYPE_MATRIX_2X2: + mTypeBits = 4 * 32; + rsAssert(mVectorSize == 1); + rsAssert(mNormalized == false); + rsAssert(mKind == RS_KIND_USER); + break; + case RS_TYPE_ELEMENT: case RS_TYPE_TYPE: case RS_TYPE_ALLOCATION: @@ -148,16 +167,21 @@ void Component::set(RsDataType dt, RsDataKind dk, bool norm, uint32_t vecSize) case RS_TYPE_UNSIGNED_64: mTypeBits = 64; break; + + case RS_TYPE_BOOLEAN: + mTypeBits = 8; + break; } mBits = mTypeBits * mVectorSize; } +bool Component::isReference() const { + return (mType >= RS_TYPE_ELEMENT); +} - - -uint32_t Component::getGLType() const -{ +uint32_t Component::getGLType() const { +#ifndef ANDROID_RS_SERIALIZE switch (mType) { case RS_TYPE_UNSIGNED_5_6_5: return GL_UNSIGNED_SHORT_5_6_5; case RS_TYPE_UNSIGNED_5_5_5_1: return GL_UNSIGNED_SHORT_5_5_5_1; @@ -171,12 +195,12 @@ uint32_t Component::getGLType() const case RS_TYPE_SIGNED_16: return GL_SHORT; default: break; } - +#endif //ANDROID_RS_SERIALIZE return 0; } -uint32_t Component::getGLFormat() const -{ +uint32_t Component::getGLFormat() const { +#ifndef ANDROID_RS_SERIALIZE switch (mKind) { case RS_KIND_PIXEL_L: return GL_LUMINANCE; case RS_KIND_PIXEL_A: return GL_ALPHA; @@ -185,89 +209,13 @@ uint32_t Component::getGLFormat() const case RS_KIND_PIXEL_RGBA: return GL_RGBA; default: break; } +#endif //ANDROID_RS_SERIALIZE return 0; } -static const char * gCTypeStrings[] = { - 0, - 0,//"F16", - "float", - "double", - "char", - "short", - "int", - 0,//"S64", - "char",//U8", - "short",//U16", - "int",//U32", - 0,//"U64", - 0,//"UP_565", - 0,//"UP_5551", - 0,//"UP_4444", - 0,//"ELEMENT", - 0,//"TYPE", - 0,//"ALLOCATION", - 0,//"SAMPLER", - 0,//"SCRIPT", - 0,//"MESH", - 0,//"PROGRAM_FRAGMENT", - 0,//"PROGRAM_VERTEX", - 0,//"PROGRAM_RASTER", - 0,//"PROGRAM_STORE", -}; - -static const char * gCVecTypeStrings[] = { - 0, - 0,//"F16", - "vecF32", - "vecF64", - "vecI8", - "vecI16", - "vecI32", - 0,//"S64", - "vecU8",//U8", - "vecU16",//U16", - "vecU32",//U32", - 0,//"U64", - 0,//"UP_565", - 0,//"UP_5551", - 0,//"UP_4444", - 0,//"ELEMENT", - 0,//"TYPE", - 0,//"ALLOCATION", - 0,//"SAMPLER", - 0,//"SCRIPT", - 0,//"MESH", - 0,//"PROGRAM_FRAGMENT", - 0,//"PROGRAM_VERTEX", - 0,//"PROGRAM_RASTER", - 0,//"PROGRAM_STORE", -}; - -String8 Component::getCType() const -{ - char buf[64]; - if (mVectorSize == 1) { - return String8(gCTypeStrings[mType]); - } - - // Yuck, acc WAR - // Appears to have problems packing chars - if (mVectorSize == 4 && mType == RS_TYPE_UNSIGNED_8) { - return String8("int"); - } - - - String8 s(gCVecTypeStrings[mType]); - sprintf(buf, "_%i_t", mVectorSize); - s.append(buf); - return s; -} - -String8 Component::getGLSLType() const -{ +String8 Component::getGLSLType() const { if (mType == RS_TYPE_SIGNED_32) { - switch(mVectorSize) { + switch (mVectorSize) { case 1: return String8("int"); case 2: return String8("ivec2"); case 3: return String8("ivec3"); @@ -275,17 +223,26 @@ String8 Component::getGLSLType() const } } if (mType == RS_TYPE_FLOAT_32) { - switch(mVectorSize) { + switch (mVectorSize) { case 1: return String8("float"); case 2: return String8("vec2"); case 3: return String8("vec3"); case 4: return String8("vec4"); } } + if ((mType == RS_TYPE_MATRIX_4X4) && (mVectorSize == 1)) { + return String8("mat4"); + } + if ((mType == RS_TYPE_MATRIX_3X3) && (mVectorSize == 1)) { + return String8("mat3"); + } + if ((mType == RS_TYPE_MATRIX_2X2) && (mVectorSize == 1)) { + return String8("mat2"); + } return String8(); } -static const char * gTypeStrings[] = { +static const char * gTypeBasicStrings[] = { "NONE", "F16", "F32", @@ -298,9 +255,16 @@ static const char * gTypeStrings[] = { "U16", "U32", "U64", + "BOOLEAN", "UP_565", "UP_5551", "UP_4444", + "MATRIX_4X4", + "MATRIX_3X3", + "MATRIX_2X2", +}; + +static const char * gTypeObjStrings[] = { "ELEMENT", "TYPE", "ALLOCATION", @@ -328,10 +292,33 @@ static const char * gKindStrings[] = { "PIXEL_RGBA", }; -void Component::dumpLOGV(const char *prefix) const -{ - LOGV("%s Component: %s, %s, vectorSize=%i, bits=%i", - prefix, gTypeStrings[mType], gKindStrings[mKind], mVectorSize, mBits); +void Component::dumpLOGV(const char *prefix) const { + if (mType >= RS_TYPE_ELEMENT) { + LOGV("%s Component: %s, %s, vectorSize=%i, bits=%i", + prefix, gTypeObjStrings[mType - RS_TYPE_ELEMENT], gKindStrings[mKind], mVectorSize, mBits); + } else { + LOGV("%s Component: %s, %s, vectorSize=%i, bits=%i", + prefix, gTypeBasicStrings[mType], gKindStrings[mKind], mVectorSize, mBits); + } +} + +void Component::serialize(OStream *stream) const { + stream->addU8((uint8_t)mType); + stream->addU8((uint8_t)mKind); + stream->addU8((uint8_t)(mNormalized ? 1 : 0)); + stream->addU32(mVectorSize); } +void Component::loadFromStream(IStream *stream) { + mType = (RsDataType)stream->loadU8(); + mKind = (RsDataKind)stream->loadU8(); + uint8_t temp = stream->loadU8(); + mNormalized = temp != 0; + mVectorSize = stream->loadU32(); + + set(mType, mKind, mNormalized, mVectorSize); +} + + + diff --git a/libs/rs/rsComponent.h b/libs/rs/rsComponent.h index 71de32435701..1bb4ff484071 100644 --- a/libs/rs/rsComponent.h +++ b/libs/rs/rsComponent.h @@ -25,8 +25,7 @@ namespace renderscript { // An element is a group of Components that occupies one cell in a structure. -class Component -{ +class Component { public: Component(); ~Component(); @@ -35,7 +34,6 @@ public: uint32_t getGLType() const; uint32_t getGLFormat() const; - String8 getCType() const; String8 getGLSLType() const; void dumpLOGV(const char *prefix) const; @@ -48,6 +46,12 @@ public: bool getIsSigned() const {return mIsSigned;} uint32_t getBits() const {return mBits;} + // Helpers for reading / writing this class out + void serialize(OStream *stream) const; + void loadFromStream(IStream *stream); + + bool isReference() const; + protected: RsDataType mType; RsDataKind mKind; diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 90a2c79267ae..c63d183c7b59 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -18,21 +18,24 @@ #include "rsContext.h" #include "rsThreadIO.h" #include <ui/FramebufferNativeWindow.h> +#include <ui/PixelFormat.h> #include <ui/EGLUtils.h> #include <ui/egl/android_natives.h> #include <sys/types.h> #include <sys/resource.h> +#include <sched.h> #include <cutils/properties.h> -#include <EGL/eglext.h> #include <GLES/gl.h> #include <GLES/glext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <cutils/sched_policy.h> +#include <sys/syscall.h> +#include <string.h> using namespace android; using namespace android::renderscript; @@ -41,6 +44,7 @@ pthread_key_t Context::gThreadTLSKey = 0; uint32_t Context::gThreadTLSKeyCount = 0; uint32_t Context::gGLContextCount = 0; pthread_mutex_t Context::gInitMutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t Context::gLibMutex = PTHREAD_MUTEX_INITIALIZER; static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) { if (returnVal != EGL_TRUE) { @@ -54,23 +58,64 @@ static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) { } } -void Context::initEGL(bool useGL2) -{ +void printEGLConfiguration(EGLDisplay dpy, EGLConfig config) { + +#define X(VAL) {VAL, #VAL} + struct {EGLint attribute; const char* name;} names[] = { + X(EGL_BUFFER_SIZE), + X(EGL_ALPHA_SIZE), + X(EGL_BLUE_SIZE), + X(EGL_GREEN_SIZE), + X(EGL_RED_SIZE), + X(EGL_DEPTH_SIZE), + X(EGL_STENCIL_SIZE), + X(EGL_CONFIG_CAVEAT), + X(EGL_CONFIG_ID), + X(EGL_LEVEL), + X(EGL_MAX_PBUFFER_HEIGHT), + X(EGL_MAX_PBUFFER_PIXELS), + X(EGL_MAX_PBUFFER_WIDTH), + X(EGL_NATIVE_RENDERABLE), + X(EGL_NATIVE_VISUAL_ID), + X(EGL_NATIVE_VISUAL_TYPE), + X(EGL_SAMPLES), + X(EGL_SAMPLE_BUFFERS), + X(EGL_SURFACE_TYPE), + X(EGL_TRANSPARENT_TYPE), + X(EGL_TRANSPARENT_RED_VALUE), + X(EGL_TRANSPARENT_GREEN_VALUE), + X(EGL_TRANSPARENT_BLUE_VALUE), + X(EGL_BIND_TO_TEXTURE_RGB), + X(EGL_BIND_TO_TEXTURE_RGBA), + X(EGL_MIN_SWAP_INTERVAL), + X(EGL_MAX_SWAP_INTERVAL), + X(EGL_LUMINANCE_SIZE), + X(EGL_ALPHA_MASK_SIZE), + X(EGL_COLOR_BUFFER_TYPE), + X(EGL_RENDERABLE_TYPE), + X(EGL_CONFORMANT), + }; +#undef X + + for (size_t j = 0; j < sizeof(names) / sizeof(names[0]); j++) { + EGLint value = -1; + EGLint returnVal = eglGetConfigAttrib(dpy, config, names[j].attribute, &value); + EGLint error = eglGetError(); + if (returnVal && error == EGL_SUCCESS) { + LOGV(" %s: %d (0x%x)", names[j].name, value, value); + } + } +} + + +bool Context::initGLThread() { + pthread_mutex_lock(&gInitMutex); + LOGV("initGLThread start %p", this); + mEGL.mNumConfigs = -1; EGLint configAttribs[128]; EGLint *configAttribsPtr = configAttribs; - EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE, GL_NONE, EGL_NONE }; - -#ifdef HAS_CONTEXT_PRIORITY -#ifdef EGL_IMG_context_priority -#warning "using EGL_IMG_context_priority" - if (mThreadPriority > 0) { - context_attribs2[2] = EGL_CONTEXT_PRIORITY_LEVEL_IMG; - context_attribs2[3] = EGL_CONTEXT_PRIORITY_LOW_IMG; - } -#endif -#endif + EGLint context_attribs2[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; memset(configAttribs, 0, sizeof(configAttribs)); @@ -78,15 +123,13 @@ void Context::initEGL(bool useGL2) configAttribsPtr[1] = EGL_WINDOW_BIT; configAttribsPtr += 2; - if (useGL2) { - configAttribsPtr[0] = EGL_RENDERABLE_TYPE; - configAttribsPtr[1] = EGL_OPENGL_ES2_BIT; - configAttribsPtr += 2; - } + configAttribsPtr[0] = EGL_RENDERABLE_TYPE; + configAttribsPtr[1] = EGL_OPENGL_ES2_BIT; + configAttribsPtr += 2; - if (mUseDepth) { + if (mUserSurfaceConfig.depthMin > 0) { configAttribsPtr[0] = EGL_DEPTH_SIZE; - configAttribsPtr[1] = 16; + configAttribsPtr[1] = mUserSurfaceConfig.depthMin; configAttribsPtr += 2; } @@ -99,38 +142,125 @@ void Context::initEGL(bool useGL2) configAttribsPtr[0] = EGL_NONE; rsAssert(configAttribsPtr < (configAttribs + (sizeof(configAttribs) / sizeof(EGLint)))); - LOGV("initEGL start"); + LOGV("%p initEGL start", this); mEGL.mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); checkEglError("eglGetDisplay"); eglInitialize(mEGL.mDisplay, &mEGL.mMajorVersion, &mEGL.mMinorVersion); checkEglError("eglInitialize"); - status_t err = EGLUtils::selectConfigForNativeWindow(mEGL.mDisplay, configAttribs, mWndSurface, &mEGL.mConfig); - if (err) { - LOGE("couldn't find an EGLConfig matching the screen format\n"); +#if 1 + PixelFormat pf = PIXEL_FORMAT_RGBA_8888; + if (mUserSurfaceConfig.alphaMin == 0) { + pf = PIXEL_FORMAT_RGBX_8888; } - //eglChooseConfig(mEGL.mDisplay, configAttribs, &mEGL.mConfig, 1, &mEGL.mNumConfigs); - - if (useGL2) { - mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, EGL_NO_CONTEXT, context_attribs2); - } else { - mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, EGL_NO_CONTEXT, NULL); + status_t err = EGLUtils::selectConfigForPixelFormat(mEGL.mDisplay, configAttribs, pf, &mEGL.mConfig); + if (err) { + LOGE("%p, couldn't find an EGLConfig matching the screen format\n", this); + } + if (props.mLogVisual) { + printEGLConfiguration(mEGL.mDisplay, mEGL.mConfig); } +#else + eglChooseConfig(mEGL.mDisplay, configAttribs, &mEGL.mConfig, 1, &mEGL.mNumConfigs); +#endif + + mEGL.mContext = eglCreateContext(mEGL.mDisplay, mEGL.mConfig, EGL_NO_CONTEXT, context_attribs2); checkEglError("eglCreateContext"); if (mEGL.mContext == EGL_NO_CONTEXT) { - LOGE("eglCreateContext returned EGL_NO_CONTEXT"); + pthread_mutex_unlock(&gInitMutex); + LOGE("%p, eglCreateContext returned EGL_NO_CONTEXT", this); + return false; } gGLContextCount++; + + + EGLint pbuffer_attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; + mEGL.mSurfaceDefault = eglCreatePbufferSurface(mEGL.mDisplay, mEGL.mConfig, pbuffer_attribs); + checkEglError("eglCreatePbufferSurface"); + if (mEGL.mSurfaceDefault == EGL_NO_SURFACE) { + LOGE("eglCreatePbufferSurface returned EGL_NO_SURFACE"); + pthread_mutex_unlock(&gInitMutex); + deinitEGL(); + return false; + } + + EGLBoolean ret = eglMakeCurrent(mEGL.mDisplay, mEGL.mSurfaceDefault, mEGL.mSurfaceDefault, mEGL.mContext); + if (ret == EGL_FALSE) { + LOGE("eglMakeCurrent returned EGL_FALSE"); + checkEglError("eglMakeCurrent", ret); + pthread_mutex_unlock(&gInitMutex); + deinitEGL(); + return false; + } + + mGL.mVersion = glGetString(GL_VERSION); + mGL.mVendor = glGetString(GL_VENDOR); + mGL.mRenderer = glGetString(GL_RENDERER); + mGL.mExtensions = glGetString(GL_EXTENSIONS); + + //LOGV("EGL Version %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion); + //LOGV("GL Version %s", mGL.mVersion); + //LOGV("GL Vendor %s", mGL.mVendor); + //LOGV("GL Renderer %s", mGL.mRenderer); + //LOGV("GL Extensions %s", mGL.mExtensions); + + const char *verptr = NULL; + if (strlen((const char *)mGL.mVersion) > 9) { + if (!memcmp(mGL.mVersion, "OpenGL ES-CM", 12)) { + verptr = (const char *)mGL.mVersion + 12; + } + if (!memcmp(mGL.mVersion, "OpenGL ES ", 10)) { + verptr = (const char *)mGL.mVersion + 9; + } + } + + if (!verptr) { + LOGE("Error, OpenGL ES Lite not supported"); + pthread_mutex_unlock(&gInitMutex); + deinitEGL(); + return false; + } else { + sscanf(verptr, " %i.%i", &mGL.mMajorVersion, &mGL.mMinorVersion); + } + + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &mGL.mMaxVertexAttribs); + glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &mGL.mMaxVertexUniformVectors); + glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGL.mMaxVertexTextureUnits); + + glGetIntegerv(GL_MAX_VARYING_VECTORS, &mGL.mMaxVaryingVectors); + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGL.mMaxTextureImageUnits); + + glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mGL.mMaxFragmentTextureImageUnits); + glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGL.mMaxFragmentUniformVectors); + + mGL.OES_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_OES_texture_npot"); + mGL.GL_IMG_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_IMG_texture_npot"); + mGL.GL_NV_texture_npot_2D_mipmap = NULL != strstr((const char *)mGL.mExtensions, "GL_NV_texture_npot_2D_mipmap"); + mGL.EXT_texture_max_aniso = 1.0f; + bool hasAniso = NULL != strstr((const char *)mGL.mExtensions, "GL_EXT_texture_filter_anisotropic"); + if (hasAniso) { + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &mGL.EXT_texture_max_aniso); + } + + LOGV("initGLThread end %p", this); + pthread_mutex_unlock(&gInitMutex); + return true; } -void Context::deinitEGL() -{ - LOGV("deinitEGL"); - setSurface(0, 0, NULL); - eglDestroyContext(mEGL.mDisplay, mEGL.mContext); - checkEglError("eglDestroyContext"); +void Context::deinitEGL() { + LOGV("%p, deinitEGL", this); + + if (mEGL.mContext != EGL_NO_CONTEXT) { + eglMakeCurrent(mEGL.mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(mEGL.mDisplay, mEGL.mSurfaceDefault); + if (mEGL.mSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEGL.mDisplay, mEGL.mSurface); + } + eglDestroyContext(mEGL.mDisplay, mEGL.mContext); + checkEglError("eglDestroyContext"); + } gGLContextCount--; if (!gGLContextCount) { @@ -138,105 +268,116 @@ void Context::deinitEGL() } } +Context::PushState::PushState(Context *con) { + mRsc = con; + if (con->mIsGraphicsContext) { + mFragment.set(con->getProgramFragment()); + mVertex.set(con->getProgramVertex()); + mStore.set(con->getProgramStore()); + mRaster.set(con->getProgramRaster()); + mFont.set(con->getFont()); + } +} + +Context::PushState::~PushState() { + if (mRsc->mIsGraphicsContext) { + mRsc->setProgramFragment(mFragment.get()); + mRsc->setProgramVertex(mVertex.get()); + mRsc->setProgramStore(mStore.get()); + mRsc->setProgramRaster(mRaster.get()); + mRsc->setFont(mFont.get()); + } +} -uint32_t Context::runScript(Script *s, uint32_t launchID) -{ - ObjectBaseRef<ProgramFragment> frag(mFragment); - ObjectBaseRef<ProgramVertex> vtx(mVertex); - ObjectBaseRef<ProgramFragmentStore> store(mFragmentStore); - ObjectBaseRef<ProgramRaster> raster(mRaster); - uint32_t ret = s->run(this, launchID); +uint32_t Context::runScript(Script *s) { + PushState(this); - mFragment.set(frag); - mVertex.set(vtx); - mFragmentStore.set(store); - mRaster.set(raster); + uint32_t ret = s->run(this); return ret; } -void Context::checkError(const char *msg) const -{ +void Context::checkError(const char *msg, bool isFatal) const { + GLenum err = glGetError(); if (err != GL_NO_ERROR) { - LOGE("GL Error, 0x%x, from %s", err, msg); + char buf[1024]; + snprintf(buf, sizeof(buf), "GL Error = 0x%08x, from: %s", err, msg); + + if (isFatal) { + setError(RS_ERROR_FATAL_DRIVER, buf); + } else { + switch (err) { + case GL_OUT_OF_MEMORY: + setError(RS_ERROR_OUT_OF_MEMORY, buf); + break; + default: + setError(RS_ERROR_DRIVER, buf); + break; + } + } + + LOGE("%p, %s", this, buf); } } -uint32_t Context::runRootScript() -{ - timerSet(RS_TIMER_CLEAR_SWAP); - rsAssert(mRootScript->mEnviroment.mIsRoot); - - eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_WIDTH, &mEGL.mWidth); - eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_HEIGHT, &mEGL.mHeight); - glViewport(0, 0, mEGL.mWidth, mEGL.mHeight); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - glClearColor(mRootScript->mEnviroment.mClearColor[0], - mRootScript->mEnviroment.mClearColor[1], - mRootScript->mEnviroment.mClearColor[2], - mRootScript->mEnviroment.mClearColor[3]); - if (mUseDepth) { - glDepthMask(GL_TRUE); - glClearDepthf(mRootScript->mEnviroment.mClearDepth); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } else { - glClear(GL_COLOR_BUFFER_BIT); - } +uint32_t Context::runRootScript() { + glViewport(0, 0, mWidth, mHeight); timerSet(RS_TIMER_SCRIPT); mStateFragmentStore.mLast.clear(); - uint32_t ret = runScript(mRootScript.get(), 0); + uint32_t ret = runScript(mRootScript.get()); checkError("runRootScript"); - if (mError != RS_ERROR_NONE) { - // If we have an error condition we stop rendering until - // somthing changes that might fix it. - ret = 0; - } return ret; } -uint64_t Context::getTime() const -{ +uint64_t Context::getTime() const { struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000); } -void Context::timerReset() -{ +void Context::timerReset() { for (int ct=0; ct < _RS_TIMER_TOTAL; ct++) { mTimers[ct] = 0; } } -void Context::timerInit() -{ +void Context::timerInit() { mTimeLast = getTime(); mTimeFrame = mTimeLast; mTimeLastFrame = mTimeLast; mTimerActive = RS_TIMER_INTERNAL; + mAverageFPSFrameCount = 0; + mAverageFPSStartTime = mTimeLast; + mAverageFPS = 0; timerReset(); } -void Context::timerFrame() -{ +void Context::timerFrame() { mTimeLastFrame = mTimeFrame; mTimeFrame = getTime(); + // Update average fps + const uint64_t averageFramerateInterval = 1000 * 1000000; + mAverageFPSFrameCount ++; + uint64_t inverval = mTimeFrame - mAverageFPSStartTime; + if (inverval >= averageFramerateInterval) { + inverval = inverval / 1000000; + mAverageFPS = (mAverageFPSFrameCount * 1000) / inverval; + mAverageFPSFrameCount = 0; + mAverageFPSStartTime = mTimeFrame; + } } -void Context::timerSet(Timers tm) -{ +void Context::timerSet(Timers tm) { uint64_t last = mTimeLast; mTimeLast = getTime(); mTimers[mTimerActive] += mTimeLast - last; mTimerActive = tm; } -void Context::timerPrint() -{ +void Context::timerPrint() { double total = 0; for (int ct = 0; ct < _RS_TIMER_TOTAL; ct++) { total += mTimers[ct]; @@ -248,46 +389,57 @@ void Context::timerPrint() if (props.mLogTimes) { - LOGV("RS: Frame (%i), Script %2.1f (%i), Clear & Swap %2.1f (%i), Idle %2.1f (%lli), Internal %2.1f (%lli)", + LOGV("RS: Frame (%i), Script %2.1f%% (%i), Swap %2.1f%% (%i), Idle %2.1f%% (%lli), Internal %2.1f%% (%lli), Avg fps: %u", mTimeMSLastFrame, 100.0 * mTimers[RS_TIMER_SCRIPT] / total, mTimeMSLastScript, 100.0 * mTimers[RS_TIMER_CLEAR_SWAP] / total, mTimeMSLastSwap, 100.0 * mTimers[RS_TIMER_IDLE] / total, mTimers[RS_TIMER_IDLE] / 1000000, - 100.0 * mTimers[RS_TIMER_INTERNAL] / total, mTimers[RS_TIMER_INTERNAL] / 1000000); + 100.0 * mTimers[RS_TIMER_INTERNAL] / total, mTimers[RS_TIMER_INTERNAL] / 1000000, + mAverageFPS); } } -bool Context::setupCheck() -{ - if (checkVersion2_0()) { - if (!mShaderCache.lookup(this, mVertex.get(), mFragment.get())) { - LOGE("Context::setupCheck() 1 fail"); - return false; - } - - mFragmentStore->setupGL2(this, &mStateFragmentStore); - mFragment->setupGL2(this, &mStateFragment, &mShaderCache); - mRaster->setupGL2(this, &mStateRaster); - mVertex->setupGL2(this, &mStateVertex, &mShaderCache); - - } else { - mFragmentStore->setupGL(this, &mStateFragmentStore); - mFragment->setupGL(this, &mStateFragment); - mRaster->setupGL(this, &mStateRaster); - mVertex->setupGL(this, &mStateVertex); +bool Context::setupCheck() { + if (!mShaderCache.lookup(this, mVertex.get(), mFragment.get())) { + LOGE("Context::setupCheck() 1 fail"); + return false; } + + mFragmentStore->setupGL2(this, &mStateFragmentStore); + mFragment->setupGL2(this, &mStateFragment, &mShaderCache); + mRaster->setupGL2(this, &mStateRaster); + mVertex->setupGL2(this, &mStateVertex, &mShaderCache); return true; } -static bool getProp(const char *str) -{ +void Context::setupProgramStore() { + mFragmentStore->setupGL2(this, &mStateFragmentStore); +} + +static bool getProp(const char *str) { char buf[PROPERTY_VALUE_MAX]; property_get(str, buf, "0"); return 0 != strcmp(buf, "0"); } -void * Context::threadProc(void *vrsc) -{ +void Context::displayDebugStats() { + char buffer[128]; + sprintf(buffer, "Avg fps %u, Frame %i ms, Script %i ms", mAverageFPS, mTimeMSLastFrame, mTimeMSLastScript); + float oldR, oldG, oldB, oldA; + mStateFont.getFontColor(&oldR, &oldG, &oldB, &oldA); + uint32_t bufferLen = strlen(buffer); + + float shadowCol = 0.1f; + mStateFont.setFontColor(shadowCol, shadowCol, shadowCol, 1.0f); + mStateFont.renderText(buffer, bufferLen, 5, getHeight() - 6); + + mStateFont.setFontColor(1.0f, 0.7f, 0.0f, 1.0f); + mStateFont.renderText(buffer, bufferLen, 4, getHeight() - 7); + + mStateFont.setFontColor(oldR, oldG, oldB, oldA); +} + +void * Context::threadProc(void *vrsc) { Context *rsc = static_cast<Context *>(vrsc); rsc->mNativeThreadId = gettid(); @@ -298,28 +450,39 @@ void * Context::threadProc(void *vrsc) rsc->props.mLogScripts = getProp("debug.rs.script"); rsc->props.mLogObjects = getProp("debug.rs.object"); rsc->props.mLogShaders = getProp("debug.rs.shader"); + rsc->props.mLogShadersAttr = getProp("debug.rs.shader.attributes"); + rsc->props.mLogShadersUniforms = getProp("debug.rs.shader.uniforms"); + rsc->props.mLogVisual = getProp("debug.rs.visual"); - ScriptTLSStruct *tlsStruct = new ScriptTLSStruct; - if (!tlsStruct) { + rsc->mTlsStruct = new ScriptTLSStruct; + if (!rsc->mTlsStruct) { LOGE("Error allocating tls storage"); + rsc->setError(RS_ERROR_OUT_OF_MEMORY, "Failed allocation for TLS"); return NULL; } - tlsStruct->mContext = rsc; - tlsStruct->mScript = NULL; - int status = pthread_setspecific(rsc->gThreadTLSKey, tlsStruct); + rsc->mTlsStruct->mContext = rsc; + rsc->mTlsStruct->mScript = NULL; + int status = pthread_setspecific(rsc->gThreadTLSKey, rsc->mTlsStruct); if (status) { LOGE("pthread_setspecific %i", status); } + if (!rsc->initGLThread()) { + rsc->setError(RS_ERROR_OUT_OF_MEMORY, "Failed initializing GL"); + return NULL; + } + if (rsc->mIsGraphicsContext) { - rsc->mStateRaster.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); - rsc->setRaster(NULL); - rsc->mStateVertex.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); - rsc->setVertex(NULL); - rsc->mStateFragment.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); - rsc->setFragment(NULL); - rsc->mStateFragmentStore.init(rsc, rsc->mEGL.mWidth, rsc->mEGL.mHeight); - rsc->setFragmentStore(NULL); + rsc->mStateRaster.init(rsc); + rsc->setProgramRaster(NULL); + rsc->mStateVertex.init(rsc); + rsc->setProgramVertex(NULL); + rsc->mStateFragment.init(rsc); + rsc->setProgramFragment(NULL); + rsc->mStateFragmentStore.init(rsc); + rsc->setProgramStore(NULL); + rsc->mStateFont.init(rsc); + rsc->setFont(NULL); rsc->mStateVertexArray.init(rsc); } @@ -333,6 +496,11 @@ void * Context::threadProc(void *vrsc) uint32_t targetTime = 0; if (mDraw && rsc->mIsGraphicsContext) { targetTime = rsc->runRootScript(); + + if (rsc->props.mLogVisual) { + rsc->displayDebugStats(); + } + mDraw = targetTime && !rsc->mPaused; rsc->timerSet(RS_TIMER_CLEAR_SWAP); eglSwapBuffers(rsc->mEGL.mDisplay, rsc->mEGL.mSurface); @@ -341,10 +509,7 @@ void * Context::threadProc(void *vrsc) rsc->timerPrint(); rsc->timerReset(); } - if (rsc->mObjDestroy.mNeedToEmpty) { - rsc->objDestroyOOBRun(); - } - if (rsc->mThreadPriority > 0 && targetTime) { + if (targetTime > 1) { int32_t t = (targetTime - (int32_t)(rsc->mTimeMSLastScript + rsc->mTimeMSLastSwap)) * 1000; if (t > 0) { usleep(t); @@ -352,35 +517,91 @@ void * Context::threadProc(void *vrsc) } } - LOGV("RS Thread exiting"); - if (rsc->mIsGraphicsContext) { - rsc->mRaster.clear(); - rsc->mFragment.clear(); - rsc->mVertex.clear(); - rsc->mFragmentStore.clear(); - rsc->mRootScript.clear(); - rsc->mStateRaster.deinit(rsc); - rsc->mStateVertex.deinit(rsc); - rsc->mStateFragment.deinit(rsc); - rsc->mStateFragmentStore.deinit(rsc); - } - ObjectBase::zeroAllUserRef(rsc); - - rsc->mObjDestroy.mNeedToEmpty = true; - rsc->objDestroyOOBRun(); + LOGV("%p, RS Thread exiting", rsc); if (rsc->mIsGraphicsContext) { pthread_mutex_lock(&gInitMutex); rsc->deinitEGL(); pthread_mutex_unlock(&gInitMutex); } + delete rsc->mTlsStruct; - LOGV("RS Thread exited"); + LOGV("%p, RS Thread exited", rsc); return NULL; } -void Context::setPriority(int32_t p) -{ +void Context::destroyWorkerThreadResources() { + //LOGV("destroyWorkerThreadResources 1"); + ObjectBase::zeroAllUserRef(this); + if (mIsGraphicsContext) { + mRaster.clear(); + mFragment.clear(); + mVertex.clear(); + mFragmentStore.clear(); + mFont.clear(); + mRootScript.clear(); + mStateRaster.deinit(this); + mStateVertex.deinit(this); + mStateFragment.deinit(this); + mStateFragmentStore.deinit(this); + mStateFont.deinit(this); + mShaderCache.cleanupAll(); + } + //LOGV("destroyWorkerThreadResources 2"); + mExit = true; +} + +void * Context::helperThreadProc(void *vrsc) { + Context *rsc = static_cast<Context *>(vrsc); + uint32_t idx = (uint32_t)android_atomic_inc(&rsc->mWorkers.mLaunchCount); + + //LOGV("RS helperThread starting %p idx=%i", rsc, idx); + + rsc->mWorkers.mLaunchSignals[idx].init(); + rsc->mWorkers.mNativeThreadId[idx] = gettid(); + +#if 0 + typedef struct {uint64_t bits[1024 / 64]; } cpu_set_t; + cpu_set_t cpuset; + memset(&cpuset, 0, sizeof(cpuset)); + cpuset.bits[idx / 64] |= 1ULL << (idx % 64); + int ret = syscall(241, rsc->mWorkers.mNativeThreadId[idx], + sizeof(cpuset), &cpuset); + LOGE("SETAFFINITY ret = %i %s", ret, EGLUtils::strerror(ret)); +#endif + + setpriority(PRIO_PROCESS, rsc->mWorkers.mNativeThreadId[idx], rsc->mThreadPriority); + int status = pthread_setspecific(rsc->gThreadTLSKey, rsc->mTlsStruct); + if (status) { + LOGE("pthread_setspecific %i", status); + } + + while (!rsc->mExit) { + rsc->mWorkers.mLaunchSignals[idx].wait(); + if (rsc->mWorkers.mLaunchCallback) { + rsc->mWorkers.mLaunchCallback(rsc->mWorkers.mLaunchData, idx); + } + android_atomic_dec(&rsc->mWorkers.mRunningCount); + rsc->mWorkers.mCompleteSignal.set(); + } + + //LOGV("RS helperThread exited %p idx=%i", rsc, idx); + return NULL; +} + +void Context::launchThreads(WorkerCallback_t cbk, void *data) { + mWorkers.mLaunchData = data; + mWorkers.mLaunchCallback = cbk; + android_atomic_release_store(mWorkers.mCount, &mWorkers.mRunningCount); + for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) { + mWorkers.mLaunchSignals[ct].set(); + } + while (android_atomic_acquire_load(&mWorkers.mRunningCount) != 0) { + mWorkers.mCompleteSignal.wait(); + } +} + +void Context::setPriority(int32_t p) { // Note: If we put this in the proper "background" policy // the wallpapers can become completly unresponsive at times. // This is probably not what we want for something the user is actively @@ -395,27 +616,46 @@ void Context::setPriority(int32_t p) // success; reset the priority as well } #else - setpriority(PRIO_PROCESS, mNativeThreadId, p); + setpriority(PRIO_PROCESS, mNativeThreadId, p); + for (uint32_t ct=0; ct < mWorkers.mCount; ct++) { + setpriority(PRIO_PROCESS, mWorkers.mNativeThreadId[ct], p); + } #endif } -Context::Context(Device *dev, bool isGraphics, bool useDepth) -{ - pthread_mutex_lock(&gInitMutex); - - dev->addContext(this); - mDev = dev; +Context::Context() { + mDev = NULL; mRunning = false; mExit = false; - mUseDepth = useDepth; mPaused = false; mObjHead = NULL; mError = RS_ERROR_NONE; - mErrorMsg = NULL; + mDPI = 96; +} + +Context * Context::createContext(Device *dev, const RsSurfaceConfig *sc) { + Context * rsc = new Context(); + if (!rsc->initContext(dev, sc)) { + delete rsc; + return NULL; + } + return rsc; +} + +bool Context::initContext(Device *dev, const RsSurfaceConfig *sc) { + pthread_mutex_lock(&gInitMutex); + + dev->addContext(this); + mDev = dev; + if (sc) { + mUserSurfaceConfig = *sc; + } else { + memset(&mUserSurfaceConfig, 0, sizeof(mUserSurfaceConfig)); + } memset(&mEGL, 0, sizeof(mEGL)); memset(&mGL, 0, sizeof(mGL)); - mIsGraphicsContext = isGraphics; + mIsGraphicsContext = sc != NULL; int status; pthread_attr_t threadAttr; @@ -425,10 +665,11 @@ Context::Context(Device *dev, bool isGraphics, bool useDepth) if (status) { LOGE("Failed to init thread tls key."); pthread_mutex_unlock(&gInitMutex); - return; + return false; } } gThreadTLSKeyCount++; + pthread_mutex_unlock(&gInitMutex); // Global init done at this point. @@ -436,39 +677,77 @@ Context::Context(Device *dev, bool isGraphics, bool useDepth) status = pthread_attr_init(&threadAttr); if (status) { LOGE("Failed to init thread attribute."); - return; + return false; } mWndSurface = NULL; - objDestroyOOBInit(); timerInit(); timerSet(RS_TIMER_INTERNAL); - LOGV("RS Launching thread"); + int cpu = sysconf(_SC_NPROCESSORS_ONLN); + LOGV("RS Launching thread(s), reported CPU count %i", cpu); + if (cpu < 2) cpu = 0; + + mWorkers.mCount = (uint32_t)cpu; + mWorkers.mThreadId = (pthread_t *) calloc(mWorkers.mCount, sizeof(pthread_t)); + mWorkers.mNativeThreadId = (pid_t *) calloc(mWorkers.mCount, sizeof(pid_t)); + mWorkers.mLaunchSignals = new Signal[mWorkers.mCount]; + mWorkers.mLaunchCallback = NULL; status = pthread_create(&mThreadId, &threadAttr, threadProc, this); if (status) { LOGE("Failed to start rs context thread."); + return false; } - - while(!mRunning) { + while (!mRunning && (mError == RS_ERROR_NONE)) { usleep(100); } + if (mError != RS_ERROR_NONE) { + return false; + } + + mWorkers.mCompleteSignal.init(); + android_atomic_release_store(mWorkers.mCount, &mWorkers.mRunningCount); + android_atomic_release_store(0, &mWorkers.mLaunchCount); + for (uint32_t ct=0; ct < mWorkers.mCount; ct++) { + status = pthread_create(&mWorkers.mThreadId[ct], &threadAttr, helperThreadProc, this); + if (status) { + mWorkers.mCount = ct; + LOGE("Created fewer than expected number of RS threads."); + break; + } + } + while (android_atomic_acquire_load(&mWorkers.mRunningCount) != 0) { + usleep(100); + } pthread_attr_destroy(&threadAttr); + return true; } -Context::~Context() -{ +Context::~Context() { LOGV("Context::~Context"); + + mIO.mToCore.flush(); + rsAssert(mExit); mExit = true; mPaused = false; void *res; mIO.shutdown(); int status = pthread_join(mThreadId, &res); - mObjDestroy.mNeedToEmpty = true; - objDestroyOOBRun(); + + // Cleanup compute threads. + mWorkers.mLaunchData = NULL; + mWorkers.mLaunchCallback = NULL; + android_atomic_release_store(mWorkers.mCount, &mWorkers.mRunningCount); + for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) { + mWorkers.mLaunchSignals[ct].set(); + } + for (uint32_t ct = 0; ct < mWorkers.mCount; ct++) { + status = pthread_join(mWorkers.mThreadId[ct], &res); + } + rsAssert(android_atomic_acquire_load(&mWorkers.mRunningCount) == 0); // Global structure cleanup. pthread_mutex_lock(&gInitMutex); @@ -481,38 +760,31 @@ Context::~Context() mDev = NULL; } pthread_mutex_unlock(&gInitMutex); - - objDestroyOOBDestroy(); + LOGV("Context::~Context done"); } -void Context::setSurface(uint32_t w, uint32_t h, ANativeWindow *sur) -{ +void Context::setSurface(uint32_t w, uint32_t h, ANativeWindow *sur) { rsAssert(mIsGraphicsContext); EGLBoolean ret; - if (mEGL.mSurface != NULL) { - ret = eglMakeCurrent(mEGL.mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + // WAR: Some drivers fail to handle 0 size surfaces correcntly. + // Use the pbuffer to avoid this pitfall. + if ((mEGL.mSurface != NULL) || (w == 0) || (h == 0)) { + ret = eglMakeCurrent(mEGL.mDisplay, mEGL.mSurfaceDefault, mEGL.mSurfaceDefault, mEGL.mContext); checkEglError("eglMakeCurrent", ret); ret = eglDestroySurface(mEGL.mDisplay, mEGL.mSurface); checkEglError("eglDestroySurface", ret); mEGL.mSurface = NULL; - mEGL.mWidth = 0; - mEGL.mHeight = 0; - mWidth = 0; - mHeight = 0; + mWidth = 1; + mHeight = 1; } mWndSurface = sur; if (mWndSurface != NULL) { - bool first = false; - if (!mEGL.mContext) { - first = true; - pthread_mutex_lock(&gInitMutex); - initEGL(true); - pthread_mutex_unlock(&gInitMutex); - } + mWidth = w; + mHeight = h; mEGL.mSurface = eglCreateWindowSurface(mEGL.mDisplay, mEGL.mConfig, mWndSurface, NULL); checkEglError("eglCreateWindowSurface"); @@ -523,81 +795,26 @@ void Context::setSurface(uint32_t w, uint32_t h, ANativeWindow *sur) ret = eglMakeCurrent(mEGL.mDisplay, mEGL.mSurface, mEGL.mSurface, mEGL.mContext); checkEglError("eglMakeCurrent", ret); - eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_WIDTH, &mEGL.mWidth); - eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_HEIGHT, &mEGL.mHeight); - mWidth = w; - mHeight = h; - mStateVertex.updateSize(this, w, h); - - if ((int)mWidth != mEGL.mWidth || (int)mHeight != mEGL.mHeight) { - LOGE("EGL/Surface mismatch EGL (%i x %i) SF (%i x %i)", mEGL.mWidth, mEGL.mHeight, mWidth, mHeight); - } - - if (first) { - mGL.mVersion = glGetString(GL_VERSION); - mGL.mVendor = glGetString(GL_VENDOR); - mGL.mRenderer = glGetString(GL_RENDERER); - mGL.mExtensions = glGetString(GL_EXTENSIONS); - - //LOGV("EGL Version %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion); - LOGV("GL Version %s", mGL.mVersion); - //LOGV("GL Vendor %s", mGL.mVendor); - LOGV("GL Renderer %s", mGL.mRenderer); - //LOGV("GL Extensions %s", mGL.mExtensions); - - const char *verptr = NULL; - if (strlen((const char *)mGL.mVersion) > 9) { - if (!memcmp(mGL.mVersion, "OpenGL ES-CM", 12)) { - verptr = (const char *)mGL.mVersion + 12; - } - if (!memcmp(mGL.mVersion, "OpenGL ES ", 10)) { - verptr = (const char *)mGL.mVersion + 9; - } - } - - if (!verptr) { - LOGE("Error, OpenGL ES Lite not supported"); - } else { - sscanf(verptr, " %i.%i", &mGL.mMajorVersion, &mGL.mMinorVersion); - } - - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &mGL.mMaxVertexAttribs); - glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &mGL.mMaxVertexUniformVectors); - glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &mGL.mMaxVertexTextureUnits); - - glGetIntegerv(GL_MAX_VARYING_VECTORS, &mGL.mMaxVaryingVectors); - glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &mGL.mMaxTextureImageUnits); - - glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mGL.mMaxFragmentTextureImageUnits); - glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGL.mMaxFragmentUniformVectors); - - mGL.OES_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_OES_texture_npot"); - mGL.GL_IMG_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_IMG_texture_npot"); - } - + mStateVertex.updateSize(this); } } -void Context::pause() -{ +void Context::pause() { rsAssert(mIsGraphicsContext); mPaused = true; } -void Context::resume() -{ +void Context::resume() { rsAssert(mIsGraphicsContext); mPaused = false; } -void Context::setRootScript(Script *s) -{ +void Context::setRootScript(Script *s) { rsAssert(mIsGraphicsContext); mRootScript.set(s); } -void Context::setFragmentStore(ProgramFragmentStore *pfs) -{ +void Context::setProgramStore(ProgramStore *pfs) { rsAssert(mIsGraphicsContext); if (pfs == NULL) { mFragmentStore.set(mStateFragmentStore.mDefault); @@ -606,8 +823,7 @@ void Context::setFragmentStore(ProgramFragmentStore *pfs) } } -void Context::setFragment(ProgramFragment *pf) -{ +void Context::setProgramFragment(ProgramFragment *pf) { rsAssert(mIsGraphicsContext); if (pf == NULL) { mFragment.set(mStateFragment.mDefault); @@ -616,8 +832,7 @@ void Context::setFragment(ProgramFragment *pf) } } -void Context::setRaster(ProgramRaster *pr) -{ +void Context::setProgramRaster(ProgramRaster *pr) { rsAssert(mIsGraphicsContext); if (pr == NULL) { mRaster.set(mStateRaster.mDefault); @@ -626,8 +841,7 @@ void Context::setRaster(ProgramRaster *pr) } } -void Context::setVertex(ProgramVertex *pv) -{ +void Context::setProgramVertex(ProgramVertex *pv) { rsAssert(mIsGraphicsContext); if (pv == NULL) { mVertex.set(mStateVertex.mDefault); @@ -636,16 +850,23 @@ void Context::setVertex(ProgramVertex *pv) } } -void Context::assignName(ObjectBase *obj, const char *name, uint32_t len) -{ +void Context::setFont(Font *f) { + rsAssert(mIsGraphicsContext); + if (f == NULL) { + mFont.set(mStateFont.mDefault); + } else { + mFont.set(f); + } +} + +void Context::assignName(ObjectBase *obj, const char *name, uint32_t len) { rsAssert(!obj->getName()); obj->setName(name, len); mNames.add(obj); } -void Context::removeName(ObjectBase *obj) -{ - for(size_t ct=0; ct < mNames.size(); ct++) { +void Context::removeName(ObjectBase *obj) { + for (size_t ct=0; ct < mNames.size(); ct++) { if (obj == mNames[ct]) { mNames.removeAt(ct); return; @@ -653,174 +874,100 @@ void Context::removeName(ObjectBase *obj) } } -ObjectBase * Context::lookupName(const char *name) const -{ - for(size_t ct=0; ct < mNames.size(); ct++) { - if (!strcmp(name, mNames[ct]->getName())) { - return mNames[ct]; - } - } - return NULL; -} - -void Context::appendNameDefines(String8 *str) const -{ - char buf[256]; - for (size_t ct=0; ct < mNames.size(); ct++) { - str->append("#define NAMED_"); - str->append(mNames[ct]->getName()); - str->append(" "); - sprintf(buf, "%i\n", (int)mNames[ct]); - str->append(buf); - } -} - -bool Context::objDestroyOOBInit() -{ - int status = pthread_mutex_init(&mObjDestroy.mMutex, NULL); - if (status) { - LOGE("Context::ObjDestroyOOBInit mutex init failure"); - return false; - } - return true; -} - -void Context::objDestroyOOBRun() -{ - if (mObjDestroy.mNeedToEmpty) { - int status = pthread_mutex_lock(&mObjDestroy.mMutex); - if (status) { - LOGE("Context::ObjDestroyOOBRun: error %i locking for OOBRun.", status); - return; - } - - for (size_t ct = 0; ct < mObjDestroy.mDestroyList.size(); ct++) { - mObjDestroy.mDestroyList[ct]->decUserRef(); - } - mObjDestroy.mDestroyList.clear(); - mObjDestroy.mNeedToEmpty = false; - - status = pthread_mutex_unlock(&mObjDestroy.mMutex); - if (status) { - LOGE("Context::ObjDestroyOOBRun: error %i unlocking for set condition.", status); - } - } -} - -void Context::objDestroyOOBDestroy() -{ - rsAssert(!mObjDestroy.mNeedToEmpty); - pthread_mutex_destroy(&mObjDestroy.mMutex); -} - -void Context::objDestroyAdd(ObjectBase *obj) -{ - int status = pthread_mutex_lock(&mObjDestroy.mMutex); - if (status) { - LOGE("Context::ObjDestroyOOBRun: error %i locking for OOBRun.", status); - return; +RsMessageToClientType Context::peekMessageToClient(size_t *receiveLen, uint32_t *subID, bool wait) { + *receiveLen = 0; + if (!wait && mIO.mToClient.isEmpty()) { + return RS_MESSAGE_TO_CLIENT_NONE; } - mObjDestroy.mNeedToEmpty = true; - mObjDestroy.mDestroyList.add(obj); - - status = pthread_mutex_unlock(&mObjDestroy.mMutex); - if (status) { - LOGE("Context::ObjDestroyOOBRun: error %i unlocking for set condition.", status); + uint32_t bytesData = 0; + uint32_t commandID = 0; + const uint32_t *d = (const uint32_t *)mIO.mToClient.get(&commandID, &bytesData); + *receiveLen = bytesData - sizeof(uint32_t); + if (bytesData) { + *subID = d[0]; } + return (RsMessageToClientType)commandID; } -uint32_t Context::getMessageToClient(void *data, size_t *receiveLen, size_t bufferLen, bool wait) -{ +RsMessageToClientType Context::getMessageToClient(void *data, size_t *receiveLen, uint32_t *subID, size_t bufferLen, bool wait) { //LOGE("getMessageToClient %i %i", bufferLen, wait); - if (!wait) { - if (mIO.mToClient.isEmpty()) { - // No message to get and not going to wait for one. - receiveLen = 0; - return 0; - } + *receiveLen = 0; + if (!wait && mIO.mToClient.isEmpty()) { + return RS_MESSAGE_TO_CLIENT_NONE; } //LOGE("getMessageToClient 2 con=%p", this); uint32_t bytesData = 0; uint32_t commandID = 0; - const void *d = mIO.mToClient.get(&commandID, &bytesData); + const uint32_t *d = (const uint32_t *)mIO.mToClient.get(&commandID, &bytesData); //LOGE("getMessageToClient 3 %i %i", commandID, bytesData); - *receiveLen = bytesData; + *receiveLen = bytesData - sizeof(uint32_t); + *subID = d[0]; + + //LOGE("getMessageToClient %i %i", commandID, *subID); if (bufferLen >= bytesData) { - memcpy(data, d, bytesData); + memcpy(data, d+1, *receiveLen); mIO.mToClient.next(); - return commandID; + return (RsMessageToClientType)commandID; } - return 0; + return RS_MESSAGE_TO_CLIENT_RESIZE; } -bool Context::sendMessageToClient(void *data, uint32_t cmdID, size_t len, bool waitForSpace) -{ - //LOGE("sendMessageToClient %i %i %i", cmdID, len, waitForSpace); +bool Context::sendMessageToClient(const void *data, RsMessageToClientType cmdID, + uint32_t subID, size_t len, bool waitForSpace) const { + //LOGE("sendMessageToClient %i %i %i %i", cmdID, subID, len, waitForSpace); if (cmdID == 0) { LOGE("Attempting to send invalid command 0 to client."); return false; } if (!waitForSpace) { - if (mIO.mToClient.getFreeSpace() < len) { + if (!mIO.mToClient.makeSpaceNonBlocking(len + 12)) { // Not enough room, and not waiting. return false; } } //LOGE("sendMessageToClient 2"); - void *p = mIO.mToClient.reserve(len); - memcpy(p, data, len); - mIO.mToClient.commit(cmdID, len); + uint32_t *p = (uint32_t *)mIO.mToClient.reserve(len + sizeof(subID)); + p[0] = subID; + if (len > 0) { + memcpy(p+1, data, len); + } + mIO.mToClient.commit(cmdID, len + sizeof(subID)); //LOGE("sendMessageToClient 3"); return true; } -void Context::initToClient() -{ - while(!mRunning) { +void Context::initToClient() { + while (!mRunning) { usleep(100); } } -void Context::deinitToClient() -{ +void Context::deinitToClient() { mIO.mToClient.shutdown(); } -const char * Context::getError(RsError *err) -{ - *err = mError; - mError = RS_ERROR_NONE; - if (*err != RS_ERROR_NONE) { - return mErrorMsg; - } - return NULL; -} - -void Context::setError(RsError e, const char *msg) -{ +void Context::setError(RsError e, const char *msg) const { mError = e; - mErrorMsg = msg; + sendMessageToClient(msg, RS_MESSAGE_TO_CLIENT_ERROR, e, strlen(msg) + 1, true); } -void Context::dumpDebug() const -{ +void Context::dumpDebug() const { LOGE("RS Context debug %p", this); LOGE("RS Context debug"); LOGE(" EGL ver %i %i", mEGL.mMajorVersion, mEGL.mMinorVersion); - LOGE(" EGL context %p surface %p, w=%i h=%i Display=%p", mEGL.mContext, - mEGL.mSurface, mEGL.mWidth, mEGL.mHeight, mEGL.mDisplay); + LOGE(" EGL context %p surface %p, Display=%p", mEGL.mContext, mEGL.mSurface, mEGL.mDisplay); LOGE(" GL vendor: %s", mGL.mVendor); LOGE(" GL renderer: %s", mGL.mRenderer); LOGE(" GL Version: %s", mGL.mVersion); LOGE(" GL Extensions: %s", mGL.mExtensions); LOGE(" GL int Versions %i %i", mGL.mMajorVersion, mGL.mMinorVersion); LOGE(" RS width %i, height %i", mWidth, mHeight); - LOGE(" RS running %i, exit %i, useDepth %i, paused %i", mRunning, mExit, mUseDepth, mPaused); + LOGE(" RS running %i, exit %i, paused %i", mRunning, mExit, mPaused); LOGE(" RS pThreadID %li, nativeThreadID %i", mThreadId, mNativeThreadId); LOGV("MAX Textures %i, %i %i", mGL.mMaxVertexTextureUnits, mGL.mMaxFragmentTextureImageUnits, mGL.mMaxTextureImageUnits); @@ -835,15 +982,15 @@ void Context::dumpDebug() const namespace android { namespace renderscript { +void rsi_ContextFinish(Context *rsc) { +} -void rsi_ContextBindRootScript(Context *rsc, RsScript vs) -{ +void rsi_ContextBindRootScript(Context *rsc, RsScript vs) { Script *s = static_cast<Script *>(vs); rsc->setRootScript(s); } -void rsi_ContextBindSampler(Context *rsc, uint32_t slot, RsSampler vs) -{ +void rsi_ContextBindSampler(Context *rsc, uint32_t slot, RsSampler vs) { Sampler *s = static_cast<Sampler *>(vs); if (slot > RS_MAX_SAMPLER_SLOT) { @@ -854,124 +1001,117 @@ void rsi_ContextBindSampler(Context *rsc, uint32_t slot, RsSampler vs) s->bindToContext(&rsc->mStateSampler, slot); } -void rsi_ContextBindProgramFragmentStore(Context *rsc, RsProgramFragmentStore vpfs) -{ - ProgramFragmentStore *pfs = static_cast<ProgramFragmentStore *>(vpfs); - rsc->setFragmentStore(pfs); +void rsi_ContextBindProgramStore(Context *rsc, RsProgramStore vpfs) { + ProgramStore *pfs = static_cast<ProgramStore *>(vpfs); + rsc->setProgramStore(pfs); } -void rsi_ContextBindProgramFragment(Context *rsc, RsProgramFragment vpf) -{ +void rsi_ContextBindProgramFragment(Context *rsc, RsProgramFragment vpf) { ProgramFragment *pf = static_cast<ProgramFragment *>(vpf); - rsc->setFragment(pf); + rsc->setProgramFragment(pf); } -void rsi_ContextBindProgramRaster(Context *rsc, RsProgramRaster vpr) -{ +void rsi_ContextBindProgramRaster(Context *rsc, RsProgramRaster vpr) { ProgramRaster *pr = static_cast<ProgramRaster *>(vpr); - rsc->setRaster(pr); + rsc->setProgramRaster(pr); } -void rsi_ContextBindProgramVertex(Context *rsc, RsProgramVertex vpv) -{ +void rsi_ContextBindProgramVertex(Context *rsc, RsProgramVertex vpv) { ProgramVertex *pv = static_cast<ProgramVertex *>(vpv); - rsc->setVertex(pv); + rsc->setProgramVertex(pv); +} + +void rsi_ContextBindFont(Context *rsc, RsFont vfont) { + Font *font = static_cast<Font *>(vfont); + rsc->setFont(font); } -void rsi_AssignName(Context *rsc, void * obj, const char *name, uint32_t len) -{ +void rsi_AssignName(Context *rsc, void * obj, const char *name, uint32_t len) { ObjectBase *ob = static_cast<ObjectBase *>(obj); rsc->assignName(ob, name, len); } -void rsi_ObjDestroy(Context *rsc, void *obj) -{ - ObjectBase *ob = static_cast<ObjectBase *>(obj); +void rsi_ObjDestroy(Context *rsc, void *optr) { + ObjectBase *ob = static_cast<ObjectBase *>(optr); rsc->removeName(ob); ob->decUserRef(); } -void rsi_ContextPause(Context *rsc) -{ +void rsi_ContextPause(Context *rsc) { rsc->pause(); } -void rsi_ContextResume(Context *rsc) -{ +void rsi_ContextResume(Context *rsc) { rsc->resume(); } -void rsi_ContextSetSurface(Context *rsc, uint32_t w, uint32_t h, ANativeWindow *sur) -{ +void rsi_ContextSetSurface(Context *rsc, uint32_t w, uint32_t h, ANativeWindow *sur) { rsc->setSurface(w, h, sur); } -void rsi_ContextSetPriority(Context *rsc, int32_t p) -{ +void rsi_ContextSetPriority(Context *rsc, int32_t p) { rsc->setPriority(p); } -void rsi_ContextDump(Context *rsc, int32_t bits) -{ +void rsi_ContextDump(Context *rsc, int32_t bits) { ObjectBase::dumpAll(rsc); } -const char * rsi_ContextGetError(Context *rsc, RsError *e) -{ - const char *msg = rsc->getError(e); - if (*e != RS_ERROR_NONE) { - LOGE("RS Error %i %s", *e, msg); - } - return msg; +void rsi_ContextDestroyWorker(Context *rsc) { + rsc->destroyWorkerThreadResources();; } } } +void rsContextDestroy(RsContext vcon) { + LOGV("rsContextDestroy %p", vcon); + Context *rsc = static_cast<Context *>(vcon); + rsContextDestroyWorker(rsc); + delete rsc; + LOGV("rsContextDestroy 2 %p", vcon); +} -RsContext rsContextCreate(RsDevice vdev, uint32_t version) -{ +RsContext rsContextCreate(RsDevice vdev, uint32_t version) { LOGV("rsContextCreate %p", vdev); Device * dev = static_cast<Device *>(vdev); - Context *rsc = new Context(dev, false, false); + Context *rsc = Context::createContext(dev, NULL); return rsc; } -RsContext rsContextCreateGL(RsDevice vdev, uint32_t version, bool useDepth) -{ - LOGV("rsContextCreateGL %p, %i", vdev, useDepth); +RsContext rsContextCreateGL(RsDevice vdev, uint32_t version, + RsSurfaceConfig sc, uint32_t dpi) { + LOGV("rsContextCreateGL %p", vdev); Device * dev = static_cast<Device *>(vdev); - Context *rsc = new Context(dev, true, useDepth); + Context *rsc = Context::createContext(dev, &sc); + rsc->setDPI(dpi); + LOGV("rsContextCreateGL ret %p ", rsc); return rsc; } -void rsContextDestroy(RsContext vrsc) -{ +RsMessageToClientType rsContextPeekMessage(RsContext vrsc, size_t *receiveLen, uint32_t *subID, bool wait) { Context * rsc = static_cast<Context *>(vrsc); - delete rsc; + return rsc->peekMessageToClient(receiveLen, subID, wait); } -void rsObjDestroyOOB(RsContext vrsc, void *obj) -{ +RsMessageToClientType rsContextGetMessage(RsContext vrsc, void *data, size_t *receiveLen, uint32_t *subID, size_t bufferLen, bool wait) { Context * rsc = static_cast<Context *>(vrsc); - rsc->objDestroyAdd(static_cast<ObjectBase *>(obj)); + return rsc->getMessageToClient(data, receiveLen, subID, bufferLen, wait); } -uint32_t rsContextGetMessage(RsContext vrsc, void *data, size_t *receiveLen, size_t bufferLen, bool wait) -{ - Context * rsc = static_cast<Context *>(vrsc); - return rsc->getMessageToClient(data, receiveLen, bufferLen, wait); -} - -void rsContextInitToClient(RsContext vrsc) -{ +void rsContextInitToClient(RsContext vrsc) { Context * rsc = static_cast<Context *>(vrsc); rsc->initToClient(); } -void rsContextDeinitToClient(RsContext vrsc) -{ +void rsContextDeinitToClient(RsContext vrsc) { Context * rsc = static_cast<Context *>(vrsc); rsc->deinitToClient(); } +// Only to be called at a3d load time, before object is visible to user +// not thread safe +void rsaGetName(RsContext con, void * obj, const char **name) { + ObjectBase *ob = static_cast<ObjectBase *>(obj); + (*name) = ob->getName(); +} diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index e24fd09d547e..50f63df44401 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -18,21 +18,21 @@ #define ANDROID_RS_CONTEXT_H #include "rsUtils.h" - -#include "rsThreadIO.h" #include "rsType.h" -#include "rsMatrix.h" #include "rsAllocation.h" -#include "rsSimpleMesh.h" #include "rsMesh.h" + +#ifndef ANDROID_RS_SERIALIZE +#include "rsMutex.h" +#include "rsThreadIO.h" +#include "rsMatrix.h" #include "rsDevice.h" #include "rsScriptC.h" -#include "rsAllocation.h" #include "rsAdapter.h" #include "rsSampler.h" -#include "rsLight.h" +#include "rsFont.h" #include "rsProgramFragment.h" -#include "rsProgramFragmentStore.h" +#include "rsProgramStore.h" #include "rsProgramRaster.h" #include "rsProgramVertex.h" #include "rsShaderCache.h" @@ -42,73 +42,115 @@ #include "rsLocklessFifo.h" #include <ui/egl/android_natives.h> +#endif // ANDROID_RS_SERIALIZE // --------------------------------------------------------------------------- namespace android { namespace renderscript { -class Context -{ +#if 0 +#define CHECK_OBJ(o) { \ + GET_TLS(); \ + if (!ObjectBase::isValid(rsc, (const ObjectBase *)o)) { \ + LOGE("Bad object %p at %s, %i", o, __FILE__, __LINE__); \ + } \ +} +#define CHECK_OBJ_OR_NULL(o) { \ + GET_TLS(); \ + if (o && !ObjectBase::isValid(rsc, (const ObjectBase *)o)) { \ + LOGE("Bad object %p at %s, %i", o, __FILE__, __LINE__); \ + } \ +} +#else +#define CHECK_OBJ(o) +#define CHECK_OBJ_OR_NULL(o) +#endif + +#ifndef ANDROID_RS_SERIALIZE + +class Context { public: - Context(Device *, bool isGraphics, bool useDepth); + static Context * createContext(Device *, const RsSurfaceConfig *sc); ~Context(); static pthread_key_t gThreadTLSKey; static uint32_t gThreadTLSKeyCount; static uint32_t gGLContextCount; static pthread_mutex_t gInitMutex; + // Library mutex (for providing thread-safe calls from the runtime) + static pthread_mutex_t gLibMutex; struct ScriptTLSStruct { Context * mContext; Script * mScript; }; + class PushState { + public: + PushState(Context *); + ~PushState(); + + private: + ObjectBaseRef<ProgramFragment> mFragment; + ObjectBaseRef<ProgramVertex> mVertex; + ObjectBaseRef<ProgramStore> mStore; + ObjectBaseRef<ProgramRaster> mRaster; + ObjectBaseRef<Font> mFont; + Context *mRsc; + }; + + ScriptTLSStruct *mTlsStruct; + RsSurfaceConfig mUserSurfaceConfig; + + typedef void (*WorkerCallback_t)(void *usr, uint32_t idx); //StructuredAllocationContext mStateAllocation; ElementState mStateElement; TypeState mStateType; SamplerState mStateSampler; ProgramFragmentState mStateFragment; - ProgramFragmentStoreState mStateFragmentStore; + ProgramStoreState mStateFragmentStore; ProgramRasterState mStateRaster; ProgramVertexState mStateVertex; - LightState mStateLight; VertexArrayState mStateVertexArray; + FontState mStateFont; ScriptCState mScriptC; ShaderCache mShaderCache; void swapBuffers(); void setRootScript(Script *); - void setRaster(ProgramRaster *); - void setVertex(ProgramVertex *); - void setFragment(ProgramFragment *); - void setFragmentStore(ProgramFragmentStore *); + void setProgramRaster(ProgramRaster *); + void setProgramVertex(ProgramVertex *); + void setProgramFragment(ProgramFragment *); + void setProgramStore(ProgramStore *); + void setFont(Font *); void updateSurface(void *sur); - const ProgramFragment * getFragment() {return mFragment.get();} - const ProgramFragmentStore * getFragmentStore() {return mFragmentStore.get();} - const ProgramRaster * getRaster() {return mRaster.get();} - const ProgramVertex * getVertex() {return mVertex.get();} + ProgramFragment * getProgramFragment() {return mFragment.get();} + ProgramStore * getProgramStore() {return mFragmentStore.get();} + ProgramRaster * getProgramRaster() {return mRaster.get();} + ProgramVertex * getProgramVertex() {return mVertex.get();} + Font * getFont() {return mFont.get();} bool setupCheck(); - bool checkDriver() const {return mEGL.mSurface != 0;} + void setupProgramStore(); void pause(); void resume(); void setSurface(uint32_t w, uint32_t h, ANativeWindow *sur); void setPriority(int32_t p); + void destroyWorkerThreadResources(); void assignName(ObjectBase *obj, const char *name, uint32_t len); void removeName(ObjectBase *obj); - ObjectBase * lookupName(const char *name) const; - void appendNameDefines(String8 *str) const; - uint32_t getMessageToClient(void *data, size_t *receiveLen, size_t bufferLen, bool wait); - bool sendMessageToClient(void *data, uint32_t cmdID, size_t len, bool waitForSpace); - uint32_t runScript(Script *s, uint32_t launchID); + RsMessageToClientType peekMessageToClient(size_t *receiveLen, uint32_t *subID, bool wait); + RsMessageToClientType getMessageToClient(void *data, size_t *receiveLen, uint32_t *subID, size_t bufferLen, bool wait); + bool sendMessageToClient(const void *data, RsMessageToClientType cmdID, uint32_t subID, size_t len, bool waitForSpace) const; + uint32_t runScript(Script *s); void initToClient(); void deinitToClient(); @@ -119,19 +161,20 @@ public: ProgramVertex * getDefaultProgramVertex() const { return mStateVertex.mDefault.get(); } - ProgramFragmentStore * getDefaultProgramFragmentStore() const { + ProgramStore * getDefaultProgramStore() const { return mStateFragmentStore.mDefault.get(); } ProgramRaster * getDefaultProgramRaster() const { return mStateRaster.mDefault.get(); } + Font* getDefaultFont() const { + return mStateFont.mDefault.get(); + } - uint32_t getWidth() const {return mEGL.mWidth;} - uint32_t getHeight() const {return mEGL.mHeight;} - + uint32_t getWidth() const {return mWidth;} + uint32_t getHeight() const {return mHeight;} - ThreadIO mIO; - void objDestroyAdd(ObjectBase *); + mutable ThreadIO mIO; // Timers enum Timers { @@ -148,25 +191,35 @@ public: void timerPrint(); void timerFrame(); - bool checkVersion1_1() const {return (mGL.mMajorVersion > 1) || (mGL.mMinorVersion >= 1); } - bool checkVersion2_0() const {return mGL.mMajorVersion >= 2; } - struct { bool mLogTimes; bool mLogScripts; bool mLogObjects; bool mLogShaders; + bool mLogShadersAttr; + bool mLogShadersUniforms; + bool mLogVisual; } props; void dumpDebug() const; - void checkError(const char *) const; - const char * getError(RsError *); - void setError(RsError e, const char *msg); + void checkError(const char *, bool isFatal = false) const; + void setError(RsError e, const char *msg = NULL) const; mutable const ObjectBase * mObjHead; bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;} bool ext_GL_IMG_texture_npot() const {return mGL.GL_IMG_texture_npot;} + bool ext_GL_NV_texture_npot_2D_mipmap() const {return mGL.GL_NV_texture_npot_2D_mipmap;} + float ext_texture_max_aniso() const {return mGL.EXT_texture_max_aniso; } + uint32_t getMaxFragmentTextures() const {return mGL.mMaxFragmentTextureImageUnits;} + uint32_t getMaxFragmentUniformVectors() const {return mGL.mMaxFragmentUniformVectors;} + uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;} + uint32_t getMaxVertexAttributes() const {return mGL.mMaxVertexAttribs;} + + void launchThreads(WorkerCallback_t cbk, void *data); + uint32_t getWorkerPoolSize() const {return (uint32_t)mWorkers.mCount;} + uint32_t getDPI() const {return mDPI;} + void setDPI(uint32_t dpi) {mDPI = dpi;} protected: Device *mDev; @@ -178,8 +231,7 @@ protected: EGLConfig mConfig; EGLContext mContext; EGLSurface mSurface; - EGLint mWidth; - EGLint mHeight; + EGLSurface mSurfaceDefault; EGLDisplay mDisplay; } mEGL; @@ -204,8 +256,11 @@ protected: bool OES_texture_npot; bool GL_IMG_texture_npot; + bool GL_NV_texture_npot_2D_mipmap; + float EXT_texture_max_aniso; } mGL; + uint32_t mDPI; uint32_t mWidth; uint32_t mHeight; int32_t mThreadPriority; @@ -213,40 +268,47 @@ protected: bool mRunning; bool mExit; - bool mUseDepth; bool mPaused; - RsError mError; - const char *mErrorMsg; + mutable RsError mError; pthread_t mThreadId; pid_t mNativeThreadId; + struct Workers { + volatile int mRunningCount; + volatile int mLaunchCount; + uint32_t mCount; + pthread_t *mThreadId; + pid_t *mNativeThreadId; + Signal mCompleteSignal; + + Signal *mLaunchSignals; + WorkerCallback_t mLaunchCallback; + void *mLaunchData; + }; + Workers mWorkers; + ObjectBaseRef<Script> mRootScript; ObjectBaseRef<ProgramFragment> mFragment; ObjectBaseRef<ProgramVertex> mVertex; - ObjectBaseRef<ProgramFragmentStore> mFragmentStore; + ObjectBaseRef<ProgramStore> mFragmentStore; ObjectBaseRef<ProgramRaster> mRaster; + ObjectBaseRef<Font> mFont; - - struct ObjDestroyOOB { - pthread_mutex_t mMutex; - Vector<ObjectBase *> mDestroyList; - bool mNeedToEmpty; - }; - ObjDestroyOOB mObjDestroy; - bool objDestroyOOBInit(); - void objDestroyOOBRun(); - void objDestroyOOBDestroy(); + void displayDebugStats(); private: Context(); + bool initContext(Device *, const RsSurfaceConfig *sc); + - void initEGL(bool useGL2); + bool initGLThread(); void deinitEGL(); uint32_t runRootScript(); static void * threadProc(void *); + static void * helperThreadProc(void *); ANativeWindow *mWndSurface; @@ -260,8 +322,44 @@ private: uint32_t mTimeMSLastFrame; uint32_t mTimeMSLastScript; uint32_t mTimeMSLastSwap; + uint32_t mAverageFPSFrameCount; + uint64_t mAverageFPSStartTime; + uint32_t mAverageFPS; }; -} -} +#else + +class Context { +public: + Context() { + mObjHead = NULL; + } + ~Context() { + ObjectBase::zeroAllUserRef(this); + } + + ElementState mStateElement; + TypeState mStateType; + + struct { + bool mLogTimes; + bool mLogScripts; + bool mLogObjects; + bool mLogShaders; + bool mLogShadersAttr; + bool mLogShadersUniforms; + bool mLogVisual; + } props; + + void setError(RsError e, const char *msg = NULL) { } + + mutable const ObjectBase * mObjHead; + +protected: + +}; +#endif //ANDROID_RS_SERIALIZE + +} // renderscript +} // android #endif diff --git a/libs/rs/rsDevice.cpp b/libs/rs/rsDevice.cpp index b670ad4b4902..d7d03f6a283d 100644 --- a/libs/rs/rsDevice.cpp +++ b/libs/rs/rsDevice.cpp @@ -20,24 +20,18 @@ using namespace android; using namespace android::renderscript; -Device::Device() -{ +Device::Device() { mForceSW = false; - } -Device::~Device() -{ - +Device::~Device() { } -void Device::addContext(Context *rsc) -{ - mContexts.add(rsc); +void Device::addContext(Context *rsc) { + mContexts.push(rsc); } -void Device::removeContext(Context *rsc) -{ +void Device::removeContext(Context *rsc) { for (size_t idx=0; idx < mContexts.size(); idx++) { if (mContexts[idx] == rsc) { mContexts.removeAt(idx); @@ -46,23 +40,17 @@ void Device::removeContext(Context *rsc) } } - - -RsDevice rsDeviceCreate() -{ +RsDevice rsDeviceCreate() { Device * d = new Device(); return d; } -void rsDeviceDestroy(RsDevice dev) -{ +void rsDeviceDestroy(RsDevice dev) { Device * d = static_cast<Device *>(dev); delete d; - } -void rsDeviceSetConfig(RsDevice dev, RsDeviceParam p, int32_t value) -{ +void rsDeviceSetConfig(RsDevice dev, RsDeviceParam p, int32_t value) { Device * d = static_cast<Device *>(dev); if (p == RS_DEVICE_PARAM_FORCE_SOFTWARE_GL) { d->mForceSW = value != 0; diff --git a/libs/rs/rsDevice.h b/libs/rs/rsDevice.h index a8a4e77fa5d6..ffb514bc2ecc 100644 --- a/libs/rs/rsDevice.h +++ b/libs/rs/rsDevice.h @@ -37,14 +37,8 @@ public: protected: Vector<Context *> mContexts; - - }; - - - - } } #endif diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index 6288bc425afa..477cb6166b21 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -14,26 +14,21 @@ * limitations under the License. */ -#include "rsContext.h" -#include <GLES/gl.h> +#include "rsContext.h" using namespace android; using namespace android::renderscript; -Element::Element(Context *rsc) : ObjectBase(rsc) -{ +Element::Element(Context *rsc) : ObjectBase(rsc) { mBits = 0; - mAllocFile = __FILE__; - mAllocLine = __LINE__; mFields = NULL; mFieldCount = 0; + mHasReference = false; } - -Element::~Element() -{ +Element::~Element() { for (uint32_t ct = 0; ct < mRSC->mStateElement.mElements.size(); ct++) { if (mRSC->mStateElement.mElements[ct] == this) { mRSC->mStateElement.mElements.removeAt(ct); @@ -43,50 +38,142 @@ Element::~Element() clear(); } -void Element::clear() -{ +void Element::clear() { delete [] mFields; mFields = NULL; mFieldCount = 0; + mHasReference = false; } -size_t Element::getSizeBits() const -{ +size_t Element::getSizeBits() const { if (!mFieldCount) { return mBits; } size_t total = 0; for (size_t ct=0; ct < mFieldCount; ct++) { - total += mFields[ct].e->mBits; + total += mFields[ct].e->mBits * mFields[ct].arraySize; } return total; } -size_t Element::getFieldOffsetBits(uint32_t componentNumber) const -{ - size_t offset = 0; - for (uint32_t ct = 0; ct < componentNumber; ct++) { - offset += mFields[ct].e->mBits; +void Element::dumpLOGV(const char *prefix) const { + ObjectBase::dumpLOGV(prefix); + LOGV("%s Element: fieldCount: %zu, size bytes: %zu", prefix, mFieldCount, getSizeBytes()); + for (uint32_t ct = 0; ct < mFieldCount; ct++) { + LOGV("%s Element field index: %u ------------------", prefix, ct); + LOGV("%s name: %s, offsetBits: %u, arraySize: %u", + prefix, mFields[ct].name.string(), mFields[ct].offsetBits, mFields[ct].arraySize); + mFields[ct].e->dumpLOGV(prefix); } - return offset; } -void Element::dumpLOGV(const char *prefix) const -{ - ObjectBase::dumpLOGV(prefix); - LOGV("%s Element: components %i, size %i", prefix, mFieldCount, mBits); +void Element::serialize(OStream *stream) const { + // Need to identify ourselves + stream->addU32((uint32_t)getClassId()); + + String8 name(getName()); + stream->addString(&name); + + mComponent.serialize(stream); + + // Now serialize all the fields + stream->addU32(mFieldCount); for (uint32_t ct = 0; ct < mFieldCount; ct++) { - char buf[1024]; - sprintf(buf, "%s component %i: ", prefix, ct); - //mComponents[ct]->dumpLOGV(buf); + stream->addString(&mFields[ct].name); + stream->addU32(mFields[ct].arraySize); + mFields[ct].e->serialize(stream); + } +} + +Element *Element::createFromStream(Context *rsc, IStream *stream) { + // First make sure we are reading the correct object + RsA3DClassID classID = (RsA3DClassID)stream->loadU32(); + if (classID != RS_A3D_CLASS_ID_ELEMENT) { + LOGE("element loading skipped due to invalid class id\n"); + return NULL; + } + + String8 name; + stream->loadString(&name); + + Element *elem = new Element(rsc); + elem->mComponent.loadFromStream(stream); + + elem->mFieldCount = stream->loadU32(); + if (elem->mFieldCount) { + elem->mFields = new ElementField_t [elem->mFieldCount]; + for (uint32_t ct = 0; ct < elem->mFieldCount; ct ++) { + stream->loadString(&elem->mFields[ct].name); + elem->mFields[ct].arraySize = stream->loadU32(); + Element *fieldElem = Element::createFromStream(rsc, stream); + elem->mFields[ct].e.set(fieldElem); + } + } + + // We need to check if this already exists + for (uint32_t ct=0; ct < rsc->mStateElement.mElements.size(); ct++) { + Element *ee = rsc->mStateElement.mElements[ct]; + if (ee->isEqual(elem)) { + ObjectBase::checkDelete(elem); + ee->incUserRef(); + return ee; + } } + + elem->compute(); + rsc->mStateElement.mElements.push(elem); + return elem; } +bool Element::isEqual(const Element *other) const { + if (other == NULL) { + return false; + } + if (!other->getFieldCount() && !mFieldCount) { + if ((other->getType() == getType()) && + (other->getKind() == getKind()) && + (other->getComponent().getIsNormalized() == getComponent().getIsNormalized()) && + (other->getComponent().getVectorSize() == getComponent().getVectorSize())) { + return true; + } + return false; + } + if (other->getFieldCount() == mFieldCount) { + for (uint32_t i=0; i < mFieldCount; i++) { + if ((!other->mFields[i].e->isEqual(mFields[i].e.get())) || + (other->mFields[i].name.length() != mFields[i].name.length()) || + (other->mFields[i].name != mFields[i].name) || + (other->mFields[i].arraySize != mFields[i].arraySize)) { + return false; + } + } + return true; + } + return false; +} + +void Element::compute() { + if (mFieldCount == 0) { + mBits = mComponent.getBits(); + mHasReference = mComponent.isReference(); + return; + } + + size_t bits = 0; + for (size_t ct=0; ct < mFieldCount; ct++) { + mFields[ct].offsetBits = bits; + bits += mFields[ct].e->getSizeBits() * mFields[ct].arraySize; + + if (mFields[ct].e->mHasReference) { + mHasReference = true; + } + } + +} const Element * Element::create(Context *rsc, RsDataType dt, RsDataKind dk, - bool isNorm, uint32_t vecSize) -{ + bool isNorm, uint32_t vecSize) { // Look for an existing match. for (uint32_t ct=0; ct < rsc->mStateElement.mElements.size(); ct++) { const Element *ee = rsc->mStateElement.mElements[ct]; @@ -103,14 +190,13 @@ const Element * Element::create(Context *rsc, RsDataType dt, RsDataKind dk, Element *e = new Element(rsc); e->mComponent.set(dt, dk, isNorm, vecSize); - e->mBits = e->mComponent.getBits(); + e->compute(); rsc->mStateElement.mElements.push(e); return e; } const Element * Element::create(Context *rsc, size_t count, const Element **ein, - const char **nin, const size_t * lengths) -{ + const char **nin, const size_t * lengths, const uint32_t *asin) { // Look for an existing match. for (uint32_t ct=0; ct < rsc->mStateElement.mElements.size(); ct++) { const Element *ee = rsc->mStateElement.mElements[ct]; @@ -119,7 +205,8 @@ const Element * Element::create(Context *rsc, size_t count, const Element **ein, for (uint32_t i=0; i < count; i++) { if ((ee->mFields[i].e.get() != ein[i]) || (ee->mFields[i].name.length() != lengths[i]) || - (ee->mFields[i].name != nin[i])) { + (ee->mFields[i].name != nin[i]) || + (ee->mFields[i].arraySize != asin[i])) { match = false; break; } @@ -137,35 +224,15 @@ const Element * Element::create(Context *rsc, size_t count, const Element **ein, for (size_t ct=0; ct < count; ct++) { e->mFields[ct].e.set(ein[ct]); e->mFields[ct].name.setTo(nin[ct], lengths[ct]); + e->mFields[ct].arraySize = asin[ct]; } + e->compute(); rsc->mStateElement.mElements.push(e); return e; } -String8 Element::getCStructBody(uint32_t indent) const -{ - String8 si; - for (uint32_t ct=0; ct < indent; ct++) { - si.append(" "); - } - - String8 s(si); - s.append("{\n"); - for (uint32_t ct = 0; ct < mFieldCount; ct++) { - s.append(si); - s.append(mFields[ct].e->getCType(indent+4)); - s.append(" "); - s.append(mFields[ct].name); - s.append(";\n"); - } - s.append(si); - s.append("}"); - return s; -} - -String8 Element::getCType(uint32_t indent) const -{ +String8 Element::getGLSLType(uint32_t indent) const { String8 s; for (uint32_t ct=0; ct < indent; ct++) { s.append(" "); @@ -173,45 +240,96 @@ String8 Element::getCType(uint32_t indent) const if (!mFieldCount) { // Basic component. - s.append(mComponent.getCType()); + s.append(mComponent.getGLSLType()); } else { - s.append("struct "); - s.append(getCStructBody(indent)); + rsAssert(0); + //s.append("struct "); + //s.append(getCStructBody(indent)); } return s; } -String8 Element::getGLSLType(uint32_t indent) const -{ - String8 s; - for (uint32_t ct=0; ct < indent; ct++) { - s.append(" "); +void Element::incRefs(const void *ptr) const { + if (!mFieldCount) { + if (mComponent.isReference()) { + ObjectBase *const*obp = static_cast<ObjectBase *const*>(ptr); + ObjectBase *ob = obp[0]; + if (ob) ob->incSysRef(); + } + return; } + const uint8_t *p = static_cast<const uint8_t *>(ptr); + for (uint32_t i=0; i < mFieldCount; i++) { + if (mFields[i].e->mHasReference) { + p = &p[mFields[i].offsetBits >> 3]; + for (uint32_t ct=0; ct < mFields[i].arraySize; ct++) { + mFields[i].e->incRefs(p); + p += mFields[i].e->getSizeBytes(); + } + } + } +} + +void Element::decRefs(const void *ptr) const { if (!mFieldCount) { - // Basic component. - s.append(mComponent.getGLSLType()); - } else { - rsAssert(0); - //s.append("struct "); - //s.append(getCStructBody(indent)); + if (mComponent.isReference()) { + ObjectBase *const*obp = static_cast<ObjectBase *const*>(ptr); + ObjectBase *ob = obp[0]; + if (ob) ob->decSysRef(); + } + return; } - return s; + const uint8_t *p = static_cast<const uint8_t *>(ptr); + for (uint32_t i=0; i < mFieldCount; i++) { + if (mFields[i].e->mHasReference) { + p = &p[mFields[i].offsetBits >> 3]; + for (uint32_t ct=0; ct < mFields[i].arraySize; ct++) { + mFields[i].e->decRefs(p); + p += mFields[i].e->getSizeBytes(); + } + } + } } - -ElementState::ElementState() -{ +ElementState::ElementState() { + const uint32_t initialCapacity = 32; + mBuilderElements.setCapacity(initialCapacity); + mBuilderNameStrings.setCapacity(initialCapacity); + mBuilderNameLengths.setCapacity(initialCapacity); + mBuilderArrays.setCapacity(initialCapacity); } -ElementState::~ElementState() -{ +ElementState::~ElementState() { rsAssert(!mElements.size()); } +void ElementState::elementBuilderBegin() { + mBuilderElements.clear(); + mBuilderNameStrings.clear(); + mBuilderNameLengths.clear(); + mBuilderArrays.clear(); +} + +void ElementState::elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize) { + mBuilderElements.push(e); + mBuilderNameStrings.push(nameStr); + mBuilderNameLengths.push(strlen(nameStr)); + mBuilderArrays.push(arraySize); + +} + +const Element *ElementState::elementBuilderCreate(Context *rsc) { + return Element::create(rsc, mBuilderElements.size(), + &(mBuilderElements.editArray()[0]), + &(mBuilderNameStrings.editArray()[0]), + mBuilderNameLengths.editArray(), + mBuilderArrays.editArray()); +} + ///////////////////////////////////////// // @@ -223,9 +341,7 @@ RsElement rsi_ElementCreate(Context *rsc, RsDataType dt, RsDataKind dk, bool norm, - uint32_t vecSize) -{ - //LOGE("rsi_ElementCreate %i %i %i %i", dt, dk, norm, vecSize); + uint32_t vecSize) { const Element *e = Element::create(rsc, dt, dk, norm, vecSize); e->incUserRef(); return (RsElement)e; @@ -235,14 +351,35 @@ RsElement rsi_ElementCreate2(Context *rsc, size_t count, const RsElement * ein, const char ** names, - const size_t * nameLengths) -{ - //LOGE("rsi_ElementCreate2 %i", count); - const Element *e = Element::create(rsc, count, (const Element **)ein, names, nameLengths); + const size_t * nameLengths, + const uint32_t * arraySizes) { + const Element *e = Element::create(rsc, count, (const Element **)ein, names, nameLengths, arraySizes); e->incUserRef(); return (RsElement)e; } +} +} + +void rsaElementGetNativeData(RsContext con, RsElement elem, uint32_t *elemData, uint32_t elemDataSize) { + rsAssert(elemDataSize == 5); + // we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements + Element *e = static_cast<Element *>(elem); + (*elemData++) = (uint32_t)e->getType(); + (*elemData++) = (uint32_t)e->getKind(); + (*elemData++) = e->getComponent().getIsNormalized() ? 1 : 0; + (*elemData++) = e->getComponent().getVectorSize(); + (*elemData++) = e->getFieldCount(); } + +void rsaElementGetSubElements(RsContext con, RsElement elem, uint32_t *ids, const char **names, uint32_t dataSize) { + Element *e = static_cast<Element *>(elem); + rsAssert(e->getFieldCount() == dataSize); + + for (uint32_t i = 0; i < dataSize; i ++) { + e->getField(i)->incUserRef(); + ids[i] = (uint32_t)e->getField(i); + names[i] = e->getFieldName(i); + } } diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h index 02a1ca2f7210..26e2760c124b 100644 --- a/libs/rs/rsElement.h +++ b/libs/rs/rsElement.h @@ -25,10 +25,8 @@ namespace android { namespace renderscript { - // An element is a group of Components that occupies one cell in a structure. -class Element : public ObjectBase -{ +class Element : public ObjectBase { public: ~Element(); @@ -40,30 +38,40 @@ public: return (getSizeBits() + 7) >> 3; } - size_t getFieldOffsetBits(uint32_t componentNumber) const; + size_t getFieldOffsetBits(uint32_t componentNumber) const { + return mFields[componentNumber].offsetBits; + } size_t getFieldOffsetBytes(uint32_t componentNumber) const { - return (getFieldOffsetBits(componentNumber) + 7) >> 3; + return mFields[componentNumber].offsetBits >> 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();} + uint32_t getFieldArraySize(uint32_t idx) const {return mFields[idx].arraySize;} const Component & getComponent() const {return mComponent;} RsDataType getType() const {return mComponent.getType();} RsDataKind getKind() const {return mComponent.getKind();} uint32_t getBits() const {return mBits;} - String8 getCType(uint32_t indent=0) const; - String8 getCStructBody(uint32_t indent=0) const; String8 getGLSLType(uint32_t indent=0) const; void dumpLOGV(const char *prefix) const; + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_ELEMENT; } + static Element *createFromStream(Context *rsc, IStream *stream); static const Element * create(Context *rsc, RsDataType dt, RsDataKind dk, bool isNorm, uint32_t vecSize); static const Element * create(Context *rsc, size_t count, const Element **, - const char **, const size_t * lengths); + const char **, const size_t * lengths, const uint32_t *asin); + + void incRefs(const void *) const; + void decRefs(const void *) const; + bool getHasReferences() const {return mHasReference;} + + bool isEqual(const Element *other) const; protected: // deallocate any components that are part of this element. @@ -72,15 +80,20 @@ protected: typedef struct { String8 name; ObjectBaseRef<const Element> e; + uint32_t offsetBits; + uint32_t arraySize; } ElementField_t; ElementField_t *mFields; size_t mFieldCount; + bool mHasReference; Element(Context *); Component mComponent; uint32_t mBits; + + void compute(); }; @@ -89,8 +102,17 @@ public: ElementState(); ~ElementState(); + void elementBuilderBegin(); + void elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize); + const Element *elementBuilderCreate(Context *rsc); + // Cache of all existing elements. - Vector<const Element *> mElements; + Vector<Element *> mElements; +private: + Vector<const Element *> mBuilderElements; + Vector<const char*> mBuilderNameStrings; + Vector<size_t> mBuilderNameLengths; + Vector<uint32_t> mBuilderArrays; }; diff --git a/libs/rs/rsFileA3D.cpp b/libs/rs/rsFileA3D.cpp index e3272c5d68e9..cd02c24b981f 100644 --- a/libs/rs/rsFileA3D.cpp +++ b/libs/rs/rsFileA3D.cpp @@ -16,80 +16,175 @@ */ #include "rsContext.h" - - -#include <utils/String8.h> #include "rsFileA3D.h" #include "rsMesh.h" +#include "rsAnimation.h" + using namespace android; using namespace android::renderscript; +FileA3D::FileA3D(Context *rsc) : ObjectBase(rsc) { + mAlloc = NULL; + mData = NULL; + mWriteStream = NULL; + mReadStream = NULL; + mAsset = NULL; + + mMajorVersion = 0; + mMinorVersion = 1; + mDataSize = 0; +} + +FileA3D::~FileA3D() { + for (size_t i = 0; i < mIndex.size(); i ++) { + delete mIndex[i]; + } + for (size_t i = 0; i < mWriteIndex.size(); i ++) { + delete mWriteIndex[i]; + } + if (mWriteStream) { + delete mWriteStream; + } + if (mReadStream) { + delete mWriteStream; + } + if (mAlloc) { + free(mAlloc); + } + if (mAsset) { + delete mAsset; + } +} +void FileA3D::parseHeader(IStream *headerStream) { + mMajorVersion = headerStream->loadU32(); + mMinorVersion = headerStream->loadU32(); + uint32_t flags = headerStream->loadU32(); + mUse64BitOffsets = (flags & 1) != 0; -FileA3D::FileA3D() -{ - mRsc = NULL; + uint32_t numIndexEntries = headerStream->loadU32(); + for (uint32_t i = 0; i < numIndexEntries; i ++) { + A3DIndexEntry *entry = new A3DIndexEntry(); + headerStream->loadString(&entry->mObjectName); + LOGV("Header data, entry name = %s", entry->mObjectName.string()); + entry->mType = (RsA3DClassID)headerStream->loadU32(); + if (mUse64BitOffsets){ + entry->mOffset = headerStream->loadOffset(); + entry->mLength = headerStream->loadOffset(); + } else { + entry->mOffset = headerStream->loadU32(); + entry->mLength = headerStream->loadU32(); + } + entry->mRsObj = NULL; + mIndex.push(entry); + } } -FileA3D::~FileA3D() -{ +bool FileA3D::load(Asset *asset) { + mAsset = asset; + return load(asset->getBuffer(false), asset->getLength()); } -bool FileA3D::load(Context *rsc, FILE *f) -{ +bool FileA3D::load(const void *data, size_t length) { + const uint8_t *localData = (const uint8_t *)data; + + size_t lengthRemaining = length; + size_t magicStrLen = 12; + if ((length < magicStrLen) || + memcmp(data, "Android3D_ff", magicStrLen)) { + return false; + } + + localData += magicStrLen; + lengthRemaining -= magicStrLen; + + // Next we get our header size + uint64_t headerSize = 0; + if (lengthRemaining < sizeof(headerSize)) { + return false; + } + + memcpy(&headerSize, localData, sizeof(headerSize)); + localData += sizeof(headerSize); + lengthRemaining -= sizeof(headerSize); + + if (lengthRemaining < headerSize) { + return false; + } + + // Now open the stream to parse the header + IStream headerStream(localData, false); + parseHeader(&headerStream); + + localData += headerSize; + lengthRemaining -= headerSize; + + if (lengthRemaining < sizeof(mDataSize)) { + return false; + } + + // Read the size of the data + memcpy(&mDataSize, localData, sizeof(mDataSize)); + localData += sizeof(mDataSize); + lengthRemaining -= sizeof(mDataSize); + + if (lengthRemaining < mDataSize) { + return false; + } + + // We should know enough to read the file in at this point. + mData = (uint8_t *)localData; + mReadStream = new IStream(mData, mUse64BitOffsets); + + return true; +} + +bool FileA3D::load(FILE *f) { char magicString[12]; size_t len; - LOGE("file open 1"); + LOGV("file open 1"); len = fread(magicString, 1, 12, f); if ((len != 12) || memcmp(magicString, "Android3D_ff", 12)) { return false; } - LOGE("file open 2"); - len = fread(&mMajorVersion, 1, sizeof(mMajorVersion), f); - if (len != sizeof(mMajorVersion)) { + // Next thing is the size of the header + uint64_t headerSize = 0; + len = fread(&headerSize, 1, sizeof(headerSize), f); + if (len != sizeof(headerSize) || headerSize == 0) { return false; } - LOGE("file open 3"); - len = fread(&mMinorVersion, 1, sizeof(mMinorVersion), f); - if (len != sizeof(mMinorVersion)) { + uint8_t *headerData = (uint8_t *)malloc(headerSize); + if (!headerData) { return false; } - LOGE("file open 4"); - uint32_t flags; - len = fread(&flags, 1, sizeof(flags), f); - if (len != sizeof(flags)) { + len = fread(headerData, 1, headerSize, f); + if (len != headerSize) { return false; } - mUse64BitOffsets = (flags & 1) != 0; - LOGE("file open 64bit = %i", mUse64BitOffsets); + // Now open the stream to parse the header + IStream headerStream(headerData, false); + parseHeader(&headerStream); - if (mUse64BitOffsets) { - len = fread(&mDataSize, 1, sizeof(mDataSize), f); - if (len != sizeof(mDataSize)) { - return false; - } - } else { - uint32_t tmp; - len = fread(&tmp, 1, sizeof(tmp), f); - if (len != sizeof(tmp)) { - return false; - } - mDataSize = tmp; + free(headerData); + + // Next thing is the size of the header + len = fread(&mDataSize, 1, sizeof(mDataSize), f); + if (len != sizeof(mDataSize) || mDataSize == 0) { + return false; } - LOGE("file open size = %lli", mDataSize); + LOGV("file open size = %lli", mDataSize); // We should know enough to read the file in at this point. - fseek(f, SEEK_SET, 0); - mAlloc= malloc(mDataSize); + mAlloc = malloc(mDataSize); if (!mAlloc) { return false; } @@ -99,288 +194,260 @@ bool FileA3D::load(Context *rsc, FILE *f) return false; } - LOGE("file start processing"); - return process(rsc); + mReadStream = new IStream(mData, mUse64BitOffsets); + + LOGV("Header is read an stream initialized"); + return true; } -bool FileA3D::processIndex(Context *rsc, A3DIndexEntry *ie) -{ - bool ret = false; - IO io(mData + ie->mOffset, mUse64BitOffsets); - - LOGE("process index, type %i", ie->mType); - - switch(ie->mType) { - case CHUNK_ELEMENT: - processChunk_Element(rsc, &io, ie); - break; - case CHUNK_ELEMENT_SOURCE: - processChunk_ElementSource(rsc, &io, ie); - break; - case CHUNK_VERTICIES: - processChunk_Verticies(rsc, &io, ie); - break; - case CHUNK_MESH: - processChunk_Mesh(rsc, &io, ie); - break; - case CHUNK_PRIMITIVE: - processChunk_Primitive(rsc, &io, ie); - break; - default: - LOGE("FileA3D Unknown chunk type"); - break; - } - return (ie->mRsObj != NULL); +size_t FileA3D::getNumIndexEntries() const { + return mIndex.size(); } -bool FileA3D::process(Context *rsc) -{ - LOGE("process"); - IO io(mData + 12, mUse64BitOffsets); - bool ret = true; - - // Build the index first - LOGE("process 1"); - io.loadU32(); // major version, already loaded - io.loadU32(); // minor version, already loaded - LOGE("process 2"); - - io.loadU32(); // flags - io.loadOffset(); // filesize, already loaded. - LOGE("process 4"); - uint64_t mIndexOffset = io.loadOffset(); - uint64_t mStringOffset = io.loadOffset(); - - LOGE("process mIndexOffset= 0x%016llx", mIndexOffset); - LOGE("process mStringOffset= 0x%016llx", mStringOffset); - - IO index(mData + mIndexOffset, mUse64BitOffsets); - IO stringTable(mData + mStringOffset, mUse64BitOffsets); - - uint32_t stringEntryCount = stringTable.loadU32(); - LOGE("stringEntryCount %i", stringEntryCount); - mStrings.setCapacity(stringEntryCount); - mStringIndexValues.setCapacity(stringEntryCount); - if (stringEntryCount) { - uint32_t stringType = stringTable.loadU32(); - LOGE("stringType %i", stringType); - rsAssert(stringType==0); - for (uint32_t ct = 0; ct < stringEntryCount; ct++) { - uint64_t offset = stringTable.loadOffset(); - LOGE("string offset 0x%016llx", offset); - IO tmp(mData + offset, mUse64BitOffsets); - String8 s; - tmp.loadString(&s); - LOGE("string %s", s.string()); - mStrings.push(s); - } +const FileA3D::A3DIndexEntry *FileA3D::getIndexEntry(size_t index) const { + if (index < mIndex.size()) { + return mIndex[index]; } + return NULL; +} - LOGE("strings done"); - uint32_t indexEntryCount = index.loadU32(); - LOGE("index count %i", indexEntryCount); - mIndex.setCapacity(indexEntryCount); - for (uint32_t ct = 0; ct < indexEntryCount; ct++) { - A3DIndexEntry e; - uint32_t stringIndex = index.loadU32(); - LOGE("index %i", ct); - LOGE(" string index %i", stringIndex); - e.mType = (A3DChunkType)index.loadU32(); - LOGE(" type %i", e.mType); - e.mOffset = index.loadOffset(); - LOGE(" offset 0x%016llx", e.mOffset); - - if (stringIndex && (stringIndex < mStrings.size())) { - e.mID = mStrings[stringIndex]; - mStringIndexValues.editItemAt(stringIndex) = ct; - LOGE(" id %s", e.mID.string()); - } +ObjectBase *FileA3D::initializeFromEntry(size_t index) { + if (index >= mIndex.size()) { + return NULL; + } - mIndex.push(e); + FileA3D::A3DIndexEntry *entry = mIndex[index]; + if (!entry) { + return NULL; } - LOGE("index done"); - // At this point the index should be fully populated. - // We can now walk though it and load all the objects. - for (uint32_t ct = 0; ct < indexEntryCount; ct++) { - LOGE("processing index entry %i", ct); - processIndex(rsc, &mIndex.editItemAt(ct)); + if (entry->mRsObj) { + entry->mRsObj->incUserRef(); + return entry->mRsObj; } - return ret; + // Seek to the beginning of object + mReadStream->reset(entry->mOffset); + switch (entry->mType) { + case RS_A3D_CLASS_ID_UNKNOWN: + return NULL; + case RS_A3D_CLASS_ID_MESH: + entry->mRsObj = Mesh::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_TYPE: + entry->mRsObj = Type::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_ELEMENT: + entry->mRsObj = Element::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_ALLOCATION: + entry->mRsObj = Allocation::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_PROGRAM_VERTEX: + //entry->mRsObj = ProgramVertex::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_PROGRAM_RASTER: + //entry->mRsObj = ProgramRaster::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_PROGRAM_FRAGMENT: + //entry->mRsObj = ProgramFragment::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_PROGRAM_STORE: + //entry->mRsObj = ProgramStore::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_SAMPLER: + //entry->mRsObj = Sampler::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_ANIMATION: + //entry->mRsObj = Animation::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_ADAPTER_1D: + //entry->mRsObj = Adapter1D::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_ADAPTER_2D: + //entry->mRsObj = Adapter2D::createFromStream(mRSC, mReadStream); + break; + case RS_A3D_CLASS_ID_SCRIPT_C: + break; + } + if (entry->mRsObj) { + entry->mRsObj->incUserRef(); + } + return entry->mRsObj; } +bool FileA3D::writeFile(const char *filename) { + if (!mWriteStream) { + LOGE("No objects to write\n"); + return false; + } + if (mWriteStream->getPos() == 0) { + LOGE("No objects to write\n"); + return false; + } -FileA3D::IO::IO(const uint8_t *buf, bool use64) -{ - mData = buf; - mPos = 0; - mUse64 = use64; -} + FILE *writeHandle = fopen(filename, "wb"); + if (!writeHandle) { + LOGE("Couldn't open the file for writing\n"); + return false; + } -uint64_t FileA3D::IO::loadOffset() -{ - uint64_t tmp; - if (mUse64) { - mPos = (mPos + 7) & (~7); - tmp = reinterpret_cast<const uint64_t *>(&mData[mPos])[0]; - mPos += sizeof(uint64_t); - return tmp; + // Open a new stream to make writing the header easier + OStream headerStream(5*1024, false); + headerStream.addU32(mMajorVersion); + headerStream.addU32(mMinorVersion); + uint32_t is64Bit = 0; + headerStream.addU32(is64Bit); + + uint32_t writeIndexSize = mWriteIndex.size(); + headerStream.addU32(writeIndexSize); + for (uint32_t i = 0; i < writeIndexSize; i ++) { + headerStream.addString(&mWriteIndex[i]->mObjectName); + headerStream.addU32((uint32_t)mWriteIndex[i]->mType); + if (mUse64BitOffsets){ + headerStream.addOffset(mWriteIndex[i]->mOffset); + headerStream.addOffset(mWriteIndex[i]->mLength); + } else { + uint32_t offset = (uint32_t)mWriteIndex[i]->mOffset; + headerStream.addU32(offset); + offset = (uint32_t)mWriteIndex[i]->mLength; + headerStream.addU32(offset); + } } - return loadU32(); -} -void FileA3D::IO::loadString(String8 *s) -{ - LOGE("loadString"); - uint32_t len = loadU32(); - LOGE("loadString len %i", len); - s->setTo((const char *)&mData[mPos], len); - mPos += len; -} + // Write our magic string so we know we are reading the right file + String8 magicString(A3D_MAGIC_KEY); + fwrite(magicString.string(), sizeof(char), magicString.size(), writeHandle); + // Store the size of the header to make it easier to parse when we read it + uint64_t headerSize = headerStream.getPos(); + fwrite(&headerSize, sizeof(headerSize), 1, writeHandle); -void FileA3D::processChunk_Mesh(Context *rsc, IO *io, A3DIndexEntry *ie) -{ - Mesh * m = new Mesh(rsc); + // Now write our header + fwrite(headerStream.getPtr(), sizeof(uint8_t), headerStream.getPos(), writeHandle); - m->mPrimitivesCount = io->loadU32(); - m->mPrimitives = new Mesh::Primitive_t *[m->mPrimitivesCount]; + // Now write the size of the data part of the file for easier parsing later + uint64_t fileDataSize = mWriteStream->getPos(); + fwrite(&fileDataSize, sizeof(fileDataSize), 1, writeHandle); - for (uint32_t ct = 0; ct < m->mPrimitivesCount; ct++) { - uint32_t index = io->loadU32(); + fwrite(mWriteStream->getPtr(), sizeof(uint8_t), mWriteStream->getPos(), writeHandle); - m->mPrimitives[ct] = (Mesh::Primitive_t *)mIndex[index].mRsObj; + int status = fclose(writeHandle); + + if (status != 0) { + LOGE("Couldn't close file\n"); + return false; } - ie->mRsObj = m; + + return true; } -void FileA3D::processChunk_Primitive(Context *rsc, IO *io, A3DIndexEntry *ie) -{ - Mesh::Primitive_t * p = new Mesh::Primitive_t; +void FileA3D::appendToFile(ObjectBase *obj) { + if (!obj) { + return; + } + if (!mWriteStream) { + const uint64_t initialStreamSize = 256*1024; + mWriteStream = new OStream(initialStreamSize, false); + } + A3DIndexEntry *indexEntry = new A3DIndexEntry(); + indexEntry->mObjectName.setTo(obj->getName()); + indexEntry->mType = obj->getClassId(); + indexEntry->mOffset = mWriteStream->getPos(); + indexEntry->mRsObj = obj; + mWriteIndex.push(indexEntry); + obj->serialize(mWriteStream); + indexEntry->mLength = mWriteStream->getPos() - indexEntry->mOffset; + mWriteStream->align(4); +} - p->mIndexCount = io->loadU32(); - uint32_t vertIdx = io->loadU32(); - p->mRestartCounts = io->loadU16(); - uint32_t bits = io->loadU8(); - p->mType = (RsPrimitive)io->loadU8(); +RsObjectBase rsaFileA3DGetEntryByIndex(RsContext con, uint32_t index, RsFile file) { + FileA3D *fa3d = static_cast<FileA3D *>(file); + if (!fa3d) { + LOGE("Can't load entry. No valid file"); + return NULL; + } - LOGE("processChunk_Primitive count %i, bits %i", p->mIndexCount, bits); + ObjectBase *obj = fa3d->initializeFromEntry(index); + LOGV("Returning object with name %s", obj->getName()); - p->mVerticies = (Mesh::Verticies_t *)mIndex[vertIdx].mRsObj; + return obj; +} - p->mIndicies = new uint16_t[p->mIndexCount]; - for (uint32_t ct = 0; ct < p->mIndexCount; ct++) { - switch(bits) { - case 8: - p->mIndicies[ct] = io->loadU8(); - break; - case 16: - p->mIndicies[ct] = io->loadU16(); - break; - case 32: - p->mIndicies[ct] = io->loadU32(); - break; - } - LOGE(" idx %i", p->mIndicies[ct]); - } - - if (p->mRestartCounts) { - p->mRestarts = new uint16_t[p->mRestartCounts]; - for (uint32_t ct = 0; ct < p->mRestartCounts; ct++) { - switch(bits) { - case 8: - p->mRestarts[ct] = io->loadU8(); - break; - case 16: - p->mRestarts[ct] = io->loadU16(); - break; - case 32: - p->mRestarts[ct] = io->loadU32(); - break; - } - LOGE(" idx %i", p->mRestarts[ct]); - } + +void rsaFileA3DGetNumIndexEntries(RsContext con, int32_t *numEntries, RsFile file) { + FileA3D *fa3d = static_cast<FileA3D *>(file); + + if (fa3d) { + *numEntries = fa3d->getNumIndexEntries(); } else { - p->mRestarts = NULL; + *numEntries = 0; } - - ie->mRsObj = p; } -void FileA3D::processChunk_Verticies(Context *rsc, IO *io, A3DIndexEntry *ie) -{ - Mesh::Verticies_t *cv = new Mesh::Verticies_t; - cv->mAllocationCount = io->loadU32(); - cv->mAllocations = new Allocation *[cv->mAllocationCount]; - LOGE("processChunk_Verticies count %i", cv->mAllocationCount); - for (uint32_t ct = 0; ct < cv->mAllocationCount; ct++) { - uint32_t i = io->loadU32(); - cv->mAllocations[ct] = (Allocation *)mIndex[i].mRsObj; - LOGE(" idx %i", i); - } - ie->mRsObj = cv; -} +void rsaFileA3DGetIndexEntries(RsContext con, RsFileIndexEntry *fileEntries, uint32_t numEntries, RsFile file) { + FileA3D *fa3d = static_cast<FileA3D *>(file); -void FileA3D::processChunk_Element(Context *rsc, IO *io, A3DIndexEntry *ie) -{ - /* - rsi_ElementBegin(rsc); - - uint32_t count = io->loadU32(); - LOGE("processChunk_Element count %i", count); - while (count--) { - RsDataKind dk = (RsDataKind)io->loadU8(); - RsDataType dt = (RsDataType)io->loadU8(); - uint32_t bits = io->loadU8(); - bool isNorm = io->loadU8() != 0; - LOGE(" %i %i %i %i", dk, dt, bits, isNorm); - rsi_ElementAdd(rsc, dk, dt, isNorm, bits, 0); - } - LOGE("processChunk_Element create"); - ie->mRsObj = rsi_ElementCreate(rsc); - */ -} + if (!fa3d) { + LOGE("Can't load index entries. No valid file"); + return; + } -void FileA3D::processChunk_ElementSource(Context *rsc, IO *io, A3DIndexEntry *ie) -{ - uint32_t index = io->loadU32(); - uint32_t count = io->loadU32(); + uint32_t numFileEntries = fa3d->getNumIndexEntries(); + if (numFileEntries != numEntries || numEntries == 0 || fileEntries == NULL) { + LOGE("Can't load index entries. Invalid number requested"); + return; + } - LOGE("processChunk_ElementSource count %i, index %i", count, index); + for (uint32_t i = 0; i < numFileEntries; i ++) { + const FileA3D::A3DIndexEntry *entry = fa3d->getIndexEntry(i); + fileEntries[i].classID = entry->getType(); + fileEntries[i].objectName = entry->getObjectName().string(); + } +} - RsElement e = (RsElement)mIndex[index].mRsObj; +RsFile rsaFileA3DCreateFromMemory(RsContext con, const void *data, uint32_t len) { + if (data == NULL) { + LOGE("File load failed. Asset stream is NULL"); + return NULL; + } - RsAllocation a = rsi_AllocationCreateSized(rsc, e, count); - Allocation * alloc = static_cast<Allocation *>(a); + Context *rsc = static_cast<Context *>(con); + FileA3D *fa3d = new FileA3D(rsc); + fa3d->incUserRef(); - float * data = (float *)alloc->getPtr(); - while(count--) { - *data = io->loadF(); - LOGE(" %f", *data); - data++; - } - ie->mRsObj = alloc; + fa3d->load(data, len); + return fa3d; } -namespace android { -namespace renderscript { +RsFile rsaFileA3DCreateFromAsset(RsContext con, void *_asset) { + Context *rsc = static_cast<Context *>(con); + Asset *asset = static_cast<Asset *>(_asset); + FileA3D *fa3d = new FileA3D(rsc); + fa3d->incUserRef(); + fa3d->load(asset); + return fa3d; +} -RsFile rsi_FileOpen(Context *rsc, char const *path, unsigned int len) -{ - FileA3D *fa3d = new FileA3D; +RsFile rsaFileA3DCreateFromFile(RsContext con, const char *path) { + if (path == NULL) { + LOGE("File load failed. Path is NULL"); + return NULL; + } - FILE *f = fopen("/sdcard/test.a3d", "rb"); + Context *rsc = static_cast<Context *>(con); + FileA3D *fa3d = NULL; + + FILE *f = fopen(path, "rb"); if (f) { - fa3d->load(rsc, f); + fa3d = new FileA3D(rsc); + fa3d->incUserRef(); + fa3d->load(f); fclose(f); - return fa3d; + } else { + LOGE("Could not open file %s", path); } - delete fa3d; - return NULL; -} - -} + return fa3d; } diff --git a/libs/rs/rsFileA3D.h b/libs/rs/rsFileA3D.h index 9ee08ec43f58..056b5af4c1d5 100644 --- a/libs/rs/rsFileA3D.h +++ b/libs/rs/rsFileA3D.h @@ -18,20 +18,23 @@ #define ANDROID_RS_FILE_A3D_H #include "RenderScript.h" -#include "rsFileA3DDecls.h" #include "rsMesh.h" #include <utils/String8.h> +#include <utils/Asset.h> +#include "rsStream.h" #include <stdio.h> +#define A3D_MAGIC_KEY "Android3D_ff" + // --------------------------------------------------------------------------- namespace android { + namespace renderscript { -class FileA3D -{ +class FileA3D : public ObjectBase { public: - FileA3D(); + FileA3D(Context *rsc); ~FileA3D(); uint32_t mMajorVersion; @@ -40,78 +43,55 @@ public: uint64_t mStringTableOffset; bool mUse64BitOffsets; - struct A3DIndexEntry { - String8 mID; - A3DChunkType mType; + class A3DIndexEntry { + String8 mObjectName; + RsA3DClassID mType; uint64_t mOffset; - void * mRsObj; - }; - - bool load(Context *rsc, FILE *f); - -protected: - class IO - { + uint64_t mLength; + ObjectBase *mRsObj; public: - IO(const uint8_t *, bool use64); - - float loadF() { - mPos = (mPos + 3) & (~3); - float tmp = reinterpret_cast<const float *>(&mData[mPos])[0]; - mPos += sizeof(float); - return tmp; - } - int32_t loadI32() { - mPos = (mPos + 3) & (~3); - int32_t tmp = reinterpret_cast<const int32_t *>(&mData[mPos])[0]; - mPos += sizeof(int32_t); - return tmp; + friend class FileA3D; + const String8 &getObjectName() const { + return mObjectName; } - uint32_t loadU32() { - mPos = (mPos + 3) & (~3); - uint32_t tmp = reinterpret_cast<const uint32_t *>(&mData[mPos])[0]; - mPos += sizeof(uint32_t); - return tmp; + RsA3DClassID getType() const { + return mType; } - uint16_t loadU16() { - mPos = (mPos + 1) & (~1); - uint16_t tmp = reinterpret_cast<const uint16_t *>(&mData[mPos])[0]; - mPos += sizeof(uint16_t); - return tmp; - } - uint8_t loadU8() { - uint8_t tmp = reinterpret_cast<const uint8_t *>(&mData[mPos])[0]; - mPos += sizeof(uint8_t); - return tmp; - } - uint64_t loadOffset(); - void loadString(String8 *s); - uint64_t getPos() const {return mPos;} - const uint8_t * getPtr() const; - protected: - const uint8_t * mData; - uint64_t mPos; - bool mUse64; }; + bool load(FILE *f); + bool load(Asset *asset); + bool load(const void *data, size_t length); + + size_t getNumIndexEntries() const; + const A3DIndexEntry* getIndexEntry(size_t index) const; + ObjectBase *initializeFromEntry(size_t index); + + void appendToFile(ObjectBase *obj); + bool writeFile(const char *filename); + + // Currently files do not get serialized, + // but we need to inherit from ObjectBase for ref tracking + virtual void serialize(OStream *stream) const { + } + virtual RsA3DClassID getClassId() const { + return RS_A3D_CLASS_ID_UNKNOWN; + } + +protected: - bool process(Context *rsc); - bool processIndex(Context *rsc, A3DIndexEntry *); - void processChunk_Mesh(Context *rsc, IO *io, A3DIndexEntry *ie); - void processChunk_Primitive(Context *rsc, IO *io, A3DIndexEntry *ie); - void processChunk_Verticies(Context *rsc, IO *io, A3DIndexEntry *ie); - void processChunk_Element(Context *rsc, IO *io, A3DIndexEntry *ie); - void processChunk_ElementSource(Context *rsc, IO *io, A3DIndexEntry *ie); + void parseHeader(IStream *headerStream); const uint8_t * mData; void * mAlloc; uint64_t mDataSize; - Context * mRsc; + Asset *mAsset; - Vector<A3DIndexEntry> mIndex; - Vector<String8> mStrings; - Vector<uint32_t> mStringIndexValues; + OStream *mWriteStream; + Vector<A3DIndexEntry*> mWriteIndex; + IStream *mReadStream; + Vector<A3DIndexEntry*> mIndex; }; diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp new file mode 100644 index 000000000000..01dbab8705de --- /dev/null +++ b/libs/rs/rsFont.cpp @@ -0,0 +1,846 @@ + +/* + * 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. + */ + +#include "rsContext.h" + +#include "rsFont.h" +#include "rsProgramFragment.h" +#include <cutils/properties.h> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_BITMAP_H + +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +using namespace android; +using namespace android::renderscript; + +Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL) { + mInitialized = false; + mHasKerning = false; + mFace = NULL; +} + +bool Font::init(const char *name, float fontSize, uint32_t dpi, const void *data, uint32_t dataLen) { + if (mInitialized) { + LOGE("Reinitialization of fonts not supported"); + return false; + } + + FT_Error error = 0; + if (data != NULL && dataLen > 0) { + error = FT_New_Memory_Face(mRSC->mStateFont.getLib(), (const FT_Byte*)data, dataLen, 0, &mFace); + } else { + error = FT_New_Face(mRSC->mStateFont.getLib(), name, 0, &mFace); + } + + if (error) { + LOGE("Unable to initialize font %s", name); + return false; + } + + mFontName = name; + mFontSize = fontSize; + mDpi = dpi; + + error = FT_Set_Char_Size(mFace, (FT_F26Dot6)(fontSize * 64.0f), 0, dpi, 0); + if (error) { + LOGE("Unable to set font size on %s", name); + return false; + } + + mHasKerning = FT_HAS_KERNING(mFace); + + mInitialized = true; + return true; +} + +void Font::preDestroy() const { + for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) { + if (mRSC->mStateFont.mActiveFonts[ct] == this) { + mRSC->mStateFont.mActiveFonts.removeAt(ct); + break; + } + } +} + +void Font::invalidateTextureCache() { + for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) { + mCachedGlyphs.valueAt(i)->mIsValid = false; + } +} + +void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y) { + FontState *state = &mRSC->mStateFont; + + int32_t nPenX = x + glyph->mBitmapLeft; + int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight; + + float u1 = glyph->mBitmapMinU; + float u2 = glyph->mBitmapMaxU; + float v1 = glyph->mBitmapMinV; + float v2 = glyph->mBitmapMaxV; + + int32_t width = (int32_t) glyph->mBitmapWidth; + int32_t height = (int32_t) glyph->mBitmapHeight; + + state->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); +} + +void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int32_t x, int32_t y, + uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) { + int32_t nPenX = x + glyph->mBitmapLeft; + int32_t nPenY = y + glyph->mBitmapTop; + + uint32_t endX = glyph->mBitmapMinX + glyph->mBitmapWidth; + uint32_t endY = glyph->mBitmapMinY + glyph->mBitmapHeight; + + FontState *state = &mRSC->mStateFont; + uint32_t cacheWidth = state->getCacheTextureType()->getDimX(); + const uint8_t* cacheBuffer = state->getTextTextureData(); + + uint32_t cacheX = 0, cacheY = 0; + int32_t bX = 0, bY = 0; + for (cacheX = glyph->mBitmapMinX, bX = nPenX; cacheX < endX; cacheX++, bX++) { + for (cacheY = glyph->mBitmapMinY, bY = nPenY; cacheY < endY; cacheY++, bY++) { + if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) { + LOGE("Skipping invalid index"); + continue; + } + uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX]; + bitmap[bY * bitmapW + bX] = tempCol; + } + } +} + +void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, Rect *bounds) { + int32_t nPenX = x + glyph->mBitmapLeft; + int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight; + + int32_t width = (int32_t) glyph->mBitmapWidth; + int32_t height = (int32_t) glyph->mBitmapHeight; + + // 0, 0 is top left, so bottom is a positive number + if (bounds->bottom < nPenY) { + bounds->bottom = nPenY; + } + if (bounds->left > nPenX) { + bounds->left = nPenX; + } + if (bounds->right < nPenX + width) { + bounds->right = nPenX + width; + } + if (bounds->top > nPenY - height) { + bounds->top = nPenY - height; + } +} + +void Font::renderUTF(const char *text, uint32_t len, int32_t x, int32_t y, + uint32_t start, int32_t numGlyphs, + RenderMode mode, Rect *bounds, + uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { + if (!mInitialized || numGlyphs == 0 || text == NULL || len == 0) { + return; + } + + if (mode == Font::MEASURE) { + if (bounds == NULL) { + LOGE("No return rectangle provided to measure text"); + return; + } + // Reset min and max of the bounding box to something large + bounds->set(1e6, -1e6, 1e6, -1e6); + } + + int32_t penX = x, penY = y; + int32_t glyphsLeft = 1; + if (numGlyphs > 0) { + glyphsLeft = numGlyphs; + } + + size_t index = start; + size_t nextIndex = 0; + + while (glyphsLeft > 0) { + + int32_t utfChar = utf32_from_utf8_at(text, len, index, &nextIndex); + + // Reached the end of the string or encountered + if (utfChar < 0) { + break; + } + + // Move to the next character in the array + index = nextIndex; + + CachedGlyphInfo *cachedGlyph = getCachedUTFChar(utfChar); + + // 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, penX, penY); + break; + case BITMAP: + drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH); + break; + case MEASURE: + measureCachedGlyph(cachedGlyph, penX, penY, bounds); + break; + } + } + + penX += (cachedGlyph->mAdvanceX >> 6); + + // If we were given a specific number of glyphs, decrement + if (numGlyphs > 0) { + glyphsLeft --; + } + } +} + +Font::CachedGlyphInfo* Font::getCachedUTFChar(int32_t utfChar) { + + CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar); + if (cachedGlyph == NULL) { + cachedGlyph = cacheGlyph((uint32_t)utfChar); + } + // Is the glyph still in texture cache? + if (!cachedGlyph->mIsValid) { + updateGlyphCache(cachedGlyph); + } + + return cachedGlyph; +} + +void Font::updateGlyphCache(CachedGlyphInfo *glyph) { + FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER ); + if (error) { + LOGE("Couldn't load glyph."); + return; + } + + glyph->mAdvanceX = mFace->glyph->advance.x; + glyph->mAdvanceY = mFace->glyph->advance.y; + glyph->mBitmapLeft = mFace->glyph->bitmap_left; + glyph->mBitmapTop = mFace->glyph->bitmap_top; + + FT_Bitmap *bitmap = &mFace->glyph->bitmap; + + // Now copy the bitmap into the cache texture + uint32_t startX = 0; + uint32_t startY = 0; + + // Let the font state figure out where to put the bitmap + FontState *state = &mRSC->mStateFont; + glyph->mIsValid = state->cacheBitmap(bitmap, &startX, &startY); + + if (!glyph->mIsValid) { + return; + } + + uint32_t endX = startX + bitmap->width; + uint32_t endY = startY + bitmap->rows; + + glyph->mBitmapMinX = startX; + glyph->mBitmapMinY = startY; + glyph->mBitmapWidth = bitmap->width; + glyph->mBitmapHeight = bitmap->rows; + + uint32_t cacheWidth = state->getCacheTextureType()->getDimX(); + uint32_t cacheHeight = state->getCacheTextureType()->getDimY(); + + glyph->mBitmapMinU = (float)startX / (float)cacheWidth; + glyph->mBitmapMinV = (float)startY / (float)cacheHeight; + glyph->mBitmapMaxU = (float)endX / (float)cacheWidth; + glyph->mBitmapMaxV = (float)endY / (float)cacheHeight; +} + +Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph) { + CachedGlyphInfo *newGlyph = new CachedGlyphInfo(); + mCachedGlyphs.add(glyph, newGlyph); + + newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph); + newGlyph->mIsValid = false; + + updateGlyphCache(newGlyph); + + return newGlyph; +} + +Font * Font::create(Context *rsc, const char *name, float fontSize, uint32_t dpi, + const void *data, uint32_t dataLen) { + rsc->mStateFont.checkInit(); + Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts; + + for (uint32_t i = 0; i < activeFonts.size(); i ++) { + Font *ithFont = activeFonts[i]; + if (ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) { + return ithFont; + } + } + + Font *newFont = new Font(rsc); + bool isInitialized = newFont->init(name, fontSize, dpi, data, dataLen); + if (isInitialized) { + activeFonts.push(newFont); + rsc->mStateFont.precacheLatin(newFont); + return newFont; + } + + ObjectBase::checkDelete(newFont); + return NULL; +} + +Font::~Font() { + if (mFace) { + FT_Done_Face(mFace); + } + + for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) { + CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i); + delete glyph; + } +} + +FontState::FontState() { + mInitialized = false; + mMaxNumberOfQuads = 1024; + mCurrentQuadIndex = 0; + mRSC = NULL; + mLibrary = NULL; + + // Get the renderer properties + char property[PROPERTY_VALUE_MAX]; + + // Get the gamma + float gamma = DEFAULT_TEXT_GAMMA; + if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) { + gamma = atof(property); + } + + // Get the black gamma threshold + int32_t blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD; + if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) { + blackThreshold = atoi(property); + } + mBlackThreshold = (float)(blackThreshold) / 255.0f; + + // Get the white gamma threshold + int32_t whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD; + if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) { + whiteThreshold = atoi(property); + } + mWhiteThreshold = (float)(whiteThreshold) / 255.0f; + + // Compute the gamma tables + mBlackGamma = gamma; + mWhiteGamma = 1.0f / gamma; + + setFontColor(0.1f, 0.1f, 0.1f, 1.0f); +} + +FontState::~FontState() { + for (uint32_t i = 0; i < mCacheLines.size(); i ++) { + delete mCacheLines[i]; + } + + rsAssert(!mActiveFonts.size()); +} + +FT_Library FontState::getLib() { + if (!mLibrary) { + FT_Error error = FT_Init_FreeType(&mLibrary); + if (error) { + LOGE("Unable to initialize freetype"); + return NULL; + } + } + + return mLibrary; +} + +void FontState::init(Context *rsc) { + mRSC = rsc; +} + +void FontState::flushAllAndInvalidate() { + if (mCurrentQuadIndex != 0) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } + for (uint32_t i = 0; i < mActiveFonts.size(); i ++) { + mActiveFonts[i]->invalidateTextureCache(); + } + for (uint32_t i = 0; i < mCacheLines.size(); i ++) { + mCacheLines[i]->mCurrentCol = 0; + } +} + +bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) { + // If the glyph is too tall, don't cache it + if ((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) { + LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); + return false; + } + + // Now copy the bitmap into the cache texture + uint32_t startX = 0; + uint32_t startY = 0; + + bool bitmapFit = false; + for (uint32_t i = 0; i < mCacheLines.size(); i ++) { + bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY); + if (bitmapFit) { + break; + } + } + + // If the new glyph didn't fit, flush the state so far and invalidate everything + if (!bitmapFit) { + flushAllAndInvalidate(); + + // Try to fit it again + for (uint32_t i = 0; i < mCacheLines.size(); i ++) { + bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY); + if (bitmapFit) { + break; + } + } + + // if we still don't fit, something is wrong and we shouldn't draw + if (!bitmapFit) { + LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); + return false; + } + } + + *retOriginX = startX; + *retOriginY = startY; + + uint32_t endX = startX + bitmap->width; + uint32_t endY = startY + bitmap->rows; + + uint32_t cacheWidth = getCacheTextureType()->getDimX(); + + uint8_t *cacheBuffer = (uint8_t*)mTextTexture->getPtr(); + uint8_t *bitmapBuffer = bitmap->buffer; + + uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; + for (cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) { + for (cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) { + uint8_t tempCol = bitmapBuffer[bY * bitmap->width + bX]; + cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol; + } + } + + // This will dirty the texture and the shader so next time + // we draw it will upload the data + mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT); + mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get()); + + // Some debug code + /*for (uint32_t i = 0; i < mCacheLines.size(); i ++) { + LOGE("Cache Line: H: %u Empty Space: %f", + mCacheLines[i]->mMaxHeight, + (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f); + + }*/ + + return true; +} + +void FontState::initRenderState() { + String8 shaderString("varying vec2 varTex0;\n"); + shaderString.append("void main() {\n"); + shaderString.append(" lowp vec4 col = UNI_Color;\n"); + shaderString.append(" col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n"); + shaderString.append(" col.a = pow(col.a, UNI_Gamma);\n"); + shaderString.append(" gl_FragColor = col;\n"); + shaderString.append("}\n"); + + const Element *colorElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4); + const Element *gammaElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1); + mRSC->mStateElement.elementBuilderBegin(); + mRSC->mStateElement.elementBuilderAdd(colorElem, "Color", 1); + mRSC->mStateElement.elementBuilderAdd(gammaElem, "Gamma", 1); + const Element *constInput = mRSC->mStateElement.elementBuilderCreate(mRSC); + + Type *inputType = Type::getType(mRSC, constInput, 1, 0, 0, false, false); + + uint32_t tmp[4]; + tmp[0] = RS_PROGRAM_PARAM_CONSTANT; + tmp[1] = (uint32_t)inputType; + tmp[2] = RS_PROGRAM_PARAM_TEXTURE_TYPE; + tmp[3] = RS_TEXTURE_2D; + + mFontShaderFConstant.set(new Allocation(mRSC, inputType, + RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS)); + ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(), + shaderString.length(), tmp, 4); + mFontShaderF.set(pf); + mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0); + + Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST, + RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP); + mFontSampler.set(sampler); + mFontShaderF->bindSampler(mRSC, 0, sampler); + + ProgramStore *fontStore = new ProgramStore(mRSC); + mFontProgramStore.set(fontStore); + mFontProgramStore->setDepthFunc(RS_DEPTH_FUNC_ALWAYS); + mFontProgramStore->setBlendFunc(RS_BLEND_SRC_SRC_ALPHA, RS_BLEND_DST_ONE_MINUS_SRC_ALPHA); + mFontProgramStore->setDitherEnable(false); + mFontProgramStore->setDepthMask(false); +} + +void FontState::initTextTexture() { + const Element *alphaElem = Element::create(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1); + + // We will allocate a texture to initially hold 32 character bitmaps + Type *texType = Type::getType(mRSC, alphaElem, 1024, 256, 0, false, false); + + Allocation *cacheAlloc = new Allocation(mRSC, texType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE); + mTextTexture.set(cacheAlloc); + mTextTexture->syncAll(mRSC, RS_ALLOCATION_USAGE_SCRIPT); + + // Split up our cache texture into lines of certain widths + int32_t nextLine = 0; + mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0)); +} + +// Avoid having to reallocate memory and render quad by quad +void FontState::initVertexArrayBuffers() { + // Now lets write index data + const Element *indexElem = Element::create(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1); + uint32_t numIndicies = mMaxNumberOfQuads * 6; + Type *indexType = Type::getType(mRSC, indexElem, numIndicies, 0, 0, false, false); + + Allocation *indexAlloc = new Allocation(mRSC, indexType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_VERTEX); + uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr(); + + // Four verts, two triangles , six indices per quad + for (uint32_t i = 0; i < mMaxNumberOfQuads; i ++) { + int32_t i6 = i * 6; + int32_t i4 = i * 4; + + indexPtr[i6 + 0] = i4 + 0; + indexPtr[i6 + 1] = i4 + 1; + indexPtr[i6 + 2] = i4 + 2; + + indexPtr[i6 + 3] = i4 + 0; + indexPtr[i6 + 4] = i4 + 2; + indexPtr[i6 + 5] = i4 + 3; + } + + indexAlloc->deferedUploadToBufferObject(mRSC); + mIndexBuffer.set(indexAlloc); + + const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3); + const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2); + + mRSC->mStateElement.elementBuilderBegin(); + mRSC->mStateElement.elementBuilderAdd(posElem, "position", 1); + mRSC->mStateElement.elementBuilderAdd(texElem, "texture0", 1); + const Element *vertexDataElem = mRSC->mStateElement.elementBuilderCreate(mRSC); + + Type *vertexDataType = Type::getType(mRSC, vertexDataElem, + mMaxNumberOfQuads * 4, + 0, 0, false, false); + + Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_VERTEX); + mTextMeshPtr = (float*)vertexAlloc->getPtr(); + + mVertexArray.set(vertexAlloc); +} + +// We don't want to allocate anything unless we actually draw text +void FontState::checkInit() { + if (mInitialized) { + return; + } + + initTextTexture(); + initRenderState(); + + initVertexArrayBuffers(); + + // We store a string with letters in a rough frequency of occurrence + mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq"); + mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ"); + mLatinPrecache += String8(",.?!()-+@;:`'"); + mLatinPrecache += String8("0123456789"); + + mInitialized = true; +} + +void FontState::issueDrawCommand() { + Context::PushState ps(mRSC); + + mRSC->setProgramVertex(mRSC->getDefaultProgramVertex()); + mRSC->setProgramRaster(mRSC->getDefaultProgramRaster()); + mRSC->setProgramFragment(mFontShaderF.get()); + mRSC->setProgramStore(mFontProgramStore.get()); + + if (mConstantsDirty) { + mFontShaderFConstant->data(mRSC, 0, 0, 1, &mConstants, sizeof(mConstants)); + mConstantsDirty = false; + } + + if (!mRSC->setupCheck()) { + return; + } + + float *vtx = (float*)mVertexArray->getPtr(); + float *tex = vtx + 3; + + VertexArray::Attrib attribs[2]; + attribs[0].set(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "ATTRIB_position"); + attribs[1].set(GL_FLOAT, 2, 20, false, (uint32_t)tex, "ATTRIB_texture0"); + VertexArray va(attribs, 2); + va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache); + + mIndexBuffer->uploadCheck(mRSC); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID()); + glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0)); +} + +void FontState::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) { + const uint32_t vertsPerQuad = 4; + const uint32_t floatsPerVert = 5; + 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) { + return; + } + + /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1); + LOGE("V1 x: %f y: %f z: %f", x2, y2, z2); + LOGE("V2 x: %f y: %f z: %f", x3, y3, z3); + LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/ + + (*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; + + mCurrentQuadIndex ++; + + if (mCurrentQuadIndex == mMaxNumberOfQuads) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } +} + +uint32_t FontState::getRemainingCacheCapacity() { + uint32_t remainingCapacity = 0; + uint32_t totalPixels = 0; + for (uint32_t i = 0; i < mCacheLines.size(); i ++) { + remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol); + totalPixels += mCacheLines[i]->mMaxWidth; + } + remainingCapacity = (remainingCapacity * 100) / totalPixels; + return remainingCapacity; +} + +void FontState::precacheLatin(Font *font) { + // Remaining capacity is measured in % + uint32_t remainingCapacity = getRemainingCacheCapacity(); + uint32_t precacheIdx = 0; + while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { + font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]); + remainingCapacity = getRemainingCacheCapacity(); + precacheIdx ++; + } +} + + +void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y, + uint32_t startIndex, int32_t numGlyphs, + Font::RenderMode mode, + Font::Rect *bounds, + uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { + checkInit(); + + // Render code here + Font *currentFont = mRSC->getFont(); + if (!currentFont) { + if (!mDefault.get()) { + String8 fontsDir("/fonts/DroidSans.ttf"); + String8 fullPath(getenv("ANDROID_ROOT")); + fullPath += fontsDir; + + mDefault.set(Font::create(mRSC, fullPath.string(), 8, mRSC->getDPI())); + } + currentFont = mDefault.get(); + } + if (!currentFont) { + LOGE("Unable to initialize any fonts"); + return; + } + + currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs, + mode, bounds, bitmap, bitmapW, bitmapH); + + if (mCurrentQuadIndex != 0) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } +} + +void FontState::measureText(const char *text, uint32_t len, Font::Rect *bounds) { + renderText(text, len, 0, 0, 0, -1, Font::MEASURE, bounds); + bounds->bottom = - bounds->bottom; + bounds->top = - bounds->top; +} + +void FontState::setFontColor(float r, float g, float b, float a) { + mConstants.mFontColor[0] = r; + mConstants.mFontColor[1] = g; + mConstants.mFontColor[2] = b; + mConstants.mFontColor[3] = a; + + mConstants.mGamma = 1.0f; + const float luminance = (r * 2.0f + g * 5.0f + b) / 8.0f; + if (luminance <= mBlackThreshold) { + mConstants.mGamma = mBlackGamma; + } else if (luminance >= mWhiteThreshold) { + mConstants.mGamma = mWhiteGamma; + } + + mConstantsDirty = true; +} + +void FontState::getFontColor(float *r, float *g, float *b, float *a) const { + *r = mConstants.mFontColor[0]; + *g = mConstants.mFontColor[1]; + *b = mConstants.mFontColor[2]; + *a = mConstants.mFontColor[3]; +} + +void FontState::deinit(Context *rsc) { + mInitialized = false; + + mFontShaderFConstant.clear(); + + mIndexBuffer.clear(); + mVertexArray.clear(); + + mFontShaderF.clear(); + mFontSampler.clear(); + mFontProgramStore.clear(); + + mTextTexture.clear(); + for (uint32_t i = 0; i < mCacheLines.size(); i ++) { + delete mCacheLines[i]; + } + mCacheLines.clear(); + + mDefault.clear(); + + if (mLibrary) { + FT_Done_FreeType( mLibrary ); + mLibrary = NULL; + } +} + +bool FontState::CacheTextureLine::fitBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) { + if ((uint32_t)bitmap->rows > mMaxHeight) { + return false; + } + + if (mCurrentCol + (uint32_t)bitmap->width < mMaxWidth) { + *retOriginX = mCurrentCol; + *retOriginY = mCurrentRow; + mCurrentCol += bitmap->width; + mDirty = true; + return true; + } + + return false; +} + +namespace android { +namespace renderscript { + +RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, float fontSize, uint32_t dpi) { + Font *newFont = Font::create(rsc, name, fontSize, dpi); + if (newFont) { + newFont->incUserRef(); + } + return newFont; +} + +RsFont rsi_FontCreateFromMemory(Context *rsc, char const *name, float fontSize, uint32_t dpi, const void *data, uint32_t dataLen) { + Font *newFont = Font::create(rsc, name, fontSize, dpi, data, dataLen); + if (newFont) { + newFont->incUserRef(); + } + return newFont; +} + +} // renderscript +} // android diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h new file mode 100644 index 000000000000..91a5da90d067 --- /dev/null +++ b/libs/rs/rsFont.h @@ -0,0 +1,256 @@ +/* + * 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. + */ + +#ifndef ANDROID_RS_FONT_H +#define ANDROID_RS_FONT_H + +#include "RenderScript.h" +#include "rsStream.h" +#include <utils/String8.h> +#include <utils/Vector.h> +#include <utils/KeyedVector.h> + +struct FT_LibraryRec_; +struct FT_FaceRec_; +struct FT_Bitmap_; + +// --------------------------------------------------------------------------- +namespace android { + +namespace renderscript { + +// Gamma (>= 1.0, <= 10.0) +#define PROPERTY_TEXT_GAMMA "ro.text_gamma" +#define PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD "ro.text_gamma.black_threshold" +#define PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD "ro.text_gamma.white_threshold" + +#define DEFAULT_TEXT_GAMMA 1.4f +#define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64 +#define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192 + +class FontState; + +class Font : public ObjectBase { +public: + enum RenderMode { + FRAMEBUFFER, + BITMAP, + MEASURE, + }; + + struct Rect { + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; + void set(int32_t l, int32_t r, int32_t t, int32_t b) { + left = l; + right = r; + top = t; + bottom = b; + } + }; + + ~Font(); + + // Currently files do not get serialized, + // but we need to inherit from ObjectBase for ref tracking + virtual void serialize(OStream *stream) const { + } + virtual RsA3DClassID getClassId() const { + return RS_A3D_CLASS_ID_UNKNOWN; + } + + static Font * create(Context *rsc, const char *name, float fontSize, uint32_t dpi, + const void *data = NULL, uint32_t dataLen = 0); + +protected: + + friend class FontState; + + // Pointer to the utf data, length of data, where to start, number of glyphs ot read + // (each glyph may be longer than a char because we are dealing with utf data) + // Last two variables are the initial pen position + void renderUTF(const char *text, uint32_t len, int32_t x, int32_t y, + uint32_t start, int32_t numGlyphs, + RenderMode mode = FRAMEBUFFER, Rect *bounds = NULL, + uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0); + + void invalidateTextureCache(); + 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 + uint32_t mBitmapMinX; + uint32_t mBitmapMinY; + 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 + int32_t mGlyphIndex; + int32_t mAdvanceX; + int32_t mAdvanceY; + // Values below contain a glyph's origin in the bitmap + int32_t mBitmapLeft; + int32_t mBitmapTop; + }; + + String8 mFontName; + float mFontSize; + uint32_t mDpi; + + Font(Context *rsc); + bool init(const char *name, float fontSize, uint32_t dpi, const void *data = NULL, uint32_t dataLen = 0); + + virtual void preDestroy() const; + FT_FaceRec_ *mFace; + bool mInitialized; + bool mHasKerning; + + DefaultKeyedVector<uint32_t, CachedGlyphInfo* > mCachedGlyphs; + CachedGlyphInfo* getCachedUTFChar(int32_t utfChar); + + CachedGlyphInfo *cacheGlyph(uint32_t glyph); + void updateGlyphCache(CachedGlyphInfo *glyph); + void measureCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, Rect *bounds); + void drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y); + void drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, + uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH); +}; + +class FontState { +public: + FontState(); + ~FontState(); + + void init(Context *rsc); + void deinit(Context *rsc); + + ObjectBaseRef<Font> mDefault; + ObjectBaseRef<Font> mLast; + + void renderText(const char *text, uint32_t len, int32_t x, int32_t y, + uint32_t startIndex = 0, int numGlyphs = -1, + Font::RenderMode mode = Font::FRAMEBUFFER, + Font::Rect *bounds = NULL, + uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0); + + void measureText(const char *text, uint32_t len, Font::Rect *bounds); + + void setFontColor(float r, float g, float b, float a); + void getFontColor(float *r, float *g, float *b, float *a) const; + +protected: + + friend class Font; + + struct CacheTextureLine { + uint32_t mMaxHeight; + uint32_t mMaxWidth; + uint32_t mCurrentRow; + uint32_t mCurrentCol; + bool mDirty; + + CacheTextureLine(uint32_t maxHeight, uint32_t maxWidth, uint32_t currentRow, uint32_t currentCol) + : mMaxHeight(maxHeight), mMaxWidth(maxWidth), mCurrentRow(currentRow), + mCurrentCol(currentCol), mDirty(false) { + } + + bool fitBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY); + }; + + Vector<CacheTextureLine*> mCacheLines; + uint32_t getRemainingCacheCapacity(); + + void precacheLatin(Font *font); + String8 mLatinPrecache; + + Context *mRSC; + + struct { + float mFontColor[4]; + float mGamma; + } mConstants; + bool mConstantsDirty; + + float mBlackGamma; + float mWhiteGamma; + + float mBlackThreshold; + float mWhiteThreshold; + + // Free type library, we only need one copy + FT_LibraryRec_ *mLibrary; + FT_LibraryRec_ *getLib(); + Vector<Font*> mActiveFonts; + + // Render state for the font + ObjectBaseRef<Allocation> mFontShaderFConstant; + ObjectBaseRef<ProgramFragment> mFontShaderF; + ObjectBaseRef<Sampler> mFontSampler; + ObjectBaseRef<ProgramStore> mFontProgramStore; + void initRenderState(); + + // Texture to cache glyph bitmaps + ObjectBaseRef<Allocation> mTextTexture; + void initTextTexture(); + const uint8_t* getTextTextureData() const { + return (uint8_t*)mTextTexture->getPtr(); + } + + bool cacheBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY); + const Type* getCacheTextureType() { + return mTextTexture->getType(); + } + + void flushAllAndInvalidate(); + + // Pointer to vertex data to speed up frame to frame work + float *mTextMeshPtr; + uint32_t mCurrentQuadIndex; + uint32_t mMaxNumberOfQuads; + + void initVertexArrayBuffers(); + ObjectBaseRef<Allocation> mIndexBuffer; + ObjectBaseRef<Allocation> mVertexArray; + + + bool mInitialized; + + void checkInit(); + + 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); +}; + +} +} + +#endif diff --git a/libs/rs/rsHandcode.h b/libs/rs/rsHandcode.h index 800eddde7052..57da10a65b1f 100644 --- a/libs/rs/rsHandcode.h +++ b/libs/rs/rsHandcode.h @@ -1,47 +1,96 @@ #define DATA_SYNC_SIZE 1024 -static inline void rsHCAPI_AllocationData (RsContext rsc, RsAllocation va, const void * data, uint32_t sizeBytes) -{ +static inline void rsHCAPI_ContextFinish (RsContext rsc) { ThreadIO *io = &((Context *)rsc)->mIO; - uint32_t size = sizeof(RS_CMD_AllocationData); + uint32_t size = sizeof(RS_CMD_ContextFinish); + io->mToCore.commitSync(RS_CMD_ID_ContextFinish, size); +} + +static inline void rsHCAPI_ScriptInvokeV (RsContext rsc, RsScript va, uint32_t slot, const void * data, uint32_t sizeBytes) { + ThreadIO *io = &((Context *)rsc)->mIO; + uint32_t size = sizeof(RS_CMD_ScriptInvokeV); if (sizeBytes < DATA_SYNC_SIZE) { size += (sizeBytes + 3) & ~3; } - RS_CMD_AllocationData *cmd = static_cast<RS_CMD_AllocationData *>(io->mToCore.reserve(size)); - cmd->va = va; - cmd->bytes = sizeBytes; + RS_CMD_ScriptInvokeV *cmd = static_cast<RS_CMD_ScriptInvokeV *>(io->mToCore.reserve(size)); + cmd->s = va; + cmd->slot = slot; + cmd->dataLen = sizeBytes; cmd->data = data; if (sizeBytes < DATA_SYNC_SIZE) { cmd->data = (void *)(cmd+1); memcpy(cmd+1, data, sizeBytes); - io->mToCore.commit(RS_CMD_ID_AllocationData, size); + io->mToCore.commit(RS_CMD_ID_ScriptInvokeV, size); } else { - io->mToCore.commitSync(RS_CMD_ID_AllocationData, size); + io->mToCore.commitSync(RS_CMD_ID_ScriptInvokeV, size); } } -static inline void rsHCAPI_Allocation1DSubData (RsContext rsc, RsAllocation va, uint32_t xoff, uint32_t count, const void * data, uint32_t sizeBytes) -{ +static inline void rsHCAPI_ScriptSetVarV (RsContext rsc, RsScript va, uint32_t slot, const void * data, uint32_t sizeBytes) { + ThreadIO *io = &((Context *)rsc)->mIO; + uint32_t size = sizeof(RS_CMD_ScriptSetVarV); + if (sizeBytes < DATA_SYNC_SIZE) { + size += (sizeBytes + 3) & ~3; + } + RS_CMD_ScriptSetVarV *cmd = static_cast<RS_CMD_ScriptSetVarV *>(io->mToCore.reserve(size)); + cmd->s = va; + cmd->slot = slot; + cmd->dataLen = sizeBytes; + cmd->data = data; + if (sizeBytes < DATA_SYNC_SIZE) { + cmd->data = (void *)(cmd+1); + memcpy(cmd+1, data, sizeBytes); + io->mToCore.commit(RS_CMD_ID_ScriptSetVarV, size); + } else { + io->mToCore.commitSync(RS_CMD_ID_ScriptSetVarV, size); + } +} + +static inline void rsHCAPI_Allocation1DData (RsContext rsc, RsAllocation va, uint32_t xoff, uint32_t lod, + uint32_t count, const void * data, uint32_t sizeBytes) { ThreadIO *io = &((Context *)rsc)->mIO; - uint32_t size = sizeof(RS_CMD_Allocation1DSubData); + uint32_t size = sizeof(RS_CMD_Allocation1DData); if (sizeBytes < DATA_SYNC_SIZE) { size += (sizeBytes + 3) & ~3; } - RS_CMD_Allocation1DSubData *cmd = static_cast<RS_CMD_Allocation1DSubData *>(io->mToCore.reserve(size)); + RS_CMD_Allocation1DData *cmd = static_cast<RS_CMD_Allocation1DData *>(io->mToCore.reserve(size)); cmd->va = va; cmd->xoff = xoff; + cmd->lod = lod; cmd->count = count; cmd->data = data; cmd->bytes = sizeBytes; if (sizeBytes < DATA_SYNC_SIZE) { cmd->data = (void *)(cmd+1); memcpy(cmd+1, data, sizeBytes); - io->mToCore.commit(RS_CMD_ID_Allocation1DSubData, size); + io->mToCore.commit(RS_CMD_ID_Allocation1DData, size); } else { - io->mToCore.commitSync(RS_CMD_ID_Allocation1DSubData, size); + io->mToCore.commitSync(RS_CMD_ID_Allocation1DData, size); } +} +static inline void rsHCAPI_Allocation1DElementData (RsContext rsc, RsAllocation va, uint32_t x, uint32_t lod, + const void * data, uint32_t comp_offset, uint32_t sizeBytes) { + ThreadIO *io = &((Context *)rsc)->mIO; + uint32_t size = sizeof(RS_CMD_Allocation1DElementData); + if (sizeBytes < DATA_SYNC_SIZE) { + size += (sizeBytes + 3) & ~3; + } + RS_CMD_Allocation1DElementData *cmd = static_cast<RS_CMD_Allocation1DElementData *>(io->mToCore.reserve(size)); + cmd->va = va; + cmd->x = x; + cmd->lod = lod; + cmd->data = data; + cmd->comp_offset = comp_offset; + cmd->bytes = sizeBytes; + if (sizeBytes < DATA_SYNC_SIZE) { + cmd->data = (void *)(cmd+1); + memcpy(cmd+1, data, sizeBytes); + io->mToCore.commit(RS_CMD_ID_Allocation1DElementData, size); + } else { + io->mToCore.commitSync(RS_CMD_ID_Allocation1DElementData, size); + } } diff --git a/libs/rs/rsLight.cpp b/libs/rs/rsLight.cpp deleted file mode 100644 index 6f2cf3e7f7f6..000000000000 --- a/libs/rs/rsLight.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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. - */ - -#include "rsContext.h" - -#include <GLES/gl.h> - -using namespace android; -using namespace android::renderscript; - - -Light::Light(Context *rsc, bool isLocal, bool isMono) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - mIsLocal = isLocal; - mIsMono = isMono; - - mPosition[0] = 0; - mPosition[1] = 0; - mPosition[2] = 1; - mPosition[3] = 0; - - mColor[0] = 1.f; - mColor[1] = 1.f; - mColor[2] = 1.f; - mColor[3] = 1.f; -} - -Light::~Light() -{ -} - -void Light::setPosition(float x, float y, float z) -{ - mPosition[0] = x; - mPosition[1] = y; - mPosition[2] = z; -} - -void Light::setColor(float r, float g, float b) -{ - mColor[0] = r; - mColor[1] = g; - mColor[2] = b; -} - -void Light::setupGL(uint32_t num) const -{ - glLightfv(GL_LIGHT0 + num, GL_DIFFUSE, mColor); - glLightfv(GL_LIGHT0 + num, GL_SPECULAR, mColor); - glLightfv(GL_LIGHT0 + num, GL_POSITION, mPosition); -} - -//////////////////////////////////////////// - -LightState::LightState() -{ - clear(); -} - -LightState::~LightState() -{ -} - -void LightState::clear() -{ - mIsLocal = false; - mIsMono = false; -} - - -//////////////////////////////////////////////////// -// - -namespace android { -namespace renderscript { - -void rsi_LightBegin(Context *rsc) -{ - rsc->mStateLight.clear(); -} - -void rsi_LightSetLocal(Context *rsc, bool isLocal) -{ - rsc->mStateLight.mIsLocal = isLocal; -} - -void rsi_LightSetMonochromatic(Context *rsc, bool isMono) -{ - rsc->mStateLight.mIsMono = isMono; -} - -RsLight rsi_LightCreate(Context *rsc) -{ - Light *l = new Light(rsc, rsc->mStateLight.mIsLocal, - rsc->mStateLight.mIsMono); - l->incUserRef(); - return l; -} - -void rsi_LightSetColor(Context *rsc, RsLight vl, float r, float g, float b) -{ - Light *l = static_cast<Light *>(vl); - l->setColor(r, g, b); -} - -void rsi_LightSetPosition(Context *rsc, RsLight vl, float x, float y, float z) -{ - Light *l = static_cast<Light *>(vl); - l->setPosition(x, y, z); -} - - - -} -} diff --git a/libs/rs/rsLight.h b/libs/rs/rsLight.h deleted file mode 100644 index d8796e6814af..000000000000 --- a/libs/rs/rsLight.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_LIGHT_H -#define ANDROID_LIGHT_H - - -#include "rsObjectBase.h" - -// --------------------------------------------------------------------------- -namespace android { -namespace renderscript { - - -// An element is a group of Components that occupies one cell in a structure. -class Light : public ObjectBase -{ -public: - Light(Context *, bool isLocal, bool isMono); - virtual ~Light(); - - // Values, mutable after creation. - void setPosition(float x, float y, float z); - void setColor(float r, float g, float b); - - void setupGL(uint32_t num) const; - -protected: - float mColor[4]; - float mPosition[4]; - bool mIsLocal; - bool mIsMono; -}; - - -class LightState { -public: - LightState(); - ~LightState(); - - void clear(); - - bool mIsMono; - bool mIsLocal; -}; - - -} -} -#endif //ANDROID_LIGHT_H - diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp index c79652016f6e..70b72783863e 100644 --- a/libs/rs/rsLocklessFifo.cpp +++ b/libs/rs/rsLocklessFifo.cpp @@ -15,30 +15,28 @@ */ #include "rsLocklessFifo.h" +#include "utils/Timers.h" +#include "utils/StopWatch.h" using namespace android; +using namespace android::renderscript; - -LocklessCommandFifo::LocklessCommandFifo() -{ +LocklessCommandFifo::LocklessCommandFifo() { } -LocklessCommandFifo::~LocklessCommandFifo() -{ +LocklessCommandFifo::~LocklessCommandFifo() { if (!mInShutdown) { shutdown(); } free(mBuffer); } -void LocklessCommandFifo::shutdown() -{ +void LocklessCommandFifo::shutdown() { mInShutdown = true; mSignalToWorker.set(); } -bool LocklessCommandFifo::init(uint32_t sizeInBytes) -{ +bool LocklessCommandFifo::init(uint32_t sizeInBytes) { // Add room for a buffer reset command mBuffer = static_cast<uint8_t *>(malloc(sizeInBytes + 4)); if (!mBuffer) { @@ -61,8 +59,7 @@ bool LocklessCommandFifo::init(uint32_t sizeInBytes) return true; } -uint32_t LocklessCommandFifo::getFreeSpace() const -{ +uint32_t LocklessCommandFifo::getFreeSpace() const { int32_t freeSpace = 0; //dumpState("getFreeSpace"); @@ -78,14 +75,13 @@ uint32_t LocklessCommandFifo::getFreeSpace() const return freeSpace; } -bool LocklessCommandFifo::isEmpty() const -{ - return mPut == mGet; +bool LocklessCommandFifo::isEmpty() const { + uint32_t p = android_atomic_acquire_load((int32_t *)&mPut); + return ((uint8_t *)p) == mGet; } -void * LocklessCommandFifo::reserve(uint32_t sizeInBytes) -{ +void * LocklessCommandFifo::reserve(uint32_t sizeInBytes) { // Add space for command header and loop token; sizeInBytes += 8; @@ -97,46 +93,51 @@ void * LocklessCommandFifo::reserve(uint32_t sizeInBytes) return mPut + 4; } -void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes) -{ +void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes) { if (mInShutdown) { return; } //dumpState("commit 1"); reinterpret_cast<uint16_t *>(mPut)[0] = command; reinterpret_cast<uint16_t *>(mPut)[1] = sizeInBytes; - mPut += ((sizeInBytes + 3) & ~3) + 4; + + int32_t s = ((sizeInBytes + 3) & ~3) + 4; + android_atomic_add(s, (int32_t *)&mPut); //dumpState("commit 2"); mSignalToWorker.set(); } -void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes) -{ +void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes) { if (mInShutdown) { return; } + + //char buf[1024]; + //sprintf(buf, "RenderScript LocklessCommandFifo::commitSync %p %i %i", this, command, sizeInBytes); + //StopWatch compileTimer(buf); commit(command, sizeInBytes); flush(); } -void LocklessCommandFifo::flush() -{ +void LocklessCommandFifo::flush() { //dumpState("flush 1"); - while(mPut != mGet) { + while (mPut != mGet) { mSignalToControl.wait(); } //dumpState("flush 2"); } -const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData) -{ - while(1) { - //dumpState("get"); - while(isEmpty() && !mInShutdown) { - mSignalToControl.set(); - mSignalToWorker.wait(); - } +void LocklessCommandFifo::wait() { + while (isEmpty() && !mInShutdown) { + mSignalToControl.set(); + mSignalToWorker.wait(); + } +} +const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData) { + while (1) { + //dumpState("get"); + wait(); if (mInShutdown) { *command = 0; *bytesData = 0; @@ -155,116 +156,78 @@ const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData) } } -void LocklessCommandFifo::next() -{ +void LocklessCommandFifo::next() { uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1]; - mGet += ((bytes + 3) & ~3) + 4; + + android_atomic_add(((bytes + 3) & ~3) + 4, (int32_t *)&mGet); + //mGet += ((bytes + 3) & ~3) + 4; if (isEmpty()) { mSignalToControl.set(); } //dumpState("next"); } -void LocklessCommandFifo::makeSpace(uint32_t bytes) -{ - //dumpState("make space"); +bool LocklessCommandFifo::makeSpaceNonBlocking(uint32_t bytes) { + //dumpState("make space non-blocking"); if ((mPut+bytes) > mEnd) { // Need to loop regardless of where get is. - while((mGet > mPut) && (mBuffer+4 >= mGet)) { - usleep(100); + if ((mGet > mPut) || (mBuffer+4 >= mGet)) { + return false; } // Toss in a reset then the normal wait for space will do the rest. reinterpret_cast<uint16_t *>(mPut)[0] = 0; reinterpret_cast<uint16_t *>(mPut)[1] = 0; mPut = mBuffer; + mSignalToWorker.set(); } // it will fit here so we just need to wait for space. - while(getFreeSpace() < bytes) { - usleep(100); - } - -} - -void LocklessCommandFifo::dumpState(const char *s) const -{ - LOGV("%s put %p, get %p, buf %p, end %p", s, mPut, mGet, mBuffer, mEnd); -} - -LocklessCommandFifo::Signal::Signal() -{ - mSet = true; -} - -LocklessCommandFifo::Signal::~Signal() -{ - pthread_mutex_destroy(&mMutex); - pthread_cond_destroy(&mCondition); -} - -bool LocklessCommandFifo::Signal::init() -{ - int status = pthread_mutex_init(&mMutex, NULL); - if (status) { - LOGE("LocklessFifo mutex init failure"); - return false; - } - - status = pthread_cond_init(&mCondition, NULL); - if (status) { - LOGE("LocklessFifo condition init failure"); - pthread_mutex_destroy(&mMutex); + if (getFreeSpace() < bytes) { return false; } return true; } -void LocklessCommandFifo::Signal::set() -{ - int status; +void LocklessCommandFifo::makeSpace(uint32_t bytes) { + //dumpState("make space"); + if ((mPut+bytes) > mEnd) { + // Need to loop regardless of where get is. + while ((mGet > mPut) || (mBuffer+4 >= mGet)) { + usleep(100); + } - status = pthread_mutex_lock(&mMutex); - if (status) { - LOGE("LocklessCommandFifo: error %i locking for set condition.", status); - return; + // Toss in a reset then the normal wait for space will do the rest. + reinterpret_cast<uint16_t *>(mPut)[0] = 0; + reinterpret_cast<uint16_t *>(mPut)[1] = 0; + mPut = mBuffer; + mSignalToWorker.set(); } - mSet = true; - - status = pthread_cond_signal(&mCondition); - if (status) { - LOGE("LocklessCommandFifo: error %i on set condition.", status); + // it will fit here so we just need to wait for space. + while (getFreeSpace() < bytes) { + usleep(100); } - status = pthread_mutex_unlock(&mMutex); - if (status) { - LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status); - } } -void LocklessCommandFifo::Signal::wait() -{ - int status; +void LocklessCommandFifo::dumpState(const char *s) const { + LOGV("%s %p put %p, get %p, buf %p, end %p", s, this, mPut, mGet, mBuffer, mEnd); +} - status = pthread_mutex_lock(&mMutex); - if (status) { - LOGE("LocklessCommandFifo: error %i locking for condition.", status); - return; +void LocklessCommandFifo::printDebugData() const { + dumpState("printing fifo debug"); + const uint32_t *pptr = (const uint32_t *)mGet; + pptr -= 8 * 4; + if (mGet < mBuffer) { + pptr = (const uint32_t *)mBuffer; } - if (!mSet) { - status = pthread_cond_wait(&mCondition, &mMutex); - if (status) { - LOGE("LocklessCommandFifo: error %i waiting on condition.", status); - } - } - mSet = false; - status = pthread_mutex_unlock(&mMutex); - if (status) { - LOGE("LocklessCommandFifo: error %i unlocking for condition.", status); + for (int ct=0; ct < 16; ct++) { + LOGV("fifo %p = 0x%08x 0x%08x 0x%08x 0x%08x", pptr, pptr[0], pptr[1], pptr[2], pptr[3]); + pptr += 4; } -} +} diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h index d0a4356a6cf6..eabdc3e97f05 100644 --- a/libs/rs/rsLocklessFifo.h +++ b/libs/rs/rsLocklessFifo.h @@ -19,8 +19,10 @@ #include "rsUtils.h" +#include "rsSignal.h" namespace android { +namespace renderscript { // A simple FIFO to be used as a producer / consumer between two @@ -28,33 +30,17 @@ namespace android { // will not require locking. It is not threadsafe for multiple // readers or writers by design. -class LocklessCommandFifo -{ +class LocklessCommandFifo { public: bool init(uint32_t size); void shutdown(); + void printDebugData() const; + LocklessCommandFifo(); ~LocklessCommandFifo(); - protected: - class Signal { - public: - Signal(); - ~Signal(); - - bool init(); - - void set(); - void wait(); - - protected: - bool mSet; - pthread_mutex_t mMutex; - pthread_cond_t mCondition; - }; - uint8_t * volatile mPut; uint8_t * volatile mGet; uint8_t * mBuffer; @@ -65,27 +51,28 @@ protected: Signal mSignalToWorker; Signal mSignalToControl; - - public: void * reserve(uint32_t bytes); void commit(uint32_t command, uint32_t bytes); void commitSync(uint32_t command, uint32_t bytes); void flush(); + void wait(); + const void * get(uint32_t *command, uint32_t *bytesData); void next(); void makeSpace(uint32_t bytes); + bool makeSpaceNonBlocking(uint32_t bytes); bool isEmpty() const; uint32_t getFreeSpace() const; - private: void dumpState(const char *) const; }; } +} #endif diff --git a/libs/rs/rsMatrix.cpp b/libs/rs/rsMatrix.cpp index 2f2140523c4e..ca41886631f4 100644 --- a/libs/rs/rsMatrix.cpp +++ b/libs/rs/rsMatrix.cpp @@ -23,10 +23,7 @@ using namespace android; using namespace android::renderscript; - - -void Matrix::loadIdentity() -{ +void Matrix::loadIdentity() { set(0, 0, 1); set(1, 0, 0); set(2, 0, 0); @@ -48,18 +45,15 @@ void Matrix::loadIdentity() set(3, 3, 1); } -void Matrix::load(const float *v) -{ +void Matrix::load(const float *v) { memcpy(m, v, sizeof(m)); } -void Matrix::load(const Matrix *v) -{ +void Matrix::load(const Matrix *v) { memcpy(m, v->m, sizeof(m)); } -void Matrix::loadRotate(float rot, float x, float y, float z) -{ +void Matrix::loadRotate(float rot, float x, float y, float z) { float c, s; m[3] = 0; m[7] = 0; @@ -73,7 +67,7 @@ void Matrix::loadRotate(float rot, float x, float y, float z) s = sinf(rot); const float len = sqrtf(x*x + y*y + z*z); - if (!(len != 1)) { + if (len != 1) { const float recipLen = 1.f / len; x *= recipLen; y *= recipLen; @@ -97,24 +91,21 @@ void Matrix::loadRotate(float rot, float x, float y, float z) m[10] = z*z*nc + c; } -void Matrix::loadScale(float x, float y, float z) -{ +void Matrix::loadScale(float x, float y, float z) { loadIdentity(); m[0] = x; m[5] = y; m[10] = z; } -void Matrix::loadTranslate(float x, float y, float z) -{ +void Matrix::loadTranslate(float x, float y, float z) { loadIdentity(); m[12] = x; m[13] = y; m[14] = z; } -void Matrix::loadMultiply(const Matrix *lhs, const Matrix *rhs) -{ +void Matrix::loadMultiply(const Matrix *lhs, const Matrix *rhs) { for (int i=0 ; i<4 ; i++) { float ri0 = 0; float ri1 = 0; diff --git a/libs/rs/rsMatrix.h b/libs/rs/rsMatrix.h index 11ce42eb4a86..4130b8e5afd4 100644 --- a/libs/rs/rsMatrix.h +++ b/libs/rs/rsMatrix.h @@ -23,8 +23,7 @@ namespace android { namespace renderscript { -struct Matrix -{ +struct Matrix { float m[16]; inline float get(int i, int j) const { @@ -69,13 +68,8 @@ struct Matrix tmp.loadTranslate(x, y, z); multiply(&tmp); } - - - }; - - } } diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp index d595b4e09e5e..76fe62da8830 100644 --- a/libs/rs/rsMesh.cpp +++ b/libs/rs/rsMesh.cpp @@ -15,34 +15,413 @@ */ #include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE +#include <GLES/gl.h> +#include <GLES2/gl2.h> +#include <GLES/glext.h> +#endif using namespace android; using namespace android::renderscript; -#include <GLES/gl.h> -#include <GLES/glext.h> - -Mesh::Mesh(Context *rsc) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - mVerticies = NULL; - mVerticiesCount = 0; +Mesh::Mesh(Context *rsc) : ObjectBase(rsc) { mPrimitives = NULL; mPrimitivesCount = 0; + mVertexBuffers = NULL; + mVertexBufferCount = 0; + +#ifndef ANDROID_RS_SERIALIZE + mAttribs = NULL; + mAttribAllocationIndex = NULL; + + mAttribCount = 0; +#endif +} + +Mesh::~Mesh() { + if (mVertexBuffers) { + delete[] mVertexBuffers; + } + + if (mPrimitives) { + for (uint32_t i = 0; i < mPrimitivesCount; i ++) { + delete mPrimitives[i]; + } + delete[] mPrimitives; + } + +#ifndef ANDROID_RS_SERIALIZE + if (mAttribs) { + delete[] mAttribs; + delete[] mAttribAllocationIndex; + } +#endif +} + +void Mesh::serialize(OStream *stream) const { + // Need to identify ourselves + stream->addU32((uint32_t)getClassId()); + + String8 name(getName()); + stream->addString(&name); + + // Store number of vertex streams + stream->addU32(mVertexBufferCount); + for (uint32_t vCount = 0; vCount < mVertexBufferCount; vCount ++) { + mVertexBuffers[vCount]->serialize(stream); + } + + stream->addU32(mPrimitivesCount); + // Store the primitives + for (uint32_t pCount = 0; pCount < mPrimitivesCount; pCount ++) { + Primitive_t * prim = mPrimitives[pCount]; + + stream->addU8((uint8_t)prim->mPrimitive); + + if (prim->mIndexBuffer.get()) { + stream->addU32(1); + prim->mIndexBuffer->serialize(stream); + } else { + stream->addU32(0); + } + } +} + +Mesh *Mesh::createFromStream(Context *rsc, IStream *stream) { + // First make sure we are reading the correct object + RsA3DClassID classID = (RsA3DClassID)stream->loadU32(); + if (classID != RS_A3D_CLASS_ID_MESH) { + LOGE("mesh loading skipped due to invalid class id"); + return NULL; + } + + Mesh * mesh = new Mesh(rsc); + + String8 name; + stream->loadString(&name); + mesh->setName(name.string(), name.size()); + + mesh->mVertexBufferCount = stream->loadU32(); + if (mesh->mVertexBufferCount) { + mesh->mVertexBuffers = new ObjectBaseRef<Allocation>[mesh->mVertexBufferCount]; + + for (uint32_t vCount = 0; vCount < mesh->mVertexBufferCount; vCount ++) { + Allocation *vertexAlloc = Allocation::createFromStream(rsc, stream); + mesh->mVertexBuffers[vCount].set(vertexAlloc); + } + } + + mesh->mPrimitivesCount = stream->loadU32(); + if (mesh->mPrimitivesCount) { + mesh->mPrimitives = new Primitive_t *[mesh->mPrimitivesCount]; + + // load all primitives + for (uint32_t pCount = 0; pCount < mesh->mPrimitivesCount; pCount ++) { + Primitive_t * prim = new Primitive_t; + mesh->mPrimitives[pCount] = prim; + + prim->mPrimitive = (RsPrimitive)stream->loadU8(); + + // Check to see if the index buffer was stored + uint32_t isIndexPresent = stream->loadU32(); + if (isIndexPresent) { + Allocation *indexAlloc = Allocation::createFromStream(rsc, stream); + prim->mIndexBuffer.set(indexAlloc); + } + } + } + +#ifndef ANDROID_RS_SERIALIZE + mesh->updateGLPrimitives(); + mesh->initVertexAttribs(); + mesh->uploadAll(rsc); +#endif + return mesh; +} + +#ifndef ANDROID_RS_SERIALIZE + +bool Mesh::isValidGLComponent(const Element *elem, uint32_t fieldIdx) { + // Do not create attribs for padding + if (elem->getFieldName(fieldIdx)[0] == '#') { + return false; + } + + // Only GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT, GL_UNSIGNED_SHORT, GL_FIXED, GL_FLOAT are accepted. + // Filter rs types accordingly + RsDataType dt = elem->getField(fieldIdx)->getComponent().getType(); + if (dt != RS_TYPE_FLOAT_32 && dt != RS_TYPE_UNSIGNED_8 && + dt != RS_TYPE_UNSIGNED_16 && dt != RS_TYPE_SIGNED_8 && + dt != RS_TYPE_SIGNED_16) { + return false; + } + + // Now make sure they are not arrays + uint32_t arraySize = elem->getFieldArraySize(fieldIdx); + if (arraySize != 1) { + return false; + } + + return true; +} + +void Mesh::initVertexAttribs() { + // Count the number of gl attrs to initialize + mAttribCount = 0; + for (uint32_t ct=0; ct < mVertexBufferCount; ct++) { + const Element *elem = mVertexBuffers[ct]->getType()->getElement(); + for (uint32_t ct=0; ct < elem->getFieldCount(); ct++) { + if (isValidGLComponent(elem, ct)) { + mAttribCount ++; + } + } + } + + if (mAttribs) { + delete [] mAttribs; + delete [] mAttribAllocationIndex; + mAttribs = NULL; + mAttribAllocationIndex = NULL; + } + if (!mAttribCount) { + return; + } + + mAttribs = new VertexArray::Attrib[mAttribCount]; + mAttribAllocationIndex = new uint32_t[mAttribCount]; + + uint32_t userNum = 0; + for (uint32_t ct=0; ct < mVertexBufferCount; ct++) { + const Element *elem = mVertexBuffers[ct]->getType()->getElement(); + uint32_t stride = elem->getSizeBytes(); + for (uint32_t fieldI=0; fieldI < elem->getFieldCount(); fieldI++) { + const Component &c = elem->getField(fieldI)->getComponent(); + + if (!isValidGLComponent(elem, fieldI)) { + continue; + } + + mAttribs[userNum].size = c.getVectorSize(); + mAttribs[userNum].offset = elem->getFieldOffsetBytes(fieldI); + mAttribs[userNum].type = c.getGLType(); + mAttribs[userNum].normalized = c.getType() != RS_TYPE_FLOAT_32;//c.getIsNormalized(); + mAttribs[userNum].stride = stride; + String8 tmp(RS_SHADER_ATTR); + tmp.append(elem->getFieldName(fieldI)); + mAttribs[userNum].name.setTo(tmp.string()); + + // Remember which allocation this attribute came from + mAttribAllocationIndex[userNum] = ct; + userNum ++; + } + } +} + +void Mesh::render(Context *rsc) const { + for (uint32_t ct = 0; ct < mPrimitivesCount; ct ++) { + renderPrimitive(rsc, ct); + } +} + +void Mesh::renderPrimitive(Context *rsc, uint32_t primIndex) const { + if (primIndex >= mPrimitivesCount) { + LOGE("Invalid primitive index"); + return; + } + + Primitive_t *prim = mPrimitives[primIndex]; + + if (prim->mIndexBuffer.get()) { + renderPrimitiveRange(rsc, primIndex, 0, prim->mIndexBuffer->getType()->getDimX()); + return; + } + + renderPrimitiveRange(rsc, primIndex, 0, mVertexBuffers[0]->getType()->getDimX()); +} + +void Mesh::renderPrimitiveRange(Context *rsc, uint32_t primIndex, uint32_t start, uint32_t len) const { + if (len < 1 || primIndex >= mPrimitivesCount || mAttribCount == 0) { + LOGE("Invalid mesh or parameters"); + return; + } + + rsc->checkError("Mesh::renderPrimitiveRange 1"); + for (uint32_t ct=0; ct < mVertexBufferCount; ct++) { + mVertexBuffers[ct]->uploadCheck(rsc); + } + // update attributes with either buffer information or data ptr based on their current state + for (uint32_t ct=0; ct < mAttribCount; ct++) { + uint32_t allocIndex = mAttribAllocationIndex[ct]; + Allocation *alloc = mVertexBuffers[allocIndex].get(); + if (alloc->getIsBufferObject()) { + mAttribs[ct].buffer = alloc->getBufferObjectID(); + mAttribs[ct].ptr = NULL; + } else { + mAttribs[ct].buffer = 0; + mAttribs[ct].ptr = (const uint8_t*)alloc->getPtr(); + } + } + + VertexArray va(mAttribs, mAttribCount); + va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache); + + rsc->checkError("Mesh::renderPrimitiveRange 2"); + Primitive_t *prim = mPrimitives[primIndex]; + if (prim->mIndexBuffer.get()) { + prim->mIndexBuffer->uploadCheck(rsc); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, prim->mIndexBuffer->getBufferObjectID()); + glDrawElements(prim->mGLPrimitive, len, GL_UNSIGNED_SHORT, (uint16_t *)(start * 2)); + } else { + glDrawArrays(prim->mGLPrimitive, start, len); + } + + rsc->checkError("Mesh::renderPrimitiveRange"); +} + + +void Mesh::uploadAll(Context *rsc) { + for (uint32_t ct = 0; ct < mVertexBufferCount; ct ++) { + if (mVertexBuffers[ct].get()) { + mVertexBuffers[ct]->deferedUploadToBufferObject(rsc); + } + } + + for (uint32_t ct = 0; ct < mPrimitivesCount; ct ++) { + if (mPrimitives[ct]->mIndexBuffer.get()) { + mPrimitives[ct]->mIndexBuffer->deferedUploadToBufferObject(rsc); + } + } +} + +void Mesh::updateGLPrimitives() { + for (uint32_t i = 0; i < mPrimitivesCount; i ++) { + switch (mPrimitives[i]->mPrimitive) { + case RS_PRIMITIVE_POINT: mPrimitives[i]->mGLPrimitive = GL_POINTS; break; + case RS_PRIMITIVE_LINE: mPrimitives[i]->mGLPrimitive = GL_LINES; break; + case RS_PRIMITIVE_LINE_STRIP: mPrimitives[i]->mGLPrimitive = GL_LINE_STRIP; break; + case RS_PRIMITIVE_TRIANGLE: mPrimitives[i]->mGLPrimitive = GL_TRIANGLES; break; + case RS_PRIMITIVE_TRIANGLE_STRIP: mPrimitives[i]->mGLPrimitive = GL_TRIANGLE_STRIP; break; + case RS_PRIMITIVE_TRIANGLE_FAN: mPrimitives[i]->mGLPrimitive = GL_TRIANGLE_FAN; break; + } + } +} + +void Mesh::computeBBox() { + float *posPtr = NULL; + uint32_t vectorSize = 0; + uint32_t stride = 0; + uint32_t numVerts = 0; + // First we need to find the position ptr and stride + for (uint32_t ct=0; ct < mVertexBufferCount; ct++) { + const Type *bufferType = mVertexBuffers[ct]->getType(); + const Element *bufferElem = bufferType->getElement(); + + for (uint32_t ct=0; ct < bufferElem->getFieldCount(); ct++) { + if (strcmp(bufferElem->getFieldName(ct), "position") == 0) { + vectorSize = bufferElem->getField(ct)->getComponent().getVectorSize(); + stride = bufferElem->getSizeBytes() / sizeof(float); + uint32_t offset = bufferElem->getFieldOffsetBytes(ct); + posPtr = (float*)((uint8_t*)mVertexBuffers[ct]->getPtr() + offset); + numVerts = bufferType->getDimX(); + break; + } + } + if (posPtr) { + break; + } + } + + mBBoxMin[0] = mBBoxMin[1] = mBBoxMin[2] = 1e6; + mBBoxMax[0] = mBBoxMax[1] = mBBoxMax[2] = -1e6; + if (!posPtr) { + LOGE("Unable to compute bounding box"); + mBBoxMin[0] = mBBoxMin[1] = mBBoxMin[2] = 0.0f; + mBBoxMax[0] = mBBoxMax[1] = mBBoxMax[2] = 0.0f; + return; + } + + for (uint32_t i = 0; i < numVerts; i ++) { + for (uint32_t v = 0; v < vectorSize; v ++) { + mBBoxMin[v] = rsMin(mBBoxMin[v], posPtr[v]); + mBBoxMax[v] = rsMax(mBBoxMax[v], posPtr[v]); + } + posPtr += stride; + } } -Mesh::~Mesh() -{ +namespace android { +namespace renderscript { + +RsMesh rsi_MeshCreate(Context *rsc, uint32_t vtxCount, uint32_t idxCount) { + Mesh *sm = new Mesh(rsc); + sm->incUserRef(); + + sm->mPrimitivesCount = idxCount; + sm->mPrimitives = new Mesh::Primitive_t *[sm->mPrimitivesCount]; + for (uint32_t ct = 0; ct < idxCount; ct ++) { + sm->mPrimitives[ct] = new Mesh::Primitive_t; + } + + sm->mVertexBufferCount = vtxCount; + sm->mVertexBuffers = new ObjectBaseRef<Allocation>[vtxCount]; + + return sm; } +void rsi_MeshBindVertex(Context *rsc, RsMesh mv, RsAllocation va, uint32_t slot) { + Mesh *sm = static_cast<Mesh *>(mv); + rsAssert(slot < sm->mVertexBufferCount); + sm->mVertexBuffers[slot].set((Allocation *)va); +} -MeshContext::MeshContext() -{ +void rsi_MeshBindIndex(Context *rsc, RsMesh mv, RsAllocation va, uint32_t primType, uint32_t slot) { + Mesh *sm = static_cast<Mesh *>(mv); + rsAssert(slot < sm->mPrimitivesCount); + + sm->mPrimitives[slot]->mIndexBuffer.set((Allocation *)va); + sm->mPrimitives[slot]->mPrimitive = (RsPrimitive)primType; + sm->updateGLPrimitives(); } -MeshContext::~MeshContext() -{ +void rsi_MeshInitVertexAttribs(Context *rsc, RsMesh mv) { + Mesh *sm = static_cast<Mesh *>(mv); + sm->initVertexAttribs(); +} + +}} + +void rsaMeshGetVertexBufferCount(RsContext con, RsMesh mv, int32_t *numVtx) { + Mesh *sm = static_cast<Mesh *>(mv); + *numVtx = sm->mVertexBufferCount; +} + +void rsaMeshGetIndexCount(RsContext con, RsMesh mv, int32_t *numIdx) { + Mesh *sm = static_cast<Mesh *>(mv); + *numIdx = sm->mPrimitivesCount; +} + +void rsaMeshGetVertices(RsContext con, RsMesh mv, RsAllocation *vtxData, uint32_t vtxDataCount) { + Mesh *sm = static_cast<Mesh *>(mv); + rsAssert(vtxDataCount == sm->mVertexBufferCount); + + for (uint32_t ct = 0; ct < vtxDataCount; ct ++) { + vtxData[ct] = sm->mVertexBuffers[ct].get(); + sm->mVertexBuffers[ct]->incUserRef(); + } +} + +void rsaMeshGetIndices(RsContext con, RsMesh mv, RsAllocation *va, uint32_t *primType, uint32_t idxDataCount) { + Mesh *sm = static_cast<Mesh *>(mv); + rsAssert(idxDataCount == sm->mPrimitivesCount); + + for (uint32_t ct = 0; ct < idxDataCount; ct ++) { + va[ct] = sm->mPrimitives[ct]->mIndexBuffer.get(); + primType[ct] = sm->mPrimitives[ct]->mPrimitive; + if (sm->mPrimitives[ct]->mIndexBuffer.get()) { + sm->mPrimitives[ct]->mIndexBuffer->incUserRef(); + } + } } +#endif diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h index 5201abd24632..3e080e2aabb9 100644 --- a/libs/rs/rsMesh.h +++ b/libs/rs/rsMesh.h @@ -26,65 +26,73 @@ namespace renderscript { // An element is a group of Components that occupies one cell in a structure. -class Mesh : public ObjectBase -{ +class Mesh : public ObjectBase { public: Mesh(Context *); ~Mesh(); - struct Verticies_t - { - Allocation ** mAllocations; - uint32_t mAllocationCount; - - size_t mVertexDataSize; - - size_t mOffsetCoord; - size_t mOffsetTex; - size_t mOffsetNorm; - - size_t mSizeCoord; - size_t mSizeTex; - size_t mSizeNorm; - - uint32_t mBufferObject; - }; + // Contains vertex data + // Position, normal, texcoord, etc could either be strided in one allocation + // of provided separetely in multiple ones + ObjectBaseRef<Allocation> *mVertexBuffers; + uint32_t mVertexBufferCount; + // Either mIndexBuffer, mPrimitiveBuffer or both could have a NULL reference + // If both are null, mPrimitive only would be used to render the mesh struct Primitive_t { - RsPrimitive mType; - Verticies_t *mVerticies; + ObjectBaseRef<Allocation> mIndexBuffer; - uint32_t mIndexCount; - uint16_t *mIndicies; - - uint32_t mRestartCounts; - uint16_t *mRestarts; + RsPrimitive mPrimitive; + uint32_t mGLPrimitive; }; - Verticies_t * mVerticies; - uint32_t mVerticiesCount; - Primitive_t ** mPrimitives; uint32_t mPrimitivesCount; + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; } + static Mesh *createFromStream(Context *rsc, IStream *stream); +#ifndef ANDROID_RS_SERIALIZE + void render(Context *) const; + void renderPrimitive(Context *, uint32_t primIndex) const; + void renderPrimitiveRange(Context *, uint32_t primIndex, uint32_t start, uint32_t len) const; + void uploadAll(Context *); + void updateGLPrimitives(); + + + + // Bounding volumes + float mBBoxMin[3]; + float mBBoxMax[3]; + void computeBBox(); + + void initVertexAttribs(); - void analyzeElement(); protected: + bool isValidGLComponent(const Element *elem, uint32_t fieldIdx); + // Attribues that allow us to map to GL + VertexArray::Attrib *mAttribs; + // This allows us to figure out which allocation the attribute + // belongs to. In the event the allocation is uploaded to GL + // buffer, it lets us properly map it + uint32_t *mAttribAllocationIndex; + uint32_t mAttribCount; +#endif }; -class MeshContext -{ +class MeshContext { public: - MeshContext(); - ~MeshContext(); - + MeshContext() { + } + ~MeshContext() { + } }; - } } #endif //ANDROID_RS_TRIANGLE_MESH_H + diff --git a/libs/rs/rsMutex.cpp b/libs/rs/rsMutex.cpp new file mode 100644 index 000000000000..2105288218bf --- /dev/null +++ b/libs/rs/rsMutex.cpp @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#include "rsMutex.h" + +using namespace android; +using namespace android::renderscript; + + +Mutex::Mutex() { +} + +Mutex::~Mutex() { + pthread_mutex_destroy(&mMutex); +} + +bool Mutex::init() { + int status = pthread_mutex_init(&mMutex, NULL); + if (status) { + LOGE("Mutex::Mutex init failure"); + return false; + } + return true; +} + +bool Mutex::lock() { + int status; + status = pthread_mutex_lock(&mMutex); + if (status) { + LOGE("Mutex: error %i locking.", status); + return false; + } + return true; +} + +bool Mutex::unlock() { + int status; + status = pthread_mutex_unlock(&mMutex); + if (status) { + LOGE("Mutex error %i unlocking.", status); + return false; + } + return true; +} + + diff --git a/libs/rs/rsNoise.h b/libs/rs/rsMutex.h index 9040751d4cb9..47725d7d8982 100644 --- a/libs/rs/rsNoise.h +++ b/libs/rs/rsMutex.h @@ -14,22 +14,30 @@ * limitations under the License. */ -#ifndef ANDROID_RS_NOISE_H -#define ANDROID_RS_NOISE_H +#ifndef ANDROID_RS_MUTEX_H +#define ANDROID_RS_MUTEX_H + + +#include "rsUtils.h" -// --------------------------------------------------------------------------- namespace android { namespace renderscript { -void SC_normalizef2(float v[]); -void SC_normalizef3(float v[]); -float SC_noisef(float x); -float SC_noisef2(float x, float y); -float SC_noisef3(float x, float y, float z); -float SC_turbulencef2(float x, float y, float octaves); -float SC_turbulencef3(float x, float y, float z, float octaves); +class Mutex { +public: + Mutex(); + ~Mutex(); + + bool init(); + bool lock(); + bool unlock(); + +protected: + pthread_mutex_t mMutex; +}; } } #endif + diff --git a/libs/rs/rsNoise.cpp b/libs/rs/rsNoise.cpp deleted file mode 100644 index 4b675861e791..000000000000 --- a/libs/rs/rsNoise.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/* - * This implementation of the noise functions was ported from the Java - * implementation by Jerry Huxtable (http://www.jhlabs.com) under - * Apache License 2.0 (see http://jhlabs.com/ip/filters/download.html) - * - * Original header: - * - * Copyright 2006 Jerry Huxtable - * - * 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 "rsNoise.h" - -#include <math.h> -#include <stdlib.h> -#include <time.h> - -namespace android { -namespace renderscript { - -#define B 0x100 -#define BM 0xff -#define N 0x1000 - -static int p[B + B + 2]; -static float g3[B + B + 2][3]; -static float g2[B + B + 2][2]; -static float g1[B + B + 2]; -static bool noise_start = true; - -#define lerpf(start, stop, amount) start + (stop - start) * amount - -static inline float noise_sCurve(float t) -{ - return t * t * (3.0f - 2.0f * t); -} - -inline void SC_normalizef2(float v[]) -{ - float s = (float)sqrtf(v[0] * v[0] + v[1] * v[1]); - v[0] = v[0] / s; - v[1] = v[1] / s; -} - -inline void SC_normalizef3(float v[]) -{ - float s = (float)sqrtf(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - v[0] = v[0] / s; - v[1] = v[1] / s; - v[2] = v[2] / s; -} - -static void noise_init() -{ - int i, j, k; - - for (i = 0; i < B; i++) { - p[i] = i; - - g1[i] = (float)((rand() % (B + B)) - B) / B; - - for (j = 0; j < 2; j++) - g2[i][j] = (float)((rand() % (B + B)) - B) / B; - SC_normalizef2(g2[i]); - - for (j = 0; j < 3; j++) - g3[i][j] = (float)((rand() % (B + B)) - B) / B; - SC_normalizef3(g3[i]); - } - - for (i = B-1; i >= 0; i--) { - k = p[i]; - p[i] = p[j = rand() % B]; - p[j] = k; - } - - for (i = 0; i < B + 2; i++) { - p[B + i] = p[i]; - g1[B + i] = g1[i]; - for (j = 0; j < 2; j++) - g2[B + i][j] = g2[i][j]; - for (j = 0; j < 3; j++) - g3[B + i][j] = g3[i][j]; - } -} - -float SC_noisef(float x) -{ - srand(time(NULL)); - int bx0, bx1; - float rx0, rx1, sx, t, u, v; - - if (noise_start) { - noise_start = false; - noise_init(); - } - - t = x + N; - bx0 = ((int)t) & BM; - bx1 = (bx0+1) & BM; - rx0 = t - (int)t; - rx1 = rx0 - 1.0f; - - sx = noise_sCurve(rx0); - - u = rx0 * g1[p[bx0]]; - v = rx1 * g1[p[bx1]]; - return 2.3f * lerpf(u, v, sx); -} - -float SC_noisef2(float x, float y) -{ - srand(time(NULL)); - int bx0, bx1, by0, by1, b00, b10, b01, b11; - float rx0, rx1, ry0, ry1, sx, sy, a, b, t, u, v; - float *q; - int i, j; - - if (noise_start) { - noise_start = false; - noise_init(); - } - - t = x + N; - bx0 = ((int)t) & BM; - bx1 = (bx0+1) & BM; - rx0 = t - (int)t; - rx1 = rx0 - 1.0f; - - t = y + N; - by0 = ((int)t) & BM; - by1 = (by0+1) & BM; - ry0 = t - (int)t; - ry1 = ry0 - 1.0f; - - i = p[bx0]; - j = p[bx1]; - - b00 = p[i + by0]; - b10 = p[j + by0]; - b01 = p[i + by1]; - b11 = p[j + by1]; - - sx = noise_sCurve(rx0); - sy = noise_sCurve(ry0); - - q = g2[b00]; u = rx0 * q[0] + ry0 * q[1]; - q = g2[b10]; v = rx1 * q[0] + ry0 * q[1]; - a = lerpf(u, v, sx); - - q = g2[b01]; u = rx0 * q[0] + ry1 * q[1]; - q = g2[b11]; v = rx1 * q[0] + ry1 * q[1]; - b = lerpf(u, v, sx); - - return 1.5f*lerpf(a, b, sy); -} - -float SC_noisef3(float x, float y, float z) -{ - srand(time(NULL)); - int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11; - float rx0, rx1, ry0, ry1, rz0, rz1, sy, sz, a, b, c, d, t, u, v; - float *q; - int i, j; - - if (noise_start) { - noise_start = false; - noise_init(); - } - - t = x + N; - bx0 = ((int)t) & BM; - bx1 = (bx0+1) & BM; - rx0 = t - (int)t; - rx1 = rx0 - 1.0f; - - t = y + N; - by0 = ((int)t) & BM; - by1 = (by0+1) & BM; - ry0 = t - (int)t; - ry1 = ry0 - 1.0f; - - t = z + N; - bz0 = ((int)t) & BM; - bz1 = (bz0+1) & BM; - rz0 = t - (int)t; - rz1 = rz0 - 1.0f; - - i = p[bx0]; - j = p[bx1]; - - b00 = p[i + by0]; - b10 = p[j + by0]; - b01 = p[i + by1]; - b11 = p[j + by1]; - - t = noise_sCurve(rx0); - sy = noise_sCurve(ry0); - sz = noise_sCurve(rz0); - - q = g3[b00 + bz0]; u = rx0 * q[0] + ry0 * q[1] + rz0 * q[2]; - q = g3[b10 + bz0]; v = rx1 * q[0] + ry0 * q[1] + rz0 * q[2]; - a = lerpf(u, v, t); - - q = g3[b01 + bz0]; u = rx0 * q[0] + ry1 * q[1] + rz0 * q[2]; - q = g3[b11 + bz0]; v = rx1 * q[0] + ry1 * q[1] + rz0 * q[2]; - b = lerpf(u, v, t); - - c = lerpf(a, b, sy); - - q = g3[b00 + bz1]; u = rx0 * q[0] + ry0 * q[1] + rz1 * q[2]; - q = g3[b10 + bz1]; v = rx1 * q[0] + ry0 * q[1] + rz1 * q[2]; - a = lerpf(u, v, t); - - q = g3[b01 + bz1]; u = rx0 * q[0] + ry1 * q[1] + rz1 * q[2]; - q = g3[b11 + bz1]; v = rx1 * q[0] + ry1 * q[1] + rz1 * q[2]; - b = lerpf(u, v, t); - - d = lerpf(a, b, sy); - - return 1.5f*lerpf(c, d, sz); -} - -float SC_turbulencef2(float x, float y, float octaves) -{ - srand(time(NULL)); - float t = 0.0f; - - for (float f = 1.0f; f <= octaves; f *= 2) - t += fabs(SC_noisef2(f * x, f * y)) / f; - return t; -} - -float SC_turbulencef3(float x, float y, float z, float octaves) -{ - srand(time(NULL)); - float t = 0.0f; - - for (float f = 1.0f; f <= octaves; f *= 2) - t += fabs(SC_noisef3(f * x, f * y, f * z)) / f; - return t; -} - -} -} diff --git a/libs/rs/rsObjectBase.cpp b/libs/rs/rsObjectBase.cpp index 677413e12b07..f428f9486d8d 100644 --- a/libs/rs/rsObjectBase.cpp +++ b/libs/rs/rsObjectBase.cpp @@ -20,115 +20,145 @@ using namespace android; using namespace android::renderscript; -ObjectBase::ObjectBase(Context *rsc) -{ +pthread_mutex_t ObjectBase::gObjectInitMutex = PTHREAD_MUTEX_INITIALIZER; + +ObjectBase::ObjectBase(Context *rsc) { mUserRefCount = 0; mSysRefCount = 0; - mName = NULL; - mRSC = NULL; + mRSC = rsc; mNext = NULL; mPrev = NULL; - mAllocFile = __FILE__; - mAllocLine = __LINE__; - setContext(rsc); + +#if RS_OBJECT_DEBUG + mStack.update(2); +#endif + + rsAssert(rsc); + add(); + //LOGV("ObjectBase %p con", this); } -ObjectBase::~ObjectBase() -{ +ObjectBase::~ObjectBase() { //LOGV("~ObjectBase %p ref %i,%i", this, mUserRefCount, mSysRefCount); +#if RS_OBJECT_DEBUG + mStack.dump(); +#endif + + if (mPrev || mNext) { + // While the normal practice is to call remove before we call + // delete. Its possible for objects without a re-use list + // for avoiding duplication to be created on the stack. In those + // cases we need to remove ourself here. + asyncLock(); + remove(); + asyncUnlock(); + } + rsAssert(!mUserRefCount); rsAssert(!mSysRefCount); - remove(); - delete[] mName; } -void ObjectBase::dumpLOGV(const char *op) const -{ - if (mName) { - LOGV("%s RSobj %p, name %s, refs %i,%i from %s,%i links %p,%p,%p", - op, this, mName, mUserRefCount, mSysRefCount, mAllocFile, mAllocLine, mNext, mPrev, mRSC); +void ObjectBase::dumpLOGV(const char *op) const { + if (mName.size()) { + LOGV("%s RSobj %p, name %s, refs %i,%i links %p,%p,%p", + op, this, mName.string(), mUserRefCount, mSysRefCount, mNext, mPrev, mRSC); } else { - LOGV("%s RSobj %p, no-name, refs %i,%i from %s,%i links %p,%p,%p", - op, this, mUserRefCount, mSysRefCount, mAllocFile, mAllocLine, mNext, mPrev, mRSC); + LOGV("%s RSobj %p, no-name, refs %i,%i links %p,%p,%p", + op, this, mUserRefCount, mSysRefCount, mNext, mPrev, mRSC); } } -void ObjectBase::setContext(Context *rsc) -{ - if (mRSC) { - remove(); - } - mRSC = rsc; - if (rsc) { - add(); - } +void ObjectBase::incUserRef() const { + android_atomic_inc(&mUserRefCount); + //LOGV("ObjectBase %p incU ref %i, %i", this, mUserRefCount, mSysRefCount); } -void ObjectBase::incUserRef() const -{ - mUserRefCount ++; - //LOGV("ObjectBase %p inc ref %i", this, mRefCount); +void ObjectBase::incSysRef() const { + android_atomic_inc(&mSysRefCount); + //LOGV("ObjectBase %p incS ref %i, %i", this, mUserRefCount, mSysRefCount); } -void ObjectBase::incSysRef() const -{ - mSysRefCount ++; - //LOGV("ObjectBase %p inc ref %i", this, mRefCount); +void ObjectBase::preDestroy() const { } -bool ObjectBase::checkDelete() const -{ - if (!(mSysRefCount | mUserRefCount)) { - if (mRSC && mRSC->props.mLogObjects) { - dumpLOGV("checkDelete"); - } - delete this; - return true; +bool ObjectBase::checkDelete(const ObjectBase *ref) { + if (!ref) { + return false; } - return false; + + asyncLock(); + // This lock protects us against the non-RS threads changing + // the ref counts. At this point we should be the only thread + // working on them. + if (ref->mUserRefCount || ref->mSysRefCount) { + asyncUnlock(); + return false; + } + + ref->remove(); + // At this point we can unlock because there should be no possible way + // for another thread to reference this object. + ref->preDestroy(); + asyncUnlock(); + delete ref; + return true; } -bool ObjectBase::decUserRef() const -{ +bool ObjectBase::decUserRef() const { rsAssert(mUserRefCount > 0); - mUserRefCount --; - //dumpObj("decUserRef"); - return checkDelete(); +#if RS_OBJECT_DEBUG + LOGV("ObjectBase %p decU ref %i, %i", this, mUserRefCount, mSysRefCount); + if (mUserRefCount <= 0) { + mStack.dump(); + } +#endif + + + if ((android_atomic_dec(&mUserRefCount) <= 1) && + (android_atomic_acquire_load(&mSysRefCount) <= 0)) { + return checkDelete(this); + } + return false; } -bool ObjectBase::zeroUserRef() const -{ - mUserRefCount = 0; - //dumpObj("zeroUserRef"); - return checkDelete(); +bool ObjectBase::zeroUserRef() const { + //LOGV("ObjectBase %p zeroU ref %i, %i", this, mUserRefCount, mSysRefCount); + android_atomic_acquire_store(0, &mUserRefCount); + if (android_atomic_acquire_load(&mSysRefCount) <= 0) { + return checkDelete(this); + } + return false; } -bool ObjectBase::decSysRef() const -{ +bool ObjectBase::decSysRef() const { + //LOGV("ObjectBase %p decS ref %i, %i", this, mUserRefCount, mSysRefCount); rsAssert(mSysRefCount > 0); - mSysRefCount --; - //dumpObj("decSysRef"); - return checkDelete(); + if ((android_atomic_dec(&mSysRefCount) <= 1) && + (android_atomic_acquire_load(&mUserRefCount) <= 0)) { + return checkDelete(this); + } + return false; } -void ObjectBase::setName(const char *name) -{ - setName(name, strlen(name)); +void ObjectBase::setName(const char *name) { + mName.setTo(name); } -void ObjectBase::setName(const char *name, uint32_t len) -{ - delete mName; - mName = NULL; - if (name) { - mName = new char[len + 1]; - memcpy(mName, name, len); - mName[len] = 0; - } +void ObjectBase::setName(const char *name, uint32_t len) { + mName.setTo(name, len); +} + +void ObjectBase::asyncLock() { + pthread_mutex_lock(&gObjectInitMutex); } -void ObjectBase::add() const -{ +void ObjectBase::asyncUnlock() { + pthread_mutex_unlock(&gObjectInitMutex); +} + +void ObjectBase::add() const { + asyncLock(); + rsAssert(!mNext); rsAssert(!mPrev); //LOGV("calling add rsc %p", mRSC); @@ -137,16 +167,18 @@ void ObjectBase::add() const mRSC->mObjHead->mPrev = this; } mRSC->mObjHead = this; + + asyncUnlock(); } -void ObjectBase::remove() const -{ +void ObjectBase::remove() const { //LOGV("calling remove rsc %p", mRSC); if (!mRSC) { rsAssert(!mPrev); rsAssert(!mNext); return; } + if (mRSC->mObjHead == this) { mRSC->mObjHead = mNext; } @@ -160,8 +192,7 @@ void ObjectBase::remove() const mNext = NULL; } -void ObjectBase::zeroAllUserRef(Context *rsc) -{ +void ObjectBase::zeroAllUserRef(Context *rsc) { if (rsc->props.mLogObjects) { LOGV("Forcing release of all outstanding user refs."); } @@ -186,8 +217,9 @@ void ObjectBase::zeroAllUserRef(Context *rsc) } } -void ObjectBase::dumpAll(Context *rsc) -{ +void ObjectBase::dumpAll(Context *rsc) { + asyncLock(); + LOGV("Dumping all objects"); const ObjectBase * o = rsc->mObjHead; while (o) { @@ -195,5 +227,22 @@ void ObjectBase::dumpAll(Context *rsc) o->dumpLOGV(" "); o = o->mNext; } + + asyncUnlock(); +} + +bool ObjectBase::isValid(const Context *rsc, const ObjectBase *obj) { + asyncLock(); + + const ObjectBase * o = rsc->mObjHead; + while (o) { + if (o == obj) { + asyncUnlock(); + return true; + } + o = o->mNext; + } + asyncUnlock(); + return false; } diff --git a/libs/rs/rsObjectBase.h b/libs/rs/rsObjectBase.h index bb03b8716933..01850f1e2511 100644 --- a/libs/rs/rsObjectBase.h +++ b/libs/rs/rsObjectBase.h @@ -19,18 +19,20 @@ #include "rsUtils.h" +#define RS_OBJECT_DEBUG 0 + +#include <utils/CallStack.h> namespace android { namespace renderscript { class Context; +class OStream; // An element is a group of Components that occupies one cell in a structure. -class ObjectBase -{ +class ObjectBase { public: ObjectBase(Context *rsc); - virtual ~ObjectBase(); void incSysRef() const; bool decSysRef() const; @@ -39,42 +41,59 @@ public: bool decUserRef() const; bool zeroUserRef() const; + static bool checkDelete(const ObjectBase *); + const char * getName() const { - return mName; + return mName.string(); } void setName(const char *); void setName(const char *, uint32_t len); Context * getContext() const {return mRSC;} - void setContext(Context *); static void zeroAllUserRef(Context *rsc); static void dumpAll(Context *rsc); virtual void dumpLOGV(const char *prefix) const; + virtual void serialize(OStream *stream) const = 0; + virtual RsA3DClassID getClassId() const = 0; + + static bool isValid(const Context *rsc, const ObjectBase *obj); + + // The async lock is taken during object creation in non-rs threads + // and object deletion in the rs thread. + static void asyncLock(); + static void asyncUnlock(); protected: - const char *mAllocFile; - uint32_t mAllocLine; + // Called inside the async lock for any object list management that is + // necessary in derived classes. + virtual void preDestroy() const; + Context *mRSC; + virtual ~ObjectBase(); private: + static pthread_mutex_t gObjectInitMutex; + void add() const; void remove() const; - bool checkDelete() const; - - char * mName; + String8 mName; mutable int32_t mSysRefCount; mutable int32_t mUserRefCount; mutable const ObjectBase * mPrev; mutable const ObjectBase * mNext; + +#if RS_OBJECT_DEBUG + CallStack mStack; +#endif + }; template<class T> -class ObjectBaseRef -{ +class ObjectBaseRef { public: ObjectBaseRef() { mRef = NULL; @@ -94,6 +113,10 @@ public: } } + ObjectBaseRef & operator= (const ObjectBaseRef &ref) { + return ObjectBaseRef(ref); + } + ~ObjectBaseRef() { clear(); } @@ -129,10 +152,8 @@ public: protected: T * mRef; - }; - } } diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp index 70e2868bc336..4ef05bf4372f 100644 --- a/libs/rs/rsProgram.cpp +++ b/libs/rs/rsProgram.cpp @@ -15,49 +15,25 @@ */ #include "rsContext.h" -#include "rsProgram.h" - +#ifndef ANDROID_RS_SERIALIZE #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#endif //ANDROID_RS_SERIALIZE + +#include "rsProgram.h" using namespace android; using namespace android::renderscript; - -Program::Program(Context *rsc) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - mDirty = true; - mShaderID = 0; - mAttribCount = 0; - mUniformCount = 0; - - mInputElements = NULL; - mOutputElements = NULL; - mConstantTypes = NULL; - mInputCount = 0; - mOutputCount = 0; - mConstantCount = 0; - mIsValid = false; +Program::Program(Context *rsc) : ObjectBase(rsc) { + initMemberVars(); } Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength, - const uint32_t * params, uint32_t paramLength) : - ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - mDirty = true; - mShaderID = 0; - mAttribCount = 0; - mUniformCount = 0; - mTextureCount = 0; - - mInputCount = 0; - mOutputCount = 0; - mConstantCount = 0; + const uint32_t * params, uint32_t paramLength) + : ObjectBase(rsc) { + initMemberVars(); for (uint32_t ct=0; ct < paramLength; ct+=2) { if (params[ct] == RS_PROGRAM_PARAM_INPUT) { mInputCount++; @@ -68,18 +44,23 @@ Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength, if (params[ct] == RS_PROGRAM_PARAM_CONSTANT) { mConstantCount++; } - if (params[ct] == RS_PROGRAM_PARAM_TEXTURE_COUNT) { - mTextureCount = params[ct+1]; + if (params[ct] == RS_PROGRAM_PARAM_TEXTURE_TYPE) { + mTextureCount++; } } + mTextures = new ObjectBaseRef<Allocation>[mTextureCount]; + mSamplers = new ObjectBaseRef<Sampler>[mTextureCount]; + mTextureTargets = new RsTextureTarget[mTextureCount]; mInputElements = new ObjectBaseRef<Element>[mInputCount]; mOutputElements = new ObjectBaseRef<Element>[mOutputCount]; mConstantTypes = new ObjectBaseRef<Type>[mConstantCount]; + mConstants = new ObjectBaseRef<Allocation>[mConstantCount]; uint32_t input = 0; uint32_t output = 0; uint32_t constant = 0; + uint32_t texture = 0; for (uint32_t ct=0; ct < paramLength; ct+=2) { if (params[ct] == RS_PROGRAM_PARAM_INPUT) { mInputElements[input++].set(reinterpret_cast<Element *>(params[ct+1])); @@ -90,27 +71,94 @@ Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength, if (params[ct] == RS_PROGRAM_PARAM_CONSTANT) { mConstantTypes[constant++].set(reinterpret_cast<Type *>(params[ct+1])); } + if (params[ct] == RS_PROGRAM_PARAM_TEXTURE_TYPE) { + mTextureTargets[texture++] = (RsTextureTarget)params[ct+1]; + } + } + mIsInternal = false; + uint32_t internalTokenLen = strlen(RS_SHADER_INTERNAL); + if (shaderLength > internalTokenLen && + strncmp(RS_SHADER_INTERNAL, shaderText, internalTokenLen) == 0) { + mIsInternal = true; + shaderText += internalTokenLen; + shaderLength -= internalTokenLen; } mUserShader.setTo(shaderText, shaderLength); + + initAttribAndUniformArray(); } -Program::~Program() -{ - for (uint32_t ct=0; ct < MAX_UNIFORMS; ct++) { - bindAllocation(NULL, ct); +Program::~Program() { + if (mRSC->props.mLogShaders) { + LOGV("Program::~Program with shader id %u", mShaderID); } + if (mShaderID) { + glDeleteShader(mShaderID); + } + + for (uint32_t ct=0; ct < mConstantCount; ct++) { + bindAllocation(NULL, NULL, ct); + } + + for (uint32_t ct=0; ct < mTextureCount; ct++) { + bindTexture(NULL, ct, NULL); + bindSampler(NULL, ct, NULL); + } + delete[] mTextures; + delete[] mSamplers; + delete[] mTextureTargets; delete[] mInputElements; delete[] mOutputElements; delete[] mConstantTypes; + delete[] mConstants; + delete[] mAttribNames; + delete[] mUniformNames; + delete[] mUniformArraySizes; mInputCount = 0; mOutputCount = 0; mConstantCount = 0; } +void Program::initMemberVars() { + mDirty = true; + mShaderID = 0; + mAttribCount = 0; + mUniformCount = 0; + mTextureCount = 0; -void Program::bindAllocation(Allocation *alloc, uint32_t slot) -{ + mTextures = NULL; + mSamplers = NULL; + mTextureTargets = NULL; + mInputElements = NULL; + mOutputElements = NULL; + mConstantTypes = NULL; + mConstants = NULL; + mAttribNames = NULL; + mUniformNames = NULL; + mUniformArraySizes = NULL; + mInputCount = 0; + mOutputCount = 0; + mConstantCount = 0; + mIsValid = false; + mIsInternal = false; +} + +void Program::bindAllocation(Context *rsc, Allocation *alloc, uint32_t slot) { + if (alloc != NULL) { + if (slot >= mConstantCount) { + LOGE("Attempt to bind alloc at slot %u, on shader id %u, but const count is %u", + slot, (uint32_t)this, mConstantCount); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation"); + return; + } + if (!alloc->getType()->isEqual(mConstantTypes[slot].get())) { + LOGE("Attempt to bind alloc at slot %u, on shader id %u, but types mismatch", + slot, (uint32_t)this); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation"); + return; + } + } if (mConstants[slot].get() == alloc) { return; } @@ -124,10 +172,16 @@ void Program::bindAllocation(Allocation *alloc, uint32_t slot) mDirty = true; } -void Program::bindTexture(uint32_t slot, Allocation *a) -{ - if (slot >= MAX_TEXTURE) { - LOGE("Attempt to bind a texture to a slot > MAX_TEXTURE"); +void Program::bindTexture(Context *rsc, uint32_t slot, Allocation *a) { + if (slot >= mTextureCount) { + LOGE("Attempt to bind texture to slot %u but tex count is %u", slot, mTextureCount); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind texture"); + return; + } + + if (a && a->getType()->getDimFaces() && mTextureTargets[slot] != RS_TEXTURE_CUBE) { + LOGE("Attempt to bind cubemap to slot %u but 2d texture needed", slot); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind cubemap to 2d texture slot"); return; } @@ -136,10 +190,10 @@ void Program::bindTexture(uint32_t slot, Allocation *a) mDirty = true; } -void Program::bindSampler(uint32_t slot, Sampler *s) -{ - if (slot >= MAX_TEXTURE) { - LOGE("Attempt to bind a Sampler to a slot > MAX_TEXTURE"); +void Program::bindSampler(Context *rsc, uint32_t slot, Sampler *s) { + if (slot >= mTextureCount) { + LOGE("Attempt to bind sampler to slot %u but tex count is %u", slot, mTextureCount); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind sampler"); return; } @@ -147,8 +201,7 @@ void Program::bindSampler(uint32_t slot, Sampler *s) mDirty = true; } -String8 Program::getGLSLInputString() const -{ +String8 Program::getGLSLInputString() const { String8 s; for (uint32_t ct=0; ct < mInputCount; ct++) { const Element *e = mInputElements[ct].get(); @@ -157,7 +210,7 @@ String8 Program::getGLSLInputString() const // Cannot be complex rsAssert(!f->getFieldCount()); - switch(f->getComponent().getVectorSize()) { + switch (f->getComponent().getVectorSize()) { case 1: s.append("attribute float ATTRIB_"); break; case 2: s.append("attribute vec2 ATTRIB_"); break; case 3: s.append("attribute vec3 ATTRIB_"); break; @@ -173,23 +226,18 @@ String8 Program::getGLSLInputString() const return s; } -String8 Program::getGLSLOutputString() const -{ +String8 Program::getGLSLOutputString() const { return String8(); } -String8 Program::getGLSLConstantString() const -{ +String8 Program::getGLSLConstantString() const { return String8(); } - -void Program::createShader() -{ +void Program::createShader() { } -bool Program::loadShader(Context *rsc, uint32_t type) -{ +bool Program::loadShader(Context *rsc, uint32_t type) { mShaderID = glCreateShader(type); rsAssert(mShaderID); @@ -230,33 +278,238 @@ bool Program::loadShader(Context *rsc, uint32_t type) return true; } -void Program::setShader(const char *txt, uint32_t len) -{ +void Program::setShader(const char *txt, uint32_t len) { mUserShader.setTo(txt, len); } +void Program::appendUserConstants() { + for (uint32_t ct=0; ct < mConstantCount; ct++) { + const Element *e = mConstantTypes[ct]->getElement(); + for (uint32_t field=0; field < e->getFieldCount(); field++) { + const Element *f = e->getField(field); + const char *fn = e->getFieldName(field); + + if (fn[0] == '#') { + continue; + } + + // Cannot be complex + rsAssert(!f->getFieldCount()); + if (f->getType() == RS_TYPE_MATRIX_4X4) { + mShader.append("uniform mat4 UNI_"); + } else if (f->getType() == RS_TYPE_MATRIX_3X3) { + mShader.append("uniform mat3 UNI_"); + } else if (f->getType() == RS_TYPE_MATRIX_2X2) { + mShader.append("uniform mat2 UNI_"); + } else { + switch (f->getComponent().getVectorSize()) { + case 1: mShader.append("uniform float UNI_"); break; + case 2: mShader.append("uniform vec2 UNI_"); break; + case 3: mShader.append("uniform vec3 UNI_"); break; + case 4: mShader.append("uniform vec4 UNI_"); break; + default: + rsAssert(0); + } + } + + mShader.append(fn); + if (e->getFieldArraySize(field) > 1) { + mShader.appendFormat("[%d]", e->getFieldArraySize(field)); + } + mShader.append(";\n"); + } + } +} + +void Program::logUniform(const Element *field, const float *fd, uint32_t arraySize ) { + RsDataType dataType = field->getType(); + uint32_t elementSize = field->getSizeBytes() / sizeof(float); + for (uint32_t i = 0; i < arraySize; i ++) { + if (arraySize > 1) { + LOGV("Array Element [%u]", i); + } + if (dataType == RS_TYPE_MATRIX_4X4) { + LOGV("Matrix4x4"); + LOGV("{%f, %f, %f, %f", fd[0], fd[4], fd[8], fd[12]); + LOGV(" %f, %f, %f, %f", fd[1], fd[5], fd[9], fd[13]); + LOGV(" %f, %f, %f, %f", fd[2], fd[6], fd[10], fd[14]); + LOGV(" %f, %f, %f, %f}", fd[3], fd[7], fd[11], fd[15]); + } else if (dataType == RS_TYPE_MATRIX_3X3) { + LOGV("Matrix3x3"); + LOGV("{%f, %f, %f", fd[0], fd[3], fd[6]); + LOGV(" %f, %f, %f", fd[1], fd[4], fd[7]); + LOGV(" %f, %f, %f}", fd[2], fd[5], fd[8]); + } else if (dataType == RS_TYPE_MATRIX_2X2) { + LOGV("Matrix2x2"); + LOGV("{%f, %f", fd[0], fd[2]); + LOGV(" %f, %f}", fd[1], fd[3]); + } else { + switch (field->getComponent().getVectorSize()) { + case 1: + LOGV("Uniform 1 = %f", fd[0]); + break; + case 2: + LOGV("Uniform 2 = %f %f", fd[0], fd[1]); + break; + case 3: + LOGV("Uniform 3 = %f %f %f", fd[0], fd[1], fd[2]); + break; + case 4: + LOGV("Uniform 4 = %f %f %f %f", fd[0], fd[1], fd[2], fd[3]); + break; + default: + rsAssert(0); + } + } + LOGE("Element size %u data=%p", elementSize, fd); + fd += elementSize; + LOGE("New data=%p", fd); + } +} + +void Program::setUniform(Context *rsc, const Element *field, const float *fd, + int32_t slot, uint32_t arraySize ) { + RsDataType dataType = field->getType(); + if (dataType == RS_TYPE_MATRIX_4X4) { + glUniformMatrix4fv(slot, arraySize, GL_FALSE, fd); + } else if (dataType == RS_TYPE_MATRIX_3X3) { + glUniformMatrix3fv(slot, arraySize, GL_FALSE, fd); + } else if (dataType == RS_TYPE_MATRIX_2X2) { + glUniformMatrix2fv(slot, arraySize, GL_FALSE, fd); + } else { + switch (field->getComponent().getVectorSize()) { + case 1: + glUniform1fv(slot, arraySize, fd); + break; + case 2: + glUniform2fv(slot, arraySize, fd); + break; + case 3: + glUniform3fv(slot, arraySize, fd); + break; + case 4: + glUniform4fv(slot, arraySize, fd); + break; + default: + rsAssert(0); + } + } +} +void Program::setupUserConstants(Context *rsc, ShaderCache *sc, bool isFragment) { + uint32_t uidx = 0; + for (uint32_t ct=0; ct < mConstantCount; ct++) { + Allocation *alloc = mConstants[ct].get(); + if (!alloc) { + LOGE("Attempting to set constants on shader id %u, but alloc at slot %u is not set", (uint32_t)this, ct); + rsc->setError(RS_ERROR_BAD_SHADER, "No constant allocation bound"); + continue; + } + + const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr()); + const Element *e = mConstantTypes[ct]->getElement(); + for (uint32_t field=0; field < e->getFieldCount(); field++) { + const Element *f = e->getField(field); + const char *fieldName = e->getFieldName(field); + // If this field is padding, skip it + if (fieldName[0] == '#') { + continue; + } + + uint32_t offset = e->getFieldOffsetBytes(field); + const float *fd = reinterpret_cast<const float *>(&data[offset]); + + int32_t slot = -1; + uint32_t arraySize = 1; + if (!isFragment) { + slot = sc->vtxUniformSlot(uidx); + arraySize = sc->vtxUniformSize(uidx); + } else { + slot = sc->fragUniformSlot(uidx); + arraySize = sc->fragUniformSize(uidx); + } + if (rsc->props.mLogShadersUniforms) { + LOGV("Uniform slot=%i, offset=%i, constant=%i, field=%i, uidx=%i, name=%s", slot, offset, ct, field, uidx, fieldName); + } + uidx ++; + if (slot < 0) { + continue; + } + + if (rsc->props.mLogShadersUniforms) { + logUniform(f, fd, arraySize); + } + setUniform(rsc, f, fd, slot, arraySize); + } + } +} + +void Program::initAttribAndUniformArray() { + mAttribCount = 0; + for (uint32_t ct=0; ct < mInputCount; ct++) { + const Element *elem = mInputElements[ct].get(); + for (uint32_t field=0; field < elem->getFieldCount(); field++) { + if (elem->getFieldName(field)[0] != '#') { + mAttribCount ++; + } + } + } + + mUniformCount = 0; + for (uint32_t ct=0; ct < mConstantCount; ct++) { + const Element *elem = mConstantTypes[ct]->getElement(); + + for (uint32_t field=0; field < elem->getFieldCount(); field++) { + if (elem->getFieldName(field)[0] != '#') { + mUniformCount ++; + } + } + } + mUniformCount += mTextureCount; + + if (mAttribCount) { + mAttribNames = new String8[mAttribCount]; + } + if (mUniformCount) { + mUniformNames = new String8[mUniformCount]; + mUniformArraySizes = new uint32_t[mUniformCount]; + } +} + +void Program::initAddUserElement(const Element *e, String8 *names, uint32_t *arrayLengths, uint32_t *count, const char *prefix) { + rsAssert(e->getFieldCount()); + for (uint32_t ct=0; ct < e->getFieldCount(); ct++) { + const Element *ce = e->getField(ct); + if (ce->getFieldCount()) { + initAddUserElement(ce, names, arrayLengths, count, prefix); + } else if (e->getFieldName(ct)[0] != '#') { + String8 tmp(prefix); + tmp.append(e->getFieldName(ct)); + names[*count].setTo(tmp.string()); + if (arrayLengths) { + arrayLengths[*count] = e->getFieldArraySize(ct); + } + (*count)++; + } + } +} namespace android { namespace renderscript { - -void rsi_ProgramBindConstants(Context *rsc, RsProgram vp, uint32_t slot, RsAllocation constants) -{ +void rsi_ProgramBindConstants(Context *rsc, RsProgram vp, uint32_t slot, RsAllocation constants) { Program *p = static_cast<Program *>(vp); - p->bindAllocation(static_cast<Allocation *>(constants), slot); + p->bindAllocation(rsc, static_cast<Allocation *>(constants), slot); } -void rsi_ProgramBindTexture(Context *rsc, RsProgram vpf, uint32_t slot, RsAllocation a) -{ +void rsi_ProgramBindTexture(Context *rsc, RsProgram vpf, uint32_t slot, RsAllocation a) { Program *p = static_cast<Program *>(vpf); - p->bindTexture(slot, static_cast<Allocation *>(a)); + p->bindTexture(rsc, slot, static_cast<Allocation *>(a)); } -void rsi_ProgramBindSampler(Context *rsc, RsProgram vpf, uint32_t slot, RsSampler s) -{ +void rsi_ProgramBindSampler(Context *rsc, RsProgram vpf, uint32_t slot, RsSampler s) { Program *p = static_cast<Program *>(vpf); - p->bindSampler(slot, static_cast<Sampler *>(s)); + p->bindSampler(rsc, slot, static_cast<Sampler *>(s)); } } diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h index 86f85fb70e48..c48464dadecc 100644 --- a/libs/rs/rsProgram.h +++ b/libs/rs/rsProgram.h @@ -23,29 +23,27 @@ // --------------------------------------------------------------------------- namespace android { namespace renderscript { - - class ShaderCache; -class Program : public ObjectBase -{ +#define RS_SHADER_INTERNAL "//rs_shader_internal\n" +#define RS_SHADER_ATTR "ATTRIB_" +#define RS_SHADER_UNI "UNI_" + +class Program : public ObjectBase { public: - const static uint32_t MAX_ATTRIBS = 8; - const static uint32_t MAX_UNIFORMS = 16; - const static uint32_t MAX_TEXTURE = 2; Program(Context *); Program(Context *, const char * shaderText, uint32_t shaderLength, const uint32_t * params, uint32_t paramLength); virtual ~Program(); - void bindAllocation(Allocation *, uint32_t slot); + void bindAllocation(Context *, Allocation *, uint32_t slot); virtual void createShader(); - bool isUserProgram() const {return mUserShader.size() > 0;} + bool isUserProgram() const {return !mIsInternal;} - void bindTexture(uint32_t slot, Allocation *); - void bindSampler(uint32_t slot, Sampler *); + void bindTexture(Context *, uint32_t slot, Allocation *); + void bindSampler(Context *, uint32_t slot, Sampler *); uint32_t getShaderID() const {return mShaderID;} void setShader(const char *, uint32_t len); @@ -54,12 +52,14 @@ public: uint32_t getUniformCount() const {return mUniformCount;} const String8 & getAttribName(uint32_t i) const {return mAttribNames[i];} const String8 & getUniformName(uint32_t i) const {return mUniformNames[i];} + uint32_t getUniformArraySize(uint32_t i) const {return mUniformArraySizes[i];} String8 getGLSLInputString() const; String8 getGLSLOutputString() const; String8 getGLSLConstantString() const; bool isValid() const {return mIsValid;} + void forceDirty() const {mDirty = true;} protected: // Components not listed in "in" will be passed though @@ -67,12 +67,19 @@ protected: ObjectBaseRef<Element> *mInputElements; ObjectBaseRef<Element> *mOutputElements; ObjectBaseRef<Type> *mConstantTypes; + ObjectBaseRef<Allocation> *mConstants; uint32_t mInputCount; uint32_t mOutputCount; uint32_t mConstantCount; bool mIsValid; + bool mIsInternal; + + // Applies to vertex and fragment shaders only + void appendUserConstants(); + void setupUserConstants(Context *rsc, ShaderCache *sc, bool isFragment); + void initAddUserElement(const Element *e, String8 *names, uint32_t *arrayLengths, uint32_t *count, const char *prefix); - ObjectBaseRef<Allocation> mConstants[MAX_UNIFORMS]; + void initAttribAndUniformArray(); mutable bool mDirty; String8 mShader; @@ -82,8 +89,13 @@ protected: uint32_t mTextureCount; uint32_t mAttribCount; uint32_t mUniformCount; - String8 mAttribNames[MAX_ATTRIBS]; - String8 mUniformNames[MAX_UNIFORMS]; + String8 *mAttribNames; + String8 *mUniformNames; + uint32_t *mUniformArraySizes; + + void logUniform(const Element *field, const float *fd, uint32_t arraySize ); + void setUniform(Context *rsc, const Element *field, const float *fd, int32_t slot, uint32_t arraySize ); + void initMemberVars(); // The difference between Textures and Constants is how they are accessed // Texture lookups go though a sampler which in effect converts normalized @@ -91,17 +103,12 @@ protected: // and filtered. // // Constants are strictly accessed by programetic loads. - ObjectBaseRef<Allocation> mTextures[MAX_TEXTURE]; - ObjectBaseRef<Sampler> mSamplers[MAX_TEXTURE]; - + ObjectBaseRef<Allocation> *mTextures; + ObjectBaseRef<Sampler> *mSamplers; + RsTextureTarget *mTextureTargets; bool loadShader(Context *, uint32_t type); - -public: - void forceDirty() const {mDirty = true;} }; - - } } #endif diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp index c17b94c110c5..ff314b7f6429 100644 --- a/libs/rs/rsProgramFragment.cpp +++ b/libs/rs/rsProgramFragment.cpp @@ -15,160 +15,104 @@ */ #include "rsContext.h" -#include "rsProgramFragment.h" - +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES/glext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#endif //ANDROID_RS_SERIALIZE + +#include "rsProgramFragment.h" using namespace android; using namespace android::renderscript; - -ProgramFragment::ProgramFragment(Context *rsc, const uint32_t * params, - uint32_t paramLength) : - Program(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - rsAssert(paramLength = 5); - - mEnvModes[0] = (RsTexEnvMode)params[0]; - mTextureFormats[0] = params[1]; - mEnvModes[1] = (RsTexEnvMode)params[2]; - mTextureFormats[1] = params[3]; - mPointSpriteEnable = params[4] != 0; - - mTextureEnableMask = 0; - if (mEnvModes[0]) { - mTextureEnableMask |= 1; - } - if (mEnvModes[1]) { - mTextureEnableMask |= 2; - } - init(rsc); -} - ProgramFragment::ProgramFragment(Context *rsc, const char * shaderText, uint32_t shaderLength, const uint32_t * params, - uint32_t paramLength) : - Program(rsc, shaderText, shaderLength, params, paramLength) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; + uint32_t paramLength) + : Program(rsc, shaderText, shaderLength, params, paramLength) { + + mConstantColor[0] = 1.f; + mConstantColor[1] = 1.f; + mConstantColor[2] = 1.f; + mConstantColor[3] = 1.f; init(rsc); - mTextureEnableMask = (1 << mTextureCount) -1; } - -ProgramFragment::~ProgramFragment() -{ +ProgramFragment::~ProgramFragment() { + if (mShaderID) { + mRSC->mShaderCache.cleanupFragment(mShaderID); + } } -void ProgramFragment::setupGL(const Context *rsc, ProgramFragmentState *state) -{ - if ((state->mLast.get() == this) && !mDirty) { +void ProgramFragment::setConstantColor(Context *rsc, float r, float g, float b, float a) { + if (isUserProgram()) { + LOGE("Attempting to set fixed function emulation color on user program"); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set fixed function emulation color on user program"); return; } - state->mLast.set(this); - - for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) { - glActiveTexture(GL_TEXTURE0 + ct); - if (!(mTextureEnableMask & (1 << ct)) || !mTextures[ct].get()) { - glDisable(GL_TEXTURE_2D); - continue; - } - - glEnable(GL_TEXTURE_2D); - if (rsc->checkVersion1_1()) { - if (mPointSpriteEnable) { - glEnable(GL_POINT_SPRITE_OES); - } else { - glDisable(GL_POINT_SPRITE_OES); - } - glTexEnvi(GL_POINT_SPRITE_OES, GL_COORD_REPLACE_OES, mPointSpriteEnable); - } - mTextures[ct]->uploadCheck(rsc); - glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID()); - - switch(mEnvModes[ct]) { - case RS_TEX_ENV_MODE_NONE: - rsAssert(0); - break; - case RS_TEX_ENV_MODE_REPLACE: - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - break; - case RS_TEX_ENV_MODE_MODULATE: - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - break; - case RS_TEX_ENV_MODE_DECAL: - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); - break; - } - - if (mSamplers[ct].get()) { - mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2()); - } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - } - - // Gross hack. - if (ct == 2) { - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD); - glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); - - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD); - glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); - glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); - glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); - } + if (mConstants[0].get() == NULL) { + LOGE("Unable to set fixed function emulation color because allocation is missing"); + rsc->setError(RS_ERROR_BAD_SHADER, "Unable to set fixed function emulation color because allocation is missing"); + return; } - glActiveTexture(GL_TEXTURE0); - mDirty = false; - rsc->checkError("ProgramFragment::setupGL"); + mConstantColor[0] = r; + mConstantColor[1] = g; + mConstantColor[2] = b; + mConstantColor[3] = a; + memcpy(mConstants[0]->getPtr(), mConstantColor, 4*sizeof(float)); + mDirty = true; } -void ProgramFragment::setupGL2(const Context *rsc, ProgramFragmentState *state, ShaderCache *sc) -{ - +void ProgramFragment::setupGL2(Context *rsc, ProgramFragmentState *state, ShaderCache *sc) { //LOGE("sgl2 frag1 %x", glGetError()); if ((state->mLast.get() == this) && !mDirty) { - //return; + return; } state->mLast.set(this); rsc->checkError("ProgramFragment::setupGL2 start"); - for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) { + + rsc->checkError("ProgramFragment::setupGL2 begin uniforms"); + setupUserConstants(rsc, sc, true); + + uint32_t numTexturesToBind = mTextureCount; + uint32_t numTexturesAvailable = rsc->getMaxFragmentTextures(); + if (numTexturesToBind >= numTexturesAvailable) { + LOGE("Attempting to bind %u textures on shader id %u, but only %u are available", + mTextureCount, (uint32_t)this, numTexturesAvailable); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind more textuers than available"); + numTexturesToBind = numTexturesAvailable; + } + + for (uint32_t ct=0; ct < numTexturesToBind; ct++) { glActiveTexture(GL_TEXTURE0 + ct); - if (!(mTextureEnableMask & (1 << ct)) || !mTextures[ct].get()) { + if (!mTextures[ct].get()) { + LOGE("No texture bound for shader id %u, texture unit %u", (uint)this, ct); + rsc->setError(RS_ERROR_BAD_SHADER, "No texture bound"); continue; } mTextures[ct]->uploadCheck(rsc); - glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID()); + GLenum target = (GLenum)mTextures[ct]->getGLTarget(); + if (target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP) { + LOGE("Attempting to bind unknown texture to shader id %u, texture unit %u", (uint)this, ct); + rsc->setError(RS_ERROR_BAD_SHADER, "Non-texture allocation bound to a shader"); + } + glBindTexture(target, mTextures[ct]->getTextureID()); rsc->checkError("ProgramFragment::setupGL2 tex bind"); if (mSamplers[ct].get()) { - mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2()); + mSamplers[ct]->setupGL(rsc, mTextures[ct].get()); } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); rsc->checkError("ProgramFragment::setupGL2 tex env"); } - glUniform1i(sc->fragUniformSlot(ct), ct); + glUniform1i(sc->fragUniformSlot(mTextureUniformIndexStart + ct), ct); rsc->checkError("ProgramFragment::setupGL2 uniforms"); } @@ -181,162 +125,104 @@ void ProgramFragment::loadShader(Context *rsc) { Program::loadShader(rsc, GL_FRAGMENT_SHADER); } -void ProgramFragment::createShader() -{ - mShader.setTo("precision mediump float;\n"); - mShader.append("varying vec4 varColor;\n"); - mShader.append("varying vec4 varTex0;\n"); - +void ProgramFragment::createShader() { if (mUserShader.length() > 1) { + mShader.append("precision mediump float;\n"); + appendUserConstants(); + char buf[256]; for (uint32_t ct=0; ct < mTextureCount; ct++) { - char buf[256]; - sprintf(buf, "uniform sampler2D uni_Tex%i;\n", ct); + if (mTextureTargets[ct] == RS_TEXTURE_2D) { + snprintf(buf, sizeof(buf), "uniform sampler2D UNI_Tex%i;\n", ct); + } else { + snprintf(buf, sizeof(buf), "uniform samplerCube UNI_Tex%i;\n", ct); + } mShader.append(buf); } - mShader.append(mUserShader); } else { - uint32_t mask = mTextureEnableMask; - uint32_t texNum = 0; - while (mask) { - if (mask & 1) { - char buf[64]; - mShader.append("uniform sampler2D uni_Tex"); - sprintf(buf, "%i", texNum); - mShader.append(buf); - mShader.append(";\n"); - } - mask >>= 1; - texNum++; - } - - - mShader.append("void main() {\n"); - mShader.append(" vec4 col = varColor;\n"); - - if (mTextureEnableMask) { - if (mPointSpriteEnable) { - mShader.append(" vec2 t0 = gl_PointCoord;\n"); - } else { - mShader.append(" vec2 t0 = varTex0.xy;\n"); - } - } - - mask = mTextureEnableMask; - texNum = 0; - while (mask) { - if (mask & 1) { - switch(mEnvModes[texNum]) { - case RS_TEX_ENV_MODE_NONE: - rsAssert(0); - break; - case RS_TEX_ENV_MODE_REPLACE: - switch(mTextureFormats[texNum]) { - case 1: - mShader.append(" col.a = texture2D(uni_Tex0, t0).a;\n"); - break; - case 2: - mShader.append(" col.rgba = texture2D(uni_Tex0, t0).rgba;\n"); - break; - case 3: - mShader.append(" col.rgb = texture2D(uni_Tex0, t0).rgb;\n"); - break; - case 4: - mShader.append(" col.rgba = texture2D(uni_Tex0, t0).rgba;\n"); - break; - } - break; - case RS_TEX_ENV_MODE_MODULATE: - switch(mTextureFormats[texNum]) { - case 1: - mShader.append(" col.a *= texture2D(uni_Tex0, t0).a;\n"); - break; - case 2: - mShader.append(" col.rgba *= texture2D(uni_Tex0, t0).rgba;\n"); - break; - case 3: - mShader.append(" col.rgb *= texture2D(uni_Tex0, t0).rgb;\n"); - break; - case 4: - mShader.append(" col.rgba *= texture2D(uni_Tex0, t0).rgba;\n"); - break; - } - break; - case RS_TEX_ENV_MODE_DECAL: - mShader.append(" col = texture2D(uni_Tex0, t0);\n"); - break; - } + LOGE("ProgramFragment::createShader cannot create program, shader code not defined"); + rsAssert(0); + } +} - } - mask >>= 1; - texNum++; +void ProgramFragment::init(Context *rsc) { + uint32_t uniformIndex = 0; + if (mUserShader.size() > 0) { + for (uint32_t ct=0; ct < mConstantCount; ct++) { + initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformIndex, RS_SHADER_UNI); } + } + mTextureUniformIndexStart = uniformIndex; + char buf[256]; + for (uint32_t ct=0; ct < mTextureCount; ct++) { + snprintf(buf, sizeof(buf), "UNI_Tex%i", ct); + mUniformNames[uniformIndex].setTo(buf); + mUniformArraySizes[uniformIndex] = 1; + uniformIndex++; + } - //mShader.append(" col.a = 1.0;\n"); - //mShader.append(" col.r = 0.5;\n"); + createShader(); +} - mShader.append(" gl_FragColor = col;\n"); - mShader.append("}\n"); - } +void ProgramFragment::serialize(OStream *stream) const { } -void ProgramFragment::init(Context *rsc) -{ - mUniformCount = 2; - mUniformNames[0].setTo("uni_Tex0"); - mUniformNames[1].setTo("uni_Tex1"); +ProgramFragment *ProgramFragment::createFromStream(Context *rsc, IStream *stream) { + return NULL; +} - createShader(); +ProgramFragmentState::ProgramFragmentState() { + mPF = NULL; } -ProgramFragmentState::ProgramFragmentState() -{ +ProgramFragmentState::~ProgramFragmentState() { + ObjectBase::checkDelete(mPF); mPF = NULL; } -ProgramFragmentState::~ProgramFragmentState() -{ - delete mPF; +void ProgramFragmentState::init(Context *rsc) { + String8 shaderString(RS_SHADER_INTERNAL); + shaderString.append("varying lowp vec4 varColor;\n"); + shaderString.append("varying vec2 varTex0;\n"); + shaderString.append("void main() {\n"); + shaderString.append(" lowp vec4 col = UNI_Color;\n"); + shaderString.append(" gl_FragColor = col;\n"); + shaderString.append("}\n"); -} + const Element *colorElem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4); + rsc->mStateElement.elementBuilderBegin(); + rsc->mStateElement.elementBuilderAdd(colorElem, "Color", 1); + const Element *constInput = rsc->mStateElement.elementBuilderCreate(rsc); + + Type *inputType = Type::getType(rsc, constInput, 1, 0, 0, false, false); + + uint32_t tmp[2]; + tmp[0] = RS_PROGRAM_PARAM_CONSTANT; + tmp[1] = (uint32_t)inputType; + + Allocation *constAlloc = new Allocation(rsc, inputType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS); + ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(), + shaderString.length(), tmp, 2); + pf->bindAllocation(rsc, constAlloc, 0); + pf->setConstantColor(rsc, 1.0f, 1.0f, 1.0f, 1.0f); -void ProgramFragmentState::init(Context *rsc, int32_t w, int32_t h) -{ - uint32_t tmp[5] = { - RS_TEX_ENV_MODE_NONE, 0, - RS_TEX_ENV_MODE_NONE, 0, - 0 - }; - ProgramFragment *pf = new ProgramFragment(rsc, tmp, 5); mDefault.set(pf); - pf->init(rsc); } -void ProgramFragmentState::deinit(Context *rsc) -{ +void ProgramFragmentState::deinit(Context *rsc) { mDefault.clear(); mLast.clear(); } - namespace android { namespace renderscript { -RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc, - const uint32_t * params, - uint32_t paramLength) -{ - ProgramFragment *pf = new ProgramFragment(rsc, params, paramLength); - pf->incUserRef(); - return pf; -} - -RsProgramFragment rsi_ProgramFragmentCreate2(Context *rsc, const char * shaderText, +RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc, const char * shaderText, uint32_t shaderLength, const uint32_t * params, - uint32_t paramLength) -{ + uint32_t paramLength) { ProgramFragment *pf = new ProgramFragment(rsc, shaderText, shaderLength, params, paramLength); pf->incUserRef(); + //LOGE("rsi_ProgramFragmentCreate %p", pf); return pf; } diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h index 9fa565d4cf8d..3d2894690cb2 100644 --- a/libs/rs/rsProgramFragment.h +++ b/libs/rs/rsProgramFragment.h @@ -25,49 +25,44 @@ namespace renderscript { class ProgramFragmentState; -class ProgramFragment : public Program -{ +class ProgramFragment : public Program { public: - ProgramFragment(Context *, const uint32_t * params, uint32_t paramLength); ProgramFragment(Context *rsc, const char * shaderText, uint32_t shaderLength, const uint32_t * params, uint32_t paramLength); virtual ~ProgramFragment(); - virtual void setupGL(const Context *, ProgramFragmentState *); - virtual void setupGL2(const Context *, ProgramFragmentState *, ShaderCache *sc); + virtual void setupGL2(Context *, ProgramFragmentState *, ShaderCache *sc); virtual void createShader(); virtual void loadShader(Context *rsc); virtual void init(Context *rsc); + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_FRAGMENT; } + static ProgramFragment *createFromStream(Context *rsc, IStream *stream); + + void setConstantColor(Context *, float, float, float, float); protected: - // Hacks to create a program for now - uint32_t mTextureFormats[MAX_TEXTURE]; - uint32_t mTextureDimensions[MAX_TEXTURE]; - RsTexEnvMode mEnvModes[MAX_TEXTURE]; - uint32_t mTextureEnableMask; - bool mPointSpriteEnable; + float mConstantColor[4]; + int32_t mTextureUniformIndexStart; }; -class ProgramFragmentState -{ +class ProgramFragmentState { public: ProgramFragmentState(); ~ProgramFragmentState(); ProgramFragment *mPF; - void init(Context *rsc, int32_t w, int32_t h); + void init(Context *rsc); void deinit(Context *rsc); - ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE]; ObjectBaseRef<ProgramFragment> mDefault; Vector<ProgramFragment *> mPrograms; ObjectBaseRef<ProgramFragment> mLast; }; - } } #endif diff --git a/libs/rs/rsProgramRaster.cpp b/libs/rs/rsProgramRaster.cpp index 13887d15e74e..ace1572f4010 100644 --- a/libs/rs/rsProgramRaster.cpp +++ b/libs/rs/rsProgramRaster.cpp @@ -15,135 +15,113 @@ */ #include "rsContext.h" -#include "rsProgramRaster.h" - +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES/glext.h> +#endif //ANDROID_RS_SERIALIZE + +#include "rsProgramRaster.h" using namespace android; using namespace android::renderscript; -ProgramRaster::ProgramRaster(Context *rsc, - bool pointSmooth, - bool lineSmooth, - bool pointSprite) : - Program(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; +ProgramRaster::ProgramRaster(Context *rsc, bool pointSmooth, + bool lineSmooth, bool pointSprite) + : Program(rsc) { + mPointSmooth = pointSmooth; mLineSmooth = lineSmooth; mPointSprite = pointSprite; - - mPointSize = 1.0f; mLineWidth = 1.0f; + mCull = RS_CULL_BACK; } -ProgramRaster::~ProgramRaster() -{ +ProgramRaster::~ProgramRaster() { } -void ProgramRaster::setLineWidth(float s) -{ +void ProgramRaster::setLineWidth(float s) { mLineWidth = s; + mDirty = true; } -void ProgramRaster::setPointSize(float s) -{ - mPointSize = s; +void ProgramRaster::setCullMode(RsCullMode mode) { + mCull = mode; + mDirty = true; } -void ProgramRaster::setupGL(const Context *rsc, ProgramRasterState *state) -{ - if (state->mLast.get() == this) { +void ProgramRaster::setupGL2(const Context *rsc, ProgramRasterState *state) { + if (state->mLast.get() == this && !mDirty) { return; } state->mLast.set(this); - - glPointSize(mPointSize); - if (mPointSmooth) { - glEnable(GL_POINT_SMOOTH); - } else { - glDisable(GL_POINT_SMOOTH); - } - - glLineWidth(mLineWidth); - if (mLineSmooth) { - glEnable(GL_LINE_SMOOTH); - } else { - glDisable(GL_LINE_SMOOTH); - } - - if (rsc->checkVersion1_1()) { - if (mPointSprite) { - glEnable(GL_POINT_SPRITE_OES); - } else { - glDisable(GL_POINT_SPRITE_OES); - } + mDirty = false; + + switch (mCull) { + case RS_CULL_BACK: + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + break; + case RS_CULL_FRONT: + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); + break; + case RS_CULL_NONE: + glDisable(GL_CULL_FACE); + break; } } -void ProgramRaster::setupGL2(const Context *rsc, ProgramRasterState *state) -{ - if (state->mLast.get() == this) { - return; - } - state->mLast.set(this); +void ProgramRaster::serialize(OStream *stream) const { } +ProgramRaster *ProgramRaster::createFromStream(Context *rsc, IStream *stream) { + return NULL; +} - -ProgramRasterState::ProgramRasterState() -{ +ProgramRasterState::ProgramRasterState() { } -ProgramRasterState::~ProgramRasterState() -{ +ProgramRasterState::~ProgramRasterState() { } -void ProgramRasterState::init(Context *rsc, int32_t w, int32_t h) -{ +void ProgramRasterState::init(Context *rsc) { ProgramRaster *pr = new ProgramRaster(rsc, false, false, false); mDefault.set(pr); } -void ProgramRasterState::deinit(Context *rsc) -{ +void ProgramRasterState::deinit(Context *rsc) { mDefault.clear(); mLast.clear(); } - namespace android { namespace renderscript { -RsProgramRaster rsi_ProgramRasterCreate(Context * rsc, RsElement in, RsElement out, +RsProgramRaster rsi_ProgramRasterCreate(Context * rsc, bool pointSmooth, bool lineSmooth, - bool pointSprite) -{ - ProgramRaster *pr = new ProgramRaster(rsc, - pointSmooth, - lineSmooth, - pointSprite); + bool pointSprite) { + ProgramRaster *pr = new ProgramRaster(rsc, pointSmooth, + lineSmooth, pointSprite); pr->incUserRef(); return pr; } -void rsi_ProgramRasterSetPointSize(Context * rsc, RsProgramRaster vpr, float s) -{ +void rsi_ProgramRasterSetLineWidth(Context * rsc, + RsProgramRaster vpr, + float s) { ProgramRaster *pr = static_cast<ProgramRaster *>(vpr); - pr->setPointSize(s); + pr->setLineWidth(s); } -void rsi_ProgramRasterSetLineWidth(Context * rsc, RsProgramRaster vpr, float s) -{ +void rsi_ProgramRasterSetCullMode(Context * rsc, + RsProgramRaster vpr, + RsCullMode mode) { ProgramRaster *pr = static_cast<ProgramRaster *>(vpr); - pr->setLineWidth(s); + pr->setCullMode(mode); } - } } diff --git a/libs/rs/rsProgramRaster.h b/libs/rs/rsProgramRaster.h index c3a9c9077866..7958af90fb0f 100644 --- a/libs/rs/rsProgramRaster.h +++ b/libs/rs/rsProgramRaster.h @@ -25,8 +25,7 @@ namespace renderscript { class ProgramRasterState; -class ProgramRaster : public Program -{ +class ProgramRaster : public Program { public: ProgramRaster(Context *rsc, bool pointSmooth, @@ -34,29 +33,27 @@ public: bool pointSprite); virtual ~ProgramRaster(); - virtual void setupGL(const Context *, ProgramRasterState *); virtual void setupGL2(const Context *, ProgramRasterState *); + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_RASTER; } + static ProgramRaster *createFromStream(Context *rsc, IStream *stream); void setLineWidth(float w); - void setPointSize(float s); + void setCullMode(RsCullMode mode); protected: bool mPointSmooth; bool mLineSmooth; bool mPointSprite; - - float mPointSize; float mLineWidth; - - + RsCullMode mCull; }; -class ProgramRasterState -{ +class ProgramRasterState { public: ProgramRasterState(); ~ProgramRasterState(); - void init(Context *rsc, int32_t w, int32_t h); + void init(Context *rsc); void deinit(Context *rsc); ObjectBaseRef<ProgramRaster> mDefault; diff --git a/libs/rs/rsProgramFragmentStore.cpp b/libs/rs/rsProgramStore.cpp index 8a2157f3c0b2..09b759d9a340 100644 --- a/libs/rs/rsProgramFragmentStore.cpp +++ b/libs/rs/rsProgramStore.cpp @@ -15,20 +15,18 @@ */ #include "rsContext.h" -#include "rsProgramFragmentStore.h" - +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES/glext.h> +#endif //ANDROID_RS_SERIALIZE + +#include "rsProgramStore.h" using namespace android; using namespace android::renderscript; -ProgramFragmentStore::ProgramFragmentStore(Context *rsc) : - Program(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; +ProgramStore::ProgramStore(Context *rsc) : Program(rsc) { mDitherEnable = true; mBlendEnable = false; mColorRWriteEnable = true; @@ -38,20 +36,15 @@ ProgramFragmentStore::ProgramFragmentStore(Context *rsc) : mBlendSrc = GL_ONE; mBlendDst = GL_ZERO; - mDepthTestEnable = false; mDepthWriteEnable = true; mDepthFunc = GL_LESS; - - } -ProgramFragmentStore::~ProgramFragmentStore() -{ +ProgramStore::~ProgramStore() { } -void ProgramFragmentStore::setupGL(const Context *rsc, ProgramFragmentStoreState *state) -{ +void ProgramStore::setupGL2(const Context *rsc, ProgramStoreState *state) { if (state->mLast.get() == this) { return; } @@ -70,47 +63,23 @@ void ProgramFragmentStore::setupGL(const Context *rsc, ProgramFragmentStoreState //LOGE("pfs %i, %i, %x", mDepthWriteEnable, mDepthTestEnable, mDepthFunc); - glDepthMask(mDepthWriteEnable); - if(mDepthTestEnable || mDepthWriteEnable) { - glEnable(GL_DEPTH_TEST); - glDepthFunc(mDepthFunc); + if (rsc->mUserSurfaceConfig.depthMin > 0) { + glDepthMask(mDepthWriteEnable); + if (mDepthTestEnable || mDepthWriteEnable) { + glEnable(GL_DEPTH_TEST); + glDepthFunc(mDepthFunc); + } else { + glDisable(GL_DEPTH_TEST); + } } else { + glDepthMask(false); glDisable(GL_DEPTH_TEST); } - if (mDitherEnable) { - glEnable(GL_DITHER); - } else { - glDisable(GL_DITHER); - } -} - -void ProgramFragmentStore::setupGL2(const Context *rsc, ProgramFragmentStoreState *state) -{ - if (state->mLast.get() == this) { - return; - } - state->mLast.set(this); - - glColorMask(mColorRWriteEnable, - mColorGWriteEnable, - mColorBWriteEnable, - mColorAWriteEnable); - if (mBlendEnable) { - glEnable(GL_BLEND); - glBlendFunc(mBlendSrc, mBlendDst); - } else { - glDisable(GL_BLEND); - } - - //LOGE("pfs %i, %i, %x", mDepthWriteEnable, mDepthTestEnable, mDepthFunc); - - glDepthMask(mDepthWriteEnable); - if(mDepthTestEnable || mDepthWriteEnable) { - glEnable(GL_DEPTH_TEST); - glDepthFunc(mDepthFunc); + if (rsc->mUserSurfaceConfig.stencilMin > 0) { } else { - glDisable(GL_DEPTH_TEST); + glStencilMask(0); + glDisable(GL_STENCIL_TEST); } if (mDitherEnable) { @@ -120,17 +89,21 @@ void ProgramFragmentStore::setupGL2(const Context *rsc, ProgramFragmentStoreStat } } - -void ProgramFragmentStore::setDitherEnable(bool enable) -{ +void ProgramStore::setDitherEnable(bool enable) { mDitherEnable = enable; } -void ProgramFragmentStore::setDepthFunc(RsDepthFunc func) -{ +void ProgramStore::serialize(OStream *stream) const { +} + +ProgramStore *ProgramStore::createFromStream(Context *rsc, IStream *stream) { + return NULL; +} + +void ProgramStore::setDepthFunc(RsDepthFunc func) { mDepthTestEnable = true; - switch(func) { + switch (func) { case RS_DEPTH_FUNC_ALWAYS: mDepthTestEnable = false; mDepthFunc = GL_ALWAYS; @@ -156,20 +129,18 @@ void ProgramFragmentStore::setDepthFunc(RsDepthFunc func) } } -void ProgramFragmentStore::setDepthMask(bool mask) -{ +void ProgramStore::setDepthMask(bool mask) { mDepthWriteEnable = mask; } -void ProgramFragmentStore::setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst) -{ +void ProgramStore::setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst) { mBlendEnable = true; if ((src == RS_BLEND_SRC_ONE) && (dst == RS_BLEND_DST_ZERO)) { mBlendEnable = false; } - switch(src) { + switch (src) { case RS_BLEND_SRC_ZERO: mBlendSrc = GL_ZERO; break; @@ -199,7 +170,7 @@ void ProgramFragmentStore::setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst) break; } - switch(dst) { + switch (dst) { case RS_BLEND_DST_ZERO: mBlendDst = GL_ZERO; break; @@ -227,82 +198,66 @@ void ProgramFragmentStore::setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst) } } -void ProgramFragmentStore::setColorMask(bool r, bool g, bool b, bool a) -{ +void ProgramStore::setColorMask(bool r, bool g, bool b, bool a) { mColorRWriteEnable = r; mColorGWriteEnable = g; mColorBWriteEnable = b; mColorAWriteEnable = a; } - -ProgramFragmentStoreState::ProgramFragmentStoreState() -{ +ProgramStoreState::ProgramStoreState() { mPFS = NULL; } -ProgramFragmentStoreState::~ProgramFragmentStoreState() -{ - delete mPFS; - +ProgramStoreState::~ProgramStoreState() { + ObjectBase::checkDelete(mPFS); + mPFS = NULL; } -void ProgramFragmentStoreState::init(Context *rsc, int32_t w, int32_t h) -{ - ProgramFragmentStore *pfs = new ProgramFragmentStore(rsc); +void ProgramStoreState::init(Context *rsc) { + ProgramStore *pfs = new ProgramStore(rsc); mDefault.set(pfs); } -void ProgramFragmentStoreState::deinit(Context *rsc) -{ +void ProgramStoreState::deinit(Context *rsc) { mDefault.clear(); mLast.clear(); } - namespace android { namespace renderscript { -void rsi_ProgramFragmentStoreBegin(Context * rsc, RsElement in, RsElement out) -{ - delete rsc->mStateFragmentStore.mPFS; - rsc->mStateFragmentStore.mPFS = new ProgramFragmentStore(rsc); - +void rsi_ProgramStoreBegin(Context * rsc, RsElement in, RsElement out) { + ObjectBase::checkDelete(rsc->mStateFragmentStore.mPFS); + rsc->mStateFragmentStore.mPFS = new ProgramStore(rsc); } -void rsi_ProgramFragmentStoreDepthFunc(Context *rsc, RsDepthFunc func) -{ +void rsi_ProgramStoreDepthFunc(Context *rsc, RsDepthFunc func) { rsc->mStateFragmentStore.mPFS->setDepthFunc(func); } -void rsi_ProgramFragmentStoreDepthMask(Context *rsc, bool mask) -{ +void rsi_ProgramStoreDepthMask(Context *rsc, bool mask) { rsc->mStateFragmentStore.mPFS->setDepthMask(mask); } -void rsi_ProgramFragmentStoreColorMask(Context *rsc, bool r, bool g, bool b, bool a) -{ +void rsi_ProgramStoreColorMask(Context *rsc, bool r, bool g, bool b, bool a) { rsc->mStateFragmentStore.mPFS->setColorMask(r, g, b, a); } -void rsi_ProgramFragmentStoreBlendFunc(Context *rsc, RsBlendSrcFunc src, RsBlendDstFunc dst) -{ +void rsi_ProgramStoreBlendFunc(Context *rsc, RsBlendSrcFunc src, RsBlendDstFunc dst) { rsc->mStateFragmentStore.mPFS->setBlendFunc(src, dst); } -RsProgramFragmentStore rsi_ProgramFragmentStoreCreate(Context *rsc) -{ - ProgramFragmentStore *pfs = rsc->mStateFragmentStore.mPFS; +RsProgramStore rsi_ProgramStoreCreate(Context *rsc) { + ProgramStore *pfs = rsc->mStateFragmentStore.mPFS; pfs->incUserRef(); rsc->mStateFragmentStore.mPFS = 0; return pfs; } -void rsi_ProgramFragmentStoreDither(Context *rsc, bool enable) -{ +void rsi_ProgramStoreDither(Context *rsc, bool enable) { rsc->mStateFragmentStore.mPFS->setDitherEnable(enable); } - } } diff --git a/libs/rs/rsProgramFragmentStore.h b/libs/rs/rsProgramStore.h index 3412c9918ac1..f8eb7cf92ccc 100644 --- a/libs/rs/rsProgramFragmentStore.h +++ b/libs/rs/rsProgramStore.h @@ -18,21 +18,20 @@ #define ANDROID_RS_PROGRAM_FRAGMENT_STORE_H #include "rsProgram.h" +#include "rsStream.h" // --------------------------------------------------------------------------- namespace android { namespace renderscript { -class ProgramFragmentStoreState; +class ProgramStoreState; -class ProgramFragmentStore : public Program -{ +class ProgramStore : public Program { public: - ProgramFragmentStore(Context *); - virtual ~ProgramFragmentStore(); + ProgramStore(Context *); + virtual ~ProgramStore(); - virtual void setupGL(const Context *, ProgramFragmentStoreState *); - virtual void setupGL2(const Context *, ProgramFragmentStoreState *); + virtual void setupGL2(const Context *, ProgramStoreState *); void setDepthFunc(RsDepthFunc); void setDepthMask(bool); @@ -42,6 +41,10 @@ public: void setDitherEnable(bool); + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_STORE; } + static ProgramStore *createFromStream(Context *rsc, IStream *stream); + protected: bool mDitherEnable; @@ -60,22 +63,20 @@ protected: bool mStencilTestEnable; }; -class ProgramFragmentStoreState -{ +class ProgramStoreState { public: - ProgramFragmentStoreState(); - ~ProgramFragmentStoreState(); - void init(Context *rsc, int32_t w, int32_t h); + ProgramStoreState(); + ~ProgramStoreState(); + void init(Context *rsc); void deinit(Context *rsc); - ObjectBaseRef<ProgramFragmentStore> mDefault; - ObjectBaseRef<ProgramFragmentStore> mLast; + ObjectBaseRef<ProgramStore> mDefault; + ObjectBaseRef<ProgramStore> mLast; - ProgramFragmentStore *mPFS; + ProgramStore *mPFS; }; - } } #endif diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp index a2b2df4d967e..403c2a6e356b 100644 --- a/libs/rs/rsProgramVertex.cpp +++ b/libs/rs/rsProgramVertex.cpp @@ -15,145 +15,54 @@ */ #include "rsContext.h" -#include "rsProgramVertex.h" - +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES/glext.h> #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#endif //ANDROID_RS_SERIALIZE + +#include "rsProgramVertex.h" using namespace android; using namespace android::renderscript; -ProgramVertex::ProgramVertex(Context *rsc, bool texMat) : - Program(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - mTextureMatrixEnable = texMat; - mLightCount = 0; - init(rsc); -} - ProgramVertex::ProgramVertex(Context *rsc, const char * shaderText, uint32_t shaderLength, const uint32_t * params, - uint32_t paramLength) : - Program(rsc, shaderText, shaderLength, params, paramLength) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - mTextureMatrixEnable = false; - mLightCount = 0; - + uint32_t paramLength) + : Program(rsc, shaderText, shaderLength, params, paramLength) { init(rsc); } -ProgramVertex::~ProgramVertex() -{ -} - -static void logMatrix(const char *txt, const float *f) -{ - LOGV("Matrix %s, %p", txt, f); - LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[0], f[4], f[8], f[12]); - LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[1], f[5], f[9], f[13]); - LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[2], f[6], f[10], f[14]); - LOGV("%6.4f, %6.4f, %6.4f, %6.4f", f[3], f[7], f[11], f[15]); -} - -void ProgramVertex::setupGL(const Context *rsc, ProgramVertexState *state) -{ - if ((state->mLast.get() == this) && !mDirty) { - return; - } - state->mLast.set(this); - - const float *f = static_cast<const float *>(mConstants[0]->getPtr()); - - glMatrixMode(GL_TEXTURE); - if (mTextureMatrixEnable) { - glLoadMatrixf(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]); - } else { - glLoadIdentity(); - } - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - if (mLightCount) { - int v = 0; - glEnable(GL_LIGHTING); - glLightModelxv(GL_LIGHT_MODEL_TWO_SIDE, &v); - for (uint32_t ct = 0; ct < mLightCount; ct++) { - const Light *l = mLights[ct].get(); - glEnable(GL_LIGHT0 + ct); - l->setupGL(ct); - } - for (uint32_t ct = mLightCount; ct < MAX_LIGHTS; ct++) { - glDisable(GL_LIGHT0 + ct); - } - } else { - glDisable(GL_LIGHTING); - } - - if (!f) { - LOGE("Must bind constants to vertex program"); +ProgramVertex::~ProgramVertex() { + if (mShaderID) { + mRSC->mShaderCache.cleanupVertex(mShaderID); } - - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]); - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]); - - mDirty = false; } void ProgramVertex::loadShader(Context *rsc) { Program::loadShader(rsc, GL_VERTEX_SHADER); } -void ProgramVertex::createShader() -{ - mShader.setTo(""); - - mShader.append("varying vec4 varColor;\n"); - mShader.append("varying vec4 varTex0;\n"); - +void ProgramVertex::createShader(Context *rsc) { if (mUserShader.length() > 1) { - mShader.append("uniform mat4 "); - mShader.append(mUniformNames[0]); - mShader.append(";\n"); - - for (uint32_t ct=0; ct < mConstantCount; ct++) { - const Element *e = mConstantTypes[ct]->getElement(); - for (uint32_t field=0; field < e->getFieldCount(); field++) { - const Element *f = e->getField(field); - - // Cannot be complex - rsAssert(!f->getFieldCount()); - switch(f->getComponent().getVectorSize()) { - case 1: mShader.append("uniform float UNI_"); break; - case 2: mShader.append("uniform vec2 UNI_"); break; - case 3: mShader.append("uniform vec3 UNI_"); break; - case 4: mShader.append("uniform vec4 UNI_"); break; - default: - rsAssert(0); - } - - mShader.append(e->getFieldName(field)); - mShader.append(";\n"); - } - } + appendUserConstants(); for (uint32_t ct=0; ct < mInputCount; ct++) { const Element *e = mInputElements[ct].get(); for (uint32_t field=0; field < e->getFieldCount(); field++) { const Element *f = e->getField(field); + const char *fn = e->getFieldName(field); + + if (fn[0] == '#') { + continue; + } // Cannot be complex rsAssert(!f->getFieldCount()); - switch(f->getComponent().getVectorSize()) { + switch (f->getComponent().getVectorSize()) { case 1: mShader.append("attribute float ATTRIB_"); break; case 2: mShader.append("attribute vec2 ATTRIB_"); break; case 3: mShader.append("attribute vec3 ATTRIB_"); break; @@ -162,149 +71,115 @@ void ProgramVertex::createShader() rsAssert(0); } - mShader.append(e->getFieldName(field)); + mShader.append(fn); mShader.append(";\n"); } } mShader.append(mUserShader); } else { - mShader.append("attribute vec4 ATTRIB_LegacyPosition;\n"); - mShader.append("attribute vec4 ATTRIB_LegacyColor;\n"); - mShader.append("attribute vec3 ATTRIB_LegacyNormal;\n"); - mShader.append("attribute float ATTRIB_LegacyPointSize;\n"); - mShader.append("attribute vec4 ATTRIB_LegacyTexture;\n"); - - for (uint32_t ct=0; ct < mUniformCount; ct++) { - mShader.append("uniform mat4 "); - mShader.append(mUniformNames[ct]); - mShader.append(";\n"); - } - - mShader.append("void main() {\n"); - mShader.append(" gl_Position = UNI_MVP * ATTRIB_LegacyPosition;\n"); - mShader.append(" gl_PointSize = ATTRIB_LegacyPointSize;\n"); - - mShader.append(" varColor = ATTRIB_LegacyColor;\n"); - if (mTextureMatrixEnable) { - mShader.append(" varTex0 = UNI_TexMatrix * ATTRIB_LegacyTexture;\n"); - } else { - mShader.append(" varTex0 = ATTRIB_LegacyTexture;\n"); - } - //mShader.append(" pos.x = pos.x / 480.0;\n"); - //mShader.append(" pos.y = pos.y / 800.0;\n"); - //mShader.append(" gl_Position = pos;\n"); - mShader.append("}\n"); + rsc->setError(RS_ERROR_FATAL_UNKNOWN, + "ProgramFragment::createShader cannot create program, shader code not defined"); } } -void ProgramVertex::setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc) -{ - //LOGE("sgl2 vtx1 %x", glGetError()); +void ProgramVertex::setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc) { if ((state->mLast.get() == this) && !mDirty) { - //return; + return; } rsc->checkError("ProgramVertex::setupGL2 start"); - glVertexAttrib4f(1, state->color[0], state->color[1], state->color[2], state->color[3]); - - const float *f = static_cast<const float *>(mConstants[0]->getPtr()); - Matrix mvp; - mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]); - Matrix t; - t.load(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]); - mvp.multiply(&t); - - glUniformMatrix4fv(sc->vtxUniformSlot(0), 1, GL_FALSE, mvp.m); - if (mTextureMatrixEnable) { - glUniformMatrix4fv(sc->vtxUniformSlot(1), 1, GL_FALSE, - &f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]); - } - - rsc->checkError("ProgramVertex::setupGL2 begin uniforms"); - uint32_t uidx = 1; - for (uint32_t ct=0; ct < mConstantCount; ct++) { - Allocation *alloc = mConstants[ct+1].get(); - if (!alloc) { - continue; + if (!isUserProgram()) { + if (mConstants[0].get() == NULL) { + rsc->setError(RS_ERROR_FATAL_UNKNOWN, + "Unable to set fixed function emulation matrices because allocation is missing"); + return; } - - const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr()); - const Element *e = mConstantTypes[ct]->getElement(); - for (uint32_t field=0; field < e->getFieldCount(); field++) { - const Element *f = e->getField(field); - uint32_t offset = e->getFieldOffsetBytes(field); - int32_t slot = sc->vtxUniformSlot(uidx); - - const float *fd = reinterpret_cast<const float *>(&data[offset]); - - //LOGE("Uniform slot=%i, offset=%i, constant=%i, field=%i, uidx=%i", slot, offset, ct, field, uidx); - if (slot >= 0) { - switch(f->getComponent().getVectorSize()) { - case 1: - //LOGE("Uniform 1 = %f", fd[0]); - glUniform1fv(slot, 1, fd); - break; - case 2: - //LOGE("Uniform 2 = %f %f", fd[0], fd[1]); - glUniform2fv(slot, 1, fd); - break; - case 3: - //LOGE("Uniform 3 = %f %f %f", fd[0], fd[1], fd[2]); - glUniform3fv(slot, 1, fd); - break; - case 4: - //LOGE("Uniform 4 = %f %f %f %f", fd[0], fd[1], fd[2], fd[3]); - glUniform4fv(slot, 1, fd); - break; - default: - rsAssert(0); - } - } - uidx ++; + float *f = static_cast<float *>(mConstants[0]->getPtr()); + Matrix mvp; + mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]); + Matrix t; + t.load(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]); + mvp.multiply(&t); + for (uint32_t i = 0; i < 16; i ++) { + f[RS_PROGRAM_VERTEX_MVP_OFFSET + i] = mvp.m[i]; } } - for (uint32_t ct=0; ct < mConstantCount; ct++) { - uint32_t glSlot = sc->vtxUniformSlot(ct + 1); - - } + rsc->checkError("ProgramVertex::setupGL2 begin uniforms"); + setupUserConstants(rsc, sc, false); state->mLast.set(this); rsc->checkError("ProgramVertex::setupGL2"); } -void ProgramVertex::addLight(const Light *l) -{ - if (mLightCount < MAX_LIGHTS) { - mLights[mLightCount].set(l); - mLightCount++; +void ProgramVertex::setProjectionMatrix(Context *rsc, const rsc_Matrix *m) const { + if (isUserProgram()) { + rsc->setError(RS_ERROR_FATAL_UNKNOWN, + "Attempting to set fixed function emulation matrix projection on user program"); + return; + } + if (mConstants[0].get() == NULL) { + rsc->setError(RS_ERROR_FATAL_UNKNOWN, + "Unable to set fixed function emulation matrix projection because allocation is missing"); + return; } -} - -void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const -{ float *f = static_cast<float *>(mConstants[0]->getPtr()); memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m, sizeof(rsc_Matrix)); mDirty = true; } -void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const -{ +void ProgramVertex::setModelviewMatrix(Context *rsc, const rsc_Matrix *m) const { + if (isUserProgram()) { + rsc->setError(RS_ERROR_FATAL_UNKNOWN, + "Attempting to set fixed function emulation matrix modelview on user program"); + return; + } + if (mConstants[0].get() == NULL) { + rsc->setError(RS_ERROR_FATAL_UNKNOWN, + "Unable to set fixed function emulation matrix modelview because allocation is missing"); + return; + } float *f = static_cast<float *>(mConstants[0]->getPtr()); memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m, sizeof(rsc_Matrix)); mDirty = true; } -void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const -{ +void ProgramVertex::setTextureMatrix(Context *rsc, const rsc_Matrix *m) const { + if (isUserProgram()) { + rsc->setError(RS_ERROR_FATAL_UNKNOWN, + "Attempting to set fixed function emulation matrix texture on user program"); + return; + } + if (mConstants[0].get() == NULL) { + rsc->setError(RS_ERROR_FATAL_UNKNOWN, + "Unable to set fixed function emulation matrix texture because allocation is missing"); + return; + } float *f = static_cast<float *>(mConstants[0]->getPtr()); memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m, sizeof(rsc_Matrix)); mDirty = true; } -void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const float *v3in) const -{ +void ProgramVertex::getProjectionMatrix(Context *rsc, rsc_Matrix *m) const { + if (isUserProgram()) { + rsc->setError(RS_ERROR_FATAL_UNKNOWN, + "Attempting to get fixed function emulation matrix projection on user program"); + return; + } + if (mConstants[0].get() == NULL) { + rsc->setError(RS_ERROR_FATAL_UNKNOWN, + "Unable to get fixed function emulation matrix projection because allocation is missing"); + return; + } + float *f = static_cast<float *>(mConstants[0]->getPtr()); + memcpy(m, &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], sizeof(rsc_Matrix)); +} + +void ProgramVertex::transformToScreen(Context *rsc, float *v4out, const float *v3in) const { + if (isUserProgram()) { + return; + } float *f = static_cast<float *>(mConstants[0]->getPtr()); Matrix mvp; mvp.loadMultiply((Matrix *)&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], @@ -312,94 +187,101 @@ void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const fl mvp.vectorMultiply(v4out, v3in); } -void ProgramVertex::initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix) -{ - rsAssert(e->getFieldCount()); - for (uint32_t ct=0; ct < e->getFieldCount(); ct++) { - const Element *ce = e->getField(ct); - if (ce->getFieldCount()) { - initAddUserElement(ce, names, count, prefix); - } else { - String8 tmp(prefix); - tmp.append(e->getFieldName(ct)); - names[*count].setTo(tmp.string()); - (*count)++; - } - } -} - - -void ProgramVertex::init(Context *rsc) -{ - mAttribCount = 0; +void ProgramVertex::init(Context *rsc) { + uint32_t attribCount = 0; + uint32_t uniformCount = 0; if (mUserShader.size() > 0) { for (uint32_t ct=0; ct < mInputCount; ct++) { - initAddUserElement(mInputElements[ct].get(), mAttribNames, &mAttribCount, "ATTRIB_"); + initAddUserElement(mInputElements[ct].get(), mAttribNames, NULL, &attribCount, RS_SHADER_ATTR); } - - mUniformCount = 1; - mUniformNames[0].setTo("UNI_MVP"); for (uint32_t ct=0; ct < mConstantCount; ct++) { - initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, "UNI_"); + initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI); } - } else { - mUniformCount = 2; - mUniformNames[0].setTo("UNI_MVP"); - mUniformNames[1].setTo("UNI_TexMatrix"); } + createShader(rsc); +} + +void ProgramVertex::serialize(OStream *stream) const { +} - createShader(); +ProgramVertex *ProgramVertex::createFromStream(Context *rsc, IStream *stream) { + return NULL; } /////////////////////////////////////////////////////////////////////// -ProgramVertexState::ProgramVertexState() -{ +ProgramVertexState::ProgramVertexState() { } -ProgramVertexState::~ProgramVertexState() -{ +ProgramVertexState::~ProgramVertexState() { } -void ProgramVertexState::init(Context *rsc, int32_t w, int32_t h) -{ - RsElement e = (RsElement) Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1); - - rsi_TypeBegin(rsc, e); - rsi_TypeAdd(rsc, RS_DIMENSION_X, 48); - mAllocType.set((Type *)rsi_TypeCreate(rsc)); +void ProgramVertexState::init(Context *rsc) { + const Element *matrixElem = Element::create(rsc, RS_TYPE_MATRIX_4X4, RS_KIND_USER, false, 1); + const Element *f2Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2); + const Element *f3Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3); + const Element *f4Elem = Element::create(rsc, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4); + + rsc->mStateElement.elementBuilderBegin(); + rsc->mStateElement.elementBuilderAdd(matrixElem, "MV", 1); + rsc->mStateElement.elementBuilderAdd(matrixElem, "P", 1); + rsc->mStateElement.elementBuilderAdd(matrixElem, "TexMatrix", 1); + rsc->mStateElement.elementBuilderAdd(matrixElem, "MVP", 1); + const Element *constInput = rsc->mStateElement.elementBuilderCreate(rsc); + + rsc->mStateElement.elementBuilderBegin(); + rsc->mStateElement.elementBuilderAdd(f4Elem, "position", 1); + rsc->mStateElement.elementBuilderAdd(f4Elem, "color", 1); + rsc->mStateElement.elementBuilderAdd(f3Elem, "normal", 1); + rsc->mStateElement.elementBuilderAdd(f2Elem, "texture0", 1); + const Element *attrElem = rsc->mStateElement.elementBuilderCreate(rsc); + + Type *inputType = Type::getType(rsc, constInput, 1, 0, 0, false, false); + + String8 shaderString(RS_SHADER_INTERNAL); + shaderString.append("varying vec4 varColor;\n"); + shaderString.append("varying vec2 varTex0;\n"); + shaderString.append("void main() {\n"); + shaderString.append(" gl_Position = UNI_MVP * ATTRIB_position;\n"); + shaderString.append(" gl_PointSize = 1.0;\n"); + shaderString.append(" varColor = ATTRIB_color;\n"); + shaderString.append(" varTex0 = ATTRIB_texture0;\n"); + shaderString.append("}\n"); + + uint32_t tmp[4]; + tmp[0] = RS_PROGRAM_PARAM_CONSTANT; + tmp[1] = (uint32_t)inputType; + tmp[2] = RS_PROGRAM_PARAM_INPUT; + tmp[3] = (uint32_t)attrElem; + + ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(), + shaderString.length(), tmp, 4); + Allocation *alloc = new Allocation(rsc, inputType, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS); + pv->bindAllocation(rsc, alloc, 0); - ProgramVertex *pv = new ProgramVertex(rsc, false); - Allocation *alloc = (Allocation *)rsi_AllocationCreateTyped(rsc, mAllocType.get()); mDefaultAlloc.set(alloc); mDefault.set(pv); - pv->init(rsc); - pv->bindAllocation(alloc, 0); - color[0] = 1.f; - color[1] = 1.f; - color[2] = 1.f; - color[3] = 1.f; - - updateSize(rsc, w, h); + updateSize(rsc); } -void ProgramVertexState::updateSize(Context *rsc, int32_t w, int32_t h) -{ +void ProgramVertexState::updateSize(Context *rsc) { + float *f = static_cast<float *>(mDefaultAlloc->getPtr()); + Matrix m; - m.loadOrtho(0,w, h,0, -1,1); - mDefaultAlloc->subData(RS_PROGRAM_VERTEX_PROJECTION_OFFSET, 16, &m.m[0], 16*4); + m.loadOrtho(0,rsc->getWidth(), rsc->getHeight(),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)); m.loadIdentity(); - mDefaultAlloc->subData(RS_PROGRAM_VERTEX_MODELVIEW_OFFSET, 16, &m.m[0], 16*4); + memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m.m, sizeof(m)); + memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m.m, sizeof(m)); } -void ProgramVertexState::deinit(Context *rsc) -{ +void ProgramVertexState::deinit(Context *rsc) { mDefaultAlloc.clear(); mDefault.clear(); - mAllocType.clear(); mLast.clear(); } @@ -407,23 +289,13 @@ void ProgramVertexState::deinit(Context *rsc) namespace android { namespace renderscript { - -RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, bool texMat) -{ - ProgramVertex *pv = new ProgramVertex(rsc, texMat); - pv->incUserRef(); - return pv; -} - -RsProgramVertex rsi_ProgramVertexCreate2(Context *rsc, const char * shaderText, +RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, const char * shaderText, uint32_t shaderLength, const uint32_t * params, - uint32_t paramLength) -{ + uint32_t paramLength) { ProgramVertex *pv = new ProgramVertex(rsc, shaderText, shaderLength, params, paramLength); pv->incUserRef(); return pv; } - } } diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h index 28554cc813d6..2a5c863cd2e9 100644 --- a/libs/rs/rsProgramVertex.h +++ b/libs/rs/rsProgramVertex.h @@ -25,67 +25,44 @@ namespace renderscript { class ProgramVertexState; -class ProgramVertex : public Program -{ +class ProgramVertex : public Program { public: - const static uint32_t MAX_LIGHTS = 8; - ProgramVertex(Context *,const char * shaderText, uint32_t shaderLength, const uint32_t * params, uint32_t paramLength); - ProgramVertex(Context *, bool texMat); virtual ~ProgramVertex(); - virtual void setupGL(const Context *rsc, ProgramVertexState *state); - virtual void setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc); - - - void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;} - void addLight(const Light *); + virtual void setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc); - void setProjectionMatrix(const rsc_Matrix *) const; - void setModelviewMatrix(const rsc_Matrix *) const; - void setTextureMatrix(const rsc_Matrix *) const; + void setProjectionMatrix(Context *, const rsc_Matrix *) const; + void getProjectionMatrix(Context *, rsc_Matrix *) const; + void setModelviewMatrix(Context *, const rsc_Matrix *) const; + void setTextureMatrix(Context *, const rsc_Matrix *) const; - void transformToScreen(const Context *, float *v4out, const float *v3in) const; + void transformToScreen(Context *, float *v4out, const float *v3in) const; - virtual void createShader(); + virtual void createShader(Context *); virtual void loadShader(Context *); virtual void init(Context *); - -protected: - uint32_t mLightCount; - ObjectBaseRef<const Light> mLights[MAX_LIGHTS]; - - // Hacks to create a program for now - bool mTextureMatrixEnable; - -private: - void initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix); + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_VERTEX; } + static ProgramVertex *createFromStream(Context *rsc, IStream *stream); }; - -class ProgramVertexState -{ +class ProgramVertexState { public: ProgramVertexState(); ~ProgramVertexState(); - void init(Context *rsc, int32_t w, int32_t h); + void init(Context *rsc); void deinit(Context *rsc); - void updateSize(Context *rsc, int32_t w, int32_t h); + void updateSize(Context *rsc); ObjectBaseRef<ProgramVertex> mDefault; ObjectBaseRef<ProgramVertex> mLast; ObjectBaseRef<Allocation> mDefaultAlloc; - - ObjectBaseRef<Type> mAllocType; - - - float color[4]; }; - } } #endif diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp index 5693c8abeda8..db2383aca7eb 100644 --- a/libs/rs/rsSampler.cpp +++ b/libs/rs/rsSampler.cpp @@ -14,10 +14,12 @@ * limitations under the License. */ +#include "rsContext.h" +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES/glext.h> +#endif //ANDROID_RS_SERIALIZE -#include "rsContext.h" #include "rsSampler.h" @@ -25,10 +27,7 @@ using namespace android; using namespace android::renderscript; -Sampler::Sampler(Context *rsc) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; +Sampler::Sampler(Context *rsc) : ObjectBase(rsc) { // Should not get called. rsAssert(0); } @@ -38,78 +37,98 @@ Sampler::Sampler(Context *rsc, RsSamplerValue minFilter, RsSamplerValue wrapS, RsSamplerValue wrapT, - RsSamplerValue wrapR) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; + RsSamplerValue wrapR, + float aniso) : ObjectBase(rsc) { mMagFilter = magFilter; mMinFilter = minFilter; mWrapS = wrapS; mWrapT = wrapT; mWrapR = wrapR; + mAniso = aniso; } -Sampler::~Sampler() -{ +Sampler::~Sampler() { } -void Sampler::setupGL(const Context *rsc, bool npot) -{ +void Sampler::setupGL(const Context *rsc, const Allocation *tex) { GLenum trans[] = { GL_NEAREST, //RS_SAMPLER_NEAREST, GL_LINEAR, //RS_SAMPLER_LINEAR, GL_LINEAR_MIPMAP_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR, GL_REPEAT, //RS_SAMPLER_WRAP, GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP - + GL_LINEAR_MIPMAP_NEAREST, //RS_SAMPLER_LINEAR_MIP_NEAREST }; - bool forceNonMip = false; - if (!rsc->ext_OES_texture_npot() && npot) { - forceNonMip = true; - } + GLenum transNP[] = { + GL_NEAREST, //RS_SAMPLER_NEAREST, + GL_LINEAR, //RS_SAMPLER_LINEAR, + GL_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR, + GL_CLAMP_TO_EDGE, //RS_SAMPLER_WRAP, + GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP + GL_LINEAR, //RS_SAMPLER_LINEAR_MIP_NEAREST, + }; - if ((mMinFilter == RS_SAMPLER_LINEAR_MIP_LINEAR) && forceNonMip) { - if (rsc->ext_GL_IMG_texture_npot()) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + // This tells us the correct texture type + GLenum target = (GLenum)tex->getGLTarget(); + + if (!rsc->ext_OES_texture_npot() && tex->getType()->getIsNp2()) { + if (tex->getHasGraphicsMipmaps() && + (rsc->ext_GL_NV_texture_npot_2D_mipmap() || rsc->ext_GL_IMG_texture_npot())) { + if (rsc->ext_GL_NV_texture_npot_2D_mipmap()) { + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]); + } else { + switch (trans[mMinFilter]) { + case GL_LINEAR_MIPMAP_LINEAR: + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + break; + default: + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]); + break; + } + } } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, transNP[mMinFilter]); } + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, transNP[mMagFilter]); + glTexParameteri(target, GL_TEXTURE_WRAP_S, transNP[mWrapS]); + glTexParameteri(target, GL_TEXTURE_WRAP_T, transNP[mWrapT]); } else { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]); + if (tex->getHasGraphicsMipmaps()) { + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]); + } else { + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, transNP[mMinFilter]); + } + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]); + glTexParameteri(target, GL_TEXTURE_WRAP_S, trans[mWrapS]); + glTexParameteri(target, GL_TEXTURE_WRAP_T, trans[mWrapT]); } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]); + float anisoValue = rsMin(rsc->ext_texture_max_aniso(), mAniso); + if (rsc->ext_texture_max_aniso() > 1.0f) { + glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisoValue); + } - rsc->checkError("ProgramFragment::setupGL2 tex env"); + rsc->checkError("Sampler::setupGL2 tex env"); } -void Sampler::bindToContext(SamplerState *ss, uint32_t slot) -{ +void Sampler::bindToContext(SamplerState *ss, uint32_t slot) { ss->mSamplers[slot].set(this); mBoundSlot = slot; } -void Sampler::unbindFromContext(SamplerState *ss) -{ +void Sampler::unbindFromContext(SamplerState *ss) { int32_t slot = mBoundSlot; mBoundSlot = -1; ss->mSamplers[slot].clear(); } -/* -void SamplerState::setupGL() -{ - for (uint32_t ct=0; ct < RS_MAX_SAMPLER_SLOT; ct++) { - Sampler *s = mSamplers[ct].get(); - if (s) { - s->setupGL(rsc); - } else { - glBindTexture(GL_TEXTURE_2D, 0); - } - } -}*/ + +void Sampler::serialize(OStream *stream) const { +} + +Sampler *Sampler::createFromStream(Context *rsc, IStream *stream) { + return NULL; +} //////////////////////////////// @@ -117,8 +136,7 @@ namespace android { namespace renderscript { -void rsi_SamplerBegin(Context *rsc) -{ +void rsi_SamplerBegin(Context *rsc) { SamplerState * ss = &rsc->mStateSampler; ss->mMagFilter = RS_SAMPLER_LINEAR; @@ -126,13 +144,13 @@ void rsi_SamplerBegin(Context *rsc) ss->mWrapS = RS_SAMPLER_WRAP; ss->mWrapT = RS_SAMPLER_WRAP; ss->mWrapR = RS_SAMPLER_WRAP; + ss->mAniso = 1.0f; } -void rsi_SamplerSet(Context *rsc, RsSamplerParam param, RsSamplerValue value) -{ +void rsi_SamplerSet(Context *rsc, RsSamplerParam param, RsSamplerValue value) { SamplerState * ss = &rsc->mStateSampler; - switch(param) { + switch (param) { case RS_SAMPLER_MAG_FILTER: ss->mMagFilter = value; break; @@ -148,24 +166,37 @@ void rsi_SamplerSet(Context *rsc, RsSamplerParam param, RsSamplerValue value) case RS_SAMPLER_WRAP_R: ss->mWrapR = value; break; + default: + LOGE("Attempting to set invalid value on sampler"); + break; } - } -RsSampler rsi_SamplerCreate(Context *rsc) -{ +void rsi_SamplerSet2(Context *rsc, RsSamplerParam param, float value) { SamplerState * ss = &rsc->mStateSampler; + switch (param) { + case RS_SAMPLER_ANISO: + ss->mAniso = value; + break; + default: + LOGE("Attempting to set invalid value on sampler"); + break; + } +} + +RsSampler rsi_SamplerCreate(Context *rsc) { + SamplerState * ss = &rsc->mStateSampler; Sampler * s = new Sampler(rsc, ss->mMagFilter, ss->mMinFilter, ss->mWrapS, ss->mWrapT, - ss->mWrapR); + ss->mWrapR, + ss->mAniso); s->incUserRef(); return s; } - }} diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h index 0506081d0da6..737bb8b0141f 100644 --- a/libs/rs/rsSampler.h +++ b/libs/rs/rsSampler.h @@ -28,58 +28,55 @@ const static uint32_t RS_MAX_SAMPLER_SLOT = 16; class SamplerState; -class Sampler : public ObjectBase -{ +class Sampler : public ObjectBase { public: Sampler(Context *, RsSamplerValue magFilter, RsSamplerValue minFilter, RsSamplerValue wrapS, RsSamplerValue wrapT, - RsSamplerValue wrapR); + RsSamplerValue wrapR, + float aniso = 1.0f); virtual ~Sampler(); void bind(Allocation *); - void setupGL(const Context *, bool npot); + void setupGL(const Context *, const Allocation *); void bindToContext(SamplerState *, uint32_t slot); void unbindFromContext(SamplerState *); + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_SAMPLER; } + static Sampler *createFromStream(Context *rsc, IStream *stream); + protected: RsSamplerValue mMagFilter; RsSamplerValue mMinFilter; RsSamplerValue mWrapS; RsSamplerValue mWrapT; RsSamplerValue mWrapR; + float mAniso; int32_t mBoundSlot; private: Sampler(Context *); - }; -class SamplerState -{ +class SamplerState { public: - RsSamplerValue mMagFilter; RsSamplerValue mMinFilter; RsSamplerValue mWrapS; RsSamplerValue mWrapT; RsSamplerValue mWrapR; - + float mAniso; ObjectBaseRef<Sampler> mSamplers[RS_MAX_SAMPLER_SLOT]; - - //void setupGL(); - }; - - } } #endif //ANDROID_RS_SAMPLER_H diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp index a33933b689c8..afee2a314809 100644 --- a/libs/rs/rsScript.cpp +++ b/libs/rs/rsScript.cpp @@ -19,98 +19,129 @@ using namespace android; using namespace android::renderscript; -Script::Script(Context *rsc) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; +Script::Script(Context *rsc) : ObjectBase(rsc) { memset(&mEnviroment, 0, sizeof(mEnviroment)); - mEnviroment.mClearColor[0] = 0; - mEnviroment.mClearColor[1] = 0; - mEnviroment.mClearColor[2] = 0; - mEnviroment.mClearColor[3] = 1; - mEnviroment.mClearDepth = 1; - mEnviroment.mClearStencil = 0; - mEnviroment.mIsRoot = false; + + mSlots = NULL; + mTypes = NULL; } -Script::~Script() -{ +Script::~Script() { + if (mSlots) { + delete [] mSlots; + mSlots = NULL; + } + if (mTypes) { + delete [] mTypes; + mTypes = NULL; + } +} + +void Script::initSlots() { + if (mEnviroment.mFieldCount > 0) { + mSlots = new ObjectBaseRef<Allocation>[mEnviroment.mFieldCount]; + mTypes = new ObjectBaseRef<const Type>[mEnviroment.mFieldCount]; + } +} + +void Script::setSlot(uint32_t slot, Allocation *a) { + if (slot >= mEnviroment.mFieldCount) { + LOGE("Script::setSlot unable to set allocation, invalid slot index"); + return; + } + + mSlots[slot].set(a); +} + +void Script::setVar(uint32_t slot, const void *val, uint32_t len) { + int32_t *destPtr = ((int32_t **)mEnviroment.mFieldAddress)[slot]; + if (destPtr) { + //LOGE("setVar f1 %f", ((const float *)destPtr)[0]); + //LOGE("setVar %p %i", destPtr, len); + memcpy(destPtr, val, len); + //LOGE("setVar f2 %f", ((const float *)destPtr)[0]); + } else { + //if (rsc->props.mLogScripts) { + LOGV("Calling setVar on slot = %i which is null", slot); + //} + } +} + +void Script::setVarObj(uint32_t slot, ObjectBase *val) { + ObjectBase **destPtr = ((ObjectBase ***)mEnviroment.mFieldAddress)[slot]; + + if (destPtr) { + if (val != NULL) { + val->incSysRef(); + } + if (*destPtr) { + (*destPtr)->decSysRef(); + } + *destPtr = val; + } } namespace android { namespace renderscript { - -void rsi_ScriptBindAllocation(Context * rsc, RsScript vs, RsAllocation va, uint32_t slot) -{ +void rsi_ScriptBindAllocation(Context * rsc, RsScript vs, RsAllocation va, uint32_t slot) { Script *s = static_cast<Script *>(vs); - s->mSlots[slot].set(static_cast<Allocation *>(va)); + Allocation *a = static_cast<Allocation *>(va); + s->setSlot(slot, a); + //LOGE("rsi_ScriptBindAllocation %i %p %p", slot, a, a->getPtr()); } -void rsi_ScriptSetClearColor(Context * rsc, RsScript vs, float r, float g, float b, float a) -{ +void rsi_ScriptSetTimeZone(Context * rsc, RsScript vs, const char * timeZone, uint32_t length) { Script *s = static_cast<Script *>(vs); - s->mEnviroment.mClearColor[0] = r; - s->mEnviroment.mClearColor[1] = g; - s->mEnviroment.mClearColor[2] = b; - s->mEnviroment.mClearColor[3] = a; + s->mEnviroment.mTimeZone = timeZone; } -void rsi_ScriptSetTimeZone(Context * rsc, RsScript vs, const char * timeZone, uint32_t length) -{ +void rsi_ScriptInvoke(Context *rsc, RsScript vs, uint32_t slot) { Script *s = static_cast<Script *>(vs); - s->mEnviroment.mTimeZone = timeZone; + s->Invoke(rsc, slot, NULL, 0); } -void rsi_ScriptSetClearDepth(Context * rsc, RsScript vs, float v) -{ + +void rsi_ScriptInvokeData(Context *rsc, RsScript vs, uint32_t slot, void *data) { Script *s = static_cast<Script *>(vs); - s->mEnviroment.mClearDepth = v; + s->Invoke(rsc, slot, NULL, 0); } -void rsi_ScriptSetClearStencil(Context * rsc, RsScript vs, uint32_t v) -{ +void rsi_ScriptInvokeV(Context *rsc, RsScript vs, uint32_t slot, const void *data, uint32_t len) { Script *s = static_cast<Script *>(vs); - s->mEnviroment.mClearStencil = v; + s->Invoke(rsc, slot, data, len); } -void rsi_ScriptSetType(Context * rsc, RsType vt, uint32_t slot, bool writable, const char *name) -{ - ScriptCState *ss = &rsc->mScriptC; - const Type *t = static_cast<const Type *>(vt); - ss->mConstantBufferTypes[slot].set(t); - ss->mSlotWritable[slot] = writable; - if (name) { - ss->mSlotNames[slot].setTo(name); - } else { - ss->mSlotNames[slot].setTo(""); - } +void rsi_ScriptSetVarI(Context *rsc, RsScript vs, uint32_t slot, int value) { + Script *s = static_cast<Script *>(vs); + s->setVar(slot, &value, sizeof(value)); } -void rsi_ScriptSetInvoke(Context *rsc, const char *name, uint32_t slot) -{ - ScriptCState *ss = &rsc->mScriptC; - ss->mInvokableNames[slot] = name; +void rsi_ScriptSetVarObj(Context *rsc, RsScript vs, uint32_t slot, RsObjectBase value) { + Script *s = static_cast<Script *>(vs); + ObjectBase *o = static_cast<ObjectBase *>(value); + s->setVarObj(slot, o); } -void rsi_ScriptInvoke(Context *rsc, RsScript vs, uint32_t slot) -{ +void rsi_ScriptSetVarJ(Context *rsc, RsScript vs, uint32_t slot, long long value) { Script *s = static_cast<Script *>(vs); - if (s->mEnviroment.mInvokables[slot] == NULL) { - rsc->setError(RS_ERROR_BAD_SCRIPT, "Calling invoke on bad script"); - return; - } - s->setupScript(); - s->mEnviroment.mInvokables[slot](); + s->setVar(slot, &value, sizeof(value)); } +void rsi_ScriptSetVarF(Context *rsc, RsScript vs, uint32_t slot, float value) { + Script *s = static_cast<Script *>(vs); + s->setVar(slot, &value, sizeof(value)); +} -void rsi_ScriptSetRoot(Context * rsc, bool isRoot) -{ - ScriptCState *ss = &rsc->mScriptC; - ss->mScript->mEnviroment.mIsRoot = isRoot; +void rsi_ScriptSetVarD(Context *rsc, RsScript vs, uint32_t slot, double value) { + Script *s = static_cast<Script *>(vs); + s->setVar(slot, &value, sizeof(value)); } +void rsi_ScriptSetVarV(Context *rsc, RsScript vs, uint32_t slot, const void *data, uint32_t len) { + Script *s = static_cast<Script *>(vs); + s->setVar(slot, data, len); +} } } diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h index 5f4a536fde69..bad095bcfcdf 100644 --- a/libs/rs/rsScript.h +++ b/libs/rs/rsScript.h @@ -27,54 +27,58 @@ namespace renderscript { class ProgramVertex; class ProgramFragment; class ProgramRaster; -class ProgramFragmentStore; +class ProgramStore; -#define MAX_SCRIPT_BANKS 16 - -class Script : public ObjectBase -{ +class Script : public ObjectBase { public: typedef void (* InvokeFunc_t)(void); Script(Context *); virtual ~Script(); - struct Enviroment_t { - bool mIsRoot; - float mClearColor[4]; - float mClearDepth; - uint32_t mClearStencil; - - uint32_t mStartTimeMillis; + int64_t mStartTimeMillis; + int64_t mLastDtTime; const char* mTimeZone; ObjectBaseRef<ProgramVertex> mVertex; ObjectBaseRef<ProgramFragment> mFragment; ObjectBaseRef<ProgramRaster> mRaster; - ObjectBaseRef<ProgramFragmentStore> mFragmentStore; - InvokeFunc_t mInvokables[MAX_SCRIPT_BANKS]; + ObjectBaseRef<ProgramStore> mFragmentStore; + + uint32_t mInvokeFunctionCount; + InvokeFunc_t *mInvokeFunctions; + uint32_t mFieldCount; + void ** mFieldAddress; + char * mScriptText; uint32_t mScriptTextLength; + + bool mIsThreadable; }; Enviroment_t mEnviroment; - uint32_t mCounstantBufferCount; - + void initSlots(); + void setSlot(uint32_t slot, Allocation *a); + void setVar(uint32_t slot, const void *val, uint32_t len); + void setVarObj(uint32_t slot, ObjectBase *val); - ObjectBaseRef<Allocation> mSlots[MAX_SCRIPT_BANKS]; - ObjectBaseRef<const Type> mTypes[MAX_SCRIPT_BANKS]; - String8 mSlotNames[MAX_SCRIPT_BANKS]; - bool mSlotWritable[MAX_SCRIPT_BANKS]; + virtual void runForEach(Context *rsc, + const Allocation * ain, + Allocation * aout, + const void * usr, + const RsScriptCall *sc = NULL) = 0; + virtual void Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len) = 0; + virtual void setupScript(Context *rsc) = 0; + virtual uint32_t run(Context *) = 0; +protected: + ObjectBaseRef<Allocation> *mSlots; + ObjectBaseRef<const Type> *mTypes; - - virtual void setupScript() = 0; - virtual uint32_t run(Context *, uint32_t launchID) = 0; }; - } } #endif diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index f4d24516c08a..e12926b0a641 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -17,13 +17,17 @@ #include "rsContext.h" #include "rsScriptC.h" #include "rsMatrix.h" - -#include "acc/acc.h" #include "utils/Timers.h" +#include "utils/StopWatch.h" +extern "C" { +#include "libdex/ZipArchive.h" +} #include <GLES/gl.h> #include <GLES/glext.h> +#include <bcc/bcc.h> + using namespace android; using namespace android::renderscript; @@ -32,415 +36,615 @@ using namespace android::renderscript; Context * rsc = tls->mContext; \ ScriptC * sc = (ScriptC *) tls->mScript +// Input: cacheDir +// Input: resName +// Input: extName +// +// Note: cacheFile = resName + extName +// +// Output: Returns cachePath == cacheDir + cacheFile +char *genCacheFileName(const char *cacheDir, + const char *resName, + const char *extName) { + char cachePath[512]; + char cacheFile[sizeof(cachePath)]; + const size_t kBufLen = sizeof(cachePath) - 1; + + cacheFile[0] = '\0'; + // Note: resName today is usually something like + // "/com.android.fountain:raw/fountain" + if (resName[0] != '/') { + // Get the absolute path of the raw/***.bc file. + + // Generate the absolute path. This doesn't do everything it + // should, e.g. if resName is "./out/whatever" it doesn't crunch + // the leading "./" out because this if-block is not triggered, + // but it'll make do. + // + if (getcwd(cacheFile, kBufLen) == NULL) { + LOGE("Can't get CWD while opening raw/***.bc file\n"); + return NULL; + } + // Append "/" at the end of cacheFile so far. + strncat(cacheFile, "/", kBufLen); + } -ScriptC::ScriptC(Context *rsc) : Script(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - mAccScript = NULL; + // cacheFile = resName + extName + // + strncat(cacheFile, resName, kBufLen); + if (extName != NULL) { + // TODO(srhines): strncat() is a bit dangerous + strncat(cacheFile, extName, kBufLen); + } + + // Turn the path into a flat filename by replacing + // any slashes after the first one with '@' characters. + char *cp = cacheFile + 1; + while (*cp != '\0') { + if (*cp == '/') { + *cp = '@'; + } + cp++; + } + + // Tack on the file name for the actual cache file path. + strncpy(cachePath, cacheDir, kBufLen); + strncat(cachePath, cacheFile, kBufLen); + + LOGV("Cache file for '%s' '%s' is '%s'\n", resName, extName, cachePath); + return strdup(cachePath); +} + +ScriptC::ScriptC(Context *rsc) : Script(rsc) { + mBccScript = NULL; memset(&mProgram, 0, sizeof(mProgram)); } -ScriptC::~ScriptC() -{ - if (mAccScript) { - accDeleteScript(mAccScript); +ScriptC::~ScriptC() { + if (mBccScript) { + if (mProgram.mObjectSlotList) { + for (size_t ct=0; ct < mProgram.mObjectSlotCount; ct++) { + setVarObj(mProgram.mObjectSlotList[ct], NULL); + } + delete [] mProgram.mObjectSlotList; + mProgram.mObjectSlotList = NULL; + mProgram.mObjectSlotCount = 0; + } + + + LOGD(">>>> ~ScriptC bccDisposeScript(%p)", mBccScript); + bccDisposeScript(mBccScript); } free(mEnviroment.mScriptText); mEnviroment.mScriptText = NULL; } -void ScriptC::setupScript() -{ - for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) { - if (mProgram.mSlotPointers[ct]) { - *mProgram.mSlotPointers[ct] = mSlots[ct]->getPtr(); +void ScriptC::setupScript(Context *rsc) { + mEnviroment.mStartTimeMillis + = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC)); + + for (uint32_t ct=0; ct < mEnviroment.mFieldCount; ct++) { + if (mSlots[ct].get() && !mTypes[ct].get()) { + mTypes[ct].set(mSlots[ct]->getType()); + } + + if (!mTypes[ct].get()) + continue; + void *ptr = NULL; + if (mSlots[ct].get()) { + ptr = mSlots[ct]->getPtr(); + } + void **dest = ((void ***)mEnviroment.mFieldAddress)[ct]; + + if (rsc->props.mLogScripts) { + if (mSlots[ct].get() != NULL) { + LOGV("%p ScriptC::setupScript slot=%i dst=%p src=%p type=%p", rsc, ct, dest, ptr, mSlots[ct]->getType()); + } else { + LOGV("%p ScriptC::setupScript slot=%i dst=%p src=%p type=null", rsc, ct, dest, ptr); + } + } + + if (dest) { + *dest = ptr; } } } - -uint32_t ScriptC::run(Context *rsc, uint32_t launchIndex) -{ - if (mProgram.mScript == NULL) { - rsc->setError(RS_ERROR_BAD_SCRIPT, "Attempted to run bad script"); - return 0; +const Allocation *ScriptC::ptrToAllocation(const void *ptr) const { + if (!ptr) { + return NULL; } + for (uint32_t ct=0; ct < mEnviroment.mFieldCount; ct++) { + if (!mSlots[ct].get()) + continue; + if (mSlots[ct]->getPtr() == ptr) { + return mSlots[ct].get(); + } + } + LOGE("ScriptC::ptrToAllocation, failed to find %p", ptr); + return NULL; +} - Context::ScriptTLSStruct * tls = - (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); +Script * ScriptC::setTLS(Script *sc) { + Context::ScriptTLSStruct * tls = (Context::ScriptTLSStruct *) + pthread_getspecific(Context::gThreadTLSKey); rsAssert(tls); + Script *old = tls->mScript; + tls->mScript = sc; + return old; +} +void ScriptC::setupGLState(Context *rsc) { if (mEnviroment.mFragmentStore.get()) { - rsc->setFragmentStore(mEnviroment.mFragmentStore.get()); + rsc->setProgramStore(mEnviroment.mFragmentStore.get()); } if (mEnviroment.mFragment.get()) { - rsc->setFragment(mEnviroment.mFragment.get()); + rsc->setProgramFragment(mEnviroment.mFragment.get()); } if (mEnviroment.mVertex.get()) { - rsc->setVertex(mEnviroment.mVertex.get()); + rsc->setProgramVertex(mEnviroment.mVertex.get()); } if (mEnviroment.mRaster.get()) { - rsc->setRaster(mEnviroment.mRaster.get()); + rsc->setProgramRaster(mEnviroment.mRaster.get()); } +} - if (launchIndex == 0) { - mEnviroment.mStartTimeMillis - = nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC)); +uint32_t ScriptC::run(Context *rsc) { + if (mProgram.mRoot == NULL) { + rsc->setError(RS_ERROR_BAD_SCRIPT, "Attempted to run bad script"); + return 0; } - setupScript(); + + setupGLState(rsc); + setupScript(rsc); uint32_t ret = 0; - tls->mScript = this; - ret = mProgram.mScript(launchIndex); - tls->mScript = NULL; + Script * oldTLS = setTLS(this); + + if (rsc->props.mLogScripts) { + LOGV("%p ScriptC::run invoking root, ptr %p", rsc, mProgram.mRoot); + } + + ret = mProgram.mRoot(); + + if (rsc->props.mLogScripts) { + LOGV("%p ScriptC::run invoking complete, ret=%i", rsc, ret); + } + + setTLS(oldTLS); return ret; } -ScriptCState::ScriptCState() -{ - mScript = NULL; - clear(); +typedef struct { + Context *rsc; + ScriptC *script; + const Allocation * ain; + Allocation * aout; + const void * usr; + + uint32_t mSliceSize; + volatile int mSliceNum; + + const uint8_t *ptrIn; + uint32_t eStrideIn; + uint8_t *ptrOut; + uint32_t eStrideOut; + + uint32_t xStart; + uint32_t xEnd; + uint32_t yStart; + uint32_t yEnd; + uint32_t zStart; + uint32_t zEnd; + uint32_t arrayStart; + uint32_t arrayEnd; + + uint32_t dimX; + uint32_t dimY; + uint32_t dimZ; + uint32_t dimArray; +} MTLaunchStruct; +typedef int (*rs_t)(const void *, void *, const void *, uint32_t, uint32_t, uint32_t, uint32_t); + +static void wc_xy(void *usr, uint32_t idx) { + MTLaunchStruct *mtls = (MTLaunchStruct *)usr; + + while (1) { + uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum); + uint32_t yStart = mtls->yStart + slice * mtls->mSliceSize; + uint32_t yEnd = yStart + mtls->mSliceSize; + yEnd = rsMin(yEnd, mtls->yEnd); + if (yEnd <= yStart) { + return; + } + + //LOGE("usr idx %i, x %i,%i y %i,%i", idx, mtls->xStart, mtls->xEnd, yStart, yEnd); + //LOGE("usr ptr in %p, out %p", mtls->ptrIn, mtls->ptrOut); + for (uint32_t y = yStart; y < yEnd; y++) { + uint32_t offset = mtls->dimX * y; + uint8_t *xPtrOut = mtls->ptrOut + (mtls->eStrideOut * offset); + const uint8_t *xPtrIn = mtls->ptrIn + (mtls->eStrideIn * offset); + + for (uint32_t x = mtls->xStart; x < mtls->xEnd; x++) { + ((rs_t)mtls->script->mProgram.mRoot) (xPtrIn, xPtrOut, mtls->usr, x, y, 0, 0); + xPtrIn += mtls->eStrideIn; + xPtrOut += mtls->eStrideOut; + } + } + } } -ScriptCState::~ScriptCState() -{ - delete mScript; - mScript = NULL; +static void wc_x(void *usr, uint32_t idx) { + MTLaunchStruct *mtls = (MTLaunchStruct *)usr; + + while (1) { + uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum); + uint32_t xStart = mtls->xStart + slice * mtls->mSliceSize; + uint32_t xEnd = xStart + mtls->mSliceSize; + xEnd = rsMin(xEnd, mtls->xEnd); + if (xEnd <= xStart) { + return; + } + + //LOGE("usr idx %i, x %i,%i y %i,%i", idx, mtls->xStart, mtls->xEnd, yStart, yEnd); + //LOGE("usr ptr in %p, out %p", mtls->ptrIn, mtls->ptrOut); + uint8_t *xPtrOut = mtls->ptrOut + (mtls->eStrideOut * xStart); + const uint8_t *xPtrIn = mtls->ptrIn + (mtls->eStrideIn * xStart); + for (uint32_t x = xStart; x < xEnd; x++) { + ((rs_t)mtls->script->mProgram.mRoot) (xPtrIn, xPtrOut, mtls->usr, x, 0, 0, 0); + xPtrIn += mtls->eStrideIn; + xPtrOut += mtls->eStrideOut; + } + } } -void ScriptCState::clear() -{ - for (uint32_t ct=0; ct < MAX_SCRIPT_BANKS; ct++) { - mConstantBufferTypes[ct].clear(); - mSlotNames[ct].setTo(""); - mInvokableNames[ct].setTo(""); - mSlotWritable[ct] = false; +void ScriptC::runForEach(Context *rsc, + const Allocation * ain, + Allocation * aout, + const void * usr, + const RsScriptCall *sc) { + MTLaunchStruct mtls; + memset(&mtls, 0, sizeof(mtls)); + Context::PushState ps(rsc); + + if (ain) { + mtls.dimX = ain->getType()->getDimX(); + mtls.dimY = ain->getType()->getDimY(); + mtls.dimZ = ain->getType()->getDimZ(); + //mtls.dimArray = ain->getType()->getDimArray(); + } else if (aout) { + mtls.dimX = aout->getType()->getDimX(); + mtls.dimY = aout->getType()->getDimY(); + mtls.dimZ = aout->getType()->getDimZ(); + //mtls.dimArray = aout->getType()->getDimArray(); + } else { + rsc->setError(RS_ERROR_BAD_SCRIPT, "rsForEach called with null allocations"); + return; } - delete mScript; - mScript = new ScriptC(NULL); + if (!sc || (sc->xEnd == 0)) { + mtls.xEnd = mtls.dimX; + } else { + rsAssert(sc->xStart < mtls.dimX); + rsAssert(sc->xEnd <= mtls.dimX); + rsAssert(sc->xStart < sc->xEnd); + mtls.xStart = rsMin(mtls.dimX, sc->xStart); + mtls.xEnd = rsMin(mtls.dimX, sc->xEnd); + if (mtls.xStart >= mtls.xEnd) return; + } - mInt32Defines.clear(); - mFloatDefines.clear(); + if (!sc || (sc->yEnd == 0)) { + mtls.yEnd = mtls.dimY; + } else { + rsAssert(sc->yStart < mtls.dimY); + rsAssert(sc->yEnd <= mtls.dimY); + rsAssert(sc->yStart < sc->yEnd); + mtls.yStart = rsMin(mtls.dimY, sc->yStart); + mtls.yEnd = rsMin(mtls.dimY, sc->yEnd); + if (mtls.yStart >= mtls.yEnd) return; + } + + mtls.xEnd = rsMax((uint32_t)1, mtls.xEnd); + mtls.yEnd = rsMax((uint32_t)1, mtls.yEnd); + mtls.zEnd = rsMax((uint32_t)1, mtls.zEnd); + mtls.arrayEnd = rsMax((uint32_t)1, mtls.arrayEnd); + + rsAssert(ain->getType()->getDimZ() == 0); + + setupGLState(rsc); + setupScript(rsc); + Script * oldTLS = setTLS(this); + + mtls.rsc = rsc; + mtls.ain = ain; + mtls.aout = aout; + mtls.script = this; + mtls.usr = usr; + mtls.mSliceSize = 10; + mtls.mSliceNum = 0; + + mtls.ptrIn = NULL; + mtls.eStrideIn = 0; + if (ain) { + mtls.ptrIn = (const uint8_t *)ain->getPtr(); + mtls.eStrideIn = ain->getType()->getElementSizeBytes(); + } + + mtls.ptrOut = NULL; + mtls.eStrideOut = 0; + if (aout) { + mtls.ptrOut = (uint8_t *)aout->getPtr(); + mtls.eStrideOut = aout->getType()->getElementSizeBytes(); + } + + if ((rsc->getWorkerPoolSize() > 1) && mEnviroment.mIsThreadable) { + if (mtls.dimY > 1) { + rsc->launchThreads(wc_xy, &mtls); + } else { + rsc->launchThreads(wc_x, &mtls); + } + + //LOGE("launch 1"); + } else { + //LOGE("launch 3"); + for (uint32_t ar = mtls.arrayStart; ar < mtls.arrayEnd; ar++) { + for (uint32_t z = mtls.zStart; z < mtls.zEnd; z++) { + for (uint32_t y = mtls.yStart; y < mtls.yEnd; y++) { + uint32_t offset = mtls.dimX * mtls.dimY * mtls.dimZ * ar + + mtls.dimX * mtls.dimY * z + + mtls.dimX * y; + uint8_t *xPtrOut = mtls.ptrOut + (mtls.eStrideOut * offset); + const uint8_t *xPtrIn = mtls.ptrIn + (mtls.eStrideIn * offset); + + for (uint32_t x = mtls.xStart; x < mtls.xEnd; x++) { + ((rs_t)mProgram.mRoot) (xPtrIn, xPtrOut, usr, x, y, z, ar); + xPtrIn += mtls.eStrideIn; + xPtrOut += mtls.eStrideOut; + } + } + } + } + } + + setTLS(oldTLS); } -static ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name) -{ - const ScriptCState::SymbolTable_t *sym = ScriptCState::lookupSymbol(name); +void ScriptC::Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len) { + if ((slot >= mEnviroment.mInvokeFunctionCount) || + (mEnviroment.mInvokeFunctions[slot] == NULL)) { + rsc->setError(RS_ERROR_BAD_SCRIPT, "Calling invoke on bad script"); + return; + } + setupScript(rsc); + Script * oldTLS = setTLS(this); + + if (rsc->props.mLogScripts) { + LOGV("%p ScriptC::Invoke invoking slot %i, ptr %p", rsc, slot, mEnviroment.mInvokeFunctions[slot]); + } + ((void (*)(const void *, uint32_t)) + mEnviroment.mInvokeFunctions[slot])(data, len); + if (rsc->props.mLogScripts) { + LOGV("%p ScriptC::Invoke complete", rsc); + } + + setTLS(oldTLS); +} + +ScriptCState::ScriptCState() { +} + +ScriptCState::~ScriptCState() { +} + +static void* symbolLookup(void* pContext, char const* name) { + const ScriptCState::SymbolTable_t *sym; + ScriptC *s = (ScriptC *)pContext; + if (!strcmp(name, "__isThreadable")) { + return (void*) s->mEnviroment.mIsThreadable; + } else if (!strcmp(name, "__clearThreadable")) { + s->mEnviroment.mIsThreadable = false; + return NULL; + } + sym = ScriptCState::lookupSymbol(name); + if (!sym) { + sym = ScriptCState::lookupSymbolCL(name); + } + if (!sym) { + sym = ScriptCState::lookupSymbolGL(name); + } if (sym) { + s->mEnviroment.mIsThreadable &= sym->threadable; return sym->mPtr; } LOGE("ScriptC sym lookup failed for %s", name); return NULL; } -void ScriptCState::runCompiler(Context *rsc, ScriptC *s) -{ - s->mAccScript = accCreateScript(); - String8 tmp; - - rsc->appendNameDefines(&tmp); - appendDecls(&tmp); - appendVarDefines(rsc, &tmp); - appendTypes(rsc, &tmp); - tmp.append("#line 1\n"); - - const char* scriptSource[] = {tmp.string(), s->mEnviroment.mScriptText}; - int scriptLength[] = {tmp.length(), s->mEnviroment.mScriptTextLength} ; - accScriptSource(s->mAccScript, sizeof(scriptLength) / sizeof(int), scriptSource, scriptLength); - accRegisterSymbolCallback(s->mAccScript, symbolLookup, NULL); - accCompileScript(s->mAccScript); - accGetScriptLabel(s->mAccScript, "main", (ACCvoid**) &s->mProgram.mScript); - accGetScriptLabel(s->mAccScript, "init", (ACCvoid**) &s->mProgram.mInit); - rsAssert(s->mProgram.mScript); - - if (!s->mProgram.mScript) { - ACCchar buf[4096]; - ACCsizei len; - accGetScriptInfoLog(s->mAccScript, sizeof(buf), &len, buf); - LOGE("%s", buf); - rsc->setError(RS_ERROR_BAD_SCRIPT, "Error compiling user script."); - return; +#if 0 +extern const char rs_runtime_lib_bc[]; +extern unsigned rs_runtime_lib_bc_size; +#endif + +bool ScriptCState::runCompiler(Context *rsc, + ScriptC *s, + const char *resName, + const char *cacheDir) { + s->mBccScript = bccCreateScript(); + + s->mEnviroment.mIsThreadable = true; + + if (bccRegisterSymbolCallback(s->mBccScript, symbolLookup, s) != 0) { + LOGE("bcc: FAILS to register symbol callback"); + return false; + } + + if (bccReadBC(s->mBccScript, + resName, + s->mEnviroment.mScriptText, + s->mEnviroment.mScriptTextLength, 0) != 0) { + LOGE("bcc: FAILS to read bitcode"); + return false; + } + +#if 1 + if (bccLinkFile(s->mBccScript, "/system/lib/libclcore.bc", 0) != 0) { + LOGE("bcc: FAILS to link bitcode"); + return false; + } +#endif + char *cachePath = genCacheFileName(cacheDir, resName, ".oBCC"); + + if (bccPrepareExecutable(s->mBccScript, cachePath, 0) != 0) { + LOGE("bcc: FAILS to prepare executable"); + return false; } + free(cachePath); + + s->mProgram.mRoot = reinterpret_cast<int (*)()>(bccGetFuncAddr(s->mBccScript, "root")); + s->mProgram.mInit = reinterpret_cast<void (*)()>(bccGetFuncAddr(s->mBccScript, "init")); + if (s->mProgram.mInit) { s->mProgram.mInit(); } - for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) { - if (mSlotNames[ct].length() > 0) { - accGetScriptLabel(s->mAccScript, - mSlotNames[ct].string(), - (ACCvoid**) &s->mProgram.mSlotPointers[ct]); - } + s->mEnviroment.mInvokeFunctionCount = bccGetExportFuncCount(s->mBccScript); + if (s->mEnviroment.mInvokeFunctionCount <= 0) + s->mEnviroment.mInvokeFunctions = NULL; + else { + s->mEnviroment.mInvokeFunctions = (Script::InvokeFunc_t*) calloc(s->mEnviroment.mInvokeFunctionCount, sizeof(Script::InvokeFunc_t)); + bccGetExportFuncList(s->mBccScript, s->mEnviroment.mInvokeFunctionCount, (void **) s->mEnviroment.mInvokeFunctions); } - for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) { - if (mInvokableNames[ct].length() > 0) { - accGetScriptLabel(s->mAccScript, - mInvokableNames[ct].string(), - (ACCvoid**) &s->mEnviroment.mInvokables[ct]); - } + s->mEnviroment.mFieldCount = bccGetExportVarCount(s->mBccScript); + if (s->mEnviroment.mFieldCount <= 0) + s->mEnviroment.mFieldAddress = NULL; + else { + s->mEnviroment.mFieldAddress = (void **) calloc(s->mEnviroment.mFieldCount, sizeof(void *)); + bccGetExportVarList(s->mBccScript, s->mEnviroment.mFieldCount, (void **) s->mEnviroment.mFieldAddress); + s->initSlots(); } s->mEnviroment.mFragment.set(rsc->getDefaultProgramFragment()); s->mEnviroment.mVertex.set(rsc->getDefaultProgramVertex()); - s->mEnviroment.mFragmentStore.set(rsc->getDefaultProgramFragmentStore()); + s->mEnviroment.mFragmentStore.set(rsc->getDefaultProgramStore()); s->mEnviroment.mRaster.set(rsc->getDefaultProgramRaster()); - if (s->mProgram.mScript) { - const static int pragmaMax = 16; - ACCsizei pragmaCount; - ACCchar * str[pragmaMax]; - accGetPragmas(s->mAccScript, &pragmaCount, pragmaMax, &str[0]); + const static int pragmaMax = 16; + size_t pragmaCount = bccGetPragmaCount(s->mBccScript); + char const *keys[pragmaMax]; + char const *values[pragmaMax]; + bccGetPragmaList(s->mBccScript, pragmaMax, keys, values); - for (int ct=0; ct < pragmaCount; ct+=2) { - if (!strcmp(str[ct], "version")) { + for (size_t i=0; i < pragmaCount; ++i) { + //LOGE("pragma %s %s", keys[i], values[i]); + if (!strcmp(keys[i], "version")) { + if (!strcmp(values[i], "1")) { continue; } + LOGE("Invalid version pragma value: %s\n", values[i]); + return false; + } - if (!strcmp(str[ct], "stateVertex")) { - if (!strcmp(str[ct+1], "default")) { - continue; - } - if (!strcmp(str[ct+1], "parent")) { - s->mEnviroment.mVertex.clear(); - continue; - } - ProgramVertex * pv = (ProgramVertex *)rsc->lookupName(str[ct+1]); - if (pv != NULL) { - s->mEnviroment.mVertex.set(pv); - continue; - } - LOGE("Unreconized value %s passed to stateVertex", str[ct+1]); + if (!strcmp(keys[i], "stateVertex")) { + if (!strcmp(values[i], "default")) { + continue; } - - if (!strcmp(str[ct], "stateRaster")) { - if (!strcmp(str[ct+1], "default")) { - continue; - } - if (!strcmp(str[ct+1], "parent")) { - s->mEnviroment.mRaster.clear(); - continue; - } - ProgramRaster * pr = (ProgramRaster *)rsc->lookupName(str[ct+1]); - if (pr != NULL) { - s->mEnviroment.mRaster.set(pr); - continue; - } - LOGE("Unreconized value %s passed to stateRaster", str[ct+1]); + if (!strcmp(values[i], "parent")) { + s->mEnviroment.mVertex.clear(); + continue; } + LOGE("Unrecognized value %s passed to stateVertex", values[i]); + return false; + } - if (!strcmp(str[ct], "stateFragment")) { - if (!strcmp(str[ct+1], "default")) { - continue; - } - if (!strcmp(str[ct+1], "parent")) { - s->mEnviroment.mFragment.clear(); - continue; - } - ProgramFragment * pf = (ProgramFragment *)rsc->lookupName(str[ct+1]); - if (pf != NULL) { - s->mEnviroment.mFragment.set(pf); - continue; - } - LOGE("Unreconized value %s passed to stateFragment", str[ct+1]); + if (!strcmp(keys[i], "stateRaster")) { + if (!strcmp(values[i], "default")) { + continue; } - - if (!strcmp(str[ct], "stateStore")) { - if (!strcmp(str[ct+1], "default")) { - continue; - } - if (!strcmp(str[ct+1], "parent")) { - s->mEnviroment.mFragmentStore.clear(); - continue; - } - ProgramFragmentStore * pfs = - (ProgramFragmentStore *)rsc->lookupName(str[ct+1]); - if (pfs != NULL) { - s->mEnviroment.mFragmentStore.set(pfs); - continue; - } - LOGE("Unreconized value %s passed to stateStore", str[ct+1]); + if (!strcmp(values[i], "parent")) { + s->mEnviroment.mRaster.clear(); + continue; } - + LOGE("Unrecognized value %s passed to stateRaster", values[i]); + return false; } - - } else { - // Deal with an error. - } -} - -static void appendElementBody(String8 *s, const Element *e) -{ - s->append(" {\n"); - for (size_t ct2=0; ct2 < e->getFieldCount(); ct2++) { - const Element *c = e->getField(ct2); - s->append(" "); - s->append(c->getCType()); - s->append(" "); - s->append(e->getFieldName(ct2)); - s->append(";\n"); - } - s->append("}"); -} - -void ScriptCState::appendVarDefines(const Context *rsc, String8 *str) -{ - char buf[256]; - if (rsc->props.mLogScripts) { - LOGD("appendVarDefines mInt32Defines.size()=%d mFloatDefines.size()=%d\n", - mInt32Defines.size(), mFloatDefines.size()); - } - for (size_t ct=0; ct < mInt32Defines.size(); ct++) { - str->append("#define "); - str->append(mInt32Defines.keyAt(ct)); - str->append(" "); - sprintf(buf, "%i\n", (int)mInt32Defines.valueAt(ct)); - str->append(buf); - } - for (size_t ct=0; ct < mFloatDefines.size(); ct++) { - str->append("#define "); - str->append(mFloatDefines.keyAt(ct)); - str->append(" "); - sprintf(buf, "%ff\n", mFloatDefines.valueAt(ct)); - str->append(buf); - } -} - - - -void ScriptCState::appendTypes(const Context *rsc, String8 *str) -{ - char buf[256]; - String8 tmp; - - str->append("struct vecF32_2_s {float x; float y;};\n"); - str->append("struct vecF32_3_s {float x; float y; float z;};\n"); - str->append("struct vecF32_4_s {float x; float y; float z; float w;};\n"); - str->append("struct vecU8_4_s {char r; char g; char b; char a;};\n"); - str->append("#define vecF32_2_t struct vecF32_2_s\n"); - str->append("#define vecF32_3_t struct vecF32_3_s\n"); - str->append("#define vecF32_4_t struct vecF32_4_s\n"); - str->append("#define vecU8_4_t struct vecU8_4_s\n"); - str->append("#define vecI8_4_t struct vecU8_4_s\n"); - - for (size_t ct=0; ct < MAX_SCRIPT_BANKS; ct++) { - const Type *t = mConstantBufferTypes[ct].get(); - if (!t) { - continue; - } - const Element *e = t->getElement(); - if (e->getName() && (e->getFieldCount() > 1)) { - String8 s("struct struct_"); - s.append(e->getName()); - s.append(e->getCStructBody()); - s.append(";\n"); - - s.append("#define "); - s.append(e->getName()); - s.append("_t struct struct_"); - s.append(e->getName()); - s.append("\n\n"); - if (rsc->props.mLogScripts) { - LOGV("%s", static_cast<const char*>(s)); + if (!strcmp(keys[i], "stateFragment")) { + if (!strcmp(values[i], "default")) { + continue; } - str->append(s); + if (!strcmp(values[i], "parent")) { + s->mEnviroment.mFragment.clear(); + continue; + } + LOGE("Unrecognized value %s passed to stateFragment", values[i]); + return false; } - if (mSlotNames[ct].length() > 0) { - String8 s; - if (e->getName()) { - // Use the named struct - s.setTo(e->getName()); - } else { - // create an struct named from the slot. - s.setTo("struct "); - s.append(mSlotNames[ct]); - s.append("_s"); - s.append(e->getCStructBody()); - //appendElementBody(&s, e); - s.append(";\n"); - s.append("struct "); - s.append(mSlotNames[ct]); - s.append("_s"); + if (!strcmp(keys[i], "stateStore")) { + if (!strcmp(values[i], "default")) { + continue; } - - s.append(" * "); - s.append(mSlotNames[ct]); - s.append(";\n"); - if (rsc->props.mLogScripts) { - LOGV("%s", static_cast<const char*>(s)); + if (!strcmp(values[i], "parent")) { + s->mEnviroment.mFragmentStore.clear(); + continue; } - str->append(s); + LOGE("Unrecognized value %s passed to stateStore", values[i]); + return false; } } -} + size_t objectSlotCount = bccGetObjectSlotCount(s->mBccScript); + uint32_t *objectSlots = NULL; + if (objectSlotCount) { + objectSlots = new uint32_t[objectSlotCount]; + bccGetObjectSlotList(s->mBccScript, objectSlotCount, objectSlots); + s->mProgram.mObjectSlotList = objectSlots; + s->mProgram.mObjectSlotCount = objectSlotCount; + } + + return true; +} namespace android { namespace renderscript { -void rsi_ScriptCBegin(Context * rsc) -{ - ScriptCState *ss = &rsc->mScriptC; - ss->clear(); -} - -void rsi_ScriptCSetScript(Context * rsc, void *vp) -{ - rsAssert(0); - //ScriptCState *ss = &rsc->mScriptC; - //ss->mProgram.mScript = reinterpret_cast<ScriptC::RunScript_t>(vp); +void rsi_ScriptCBegin(Context * rsc) { } -void rsi_ScriptCSetText(Context *rsc, const char *text, uint32_t len) -{ +void rsi_ScriptCSetText(Context *rsc, const char *text, uint32_t len) { ScriptCState *ss = &rsc->mScriptC; char *t = (char *)malloc(len + 1); memcpy(t, text, len); t[len] = 0; - ss->mScript->mEnviroment.mScriptText = t; - ss->mScript->mEnviroment.mScriptTextLength = len; + ss->mScriptText = t; + ss->mScriptLen = len; } -RsScript rsi_ScriptCCreate(Context * rsc) +RsScript rsi_ScriptCCreate(Context *rsc, + const char *packageName /* deprecated */, + const char *resName, + const char *cacheDir) { ScriptCState *ss = &rsc->mScriptC; - ScriptC *s = ss->mScript; - ss->mScript = NULL; - - ss->runCompiler(rsc, s); + ScriptC *s = new ScriptC(rsc); + s->mEnviroment.mScriptText = ss->mScriptText; + s->mEnviroment.mScriptTextLength = ss->mScriptLen; + ss->mScriptText = NULL; + ss->mScriptLen = 0; s->incUserRef(); - s->setContext(rsc); - for (int ct=0; ct < MAX_SCRIPT_BANKS; ct++) { - s->mTypes[ct].set(ss->mConstantBufferTypes[ct].get()); - s->mSlotNames[ct] = ss->mSlotNames[ct]; - s->mSlotWritable[ct] = ss->mSlotWritable[ct]; - } - ss->clear(); + if (!ss->runCompiler(rsc, s, resName, cacheDir)) { + // Error during compile, destroy s and return null. + delete s; + return NULL; + } return s; } -void rsi_ScriptCSetDefineF(Context *rsc, const char* name, float value) -{ - ScriptCState *ss = &rsc->mScriptC; - ss->mFloatDefines.add(String8(name), value); } - -void rsi_ScriptCSetDefineI32(Context *rsc, const char* name, int32_t value) -{ - ScriptCState *ss = &rsc->mScriptC; - ss->mInt32Defines.add(String8(name), value); } - -} -} - - diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h index 35abadfd7b95..2c74b5b3ef4e 100644 --- a/libs/rs/rsScriptC.h +++ b/libs/rs/rsScriptC.h @@ -21,20 +21,16 @@ #include "RenderScriptEnv.h" -#include <utils/KeyedVector.h> - -struct ACCscript; +struct BCCOpaqueScript; // --------------------------------------------------------------------------- namespace android { namespace renderscript { - -class ScriptC : public Script -{ +class ScriptC : public Script { public: - typedef int (*RunScript_t)(uint32_t launchIndex); + typedef int (*RunScript_t)(); typedef void (*VoidFunc_t)(); ScriptC(Context *); @@ -44,56 +40,62 @@ public: int mVersionMajor; int mVersionMinor; - RunScript_t mScript; + RunScript_t mRoot; VoidFunc_t mInit; - void ** mSlotPointers[MAX_SCRIPT_BANKS]; + uint32_t * mObjectSlotList; + uint32_t mObjectSlotCount; }; + Program_t mProgram; - ACCscript* mAccScript; + BCCOpaqueScript *mBccScript; + + const Allocation *ptrToAllocation(const void *) const; + + + virtual void Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len); + + virtual uint32_t run(Context *); - virtual void setupScript(); - virtual uint32_t run(Context *, uint32_t launchID); + virtual void runForEach(Context *rsc, + const Allocation * ain, + Allocation * aout, + const void * usr, + const RsScriptCall *sc = NULL); + + virtual void serialize(OStream *stream) const { } + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_SCRIPT_C; } + static Type *createFromStream(Context *rsc, IStream *stream) { return NULL; } + +protected: + void setupScript(Context *); + void setupGLState(Context *); + Script * setTLS(Script *); }; -class ScriptCState -{ +class ScriptCState { public: ScriptCState(); ~ScriptCState(); - ScriptC *mScript; + char * mScriptText; + size_t mScriptLen; - ObjectBaseRef<const Type> mConstantBufferTypes[MAX_SCRIPT_BANKS]; - String8 mSlotNames[MAX_SCRIPT_BANKS]; - bool mSlotWritable[MAX_SCRIPT_BANKS]; - String8 mInvokableNames[MAX_SCRIPT_BANKS]; - - void clear(); - void runCompiler(Context *rsc, ScriptC *s); - void appendVarDefines(const Context *rsc, String8 *str); - void appendTypes(const Context *rsc, String8 *str); + bool runCompiler(Context *rsc, ScriptC *s, const char *resName, const char *cacheDir); struct SymbolTable_t { const char * mName; void * mPtr; - const char * mRet; - const char * mParam; + bool threadable; }; - static SymbolTable_t gSyms[]; static const SymbolTable_t * lookupSymbol(const char *); - static void appendDecls(String8 *str); - - KeyedVector<String8,int> mInt32Defines; - KeyedVector<String8,float> mFloatDefines; + static const SymbolTable_t * lookupSymbolCL(const char *); + static const SymbolTable_t * lookupSymbolGL(const char *); }; } } #endif - - - diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp index 202ca3d4d1d0..23230a630809 100644 --- a/libs/rs/rsScriptC_Lib.cpp +++ b/libs/rs/rsScriptC_Lib.cpp @@ -17,18 +17,9 @@ #include "rsContext.h" #include "rsScriptC.h" #include "rsMatrix.h" -#include "rsNoise.h" -#include "acc/acc.h" #include "utils/Timers.h" -#define GL_GLEXT_PROTOTYPES - -#include <GLES/gl.h> -#include <GLES/glext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> - #include <time.h> using namespace android; @@ -39,254 +30,13 @@ using namespace android::renderscript; Context * rsc = tls->mContext; \ ScriptC * sc = (ScriptC *) tls->mScript -typedef struct { - float x; - float y; - float z; -} vec3_t; - -typedef struct { - float x; - float y; - float z; - float w; -} vec4_t; - -typedef struct { - float x; - float y; -} vec2_t; - -////////////////////////////////////////////////////////////////////////////// -// IO routines -////////////////////////////////////////////////////////////////////////////// - -static float SC_loadF(uint32_t bank, uint32_t offset) -{ - GET_TLS(); - const void *vp = sc->mSlots[bank]->getPtr(); - const float *f = static_cast<const float *>(vp); - //LOGE("loadF %i %i = %f %x", bank, offset, f, ((int *)&f)[0]); - return f[offset]; -} - -static int32_t SC_loadI32(uint32_t bank, uint32_t offset) -{ - GET_TLS(); - const void *vp = sc->mSlots[bank]->getPtr(); - const int32_t *i = static_cast<const int32_t *>(vp); - //LOGE("loadI32 %i %i = %i", bank, offset, t); - return i[offset]; -} - -static float* SC_loadArrayF(uint32_t bank, uint32_t offset) -{ - GET_TLS(); - void *vp = sc->mSlots[bank]->getPtr(); - float *f = static_cast<float *>(vp); - return f + offset; -} - -static int32_t* SC_loadArrayI32(uint32_t bank, uint32_t offset) -{ - GET_TLS(); - void *vp = sc->mSlots[bank]->getPtr(); - int32_t *i = static_cast<int32_t *>(vp); - return i + offset; -} - -static float* SC_loadSimpleMeshVerticesF(RsSimpleMesh mesh, uint32_t idx) -{ - SimpleMesh *tm = static_cast<SimpleMesh *>(mesh); - void *vp = tm->mVertexBuffers[idx]->getPtr();; - return static_cast<float *>(vp); -} - -static void SC_updateSimpleMesh(RsSimpleMesh mesh) -{ - GET_TLS(); - SimpleMesh *sm = static_cast<SimpleMesh *>(mesh); - sm->uploadAll(rsc); -} - -static uint32_t SC_loadU32(uint32_t bank, uint32_t offset) -{ - GET_TLS(); - const void *vp = sc->mSlots[bank]->getPtr(); - const uint32_t *i = static_cast<const uint32_t *>(vp); - return i[offset]; -} - -static void SC_loadVec4(uint32_t bank, uint32_t offset, rsc_Vector4 *v) -{ - GET_TLS(); - const void *vp = sc->mSlots[bank]->getPtr(); - const float *f = static_cast<const float *>(vp); - memcpy(v, &f[offset], sizeof(rsc_Vector4)); -} - -static void SC_loadMatrix(uint32_t bank, uint32_t offset, rsc_Matrix *m) -{ - GET_TLS(); - const void *vp = sc->mSlots[bank]->getPtr(); - const float *f = static_cast<const float *>(vp); - memcpy(m, &f[offset], sizeof(rsc_Matrix)); -} - - -static void SC_storeF(uint32_t bank, uint32_t offset, float v) -{ - //LOGE("storeF %i %i %f", bank, offset, v); - GET_TLS(); - void *vp = sc->mSlots[bank]->getPtr(); - float *f = static_cast<float *>(vp); - f[offset] = v; -} - -static void SC_storeI32(uint32_t bank, uint32_t offset, int32_t v) -{ - GET_TLS(); - void *vp = sc->mSlots[bank]->getPtr(); - int32_t *f = static_cast<int32_t *>(vp); - static_cast<int32_t *>(sc->mSlots[bank]->getPtr())[offset] = v; -} - -static void SC_storeU32(uint32_t bank, uint32_t offset, uint32_t v) -{ - GET_TLS(); - void *vp = sc->mSlots[bank]->getPtr(); - uint32_t *f = static_cast<uint32_t *>(vp); - static_cast<uint32_t *>(sc->mSlots[bank]->getPtr())[offset] = v; -} - -static void SC_storeVec4(uint32_t bank, uint32_t offset, const rsc_Vector4 *v) -{ - GET_TLS(); - void *vp = sc->mSlots[bank]->getPtr(); - float *f = static_cast<float *>(vp); - memcpy(&f[offset], v, sizeof(rsc_Vector4)); -} - -static void SC_storeMatrix(uint32_t bank, uint32_t offset, const rsc_Matrix *m) -{ - GET_TLS(); - void *vp = sc->mSlots[bank]->getPtr(); - float *f = static_cast<float *>(vp); - memcpy(&f[offset], m, sizeof(rsc_Matrix)); -} - -////////////////////////////////////////////////////////////////////////////// -// Vec3 routines -////////////////////////////////////////////////////////////////////////////// - -static void SC_vec3Norm(vec3_t *v) -{ - float len = sqrtf(v->x * v->x + v->y * v->y + v->z * v->z); - len = 1 / len; - v->x *= len; - v->y *= len; - v->z *= len; -} - -static float SC_vec3Length(const vec3_t *v) -{ - return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z); -} - -static void SC_vec3Add(vec3_t *dest, const vec3_t *lhs, const vec3_t *rhs) -{ - dest->x = lhs->x + rhs->x; - dest->y = lhs->y + rhs->y; - dest->z = lhs->z + rhs->z; -} - -static void SC_vec3Sub(vec3_t *dest, const vec3_t *lhs, const vec3_t *rhs) -{ - dest->x = lhs->x - rhs->x; - dest->y = lhs->y - rhs->y; - dest->z = lhs->z - rhs->z; -} - -static void SC_vec3Cross(vec3_t *dest, const vec3_t *lhs, const vec3_t *rhs) -{ - float x = lhs->y * rhs->z - lhs->z * rhs->y; - float y = lhs->z * rhs->x - lhs->x * rhs->z; - float z = lhs->x * rhs->y - lhs->y * rhs->x; - dest->x = x; - dest->y = y; - dest->z = z; -} - -static float SC_vec3Dot(const vec3_t *lhs, const vec3_t *rhs) -{ - return lhs->x * rhs->x + lhs->y * rhs->y + lhs->z * rhs->z; -} - -static void SC_vec3Scale(vec3_t *lhs, float scale) -{ - lhs->x *= scale; - lhs->y *= scale; - lhs->z *= scale; -} - -////////////////////////////////////////////////////////////////////////////// -// Vec4 routines -////////////////////////////////////////////////////////////////////////////// - -static void SC_vec4Norm(vec4_t *v) -{ - float len = sqrtf(v->x * v->x + v->y * v->y + v->z * v->z + v->w * v->w); - len = 1 / len; - v->x *= len; - v->y *= len; - v->z *= len; - v->w *= len; -} - -static float SC_vec4Length(const vec4_t *v) -{ - return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z + v->w * v->w); -} - -static void SC_vec4Add(vec4_t *dest, const vec4_t *lhs, const vec4_t *rhs) -{ - dest->x = lhs->x + rhs->x; - dest->y = lhs->y + rhs->y; - dest->z = lhs->z + rhs->z; - dest->w = lhs->w + rhs->w; -} - -static void SC_vec4Sub(vec4_t *dest, const vec4_t *lhs, const vec4_t *rhs) -{ - dest->x = lhs->x - rhs->x; - dest->y = lhs->y - rhs->y; - dest->z = lhs->z - rhs->z; - dest->w = lhs->w - rhs->w; -} - -static float SC_vec4Dot(const vec4_t *lhs, const vec4_t *rhs) -{ - return lhs->x * rhs->x + lhs->y * rhs->y + lhs->z * rhs->z + lhs->w * rhs->w; -} - -static void SC_vec4Scale(vec4_t *lhs, float scale) -{ - lhs->x *= scale; - lhs->y *= scale; - lhs->z *= scale; - lhs->w *= scale; -} ////////////////////////////////////////////////////////////////////////////// // Math routines ////////////////////////////////////////////////////////////////////////////// -#define PI 3.1415926f -#define DEG_TO_RAD PI / 180.0f -#define RAD_TO_DEG 180.0f / PI - -static float SC_sinf_fast(float x) -{ +#if 0 +static float SC_sinf_fast(float x) { const float A = 1.0f / (2.0f * M_PI); const float B = -16.0f; const float C = 8.0f; @@ -303,8 +53,7 @@ static float SC_sinf_fast(float x) return 0.2215f * (y * fabsf(y) - y) + y; } -static float SC_cosf_fast(float x) -{ +static float SC_cosf_fast(float x) { x += float(M_PI / 2); const float A = 1.0f / (2.0f * M_PI); @@ -322,757 +71,764 @@ static float SC_cosf_fast(float x) const float y = B * x * fabsf(x) + C * x; return 0.2215f * (y * fabsf(y) - y) + y; } +#endif -static float SC_randf(float max) -{ +static float SC_randf(float max) { float r = (float)rand(); - return r / RAND_MAX * max; + r *= max; + return r / RAND_MAX; } -static float SC_randf2(float min, float max) -{ +static float SC_randf2(float min, float max) { float r = (float)rand(); - return r / RAND_MAX * (max - min) + min; + r = r * (max - min) + min; + return r / RAND_MAX; } -static int SC_sign(int value) -{ - return (value > 0) - (value < 0); +static int SC_randi(int max) { + return (int)SC_randf(max); } -static float SC_signf(float value) -{ - return (value > 0) - (value < 0); +static int SC_randi2(int min, int max) { + return (int)SC_randf2(min, max); } -static float SC_clampf(float amount, float low, float high) -{ - return amount < low ? low : (amount > high ? high : amount); +static float SC_frac(float v) { + int i = (int)floor(v); + return fmin(v - i, 0x1.fffffep-1f); } -static int SC_clamp(int amount, int low, int high) -{ - return amount < low ? low : (amount > high ? high : amount); -} +////////////////////////////////////////////////////////////////////////////// +// Time routines +////////////////////////////////////////////////////////////////////////////// -static float SC_maxf(float a, float b) -{ - return a > b ? a : b; +static time_t SC_time(time_t *timer) { + GET_TLS(); + return time(timer); } -static float SC_minf(float a, float b) -{ - return a < b ? a : b; -} +static tm* SC_localtime(tm *local, time_t *timer) { + GET_TLS(); + if (!local) { + return NULL; + } -static float SC_sqrf(float v) -{ - return v * v; + // The native localtime function is not thread-safe, so we + // have to apply locking for proper behavior in RenderScript. + pthread_mutex_lock(&rsc->gLibMutex); + tm *tmp = localtime(timer); + memcpy(local, tmp, sizeof(*tmp)); + pthread_mutex_unlock(&rsc->gLibMutex); + return local; } -static int SC_sqr(int v) -{ - return v * v; +static int64_t SC_uptimeMillis() { + return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC)); } -static float SC_fracf(float v) -{ - return v - floorf(v); +static int64_t SC_uptimeNanos() { + return systemTime(SYSTEM_TIME_MONOTONIC); } -static float SC_roundf(float v) -{ - return floorf(v + 0.4999999999); +static float SC_getDt() { + GET_TLS(); + int64_t l = sc->mEnviroment.mLastDtTime; + sc->mEnviroment.mLastDtTime = systemTime(SYSTEM_TIME_MONOTONIC); + return ((float)(sc->mEnviroment.mLastDtTime - l)) / 1.0e9; } -static float SC_distf2(float x1, float y1, float x2, float y2) -{ - float x = x2 - x1; - float y = y2 - y1; - return sqrtf(x * x + y * y); -} +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// -static float SC_distf3(float x1, float y1, float z1, float x2, float y2, float z2) -{ - float x = x2 - x1; - float y = y2 - y1; - float z = z2 - z1; - return sqrtf(x * x + y * y + z * z); +static uint32_t SC_allocGetDimX(RsAllocation va) { + const Allocation *a = static_cast<const Allocation *>(va); + CHECK_OBJ(a); + //LOGE("SC_allocGetDimX a=%p type=%p", a, a->getType()); + return a->getType()->getDimX(); } -static float SC_magf2(float a, float b) -{ - return sqrtf(a * a + b * b); +static uint32_t SC_allocGetDimY(RsAllocation va) { + const Allocation *a = static_cast<const Allocation *>(va); + CHECK_OBJ(a); + return a->getType()->getDimY(); } -static float SC_magf3(float a, float b, float c) -{ - return sqrtf(a * a + b * b + c * c); +static uint32_t SC_allocGetDimZ(RsAllocation va) { + const Allocation *a = static_cast<const Allocation *>(va); + CHECK_OBJ(a); + return a->getType()->getDimZ(); } -static float SC_radf(float degrees) -{ - return degrees * DEG_TO_RAD; +static uint32_t SC_allocGetDimLOD(RsAllocation va) { + const Allocation *a = static_cast<const Allocation *>(va); + CHECK_OBJ(a); + return a->getType()->getDimLOD(); } -static float SC_degf(float radians) -{ - return radians * RAD_TO_DEG; +static uint32_t SC_allocGetDimFaces(RsAllocation va) { + const Allocation *a = static_cast<const Allocation *>(va); + CHECK_OBJ(a); + return a->getType()->getDimFaces(); } -static float SC_lerpf(float start, float stop, float amount) -{ - return start + (stop - start) * amount; +static const void * SC_getElementAtX(RsAllocation va, uint32_t x) { + const Allocation *a = static_cast<const Allocation *>(va); + CHECK_OBJ(a); + const Type *t = a->getType(); + CHECK_OBJ(t); + const uint8_t *p = (const uint8_t *)a->getPtr(); + return &p[t->getElementSizeBytes() * x]; } -static float SC_normf(float start, float stop, float value) -{ - return (value - start) / (stop - start); +static const void * SC_getElementAtXY(RsAllocation va, uint32_t x, uint32_t y) { + const Allocation *a = static_cast<const Allocation *>(va); + CHECK_OBJ(a); + const Type *t = a->getType(); + CHECK_OBJ(t); + const uint8_t *p = (const uint8_t *)a->getPtr(); + return &p[t->getElementSizeBytes() * (x + y*t->getDimX())]; } -static float SC_mapf(float minStart, float minStop, float maxStart, float maxStop, float value) -{ - return maxStart + (maxStart - maxStop) * ((value - minStart) / (minStop - minStart)); +static const void * SC_getElementAtXYZ(RsAllocation va, uint32_t x, uint32_t y, uint32_t z) { + const Allocation *a = static_cast<const Allocation *>(va); + CHECK_OBJ(a); + const Type *t = a->getType(); + CHECK_OBJ(t); + const uint8_t *p = (const uint8_t *)a->getPtr(); + return &p[t->getElementSizeBytes() * (x + y*t->getDimX())]; } -////////////////////////////////////////////////////////////////////////////// -// Time routines -////////////////////////////////////////////////////////////////////////////// - -static int32_t SC_second() -{ - GET_TLS(); - - time_t rawtime; - time(&rawtime); - - struct tm *timeinfo; - timeinfo = localtime(&rawtime); - return timeinfo->tm_sec; +static void SC_setObject(void **vdst, void * vsrc) { + //LOGE("SC_setObject %p,%p %p", vdst, *vdst, vsrc); + if (vsrc) { + CHECK_OBJ(vsrc); + static_cast<ObjectBase *>(vsrc)->incSysRef(); + } + if (vdst[0]) { + CHECK_OBJ(vdst[0]); + static_cast<ObjectBase *>(vdst[0])->decSysRef(); + } + *vdst = vsrc; + //LOGE("SC_setObject *"); } -static int32_t SC_minute() -{ - GET_TLS(); - - time_t rawtime; - time(&rawtime); - - struct tm *timeinfo; - timeinfo = localtime(&rawtime); - return timeinfo->tm_min; +static void SC_clearObject(void **vdst) { + //LOGE("SC_clearObject %p,%p", vdst, *vdst); + if (vdst[0]) { + CHECK_OBJ(vdst[0]); + static_cast<ObjectBase *>(vdst[0])->decSysRef(); + } + *vdst = NULL; + //LOGE("SC_clearObject *"); } -static int32_t SC_hour() -{ - GET_TLS(); - - time_t rawtime; - time(&rawtime); - - struct tm *timeinfo; - timeinfo = localtime(&rawtime); - return timeinfo->tm_hour; +static bool SC_isObject(RsAllocation vsrc) { + return vsrc != NULL; } -static int32_t SC_day() -{ - GET_TLS(); - - time_t rawtime; - time(&rawtime); - - struct tm *timeinfo; - timeinfo = localtime(&rawtime); - return timeinfo->tm_mday; +static void SC_debugF(const char *s, float f) { + LOGD("%s %f, 0x%08x", s, f, *((int *) (&f))); } - -static int32_t SC_month() -{ - GET_TLS(); - - time_t rawtime; - time(&rawtime); - - struct tm *timeinfo; - timeinfo = localtime(&rawtime); - return timeinfo->tm_mon; +static void SC_debugFv2(const char *s, float f1, float f2) { + LOGD("%s {%f, %f}", s, f1, f2); } - -static int32_t SC_year() -{ - GET_TLS(); - - time_t rawtime; - time(&rawtime); - - struct tm *timeinfo; - timeinfo = localtime(&rawtime); - return timeinfo->tm_year; +static void SC_debugFv3(const char *s, float f1, float f2, float f3) { + LOGD("%s {%f, %f, %f}", s, f1, f2, f3); } - -static int32_t SC_uptimeMillis() -{ - return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC)); +static void SC_debugFv4(const char *s, float f1, float f2, float f3, float f4) { + LOGD("%s {%f, %f, %f, %f}", s, f1, f2, f3, f4); } - -static int32_t SC_startTimeMillis() -{ - GET_TLS(); - return sc->mEnviroment.mStartTimeMillis; +static void SC_debugD(const char *s, double d) { + LOGD("%s %f, 0x%08llx", s, d, *((long long *) (&d))); } - -static int32_t SC_elapsedTimeMillis() -{ - GET_TLS(); - return nanoseconds_to_milliseconds(systemTime(SYSTEM_TIME_MONOTONIC)) - - sc->mEnviroment.mStartTimeMillis; +static void SC_debugFM4v4(const char *s, const float *f) { + LOGD("%s {%f, %f, %f, %f", s, f[0], f[4], f[8], f[12]); + LOGD("%s %f, %f, %f, %f", s, f[1], f[5], f[9], f[13]); + LOGD("%s %f, %f, %f, %f", s, f[2], f[6], f[10], f[14]); + LOGD("%s %f, %f, %f, %f}", s, f[3], f[7], f[11], f[15]); } - -////////////////////////////////////////////////////////////////////////////// -// Matrix routines -////////////////////////////////////////////////////////////////////////////// - - -static void SC_matrixLoadIdentity(rsc_Matrix *mat) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->loadIdentity(); +static void SC_debugFM3v3(const char *s, const float *f) { + LOGD("%s {%f, %f, %f", s, f[0], f[3], f[6]); + LOGD("%s %f, %f, %f", s, f[1], f[4], f[7]); + LOGD("%s %f, %f, %f}",s, f[2], f[5], f[8]); } - -static void SC_matrixLoadFloat(rsc_Matrix *mat, const float *f) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->load(f); +static void SC_debugFM2v2(const char *s, const float *f) { + LOGD("%s {%f, %f", s, f[0], f[2]); + LOGD("%s %f, %f}",s, f[1], f[3]); } -static void SC_matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->load(reinterpret_cast<const Matrix *>(newmat)); +static void SC_debugI32(const char *s, int32_t i) { + LOGD("%s %i 0x%x", s, i, i); } - -static void SC_matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->loadRotate(rot, x, y, z); +static void SC_debugU32(const char *s, uint32_t i) { + LOGD("%s %u 0x%x", s, i, i); } - -static void SC_matrixLoadScale(rsc_Matrix *mat, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->loadScale(x, y, z); +static void SC_debugLL64(const char *s, long long ll) { + LOGD("%s %lld 0x%llx", s, ll, ll); } - -static void SC_matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->loadTranslate(x, y, z); +static void SC_debugULL64(const char *s, unsigned long long ll) { + LOGD("%s %llu 0x%llx", s, ll, ll); } -static void SC_matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->loadMultiply(reinterpret_cast<const Matrix *>(lhs), - reinterpret_cast<const Matrix *>(rhs)); +static void SC_debugP(const char *s, const void *p) { + LOGD("%s %p", s, p); } -static void SC_matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->multiply(reinterpret_cast<const Matrix *>(rhs)); +static uint32_t SC_toClient2(int cmdID, void *data, int len) { + GET_TLS(); + //LOGE("SC_toClient %i %i %i", cmdID, len); + return rsc->sendMessageToClient(data, RS_MESSAGE_TO_CLIENT_USER, cmdID, len, false); } -static void SC_matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->rotate(rot, x, y, z); +static uint32_t SC_toClient(int cmdID) { + GET_TLS(); + //LOGE("SC_toClient %i", cmdID); + return rsc->sendMessageToClient(NULL, RS_MESSAGE_TO_CLIENT_USER, cmdID, 0, false); } -static void SC_matrixScale(rsc_Matrix *mat, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->scale(x, y, z); +static uint32_t SC_toClientBlocking2(int cmdID, void *data, int len) { + GET_TLS(); + //LOGE("SC_toClientBlocking %i %i", cmdID, len); + return rsc->sendMessageToClient(data, RS_MESSAGE_TO_CLIENT_USER, cmdID, len, true); } -static void SC_matrixTranslate(rsc_Matrix *mat, float x, float y, float z) -{ - Matrix *m = reinterpret_cast<Matrix *>(mat); - m->translate(x, y, z); +static uint32_t SC_toClientBlocking(int cmdID) { + GET_TLS(); + //LOGE("SC_toClientBlocking %i", cmdID); + return rsc->sendMessageToClient(NULL, RS_MESSAGE_TO_CLIENT_USER, cmdID, 0, true); } - -static void SC_vec2Rand(float *vec, float maxLen) -{ - float angle = SC_randf(PI * 2); - float len = SC_randf(maxLen); - vec[0] = len * sinf(angle); - vec[1] = len * cosf(angle); +int SC_divsi3(int a, int b) { + return a / b; } - - -////////////////////////////////////////////////////////////////////////////// -// Context -////////////////////////////////////////////////////////////////////////////// - -static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va) -{ - GET_TLS(); - rsi_ProgramBindTexture(rsc, - static_cast<ProgramFragment *>(vpf), - slot, - static_cast<Allocation *>(va)); - +int SC_modsi3(int a, int b) { + return a % b; } -static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs) -{ - GET_TLS(); - rsi_ProgramBindSampler(rsc, - static_cast<ProgramFragment *>(vpf), - slot, - static_cast<Sampler *>(vs)); - +unsigned int SC_udivsi3(unsigned int a, unsigned int b) { + return a / b; } -static void SC_bindProgramFragmentStore(RsProgramFragmentStore pfs) -{ - GET_TLS(); - rsi_ContextBindProgramFragmentStore(rsc, pfs); - +unsigned int SC_umodsi3(unsigned int a, unsigned int b) { + return a % b; } -static void SC_bindProgramFragment(RsProgramFragment pf) -{ +int SC_getAllocation(const void *ptr) { GET_TLS(); - rsi_ContextBindProgramFragment(rsc, pf); - + const Allocation *alloc = sc->ptrToAllocation(ptr); + return (int)alloc; } -static void SC_bindProgramVertex(RsProgramVertex pv) -{ - GET_TLS(); - rsi_ContextBindProgramVertex(rsc, pv); - +void SC_allocationMarkDirty(RsAllocation a) { + Allocation *alloc = static_cast<Allocation *>(a); + alloc->sendDirty(); } -////////////////////////////////////////////////////////////////////////////// -// VP -////////////////////////////////////////////////////////////////////////////// - -static void SC_vpLoadModelMatrix(const rsc_Matrix *m) -{ +void SC_ForEach(RsScript vs, + RsAllocation vin, + RsAllocation vout, + const void *usr) { GET_TLS(); - rsc->getVertex()->setModelviewMatrix(m); + const Allocation *ain = static_cast<const Allocation *>(vin); + Allocation *aout = static_cast<Allocation *>(vout); + Script *s = static_cast<Script *>(vs); + s->runForEach(rsc, ain, aout, usr); } -static void SC_vpLoadTextureMatrix(const rsc_Matrix *m) -{ +void SC_ForEach2(RsScript vs, + RsAllocation vin, + RsAllocation vout, + const void *usr, + const RsScriptCall *call) { GET_TLS(); - rsc->getVertex()->setTextureMatrix(m); + const Allocation *ain = static_cast<const Allocation *>(vin); + Allocation *aout = static_cast<Allocation *>(vout); + Script *s = static_cast<Script *>(vs); + s->runForEach(rsc, ain, aout, usr, call); } - ////////////////////////////////////////////////////////////////////////////// -// Drawing +// Heavy math functions ////////////////////////////////////////////////////////////////////////////// -static void SC_drawLine(float x1, float y1, float z1, - float x2, float y2, float z2) -{ - GET_TLS(); - if (!rsc->setupCheck()) { - return; - } - - float vtx[] = { x1, y1, z1, x2, y2, z2 }; - VertexArray va; - va.addLegacy(GL_FLOAT, 3, 12, RS_KIND_POSITION, false, (uint32_t)vtx); - if (rsc->checkVersion2_0()) { - va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache); - } else { - va.setupGL(rsc, &rsc->mStateVertexArray); - } +typedef struct { + float m[16]; +} rs_matrix4x4; - glDrawArrays(GL_LINES, 0, 2); -} +typedef struct { + float m[9]; +} rs_matrix3x3; -static void SC_drawPoint(float x, float y, float z) -{ - GET_TLS(); - if (!rsc->setupCheck()) { - return; +typedef struct { + float m[4]; +} rs_matrix2x2; + +static inline void +rsMatrixSet(rs_matrix4x4 *m, uint32_t row, uint32_t col, float v) { + m->m[row * 4 + col] = v; +} + +static inline float +rsMatrixGet(const rs_matrix4x4 *m, uint32_t row, uint32_t col) { + return m->m[row * 4 + col]; +} + +static inline void +rsMatrixSet(rs_matrix3x3 *m, uint32_t row, uint32_t col, float v) { + m->m[row * 3 + col] = v; +} + +static inline float +rsMatrixGet(const rs_matrix3x3 *m, uint32_t row, uint32_t col) { + return m->m[row * 3 + col]; +} + +static inline void +rsMatrixSet(rs_matrix2x2 *m, uint32_t row, uint32_t col, float v) { + m->m[row * 2 + col] = v; +} + +static inline float +rsMatrixGet(const rs_matrix2x2 *m, uint32_t row, uint32_t col) { + return m->m[row * 2 + col]; +} + + +static void SC_MatrixLoadIdentity_4x4(rs_matrix4x4 *m) { + m->m[0] = 1.f; + m->m[1] = 0.f; + m->m[2] = 0.f; + m->m[3] = 0.f; + m->m[4] = 0.f; + m->m[5] = 1.f; + m->m[6] = 0.f; + m->m[7] = 0.f; + m->m[8] = 0.f; + m->m[9] = 0.f; + m->m[10] = 1.f; + m->m[11] = 0.f; + m->m[12] = 0.f; + m->m[13] = 0.f; + m->m[14] = 0.f; + m->m[15] = 1.f; +} + +static void SC_MatrixLoadIdentity_3x3(rs_matrix3x3 *m) { + m->m[0] = 1.f; + m->m[1] = 0.f; + m->m[2] = 0.f; + m->m[3] = 0.f; + m->m[4] = 1.f; + m->m[5] = 0.f; + m->m[6] = 0.f; + m->m[7] = 0.f; + m->m[8] = 1.f; +} + +static void SC_MatrixLoadIdentity_2x2(rs_matrix2x2 *m) { + m->m[0] = 1.f; + m->m[1] = 0.f; + m->m[2] = 0.f; + m->m[3] = 1.f; +} + +static void SC_MatrixLoad_4x4_f(rs_matrix4x4 *m, const float *v) { + m->m[0] = v[0]; + m->m[1] = v[1]; + m->m[2] = v[2]; + m->m[3] = v[3]; + m->m[4] = v[4]; + m->m[5] = v[5]; + m->m[6] = v[6]; + m->m[7] = v[7]; + m->m[8] = v[8]; + m->m[9] = v[9]; + m->m[10] = v[10]; + m->m[11] = v[11]; + m->m[12] = v[12]; + m->m[13] = v[13]; + m->m[14] = v[14]; + m->m[15] = v[15]; +} + +static void SC_MatrixLoad_3x3_f(rs_matrix3x3 *m, const float *v) { + m->m[0] = v[0]; + m->m[1] = v[1]; + m->m[2] = v[2]; + m->m[3] = v[3]; + m->m[4] = v[4]; + m->m[5] = v[5]; + m->m[6] = v[6]; + m->m[7] = v[7]; + m->m[8] = v[8]; +} + +static void SC_MatrixLoad_2x2_f(rs_matrix2x2 *m, const float *v) { + m->m[0] = v[0]; + m->m[1] = v[1]; + m->m[2] = v[2]; + m->m[3] = v[3]; +} + +static void SC_MatrixLoad_4x4_4x4(rs_matrix4x4 *m, const rs_matrix4x4 *v) { + m->m[0] = v->m[0]; + m->m[1] = v->m[1]; + m->m[2] = v->m[2]; + m->m[3] = v->m[3]; + m->m[4] = v->m[4]; + m->m[5] = v->m[5]; + m->m[6] = v->m[6]; + m->m[7] = v->m[7]; + m->m[8] = v->m[8]; + m->m[9] = v->m[9]; + m->m[10] = v->m[10]; + m->m[11] = v->m[11]; + m->m[12] = v->m[12]; + m->m[13] = v->m[13]; + m->m[14] = v->m[14]; + m->m[15] = v->m[15]; +} + +static void SC_MatrixLoad_4x4_3x3(rs_matrix4x4 *m, const rs_matrix3x3 *v) { + m->m[0] = v->m[0]; + m->m[1] = v->m[1]; + m->m[2] = v->m[2]; + m->m[3] = 0.f; + m->m[4] = v->m[3]; + m->m[5] = v->m[4]; + m->m[6] = v->m[5]; + m->m[7] = 0.f; + m->m[8] = v->m[6]; + m->m[9] = v->m[7]; + m->m[10] = v->m[8]; + m->m[11] = 0.f; + m->m[12] = 0.f; + m->m[13] = 0.f; + m->m[14] = 0.f; + m->m[15] = 1.f; +} + +static void SC_MatrixLoad_4x4_2x2(rs_matrix4x4 *m, const rs_matrix2x2 *v) { + m->m[0] = v->m[0]; + m->m[1] = v->m[1]; + m->m[2] = 0.f; + m->m[3] = 0.f; + m->m[4] = v->m[2]; + m->m[5] = v->m[3]; + m->m[6] = 0.f; + m->m[7] = 0.f; + m->m[8] = 0.f; + m->m[9] = 0.f; + m->m[10] = 1.f; + m->m[11] = 0.f; + m->m[12] = 0.f; + m->m[13] = 0.f; + m->m[14] = 0.f; + m->m[15] = 1.f; +} + +static void SC_MatrixLoad_3x3_3x3(rs_matrix3x3 *m, const rs_matrix3x3 *v) { + m->m[0] = v->m[0]; + m->m[1] = v->m[1]; + m->m[2] = v->m[2]; + m->m[3] = v->m[3]; + m->m[4] = v->m[4]; + m->m[5] = v->m[5]; + m->m[6] = v->m[6]; + m->m[7] = v->m[7]; + m->m[8] = v->m[8]; +} + +static void SC_MatrixLoad_2x2_2x2(rs_matrix2x2 *m, const rs_matrix2x2 *v) { + m->m[0] = v->m[0]; + m->m[1] = v->m[1]; + m->m[2] = v->m[2]; + m->m[3] = v->m[3]; +} + +static void SC_MatrixLoadRotate(rs_matrix4x4 *m, float rot, float x, float y, float z) { + float c, s; + m->m[3] = 0; + m->m[7] = 0; + m->m[11]= 0; + m->m[12]= 0; + m->m[13]= 0; + m->m[14]= 0; + m->m[15]= 1; + rot *= (float)(M_PI / 180.0f); + c = cos(rot); + s = sin(rot); + + const float len = x*x + y*y + z*z; + if (len != 1) { + const float recipLen = 1.f / sqrt(len); + x *= recipLen; + y *= recipLen; + z *= recipLen; } - - float vtx[] = { x, y, z }; - - VertexArray va; - va.addLegacy(GL_FLOAT, 3, 12, RS_KIND_POSITION, false, (uint32_t)vtx); - if (rsc->checkVersion2_0()) { - va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache); - } else { - va.setupGL(rsc, &rsc->mStateVertexArray); + const float nc = 1.0f - c; + const float xy = x * y; + const float yz = y * z; + const float zx = z * x; + const float xs = x * s; + const float ys = y * s; + const float zs = z * s; + m->m[ 0] = x*x*nc + c; + m->m[ 4] = xy*nc - zs; + m->m[ 8] = zx*nc + ys; + m->m[ 1] = xy*nc + zs; + m->m[ 5] = y*y*nc + c; + m->m[ 9] = yz*nc - xs; + m->m[ 2] = zx*nc - ys; + m->m[ 6] = yz*nc + xs; + m->m[10] = z*z*nc + c; +} + +static void SC_MatrixLoadScale(rs_matrix4x4 *m, float x, float y, float z) { + SC_MatrixLoadIdentity_4x4(m); + m->m[0] = x; + m->m[5] = y; + m->m[10] = z; +} + +static void SC_MatrixLoadTranslate(rs_matrix4x4 *m, float x, float y, float z) { + SC_MatrixLoadIdentity_4x4(m); + m->m[12] = x; + m->m[13] = y; + m->m[14] = z; +} + +static void SC_MatrixLoadMultiply_4x4_4x4_4x4(rs_matrix4x4 *m, const rs_matrix4x4 *lhs, const rs_matrix4x4 *rhs) { + for (int i=0 ; i<4 ; i++) { + float ri0 = 0; + float ri1 = 0; + float ri2 = 0; + float ri3 = 0; + for (int j=0 ; j<4 ; j++) { + const float rhs_ij = rsMatrixGet(rhs, i,j); + ri0 += rsMatrixGet(lhs, j, 0) * rhs_ij; + ri1 += rsMatrixGet(lhs, j, 1) * rhs_ij; + ri2 += rsMatrixGet(lhs, j, 2) * rhs_ij; + ri3 += rsMatrixGet(lhs, j, 3) * rhs_ij; + } + rsMatrixSet(m, i, 0, ri0); + rsMatrixSet(m, i, 1, ri1); + rsMatrixSet(m, i, 2, ri2); + rsMatrixSet(m, i, 3, ri3); } - - glDrawArrays(GL_POINTS, 0, 1); } -static void SC_drawQuadTexCoords(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) -{ - GET_TLS(); - if (!rsc->setupCheck()) { - return; - } - - //LOGE("Quad"); - //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1); - //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2); - //LOGE("%4.2f, %4.2f, %4.2f", x3, y3, z3); - //LOGE("%4.2f, %4.2f, %4.2f", x4, y4, z4); - - float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4}; - const float tex[] = {u1,v1, u2,v2, u3,v3, u4,v4}; - - VertexArray va; - va.addLegacy(GL_FLOAT, 3, 12, RS_KIND_POSITION, false, (uint32_t)vtx); - va.addLegacy(GL_FLOAT, 2, 8, RS_KIND_TEXTURE, false, (uint32_t)tex); - if (rsc->checkVersion2_0()) { - va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache); - } else { - va.setupGL(rsc, &rsc->mStateVertexArray); - } - - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +static void SC_MatrixMultiply_4x4_4x4(rs_matrix4x4 *m, const rs_matrix4x4 *rhs) { + rs_matrix4x4 mt; + SC_MatrixLoadMultiply_4x4_4x4_4x4(&mt, m, rhs); + SC_MatrixLoad_4x4_4x4(m, &mt); } -static void SC_drawQuad(float x1, float y1, float z1, - float x2, float y2, float z2, - float x3, float y3, float z3, - float x4, float y4, float z4) -{ - SC_drawQuadTexCoords(x1, y1, z1, 0, 1, - x2, y2, z2, 1, 1, - x3, y3, z3, 1, 0, - x4, y4, z4, 0, 0); +static void SC_MatrixLoadMultiply_3x3_3x3_3x3(rs_matrix3x3 *m, const rs_matrix3x3 *lhs, const rs_matrix3x3 *rhs) { + for (int i=0 ; i<3 ; i++) { + float ri0 = 0; + float ri1 = 0; + float ri2 = 0; + for (int j=0 ; j<3 ; j++) { + const float rhs_ij = rsMatrixGet(rhs, i,j); + ri0 += rsMatrixGet(lhs, j, 0) * rhs_ij; + ri1 += rsMatrixGet(lhs, j, 1) * rhs_ij; + ri2 += rsMatrixGet(lhs, j, 2) * rhs_ij; + } + rsMatrixSet(m, i, 0, ri0); + rsMatrixSet(m, i, 1, ri1); + rsMatrixSet(m, i, 2, ri2); + } } -static void SC_drawSpriteScreenspace(float x, float y, float z, float w, float h) -{ - GET_TLS(); - ObjectBaseRef<const ProgramVertex> tmp(rsc->getVertex()); - rsc->setVertex(rsc->getDefaultProgramVertex()); - //rsc->setupCheck(); - - //GLint crop[4] = {0, h, w, -h}; - - float sh = rsc->getHeight(); - - SC_drawQuad(x, sh - y, z, - x+w, sh - y, z, - x+w, sh - (y+h), z, - x, sh - (y+h), z); - rsc->setVertex((ProgramVertex *)tmp.get()); +static void SC_MatrixMultiply_3x3_3x3(rs_matrix3x3 *m, const rs_matrix3x3 *rhs) { + rs_matrix3x3 mt; + SC_MatrixLoadMultiply_3x3_3x3_3x3(&mt, m, rhs); + SC_MatrixLoad_3x3_3x3(m, &mt); } -static void SC_drawSpriteScreenspaceCropped(float x, float y, float z, float w, float h, - float cx0, float cy0, float cx1, float cy1) -{ - GET_TLS(); - if (!rsc->setupCheck()) { - return; +static void SC_MatrixLoadMultiply_2x2_2x2_2x2(rs_matrix2x2 *m, const rs_matrix2x2 *lhs, const rs_matrix2x2 *rhs) { + for (int i=0 ; i<2 ; i++) { + float ri0 = 0; + float ri1 = 0; + for (int j=0 ; j<2 ; j++) { + const float rhs_ij = rsMatrixGet(rhs, i,j); + ri0 += rsMatrixGet(lhs, j, 0) * rhs_ij; + ri1 += rsMatrixGet(lhs, j, 1) * rhs_ij; + } + rsMatrixSet(m, i, 0, ri0); + rsMatrixSet(m, i, 1, ri1); } - - GLint crop[4] = {cx0, cy0, cx1, cy1}; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); - glDrawTexfOES(x, y, z, w, h); } -static void SC_drawSprite(float x, float y, float z, float w, float h) -{ - GET_TLS(); - float vin[3] = {x, y, z}; - float vout[4]; - - //LOGE("ds in %f %f %f", x, y, z); - rsc->getVertex()->transformToScreen(rsc, vout, vin); - //LOGE("ds out %f %f %f %f", vout[0], vout[1], vout[2], vout[3]); - vout[0] /= vout[3]; - vout[1] /= vout[3]; - vout[2] /= vout[3]; - - vout[0] *= rsc->getWidth() / 2; - vout[1] *= rsc->getHeight() / 2; - vout[0] += rsc->getWidth() / 2; - vout[1] += rsc->getHeight() / 2; - - vout[0] -= w/2; - vout[1] -= h/2; - - //LOGE("ds out2 %f %f %f", vout[0], vout[1], vout[2]); +static void SC_MatrixMultiply_2x2_2x2(rs_matrix2x2 *m, const rs_matrix2x2 *rhs) { + rs_matrix2x2 mt; + SC_MatrixLoadMultiply_2x2_2x2_2x2(&mt, m, rhs); + SC_MatrixLoad_2x2_2x2(m, &mt); +} - // U, V, W, H - SC_drawSpriteScreenspace(vout[0], vout[1], z, h, w); - //rsc->setupCheck(); +static void SC_MatrixRotate(rs_matrix4x4 *m, float rot, float x, float y, float z) { + rs_matrix4x4 m1; + SC_MatrixLoadRotate(&m1, rot, x, y, z); + SC_MatrixMultiply_4x4_4x4(m, &m1); } +static void SC_MatrixScale(rs_matrix4x4 *m, float x, float y, float z) { + rs_matrix4x4 m1; + SC_MatrixLoadScale(&m1, x, y, z); + SC_MatrixMultiply_4x4_4x4(m, &m1); +} -static void SC_drawRect(float x1, float y1, - float x2, float y2, float z) -{ - SC_drawQuad(x1, y2, z, - x2, y2, z, - x2, y1, z, - x1, y1, z); +static void SC_MatrixTranslate(rs_matrix4x4 *m, float x, float y, float z) { + rs_matrix4x4 m1; + SC_MatrixLoadTranslate(&m1, x, y, z); + SC_MatrixMultiply_4x4_4x4(m, &m1); } -static void SC_drawSimpleMesh(RsSimpleMesh vsm) -{ - GET_TLS(); - SimpleMesh *sm = static_cast<SimpleMesh *>(vsm); - if (!rsc->setupCheck()) { - return; - } - sm->render(rsc); +static void SC_MatrixLoadOrtho(rs_matrix4x4 *m, float left, float right, float bottom, float top, float near, float far) { + SC_MatrixLoadIdentity_4x4(m); + m->m[0] = 2.f / (right - left); + m->m[5] = 2.f / (top - bottom); + m->m[10]= -2.f / (far - near); + m->m[12]= -(right + left) / (right - left); + m->m[13]= -(top + bottom) / (top - bottom); + m->m[14]= -(far + near) / (far - near); } -static void SC_drawSimpleMeshRange(RsSimpleMesh vsm, uint32_t start, uint32_t len) -{ - GET_TLS(); - SimpleMesh *sm = static_cast<SimpleMesh *>(vsm); - if (!rsc->setupCheck()) { - return; - } - sm->renderRange(rsc, start, len); +static void SC_MatrixLoadFrustum(rs_matrix4x4 *m, float left, float right, float bottom, float top, float near, float far) { + SC_MatrixLoadIdentity_4x4(m); + m->m[0] = 2.f * near / (right - left); + m->m[5] = 2.f * near / (top - bottom); + m->m[8] = (right + left) / (right - left); + m->m[9] = (top + bottom) / (top - bottom); + m->m[10]= -(far + near) / (far - near); + m->m[11]= -1.f; + m->m[14]= -2.f * far * near / (far - near); + m->m[15]= 0.f; } +static void SC_MatrixLoadPerspective(rs_matrix4x4* m, float fovy, float aspect, float near, float far) { + float top = near * tan((float) (fovy * M_PI / 360.0f)); + float bottom = -top; + float left = bottom * aspect; + float right = top * aspect; + SC_MatrixLoadFrustum(m, left, right, bottom, top, near, far); +} -////////////////////////////////////////////////////////////////////////////// -// -////////////////////////////////////////////////////////////////////////////// -static void SC_color(float r, float g, float b, float a) -{ - GET_TLS(); - rsc->mStateVertex.color[0] = r; - rsc->mStateVertex.color[1] = g; - rsc->mStateVertex.color[2] = b; - rsc->mStateVertex.color[3] = a; - if (!rsc->checkVersion2_0()) { - glColor4f(r, g, b, a); - } -} +// Returns true if the matrix was successfully inversed +static bool SC_MatrixInverse_4x4(rs_matrix4x4 *m) { + rs_matrix4x4 result; -static void SC_ambient(float r, float g, float b, float a) -{ - GLfloat params[] = { r, g, b, a }; - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, params); -} - -static void SC_diffuse(float r, float g, float b, float a) -{ - GLfloat params[] = { r, g, b, a }; - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, params); -} - -static void SC_specular(float r, float g, float b, float a) -{ - GLfloat params[] = { r, g, b, a }; - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, params); -} - -static void SC_emission(float r, float g, float b, float a) -{ - GLfloat params[] = { r, g, b, a }; - glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, params); -} - -static void SC_shininess(float s) -{ - glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, s); -} - -static void SC_pointAttenuation(float a, float b, float c) -{ - GLfloat params[] = { a, b, c }; - glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, params); -} - -static void SC_hsbToRgb(float h, float s, float b, float* rgb) -{ - float red = 0.0f; - float green = 0.0f; - float blue = 0.0f; - - float x = h; - float y = s; - float z = b; - - float hf = (x - (int) x) * 6.0f; - int ihf = (int) hf; - float f = hf - ihf; - float pv = z * (1.0f - y); - float qv = z * (1.0f - y * f); - float tv = z * (1.0f - y * (1.0f - f)); - - switch (ihf) { - case 0: // Red is the dominant color - red = z; - green = tv; - blue = pv; - break; - case 1: // Green is the dominant color - red = qv; - green = z; - blue = pv; - break; - case 2: - red = pv; - green = z; - blue = tv; - break; - case 3: // Blue is the dominant color - red = pv; - green = qv; - blue = z; - break; - case 4: - red = tv; - green = pv; - blue = z; - break; - case 5: // Red is the dominant color - red = z; - green = pv; - blue = qv; - break; - } + int i, j; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 4; ++j) { + // computeCofactor for int i, int j + int c0 = (i+1) % 4; + int c1 = (i+2) % 4; + int c2 = (i+3) % 4; + int r0 = (j+1) % 4; + int r1 = (j+2) % 4; + int r2 = (j+3) % 4; - rgb[0] = red; - rgb[1] = green; - rgb[2] = blue; -} + float minor = (m->m[c0 + 4*r0] * (m->m[c1 + 4*r1] * m->m[c2 + 4*r2] - m->m[c1 + 4*r2] * m->m[c2 + 4*r1])) + - (m->m[c0 + 4*r1] * (m->m[c1 + 4*r0] * m->m[c2 + 4*r2] - m->m[c1 + 4*r2] * m->m[c2 + 4*r0])) + + (m->m[c0 + 4*r2] * (m->m[c1 + 4*r0] * m->m[c2 + 4*r1] - m->m[c1 + 4*r1] * m->m[c2 + 4*r0])); -static int SC_hsbToAbgr(float h, float s, float b, float a) -{ - float rgb[3]; - SC_hsbToRgb(h, s, b, rgb); - return int(a * 255.0f) << 24 | - int(rgb[2] * 255.0f) << 16 | - int(rgb[1] * 255.0f) << 8 | - int(rgb[0] * 255.0f); -} + float cofactor = (i+j) & 1 ? -minor : minor; -static void SC_hsb(float h, float s, float b, float a) -{ - GET_TLS(); - float rgb[3]; - SC_hsbToRgb(h, s, b, rgb); - if (rsc->checkVersion2_0()) { - glVertexAttrib4f(1, rgb[0], rgb[1], rgb[2], a); - } else { - glColor4f(rgb[0], rgb[1], rgb[2], a); + result.m[4*i + j] = cofactor; + } } -} -static void SC_uploadToTexture(RsAllocation va, uint32_t baseMipLevel) -{ - GET_TLS(); - rsi_AllocationUploadToTexture(rsc, va, false, baseMipLevel); -} + // Dot product of 0th column of source and 0th row of result + float det = m->m[0]*result.m[0] + m->m[4]*result.m[1] + + m->m[8]*result.m[2] + m->m[12]*result.m[3]; -static void SC_uploadToBufferObject(RsAllocation va) -{ - GET_TLS(); - rsi_AllocationUploadToBufferObject(rsc, va); -} + if (fabs(det) < 1e-6) { + return false; + } -static void SC_syncToGL(RsAllocation va) -{ - GET_TLS(); - Allocation *a = static_cast<Allocation *>(va); + det = 1.0f / det; + for (i = 0; i < 16; ++i) { + m->m[i] = result.m[i] * det; + } + return true; } -static void SC_ClearColor(float r, float g, float b, float a) -{ - //LOGE("c %f %f %f %f", r, g, b, a); - GET_TLS(); - sc->mEnviroment.mClearColor[0] = r; - sc->mEnviroment.mClearColor[1] = g; - sc->mEnviroment.mClearColor[2] = b; - sc->mEnviroment.mClearColor[3] = a; -} +// Returns true if the matrix was successfully inversed +static bool SC_MatrixInverseTranspose_4x4(rs_matrix4x4 *m) { + rs_matrix4x4 result; -static void SC_debugF(const char *s, float f) -{ - LOGE("%s %f", s, f); -} + int i, j; + for (i = 0; i < 4; ++i) { + for (j = 0; j < 4; ++j) { + // computeCofactor for int i, int j + int c0 = (i+1) % 4; + int c1 = (i+2) % 4; + int c2 = (i+3) % 4; + int r0 = (j+1) % 4; + int r1 = (j+2) % 4; + int r2 = (j+3) % 4; -static void SC_debugHexF(const char *s, float f) -{ - LOGE("%s 0x%x", s, *((int *) (&f))); -} + float minor = (m->m[c0 + 4*r0] * (m->m[c1 + 4*r1] * m->m[c2 + 4*r2] - m->m[c1 + 4*r2] * m->m[c2 + 4*r1])) + - (m->m[c0 + 4*r1] * (m->m[c1 + 4*r0] * m->m[c2 + 4*r2] - m->m[c1 + 4*r2] * m->m[c2 + 4*r0])) + + (m->m[c0 + 4*r2] * (m->m[c1 + 4*r0] * m->m[c2 + 4*r1] - m->m[c1 + 4*r1] * m->m[c2 + 4*r0])); -static void SC_debugI32(const char *s, int32_t i) -{ - LOGE("%s %i", s, i); -} + float cofactor = (i+j) & 1 ? -minor : minor; -static void SC_debugHexI32(const char *s, int32_t i) -{ - LOGE("%s 0x%x", s, i); -} + result.m[4*j + i] = cofactor; + } + } -static uint32_t SC_getWidth() -{ - GET_TLS(); - return rsc->getWidth(); -} + // Dot product of 0th column of source and 0th column of result + float det = m->m[0]*result.m[0] + m->m[4]*result.m[4] + + m->m[8]*result.m[8] + m->m[12]*result.m[12]; -static uint32_t SC_getHeight() -{ - GET_TLS(); - return rsc->getHeight(); -} + if (fabs(det) < 1e-6) { + return false; + } + + det = 1.0f / det; + for (i = 0; i < 16; ++i) { + m->m[i] = result.m[i] * det; + } -static uint32_t SC_colorFloatRGBAtoUNorm8(float r, float g, float b, float a) -{ - uint32_t c = 0; - c |= (uint32_t)(r * 255.f + 0.5f); - c |= ((uint32_t)(g * 255.f + 0.5f)) << 8; - c |= ((uint32_t)(b * 255.f + 0.5f)) << 16; - c |= ((uint32_t)(a * 255.f + 0.5f)) << 24; - return c; + return true; } -static uint32_t SC_colorFloatRGBAto565(float r, float g, float b) -{ - uint32_t ir = (uint32_t)(r * 255.f + 0.5f); - uint32_t ig = (uint32_t)(g * 255.f + 0.5f); - uint32_t ib = (uint32_t)(b * 255.f + 0.5f); - return rs888to565(ir, ig, ib); +static void SC_MatrixTranspose_4x4(rs_matrix4x4 *m) { + int i, j; + float temp; + for (i = 0; i < 3; ++i) { + for (j = i + 1; j < 4; ++j) { + temp = m->m[i*4 + j]; + m->m[i*4 + j] = m->m[j*4 + i]; + m->m[j*4 + i] = temp; + } + } } -static uint32_t SC_toClient(void *data, int cmdID, int len, int waitForSpace) -{ - GET_TLS(); - return rsc->sendMessageToClient(data, cmdID, len, waitForSpace != 0); +static void SC_MatrixTranspose_3x3(rs_matrix3x3 *m) { + int i, j; + float temp; + for (i = 0; i < 2; ++i) { + for (j = i + 1; j < 3; ++j) { + temp = m->m[i*3 + j]; + m->m[i*3 + j] = m->m[j*4 + i]; + m->m[j*3 + i] = temp; + } + } } -static void SC_scriptCall(int scriptID) -{ - GET_TLS(); - rsc->runScript((Script *)scriptID, 0); +static void SC_MatrixTranspose_2x2(rs_matrix2x2 *m) { + float temp = m->m[1]; + m->m[1] = m->m[2]; + m->m[2] = temp; } @@ -1080,315 +836,181 @@ static void SC_scriptCall(int scriptID) // Class implementation ////////////////////////////////////////////////////////////////////////////// -ScriptCState::SymbolTable_t ScriptCState::gSyms[] = { - // IO - { "loadI32", (void *)&SC_loadI32, - "int", "(int, int)" }, - //{ "loadU32", (void *)&SC_loadU32, "unsigned int", "(int, int)" }, - { "loadF", (void *)&SC_loadF, - "float", "(int, int)" }, - { "loadArrayF", (void *)&SC_loadArrayF, - "float*", "(int, int)" }, - { "loadArrayI32", (void *)&SC_loadArrayI32, - "int*", "(int, int)" }, - { "loadVec4", (void *)&SC_loadVec4, - "void", "(int, int, float *)" }, - { "loadMatrix", (void *)&SC_loadMatrix, - "void", "(int, int, float *)" }, - { "storeI32", (void *)&SC_storeI32, - "void", "(int, int, int)" }, - //{ "storeU32", (void *)&SC_storeU32, "void", "(int, int, unsigned int)" }, - { "storeF", (void *)&SC_storeF, - "void", "(int, int, float)" }, - { "storeVec4", (void *)&SC_storeVec4, - "void", "(int, int, float *)" }, - { "storeMatrix", (void *)&SC_storeMatrix, - "void", "(int, int, float *)" }, - { "loadSimpleMeshVerticesF", (void *)&SC_loadSimpleMeshVerticesF, - "float*", "(int, int)" }, - { "updateSimpleMesh", (void *)&SC_updateSimpleMesh, - "void", "(int)" }, - - // math - { "modf", (void *)&fmod, - "float", "(float, float)" }, - { "abs", (void *)&abs, - "int", "(int)" }, - { "absf", (void *)&fabsf, - "float", "(float)" }, - { "sinf_fast", (void *)&SC_sinf_fast, - "float", "(float)" }, - { "cosf_fast", (void *)&SC_cosf_fast, - "float", "(float)" }, - { "sinf", (void *)&sinf, - "float", "(float)" }, - { "cosf", (void *)&cosf, - "float", "(float)" }, - { "asinf", (void *)&asinf, - "float", "(float)" }, - { "acosf", (void *)&acosf, - "float", "(float)" }, - { "atanf", (void *)&atanf, - "float", "(float)" }, - { "atan2f", (void *)&atan2f, - "float", "(float, float)" }, - { "fabsf", (void *)&fabsf, - "float", "(float)" }, - { "randf", (void *)&SC_randf, - "float", "(float)" }, - { "randf2", (void *)&SC_randf2, - "float", "(float, float)" }, - { "floorf", (void *)&floorf, - "float", "(float)" }, - { "fracf", (void *)&SC_fracf, - "float", "(float)" }, - { "ceilf", (void *)&ceilf, - "float", "(float)" }, - { "roundf", (void *)&SC_roundf, - "float", "(float)" }, - { "expf", (void *)&expf, - "float", "(float)" }, - { "logf", (void *)&logf, - "float", "(float)" }, - { "powf", (void *)&powf, - "float", "(float, float)" }, - { "maxf", (void *)&SC_maxf, - "float", "(float, float)" }, - { "minf", (void *)&SC_minf, - "float", "(float, float)" }, - { "sqrt", (void *)&sqrt, - "int", "(int)" }, - { "sqrtf", (void *)&sqrtf, - "float", "(float)" }, - { "sqr", (void *)&SC_sqr, - "int", "(int)" }, - { "sqrf", (void *)&SC_sqrf, - "float", "(float)" }, - { "sign", (void *)&SC_sign, - "int", "(int)" }, - { "signf", (void *)&SC_signf, - "float", "(float)" }, - { "clamp", (void *)&SC_clamp, - "int", "(int, int, int)" }, - { "clampf", (void *)&SC_clampf, - "float", "(float, float, float)" }, - { "distf2", (void *)&SC_distf2, - "float", "(float, float, float, float)" }, - { "distf3", (void *)&SC_distf3, - "float", "(float, float, float, float, float, float)" }, - { "magf2", (void *)&SC_magf2, - "float", "(float, float)" }, - { "magf3", (void *)&SC_magf3, - "float", "(float, float, float)" }, - { "radf", (void *)&SC_radf, - "float", "(float)" }, - { "degf", (void *)&SC_degf, - "float", "(float)" }, - { "lerpf", (void *)&SC_lerpf, - "float", "(float, float, float)" }, - { "normf", (void *)&SC_normf, - "float", "(float, float, float)" }, - { "mapf", (void *)&SC_mapf, - "float", "(float, float, float, float, float)" }, - { "noisef", (void *)&SC_noisef, - "float", "(float)" }, - { "noisef2", (void *)&SC_noisef2, - "float", "(float, float)" }, - { "noisef3", (void *)&SC_noisef3, - "float", "(float, float, float)" }, - { "turbulencef2", (void *)&SC_turbulencef2, - "float", "(float, float, float)" }, - { "turbulencef3", (void *)&SC_turbulencef3, - "float", "(float, float, float, float)" }, +// llvm name mangling ref +// <builtin-type> ::= v # void +// ::= b # bool +// ::= c # char +// ::= a # signed char +// ::= h # unsigned char +// ::= s # short +// ::= t # unsigned short +// ::= i # int +// ::= j # unsigned int +// ::= l # long +// ::= m # unsigned long +// ::= x # long long, __int64 +// ::= y # unsigned long long, __int64 +// ::= f # float +// ::= d # double + +static ScriptCState::SymbolTable_t gSyms[] = { + { "__divsi3", (void *)&SC_divsi3, true }, + { "__modsi3", (void *)&SC_modsi3, true }, + { "__udivsi3", (void *)&SC_udivsi3, true }, + { "__umodsi3", (void *)&SC_umodsi3, true }, + { "memset", (void *)&memset, true }, + { "memcpy", (void *)&memcpy, true }, + + // allocation + { "_Z19rsAllocationGetDimX13rs_allocation", (void *)&SC_allocGetDimX, true }, + { "_Z19rsAllocationGetDimY13rs_allocation", (void *)&SC_allocGetDimY, true }, + { "_Z19rsAllocationGetDimZ13rs_allocation", (void *)&SC_allocGetDimZ, true }, + { "_Z21rsAllocationGetDimLOD13rs_allocation", (void *)&SC_allocGetDimLOD, true }, + { "_Z23rsAllocationGetDimFaces13rs_allocation", (void *)&SC_allocGetDimFaces, true }, + { "_Z15rsGetAllocationPKv", (void *)&SC_getAllocation, true }, + + { "_Z14rsGetElementAt13rs_allocationj", (void *)&SC_getElementAtX, true }, + { "_Z14rsGetElementAt13rs_allocationjj", (void *)&SC_getElementAtXY, true }, + { "_Z14rsGetElementAt13rs_allocationjjj", (void *)&SC_getElementAtXYZ, true }, + + { "_Z11rsSetObjectP10rs_elementS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP10rs_element", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject10rs_element", (void *)&SC_isObject, true }, + + { "_Z11rsSetObjectP7rs_typeS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP7rs_type", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject7rs_type", (void *)&SC_isObject, true }, + + { "_Z11rsSetObjectP13rs_allocationS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP13rs_allocation", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject13rs_allocation", (void *)&SC_isObject, true }, + + { "_Z11rsSetObjectP10rs_samplerS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP10rs_sampler", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject10rs_sampler", (void *)&SC_isObject, true }, + + { "_Z11rsSetObjectP9rs_scriptS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP9rs_script", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject9rs_script", (void *)&SC_isObject, true }, + + { "_Z11rsSetObjectP7rs_meshS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP7rs_mesh", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject7rs_mesh", (void *)&SC_isObject, true }, + + { "_Z11rsSetObjectP19rs_program_fragmentS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP19rs_program_fragment", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject19rs_program_fragment", (void *)&SC_isObject, true }, + + { "_Z11rsSetObjectP17rs_program_vertexS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP17rs_program_vertex", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject17rs_program_vertex", (void *)&SC_isObject, true }, + + { "_Z11rsSetObjectP17rs_program_rasterS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP17rs_program_raster", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject17rs_program_raster", (void *)&SC_isObject, true }, + + { "_Z11rsSetObjectP16rs_program_storeS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP16rs_program_store", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject16rs_program_store", (void *)&SC_isObject, true }, + + { "_Z11rsSetObjectP7rs_fontS_", (void *)&SC_setObject, true }, + { "_Z13rsClearObjectP7rs_font", (void *)&SC_clearObject, true }, + { "_Z10rsIsObject7rs_font", (void *)&SC_isObject, true }, + + + { "_Z21rsAllocationMarkDirty13rs_allocation", (void *)&SC_allocationMarkDirty, true }, + + + // Debug + { "_Z7rsDebugPKcf", (void *)&SC_debugF, true }, + { "_Z7rsDebugPKcff", (void *)&SC_debugFv2, true }, + { "_Z7rsDebugPKcfff", (void *)&SC_debugFv3, true }, + { "_Z7rsDebugPKcffff", (void *)&SC_debugFv4, true }, + { "_Z7rsDebugPKcd", (void *)&SC_debugD, true }, + { "_Z7rsDebugPKcPK12rs_matrix4x4", (void *)&SC_debugFM4v4, true }, + { "_Z7rsDebugPKcPK12rs_matrix3x3", (void *)&SC_debugFM3v3, true }, + { "_Z7rsDebugPKcPK12rs_matrix2x2", (void *)&SC_debugFM2v2, true }, + { "_Z7rsDebugPKci", (void *)&SC_debugI32, true }, + { "_Z7rsDebugPKcj", (void *)&SC_debugU32, true }, + // Both "long" and "unsigned long" need to be redirected to their + // 64-bit counterparts, since we have hacked Slang to use 64-bit + // for "long" on Arm (to be similar to Java). + { "_Z7rsDebugPKcl", (void *)&SC_debugLL64, true }, + { "_Z7rsDebugPKcm", (void *)&SC_debugULL64, true }, + { "_Z7rsDebugPKcx", (void *)&SC_debugLL64, true }, + { "_Z7rsDebugPKcy", (void *)&SC_debugULL64, true }, + { "_Z7rsDebugPKcPKv", (void *)&SC_debugP, true }, + + // RS Math + { "_Z6rsRandi", (void *)&SC_randi, true }, + { "_Z6rsRandii", (void *)&SC_randi2, true }, + { "_Z6rsRandf", (void *)&SC_randf, true }, + { "_Z6rsRandff", (void *)&SC_randf2, true }, + { "_Z6rsFracf", (void *)&SC_frac, true }, // time - { "second", (void *)&SC_second, - "int", "()" }, - { "minute", (void *)&SC_minute, - "int", "()" }, - { "hour", (void *)&SC_hour, - "int", "()" }, - { "day", (void *)&SC_day, - "int", "()" }, - { "month", (void *)&SC_month, - "int", "()" }, - { "year", (void *)&SC_year, - "int", "()" }, - { "uptimeMillis", (void*)&SC_uptimeMillis, - "int", "()" }, // TODO: use long instead - { "startTimeMillis", (void*)&SC_startTimeMillis, - "int", "()" }, // TODO: use long instead - { "elapsedTimeMillis", (void*)&SC_elapsedTimeMillis, - "int", "()" }, // TODO: use long instead + { "_Z6rsTimePi", (void *)&SC_time, true }, + { "_Z11rsLocaltimeP5rs_tmPKi", (void *)&SC_localtime, true }, + { "_Z14rsUptimeMillisv", (void*)&SC_uptimeMillis, true }, + { "_Z13rsUptimeNanosv", (void*)&SC_uptimeNanos, true }, + { "_Z7rsGetDtv", (void*)&SC_getDt, false }, + + { "_Z14rsSendToClienti", (void *)&SC_toClient, false }, + { "_Z14rsSendToClientiPKvj", (void *)&SC_toClient2, false }, + { "_Z22rsSendToClientBlockingi", (void *)&SC_toClientBlocking, false }, + { "_Z22rsSendToClientBlockingiPKvj", (void *)&SC_toClientBlocking2, false }, // matrix - { "matrixLoadIdentity", (void *)&SC_matrixLoadIdentity, - "void", "(float *mat)" }, - { "matrixLoadFloat", (void *)&SC_matrixLoadFloat, - "void", "(float *mat, float *f)" }, - { "matrixLoadMat", (void *)&SC_matrixLoadMat, - "void", "(float *mat, float *newmat)" }, - { "matrixLoadRotate", (void *)&SC_matrixLoadRotate, - "void", "(float *mat, float rot, float x, float y, float z)" }, - { "matrixLoadScale", (void *)&SC_matrixLoadScale, - "void", "(float *mat, float x, float y, float z)" }, - { "matrixLoadTranslate", (void *)&SC_matrixLoadTranslate, - "void", "(float *mat, float x, float y, float z)" }, - { "matrixLoadMultiply", (void *)&SC_matrixLoadMultiply, - "void", "(float *mat, float *lhs, float *rhs)" }, - { "matrixMultiply", (void *)&SC_matrixMultiply, - "void", "(float *mat, float *rhs)" }, - { "matrixRotate", (void *)&SC_matrixRotate, - "void", "(float *mat, float rot, float x, float y, float z)" }, - { "matrixScale", (void *)&SC_matrixScale, - "void", "(float *mat, float x, float y, float z)" }, - { "matrixTranslate", (void *)&SC_matrixTranslate, - "void", "(float *mat, float x, float y, float z)" }, - - // vector - { "vec2Rand", (void *)&SC_vec2Rand, - "void", "(float *vec, float maxLen)" }, - - // vec3 - { "vec3Norm", (void *)&SC_vec3Norm, - "void", "(struct vecF32_3_s *)" }, - { "vec3Length", (void *)&SC_vec3Length, - "float", "(struct vecF32_3_s *)" }, - { "vec3Add", (void *)&SC_vec3Add, - "void", "(struct vecF32_3_s *dest, struct vecF32_3_s *lhs, struct vecF32_3_s *rhs)" }, - { "vec3Sub", (void *)&SC_vec3Sub, - "void", "(struct vecF32_3_s *dest, struct vecF32_3_s *lhs, struct vecF32_3_s *rhs)" }, - { "vec3Cross", (void *)&SC_vec3Cross, - "void", "(struct vecF32_3_s *dest, struct vecF32_3_s *lhs, struct vecF32_3_s *rhs)" }, - { "vec3Dot", (void *)&SC_vec3Dot, - "float", "(struct vecF32_3_s *lhs, struct vecF32_3_s *rhs)" }, - { "vec3Scale", (void *)&SC_vec3Scale, - "void", "(struct vecF32_3_s *lhs, float scale)" }, - - // vec4 - { "vec4Norm", (void *)&SC_vec4Norm, - "void", "(struct vecF32_4_s *)" }, - { "vec4Length", (void *)&SC_vec4Length, - "float", "(struct vecF32_4_s *)" }, - { "vec4Add", (void *)&SC_vec4Add, - "void", "(struct vecF32_4_s *dest, struct vecF32_4_s *lhs, struct vecF32_4_s *rhs)" }, - { "vec4Sub", (void *)&SC_vec4Sub, - "void", "(struct vecF32_4_s *dest, struct vecF32_4_s *lhs, struct vecF32_4_s *rhs)" }, - { "vec4Dot", (void *)&SC_vec4Dot, - "float", "(struct vecF32_4_s *lhs, struct vecF32_4_s *rhs)" }, - { "vec4Scale", (void *)&SC_vec4Scale, - "void", "(struct vecF32_4_s *lhs, float scale)" }, - - // context - { "bindProgramFragment", (void *)&SC_bindProgramFragment, - "void", "(int)" }, - { "bindProgramFragmentStore", (void *)&SC_bindProgramFragmentStore, - "void", "(int)" }, - { "bindProgramStore", (void *)&SC_bindProgramFragmentStore, - "void", "(int)" }, - { "bindProgramVertex", (void *)&SC_bindProgramVertex, - "void", "(int)" }, - { "bindSampler", (void *)&SC_bindSampler, - "void", "(int, int, int)" }, - { "bindTexture", (void *)&SC_bindTexture, - "void", "(int, int, int)" }, - - // vp - { "vpLoadModelMatrix", (void *)&SC_vpLoadModelMatrix, - "void", "(void *)" }, - { "vpLoadTextureMatrix", (void *)&SC_vpLoadTextureMatrix, - "void", "(void *)" }, - - - - // drawing - { "drawRect", (void *)&SC_drawRect, - "void", "(float x1, float y1, float x2, float y2, float z)" }, - { "drawQuad", (void *)&SC_drawQuad, - "void", "(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4)" }, - { "drawQuadTexCoords", (void *)&SC_drawQuadTexCoords, - "void", "(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)" }, - { "drawSprite", (void *)&SC_drawSprite, - "void", "(float x, float y, float z, float w, float h)" }, - { "drawSpriteScreenspace", (void *)&SC_drawSpriteScreenspace, - "void", "(float x, float y, float z, float w, float h)" }, - { "drawSpriteScreenspaceCropped", (void *)&SC_drawSpriteScreenspaceCropped, - "void", "(float x, float y, float z, float w, float h, float cx0, float cy0, float cx1, float cy1)" }, - { "drawLine", (void *)&SC_drawLine, - "void", "(float x1, float y1, float z1, float x2, float y2, float z2)" }, - { "drawPoint", (void *)&SC_drawPoint, - "void", "(float x1, float y1, float z1)" }, - { "drawSimpleMesh", (void *)&SC_drawSimpleMesh, - "void", "(int ism)" }, - { "drawSimpleMeshRange", (void *)&SC_drawSimpleMeshRange, - "void", "(int ism, int start, int len)" }, - - - // misc - { "pfClearColor", (void *)&SC_ClearColor, - "void", "(float, float, float, float)" }, - { "color", (void *)&SC_color, - "void", "(float, float, float, float)" }, - { "hsb", (void *)&SC_hsb, - "void", "(float, float, float, float)" }, - { "hsbToRgb", (void *)&SC_hsbToRgb, - "void", "(float, float, float, float*)" }, - { "hsbToAbgr", (void *)&SC_hsbToAbgr, - "int", "(float, float, float, float)" }, - { "ambient", (void *)&SC_ambient, - "void", "(float, float, float, float)" }, - { "diffuse", (void *)&SC_diffuse, - "void", "(float, float, float, float)" }, - { "specular", (void *)&SC_specular, - "void", "(float, float, float, float)" }, - { "emission", (void *)&SC_emission, - "void", "(float, float, float, float)" }, - { "shininess", (void *)&SC_shininess, - "void", "(float)" }, - { "pointAttenuation", (void *)&SC_pointAttenuation, - "void", "(float, float, float)" }, - - { "uploadToTexture", (void *)&SC_uploadToTexture, - "void", "(int, int)" }, - { "uploadToBufferObject", (void *)&SC_uploadToBufferObject, - "void", "(int)" }, - - { "syncToGL", (void *)&SC_syncToGL, - "void", "(int)" }, - - { "colorFloatRGBAtoUNorm8", (void *)&SC_colorFloatRGBAtoUNorm8, - "int", "(float, float, float, float)" }, - { "colorFloatRGBto565", (void *)&SC_colorFloatRGBAto565, - "int", "(float, float, float)" }, - - - { "getWidth", (void *)&SC_getWidth, - "int", "()" }, - { "getHeight", (void *)&SC_getHeight, - "int", "()" }, - - { "sendToClient", (void *)&SC_toClient, - "int", "(void *data, int cmdID, int len, int waitForSpace)" }, - - - { "debugF", (void *)&SC_debugF, - "void", "(void *, float)" }, - { "debugI32", (void *)&SC_debugI32, - "void", "(void *, int)" }, - { "debugHexF", (void *)&SC_debugHexF, - "void", "(void *, float)" }, - { "debugHexI32", (void *)&SC_debugHexI32, - "void", "(void *, int)" }, - - { "scriptCall", (void *)&SC_scriptCall, - "void", "(int)" }, - - - { NULL, NULL, NULL, NULL } + { "_Z20rsMatrixLoadIdentityP12rs_matrix4x4", (void *)&SC_MatrixLoadIdentity_4x4, true }, + { "_Z20rsMatrixLoadIdentityP12rs_matrix3x3", (void *)&SC_MatrixLoadIdentity_3x3, true }, + { "_Z20rsMatrixLoadIdentityP12rs_matrix2x2", (void *)&SC_MatrixLoadIdentity_2x2, true }, + + { "_Z12rsMatrixLoadP12rs_matrix4x4PKf", (void *)&SC_MatrixLoad_4x4_f, true }, + { "_Z12rsMatrixLoadP12rs_matrix3x3PKf", (void *)&SC_MatrixLoad_3x3_f, true }, + { "_Z12rsMatrixLoadP12rs_matrix2x2PKf", (void *)&SC_MatrixLoad_2x2_f, true }, + + { "_Z12rsMatrixLoadP12rs_matrix4x4PKS_", (void *)&SC_MatrixLoad_4x4_4x4, true }, + { "_Z12rsMatrixLoadP12rs_matrix4x4PK12rs_matrix3x3", (void *)&SC_MatrixLoad_4x4_3x3, true }, + { "_Z12rsMatrixLoadP12rs_matrix4x4PK12rs_matrix2x2", (void *)&SC_MatrixLoad_4x4_2x2, true }, + { "_Z12rsMatrixLoadP12rs_matrix3x3PKS_", (void *)&SC_MatrixLoad_3x3_3x3, true }, + { "_Z12rsMatrixLoadP12rs_matrix2x2PKS_", (void *)&SC_MatrixLoad_2x2_2x2, true }, + + { "_Z18rsMatrixLoadRotateP12rs_matrix4x4ffff", (void *)&SC_MatrixLoadRotate, true }, + { "_Z17rsMatrixLoadScaleP12rs_matrix4x4fff", (void *)&SC_MatrixLoadScale, true }, + { "_Z21rsMatrixLoadTranslateP12rs_matrix4x4fff", (void *)&SC_MatrixLoadTranslate, true }, + { "_Z14rsMatrixRotateP12rs_matrix4x4ffff", (void *)&SC_MatrixRotate, true }, + { "_Z13rsMatrixScaleP12rs_matrix4x4fff", (void *)&SC_MatrixScale, true }, + { "_Z17rsMatrixTranslateP12rs_matrix4x4fff", (void *)&SC_MatrixTranslate, true }, + + { "_Z20rsMatrixLoadMultiplyP12rs_matrix4x4PKS_S2_", (void *)&SC_MatrixLoadMultiply_4x4_4x4_4x4, true }, + { "_Z16rsMatrixMultiplyP12rs_matrix4x4PKS_", (void *)&SC_MatrixMultiply_4x4_4x4, true }, + { "_Z20rsMatrixLoadMultiplyP12rs_matrix3x3PKS_S2_", (void *)&SC_MatrixLoadMultiply_3x3_3x3_3x3, true }, + { "_Z16rsMatrixMultiplyP12rs_matrix3x3PKS_", (void *)&SC_MatrixMultiply_3x3_3x3, true }, + { "_Z20rsMatrixLoadMultiplyP12rs_matrix2x2PKS_S2_", (void *)&SC_MatrixLoadMultiply_2x2_2x2_2x2, true }, + { "_Z16rsMatrixMultiplyP12rs_matrix2x2PKS_", (void *)&SC_MatrixMultiply_2x2_2x2, true }, + + { "_Z17rsMatrixLoadOrthoP12rs_matrix4x4ffffff", (void *)&SC_MatrixLoadOrtho, true }, + { "_Z19rsMatrixLoadFrustumP12rs_matrix4x4ffffff", (void *)&SC_MatrixLoadFrustum, true }, + { "_Z23rsMatrixLoadPerspectiveP12rs_matrix4x4ffff", (void *)&SC_MatrixLoadPerspective, true }, + + { "_Z15rsMatrixInverseP12rs_matrix4x4", (void *)&SC_MatrixInverse_4x4, true }, + { "_Z24rsMatrixInverseTransposeP12rs_matrix4x4", (void *)&SC_MatrixInverseTranspose_4x4, true }, + { "_Z17rsMatrixTransposeP12rs_matrix4x4", (void *)&SC_MatrixTranspose_4x4, true }, + { "_Z17rsMatrixTransposeP12rs_matrix4x4", (void *)&SC_MatrixTranspose_3x3, true }, + { "_Z17rsMatrixTransposeP12rs_matrix4x4", (void *)&SC_MatrixTranspose_2x2, true }, + + { "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach, false }, + //{ "_Z9rsForEach9rs_script13rs_allocationS0_PKv", (void *)&SC_ForEach2, true }, + +//////////////////////////////////////////////////////////////////// + + //{ "sinf_fast", (void *)&SC_sinf_fast, true }, + //{ "cosf_fast", (void *)&SC_cosf_fast, true }, + + { NULL, NULL, false } }; -const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym) -{ +const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym) { ScriptCState::SymbolTable_t *syms = gSyms; while (syms->mPtr) { @@ -1399,18 +1021,3 @@ const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym) } return NULL; } - -void ScriptCState::appendDecls(String8 *str) -{ - ScriptCState::SymbolTable_t *syms = gSyms; - while (syms->mPtr) { - str->append(syms->mRet); - str->append(" "); - str->append(syms->mName); - str->append(syms->mParam); - str->append(";\n"); - syms++; - } -} - - diff --git a/libs/rs/rsScriptC_LibCL.cpp b/libs/rs/rsScriptC_LibCL.cpp new file mode 100644 index 000000000000..8a0aa4725618 --- /dev/null +++ b/libs/rs/rsScriptC_LibCL.cpp @@ -0,0 +1,266 @@ +/* + * 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. + */ + +#include "rsContext.h" +#include "rsScriptC.h" + +// Implements rs_cl.rsh + + +using namespace android; +using namespace android::renderscript; + + +static float SC_exp10(float v) { + return pow(10.f, v); +} + +static float SC_fract(float v, int *iptr) { + int i = (int)floor(v); + iptr[0] = i; + return fmin(v - i, 0x1.fffffep-1f); +} + +static float SC_log2(float v) { + return log10(v) / log10(2.f); +} + +static float SC_mad(float v1, float v2, float v3) { + return v1 * v2 + v3; +} + +#if 0 +static float SC_pown(float v, int p) { + return powf(v, (float)p); +} + +static float SC_powr(float v, float p) { + return powf(v, p); +} +#endif + +float SC_rootn(float v, int r) { + return pow(v, 1.f / r); +} + +float SC_rsqrt(float v) { + return 1.f / sqrtf(v); +} + +float SC_sincos(float v, float *cosptr) { + *cosptr = cosf(v); + return sinf(v); +} + +////////////////////////////////////////////////////////////////////////////// +// Integer +////////////////////////////////////////////////////////////////////////////// + + +static uint32_t SC_abs_i32(int32_t v) {return abs(v);} +static uint16_t SC_abs_i16(int16_t v) {return (uint16_t)abs(v);} +static uint8_t SC_abs_i8(int8_t v) {return (uint8_t)abs(v);} + +static uint32_t SC_clz_u32(uint32_t v) {return __builtin_clz(v);} +static uint16_t SC_clz_u16(uint16_t v) {return (uint16_t)__builtin_clz(v);} +static uint8_t SC_clz_u8(uint8_t v) {return (uint8_t)__builtin_clz(v);} +static int32_t SC_clz_i32(int32_t v) {return (int32_t)__builtin_clz((uint32_t)v);} +static int16_t SC_clz_i16(int16_t v) {return (int16_t)__builtin_clz(v);} +static int8_t SC_clz_i8(int8_t v) {return (int8_t)__builtin_clz(v);} + +static uint32_t SC_max_u32(uint32_t v, uint32_t v2) {return rsMax(v, v2);} +static uint16_t SC_max_u16(uint16_t v, uint16_t v2) {return rsMax(v, v2);} +static uint8_t SC_max_u8(uint8_t v, uint8_t v2) {return rsMax(v, v2);} +static int32_t SC_max_i32(int32_t v, int32_t v2) {return rsMax(v, v2);} +static int16_t SC_max_i16(int16_t v, int16_t v2) {return rsMax(v, v2);} +static int8_t SC_max_i8(int8_t v, int8_t v2) {return rsMax(v, v2);} + +static uint32_t SC_min_u32(uint32_t v, uint32_t v2) {return rsMin(v, v2);} +static uint16_t SC_min_u16(uint16_t v, uint16_t v2) {return rsMin(v, v2);} +static uint8_t SC_min_u8(uint8_t v, uint8_t v2) {return rsMin(v, v2);} +static int32_t SC_min_i32(int32_t v, int32_t v2) {return rsMin(v, v2);} +static int16_t SC_min_i16(int16_t v, int16_t v2) {return rsMin(v, v2);} +static int8_t SC_min_i8(int8_t v, int8_t v2) {return rsMin(v, v2);} + +////////////////////////////////////////////////////////////////////////////// +// Float util +////////////////////////////////////////////////////////////////////////////// + +static float SC_clamp_f32(float amount, float low, float high) { + return amount < low ? low : (amount > high ? high : amount); +} + +static float SC_degrees(float radians) { + return radians * (180.f / M_PI); +} + +static float SC_max_f32(float v, float v2) { + return rsMax(v, v2); +} + +static float SC_min_f32(float v, float v2) { + return rsMin(v, v2); +} + +static float SC_mix_f32(float start, float stop, float amount) { + //LOGE("lerpf %f %f %f", start, stop, amount); + return start + (stop - start) * amount; +} + +static float SC_radians(float degrees) { + return degrees * (M_PI / 180.f); +} + +static float SC_step_f32(float edge, float v) { + if (v < edge) return 0.f; + return 1.f; +} + +static float SC_sign_f32(float value) { + if (value > 0) return 1.f; + if (value < 0) return -1.f; + return value; +} + +////////////////////////////////////////////////////////////////////////////// +// Class implementation +////////////////////////////////////////////////////////////////////////////// + +// llvm name mangling ref +// <builtin-type> ::= v # void +// ::= b # bool +// ::= c # char +// ::= a # signed char +// ::= h # unsigned char +// ::= s # short +// ::= t # unsigned short +// ::= i # int +// ::= j # unsigned int +// ::= l # long +// ::= m # unsigned long +// ::= x # long long, __int64 +// ::= y # unsigned long long, __int64 +// ::= f # float +// ::= d # double + +static ScriptCState::SymbolTable_t gSyms[] = { + // OpenCL math + { "_Z4acosf", (void *)&acosf, true }, + { "_Z5acoshf", (void *)&acoshf, true }, + { "_Z4asinf", (void *)&asinf, true }, + { "_Z5asinhf", (void *)&asinhf, true }, + { "_Z4atanf", (void *)&atanf, true }, + { "_Z5atan2ff", (void *)&atan2f, true }, + { "_Z5atanhf", (void *)&atanhf, true }, + { "_Z4cbrtf", (void *)&cbrtf, true }, + { "_Z4ceilf", (void *)&ceilf, true }, + { "_Z8copysignff", (void *)©signf, true }, + { "_Z3cosf", (void *)&cosf, true }, + { "_Z4coshf", (void *)&coshf, true }, + { "_Z4erfcf", (void *)&erfcf, true }, + { "_Z3erff", (void *)&erff, true }, + { "_Z3expf", (void *)&expf, true }, + { "_Z4exp2f", (void *)&exp2f, true }, + { "_Z5exp10f", (void *)&SC_exp10, true }, + { "_Z5expm1f", (void *)&expm1f, true }, + { "_Z4fabsf", (void *)&fabsf, true }, + { "_Z4fdimff", (void *)&fdimf, true }, + { "_Z5floorf", (void *)&floorf, true }, + { "_Z3fmafff", (void *)&fmaf, true }, + { "_Z4fmaxff", (void *)&fmaxf, true }, + { "_Z4fminff", (void *)&fminf, true }, // float fmin(float, float) + { "_Z4fmodff", (void *)&fmodf, true }, + { "_Z5fractfPf", (void *)&SC_fract, true }, + { "_Z5frexpfPi", (void *)&frexpf, true }, + { "_Z5hypotff", (void *)&hypotf, true }, + { "_Z5ilogbf", (void *)&ilogbf, true }, + { "_Z5ldexpfi", (void *)&ldexpf, true }, + { "_Z6lgammaf", (void *)&lgammaf, true }, + { "_Z6lgammafPi", (void *)&lgammaf_r, true }, + { "_Z3logf", (void *)&logf, true }, + { "_Z4log2f", (void *)&SC_log2, true }, + { "_Z5log10f", (void *)&log10f, true }, + { "_Z5log1pf", (void *)&log1pf, true }, + { "_Z4logbf", (void *)&logbf, true }, + { "_Z3madfff", (void *)&SC_mad, true }, + { "_Z4modffPf", (void *)&modff, true }, + //{ "_Z3nanj", (void *)&SC_nan, true }, + { "_Z9nextafterff", (void *)&nextafterf, true }, + { "_Z3powff", (void *)&powf, true }, + { "_Z9remainderff", (void *)&remainderf, true }, + { "_Z6remquoffPi", (void *)&remquof, true }, + { "_Z4rintf", (void *)&rintf, true }, + { "_Z5rootnfi", (void *)&SC_rootn, true }, + { "_Z5roundf", (void *)&roundf, true }, + { "_Z5rsqrtf", (void *)&SC_rsqrt, true }, + { "_Z3sinf", (void *)&sinf, true }, + { "_Z6sincosfPf", (void *)&SC_sincos, true }, + { "_Z4sinhf", (void *)&sinhf, true }, + { "_Z4sqrtf", (void *)&sqrtf, true }, + { "_Z3tanf", (void *)&tanf, true }, + { "_Z4tanhf", (void *)&tanhf, true }, + { "_Z6tgammaf", (void *)&tgammaf, true }, + { "_Z5truncf", (void *)&truncf, true }, + + // OpenCL Int + { "_Z3absi", (void *)&SC_abs_i32, true }, + { "_Z3abss", (void *)&SC_abs_i16, true }, + { "_Z3absc", (void *)&SC_abs_i8, true }, + { "_Z3clzj", (void *)&SC_clz_u32, true }, + { "_Z3clzt", (void *)&SC_clz_u16, true }, + { "_Z3clzh", (void *)&SC_clz_u8, true }, + { "_Z3clzi", (void *)&SC_clz_i32, true }, + { "_Z3clzs", (void *)&SC_clz_i16, true }, + { "_Z3clzc", (void *)&SC_clz_i8, true }, + { "_Z3maxjj", (void *)&SC_max_u32, true }, + { "_Z3maxtt", (void *)&SC_max_u16, true }, + { "_Z3maxhh", (void *)&SC_max_u8, true }, + { "_Z3maxii", (void *)&SC_max_i32, true }, + { "_Z3maxss", (void *)&SC_max_i16, true }, + { "_Z3maxcc", (void *)&SC_max_i8, true }, + { "_Z3minjj", (void *)&SC_min_u32, true }, + { "_Z3mintt", (void *)&SC_min_u16, true }, + { "_Z3minhh", (void *)&SC_min_u8, true }, + { "_Z3minii", (void *)&SC_min_i32, true }, + { "_Z3minss", (void *)&SC_min_i16, true }, + { "_Z3mincc", (void *)&SC_min_i8, true }, + + // OpenCL 6.11.4 + { "_Z5clampfff", (void *)&SC_clamp_f32, true }, + { "_Z7degreesf", (void *)&SC_degrees, true }, + { "_Z3maxff", (void *)&SC_max_f32, true }, + { "_Z3minff", (void *)&SC_min_f32, true }, + { "_Z3mixfff", (void *)&SC_mix_f32, true }, + { "_Z7radiansf", (void *)&SC_radians, true }, + { "_Z4stepff", (void *)&SC_step_f32, true }, + //{ "smoothstep", (void *)&, true }, + { "_Z4signf", (void *)&SC_sign_f32, true }, + + { NULL, NULL, false } +}; + +const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbolCL(const char *sym) { + ScriptCState::SymbolTable_t *syms = gSyms; + + while (syms->mPtr) { + if (!strcmp(syms->mName, sym)) { + return syms; + } + syms++; + } + return NULL; +} + diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp new file mode 100644 index 000000000000..15426bc33f9f --- /dev/null +++ b/libs/rs/rsScriptC_LibGL.cpp @@ -0,0 +1,464 @@ +/* + * 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. + */ + +#include "rsContext.h" +#include "rsScriptC.h" +#include "rsMatrix.h" + +#include "utils/Timers.h" + +#define GL_GLEXT_PROTOTYPES + +#include <GLES/gl.h> +#include <GLES/glext.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <time.h> + +using namespace android; +using namespace android::renderscript; + +#define GET_TLS() Context::ScriptTLSStruct * tls = \ + (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \ + Context * rsc = tls->mContext; \ + ScriptC * sc = (ScriptC *) tls->mScript + + +////////////////////////////////////////////////////////////////////////////// +// Context +////////////////////////////////////////////////////////////////////////////// + +static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va) { + CHECK_OBJ_OR_NULL(va); + CHECK_OBJ(vpf); + GET_TLS(); + rsi_ProgramBindTexture(rsc, + static_cast<ProgramFragment *>(vpf), + slot, + static_cast<Allocation *>(va)); +} + +static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs) { + CHECK_OBJ_OR_NULL(vs); + CHECK_OBJ(vpf); + GET_TLS(); + rsi_ProgramBindSampler(rsc, + static_cast<ProgramFragment *>(vpf), + slot, + static_cast<Sampler *>(vs)); +} + +static void SC_bindProgramStore(RsProgramStore pfs) { + CHECK_OBJ_OR_NULL(pfs); + GET_TLS(); + rsi_ContextBindProgramStore(rsc, pfs); +} + +static void SC_bindProgramFragment(RsProgramFragment pf) { + CHECK_OBJ_OR_NULL(pf); + GET_TLS(); + rsi_ContextBindProgramFragment(rsc, pf); +} + +static void SC_bindProgramVertex(RsProgramVertex pv) { + CHECK_OBJ_OR_NULL(pv); + GET_TLS(); + rsi_ContextBindProgramVertex(rsc, pv); +} + +static void SC_bindProgramRaster(RsProgramRaster pv) { + CHECK_OBJ_OR_NULL(pv); + GET_TLS(); + rsi_ContextBindProgramRaster(rsc, pv); +} + +////////////////////////////////////////////////////////////////////////////// +// VP +////////////////////////////////////////////////////////////////////////////// + +static void SC_vpLoadProjectionMatrix(const rsc_Matrix *m) { + GET_TLS(); + rsc->getProgramVertex()->setProjectionMatrix(rsc, m); +} + +static void SC_vpLoadModelMatrix(const rsc_Matrix *m) { + GET_TLS(); + rsc->getProgramVertex()->setModelviewMatrix(rsc, m); +} + +static void SC_vpLoadTextureMatrix(const rsc_Matrix *m) { + GET_TLS(); + rsc->getProgramVertex()->setTextureMatrix(rsc, m); +} + +static void SC_pfConstantColor(RsProgramFragment vpf, float r, float g, float b, float a) { + GET_TLS(); + CHECK_OBJ(vpf); + ProgramFragment *pf = static_cast<ProgramFragment *>(vpf); + pf->setConstantColor(rsc, r, g, b, a); +} + +static void SC_vpGetProjectionMatrix(rsc_Matrix *m) { + GET_TLS(); + rsc->getProgramVertex()->getProjectionMatrix(rsc, m); +} + +////////////////////////////////////////////////////////////////////////////// +// Drawing +////////////////////////////////////////////////////////////////////////////// + +static void SC_drawQuadTexCoords(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) { + GET_TLS(); + if (!rsc->setupCheck()) { + return; + } + + //LOGE("Quad"); + //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1); + //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2); + //LOGE("%4.2f, %4.2f, %4.2f", x3, y3, z3); + //LOGE("%4.2f, %4.2f, %4.2f", x4, y4, z4); + + float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4}; + const float tex[] = {u1,v1, u2,v2, u3,v3, u4,v4}; + + VertexArray::Attrib attribs[2]; + attribs[0].set(GL_FLOAT, 3, 12, false, (uint32_t)vtx, "ATTRIB_position"); + attribs[1].set(GL_FLOAT, 2, 8, false, (uint32_t)tex, "ATTRIB_texture0"); + + VertexArray va(attribs, 2); + va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + +static void SC_drawQuad(float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) { + SC_drawQuadTexCoords(x1, y1, z1, 0, 1, + x2, y2, z2, 1, 1, + x3, y3, z3, 1, 0, + x4, y4, z4, 0, 0); +} + +static void SC_drawSpriteScreenspace(float x, float y, float z, float w, float h) { + GET_TLS(); + ObjectBaseRef<const ProgramVertex> tmp(rsc->getProgramVertex()); + rsc->setProgramVertex(rsc->getDefaultProgramVertex()); + //rsc->setupCheck(); + + //GLint crop[4] = {0, h, w, -h}; + + float sh = rsc->getHeight(); + + SC_drawQuad(x, sh - y, z, + x+w, sh - y, z, + x+w, sh - (y+h), z, + x, sh - (y+h), z); + rsc->setProgramVertex((ProgramVertex *)tmp.get()); +} +/* +static void SC_drawSprite(float x, float y, float z, float w, float h) +{ + GET_TLS(); + float vin[3] = {x, y, z}; + float vout[4]; + + //LOGE("ds in %f %f %f", x, y, z); + rsc->getVertex()->transformToScreen(rsc, vout, vin); + //LOGE("ds out %f %f %f %f", vout[0], vout[1], vout[2], vout[3]); + vout[0] /= vout[3]; + vout[1] /= vout[3]; + vout[2] /= vout[3]; + + vout[0] *= rsc->getWidth() / 2; + vout[1] *= rsc->getHeight() / 2; + vout[0] += rsc->getWidth() / 2; + vout[1] += rsc->getHeight() / 2; + + vout[0] -= w/2; + vout[1] -= h/2; + + //LOGE("ds out2 %f %f %f", vout[0], vout[1], vout[2]); + + // U, V, W, H + SC_drawSpriteScreenspace(vout[0], vout[1], z, h, w); + //rsc->setupCheck(); +} +*/ + +static void SC_drawRect(float x1, float y1, + float x2, float y2, float z) { + //LOGE("SC_drawRect %f,%f %f,%f %f", x1, y1, x2, y2, z); + SC_drawQuad(x1, y2, z, + x2, y2, z, + x2, y1, z, + x1, y1, z); +} + +static void SC_drawMesh(RsMesh vsm) { + CHECK_OBJ(vsm); + GET_TLS(); + Mesh *sm = static_cast<Mesh *>(vsm); + if (!rsc->setupCheck()) { + return; + } + sm->render(rsc); +} + +static void SC_drawMeshPrimitive(RsMesh vsm, uint32_t primIndex) { + CHECK_OBJ(vsm); + GET_TLS(); + Mesh *sm = static_cast<Mesh *>(vsm); + if (!rsc->setupCheck()) { + return; + } + sm->renderPrimitive(rsc, primIndex); +} + +static void SC_drawMeshPrimitiveRange(RsMesh vsm, uint32_t primIndex, uint32_t start, uint32_t len) { + CHECK_OBJ(vsm); + GET_TLS(); + Mesh *sm = static_cast<Mesh *>(vsm); + if (!rsc->setupCheck()) { + return; + } + sm->renderPrimitiveRange(rsc, primIndex, start, len); +} + +static void SC_meshComputeBoundingBox(RsMesh vsm, + float *minX, float *minY, float *minZ, + float *maxX, float *maxY, float *maxZ) { + CHECK_OBJ(vsm); + GET_TLS(); + Mesh *sm = static_cast<Mesh *>(vsm); + sm->computeBBox(); + *minX = sm->mBBoxMin[0]; + *minY = sm->mBBoxMin[1]; + *minZ = sm->mBBoxMin[2]; + *maxX = sm->mBBoxMax[0]; + *maxY = sm->mBBoxMax[1]; + *maxZ = sm->mBBoxMax[2]; +} + + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + + +static void SC_color(float r, float g, float b, float a) { + GET_TLS(); + ProgramFragment *pf = (ProgramFragment *)rsc->getProgramFragment(); + pf->setConstantColor(rsc, r, g, b, a); +} + +static void SC_allocationSyncAll(RsAllocation va) { + CHECK_OBJ(va); + GET_TLS(); + static_cast<Allocation *>(va)->syncAll(rsc, RS_ALLOCATION_USAGE_SCRIPT); +} + +#if 0 +static void SC_allocationSyncAll2(RsAllocation va, RsAllocationUsageType source) { + CHECK_OBJ(va); + GET_TLS(); + static_cast<Allocation *>(va)->syncAll(rsc, source); +} +#endif + +static void SC_ClearColor(float r, float g, float b, float a) { + GET_TLS(); + rsc->setupProgramStore(); + + glClearColor(r, g, b, a); + glClear(GL_COLOR_BUFFER_BIT); +} + +static void SC_ClearDepth(float v) { + GET_TLS(); + rsc->setupProgramStore(); + + glClearDepthf(v); + glClear(GL_DEPTH_BUFFER_BIT); +} + +static uint32_t SC_getWidth() { + GET_TLS(); + return rsc->getWidth(); +} + +static uint32_t SC_getHeight() { + GET_TLS(); + return rsc->getHeight(); +} + +static void SC_DrawTextAlloc(RsAllocation va, int x, int y) { + CHECK_OBJ(va); + GET_TLS(); + Allocation *alloc = static_cast<Allocation *>(va); + const char *text = (const char *)alloc->getPtr(); + size_t allocSize = alloc->getType()->getSizeBytes(); + rsc->mStateFont.renderText(text, allocSize, x, y); +} + +static void SC_DrawText(const char *text, int x, int y) { + GET_TLS(); + size_t textLen = strlen(text); + rsc->mStateFont.renderText(text, textLen, x, y); +} + +static void SC_setMetrics(Font::Rect *metrics, + int32_t *left, int32_t *right, + int32_t *top, int32_t *bottom) { + if (left) { + *left = metrics->left; + } + if (right) { + *right = metrics->right; + } + if (top) { + *top = metrics->top; + } + if (bottom) { + *bottom = metrics->bottom; + } +} + +static void SC_MeasureTextAlloc(RsAllocation va, + int32_t *left, int32_t *right, + int32_t *top, int32_t *bottom) { + CHECK_OBJ(va); + GET_TLS(); + Allocation *alloc = static_cast<Allocation *>(va); + const char *text = (const char *)alloc->getPtr(); + size_t textLen = alloc->getType()->getSizeBytes(); + Font::Rect metrics; + rsc->mStateFont.measureText(text, textLen, &metrics); + SC_setMetrics(&metrics, left, right, top, bottom); +} + +static void SC_MeasureText(const char *text, + int32_t *left, int32_t *right, + int32_t *top, int32_t *bottom) { + GET_TLS(); + size_t textLen = strlen(text); + Font::Rect metrics; + rsc->mStateFont.measureText(text, textLen, &metrics); + SC_setMetrics(&metrics, left, right, top, bottom); +} + +static void SC_BindFont(RsFont font) { + CHECK_OBJ(font); + GET_TLS(); + rsi_ContextBindFont(rsc, font); +} + +static void SC_FontColor(float r, float g, float b, float a) { + GET_TLS(); + rsc->mStateFont.setFontColor(r, g, b, a); +} + +////////////////////////////////////////////////////////////////////////////// +// Class implementation +////////////////////////////////////////////////////////////////////////////// + +// llvm name mangling ref +// <builtin-type> ::= v # void +// ::= b # bool +// ::= c # char +// ::= a # signed char +// ::= h # unsigned char +// ::= s # short +// ::= t # unsigned short +// ::= i # int +// ::= j # unsigned int +// ::= l # long +// ::= m # unsigned long +// ::= x # long long, __int64 +// ::= y # unsigned long long, __int64 +// ::= f # float +// ::= d # double + +static ScriptCState::SymbolTable_t gSyms[] = { + { "_Z22rsgBindProgramFragment19rs_program_fragment", (void *)&SC_bindProgramFragment, false }, + { "_Z19rsgBindProgramStore16rs_program_store", (void *)&SC_bindProgramStore, false }, + { "_Z20rsgBindProgramVertex17rs_program_vertex", (void *)&SC_bindProgramVertex, false }, + { "_Z20rsgBindProgramRaster17rs_program_raster", (void *)&SC_bindProgramRaster, false }, + { "_Z14rsgBindSampler19rs_program_fragmentj10rs_sampler", (void *)&SC_bindSampler, false }, + { "_Z14rsgBindTexture19rs_program_fragmentj13rs_allocation", (void *)&SC_bindTexture, false }, + + { "_Z36rsgProgramVertexLoadProjectionMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadProjectionMatrix, false }, + { "_Z31rsgProgramVertexLoadModelMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadModelMatrix, false }, + { "_Z33rsgProgramVertexLoadTextureMatrixPK12rs_matrix4x4", (void *)&SC_vpLoadTextureMatrix, false }, + + { "_Z35rsgProgramVertexGetProjectionMatrixP12rs_matrix4x4", (void *)&SC_vpGetProjectionMatrix, false }, + + { "_Z31rsgProgramFragmentConstantColor19rs_program_fragmentffff", (void *)&SC_pfConstantColor, false }, + + { "_Z11rsgGetWidthv", (void *)&SC_getWidth, false }, + { "_Z12rsgGetHeightv", (void *)&SC_getHeight, false }, + + { "_Z20rsgAllocationSyncAll13rs_allocation", (void *)&SC_allocationSyncAll, false }, + + { "_Z11rsgDrawRectfffff", (void *)&SC_drawRect, false }, + { "_Z11rsgDrawQuadffffffffffff", (void *)&SC_drawQuad, false }, + { "_Z20rsgDrawQuadTexCoordsffffffffffffffffffff", (void *)&SC_drawQuadTexCoords, false }, + { "_Z24rsgDrawSpriteScreenspacefffff", (void *)&SC_drawSpriteScreenspace, false }, + + { "_Z11rsgDrawMesh7rs_mesh", (void *)&SC_drawMesh, false }, + { "_Z11rsgDrawMesh7rs_meshj", (void *)&SC_drawMeshPrimitive, false }, + { "_Z11rsgDrawMesh7rs_meshjjj", (void *)&SC_drawMeshPrimitiveRange, false }, + { "_Z25rsgMeshComputeBoundingBox7rs_meshPfS0_S0_S0_S0_S0_", (void *)&SC_meshComputeBoundingBox, false }, + + { "_Z13rsgClearColorffff", (void *)&SC_ClearColor, false }, + { "_Z13rsgClearDepthf", (void *)&SC_ClearDepth, false }, + + { "_Z11rsgDrawTextPKcii", (void *)&SC_DrawText, false }, + { "_Z11rsgDrawText13rs_allocationii", (void *)&SC_DrawTextAlloc, false }, + { "_Z14rsgMeasureTextPKcPiS1_S1_S1_", (void *)&SC_MeasureText, false }, + { "_Z14rsgMeasureText13rs_allocationPiS0_S0_S0_", (void *)&SC_MeasureTextAlloc, false }, + + { "_Z11rsgBindFont7rs_font", (void *)&SC_BindFont, false }, + { "_Z12rsgFontColorffff", (void *)&SC_FontColor, false }, + + // misc + { "_Z5colorffff", (void *)&SC_color, false }, + + { NULL, NULL, false } +}; + +const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbolGL(const char *sym) { + ScriptCState::SymbolTable_t *syms = gSyms; + + while (syms->mPtr) { + if (!strcmp(syms->mName, sym)) { + return syms; + } + syms++; + } + return NULL; +} + diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp index 4711d1b1ebf8..e8d89c21cb77 100644 --- a/libs/rs/rsShaderCache.cpp +++ b/libs/rs/rsShaderCache.cpp @@ -15,95 +15,116 @@ */ #include "rsContext.h" - +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES2/gl2.h> +#endif //ANDROID_RS_SERIALIZE using namespace android; using namespace android::renderscript; -ShaderCache::ShaderCache() -{ - mEntryCount = 0; - mEntryAllocationCount = 16; - mEntries = (entry_t *)calloc(mEntryAllocationCount, sizeof(entry_t)); +ShaderCache::ShaderCache() { + mEntries.setCapacity(16); +} + +ShaderCache::~ShaderCache() { + cleanupAll(); } -ShaderCache::~ShaderCache() -{ - for (uint32_t ct=0; ct < mEntryCount; ct++) { - glDeleteProgram(mEntries[ct].program); +void ShaderCache::updateUniformArrayData(Context *rsc, Program *prog, uint32_t linkedID, + UniformData *data, const char* logTag, + UniformQueryData **uniformList, uint32_t uniListSize) { + + for (uint32_t ct=0; ct < prog->getUniformCount(); ct++) { + if (data[ct].slot >= 0 && data[ct].arraySize > 1) { + //Iterate over the list of active GL uniforms and find highest array index + for (uint32_t ui = 0; ui < uniListSize; ui ++) { + if (prog->getUniformName(ct) == uniformList[ui]->name) { + data[ct].arraySize = (uint32_t)uniformList[ui]->arraySize; + break; + } + } + } + + if (rsc->props.mLogShaders) { + LOGV("%s U, %s = %d, arraySize = %d\n", logTag, + prog->getUniformName(ct).string(), data[ct].slot, data[ct].arraySize); + } } +} - mEntryCount = 0; - mEntryAllocationCount = 0; - free(mEntries); +void ShaderCache::populateUniformData(Program *prog, uint32_t linkedID, UniformData *data) { + for (uint32_t ct=0; ct < prog->getUniformCount(); ct++) { + data[ct].slot = glGetUniformLocation(linkedID, prog->getUniformName(ct)); + data[ct].arraySize = prog->getUniformArraySize(ct); + } } -bool ShaderCache::lookup(Context *rsc, ProgramVertex *vtx, ProgramFragment *frag) -{ +bool ShaderCache::hasArrayUniforms(ProgramVertex *vtx, ProgramFragment *frag) { + UniformData *data = mCurrent->vtxUniforms; + for (uint32_t ct=0; ct < vtx->getUniformCount(); ct++) { + if (data[ct].slot >= 0 && data[ct].arraySize > 1) { + return true; + } + } + data = mCurrent->fragUniforms; + for (uint32_t ct=0; ct < frag->getUniformCount(); ct++) { + if (data[ct].slot >= 0 && data[ct].arraySize > 1) { + return true; + } + } + return false; +} + +bool ShaderCache::lookup(Context *rsc, ProgramVertex *vtx, ProgramFragment *frag) { if (!vtx->getShaderID()) { vtx->loadShader(rsc); } if (!frag->getShaderID()) { frag->loadShader(rsc); } - //LOGV("ShaderCache lookup vtx %i, frag %i", vtx->getShaderID(), frag->getShaderID()); - - for (uint32_t ct=0; ct < mEntryCount; ct++) { - if ((mEntries[ct].vtx == vtx->getShaderID()) && - (mEntries[ct].frag == frag->getShaderID())) { - //LOGV("SC using program %i", mEntries[ct].program); - glUseProgram(mEntries[ct].program); - mCurrent = &mEntries[ct]; + // Don't try to cache if shaders failed to load + if (!vtx->getShaderID() || !frag->getShaderID()) { + return false; + } + //LOGV("ShaderCache lookup vtx %i, frag %i", vtx->getShaderID(), frag->getShaderID()); + uint32_t entryCount = mEntries.size(); + for (uint32_t ct = 0; ct < entryCount; ct ++) { + if ((mEntries[ct]->vtx == vtx->getShaderID()) && + (mEntries[ct]->frag == frag->getShaderID())) { + + //LOGV("SC using program %i", mEntries[ct]->program); + glUseProgram(mEntries[ct]->program); + mCurrent = mEntries[ct]; //LOGV("ShaderCache hit, using %i", ct); rsc->checkError("ShaderCache::lookup (hit)"); return true; } } - // Not in cache, add it. - - if (mEntryAllocationCount == mEntryCount) { - // Out of space, make some. - mEntryAllocationCount *= 2; - entry_t *e = (entry_t *)calloc(mEntryAllocationCount, sizeof(entry_t)); - if (!e) { - LOGE("Out of memory for ShaderCache::lookup"); - return false; - } - memcpy(e, mEntries, sizeof(entry_t) * mEntryCount); - free(mEntries); - mEntries = e; - } - //LOGV("ShaderCache miss, using %i", mEntryCount); + //LOGV("ShaderCache miss"); //LOGE("e0 %x", glGetError()); - - entry_t *e = &mEntries[mEntryCount]; + ProgramEntry *e = new ProgramEntry(vtx->getAttribCount(), + vtx->getUniformCount(), + frag->getUniformCount()); + mEntries.push(e); mCurrent = e; e->vtx = vtx->getShaderID(); e->frag = frag->getShaderID(); e->program = glCreateProgram(); - e->mUserVertexProgram = vtx->isUserProgram(); - if (mEntries[mEntryCount].program) { + if (e->program) { GLuint pgm = e->program; glAttachShader(pgm, vtx->getShaderID()); //LOGE("e1 %x", glGetError()); glAttachShader(pgm, frag->getShaderID()); if (!vtx->isUserProgram()) { - glBindAttribLocation(pgm, 0, "ATTRIB_LegacyPosition"); - glBindAttribLocation(pgm, 1, "ATTRIB_LegacyColor"); - glBindAttribLocation(pgm, 2, "ATTRIB_LegacyNormal"); - glBindAttribLocation(pgm, 3, "ATTRIB_LegacyPointSize"); - glBindAttribLocation(pgm, 4, "ATTRIB_LegacyTexture"); - e->mVtxAttribSlots[RS_KIND_POSITION] = 0; - e->mVtxAttribSlots[RS_KIND_COLOR] = 1; - e->mVtxAttribSlots[RS_KIND_NORMAL] = 2; - e->mVtxAttribSlots[RS_KIND_POINT_SIZE] = 3; - e->mVtxAttribSlots[RS_KIND_TEXTURE] = 4; + glBindAttribLocation(pgm, 0, "ATTRIB_position"); + glBindAttribLocation(pgm, 1, "ATTRIB_color"); + glBindAttribLocation(pgm, 2, "ATTRIB_normal"); + glBindAttribLocation(pgm, 3, "ATTRIB_texture0"); } //LOGE("e2 %x", glGetError()); @@ -123,48 +144,111 @@ bool ShaderCache::lookup(Context *rsc, ProgramVertex *vtx, ProgramFragment *frag } } glDeleteProgram(pgm); - rsc->setError(RS_ERROR_BAD_SHADER, "Error linking GL Programs"); + rsc->setError(RS_ERROR_FATAL_PROGRAM_LINK, "Error linking GL Programs"); return false; } - if (vtx->isUserProgram()) { - for (uint32_t ct=0; ct < vtx->getAttribCount(); ct++) { - e->mVtxAttribSlots[ct] = glGetAttribLocation(pgm, vtx->getAttribName(ct)); - if (rsc->props.mLogShaders) { - LOGV("vtx A %i, %s = %d\n", ct, vtx->getAttribName(ct).string(), e->mVtxAttribSlots[ct]); - } + + for (uint32_t ct=0; ct < e->vtxAttrCount; ct++) { + e->vtxAttrs[ct].slot = glGetAttribLocation(pgm, vtx->getAttribName(ct)); + e->vtxAttrs[ct].name = vtx->getAttribName(ct).string(); + if (rsc->props.mLogShaders) { + LOGV("vtx A %i, %s = %d\n", ct, vtx->getAttribName(ct).string(), e->vtxAttrs[ct].slot); } } - for (uint32_t ct=0; ct < vtx->getUniformCount(); ct++) { - e->mVtxUniformSlots[ct] = glGetUniformLocation(pgm, vtx->getUniformName(ct)); - if (rsc->props.mLogShaders) { - LOGV("vtx U, %s = %d\n", vtx->getUniformName(ct).string(), e->mVtxUniformSlots[ct]); + + populateUniformData(vtx, pgm, e->vtxUniforms); + populateUniformData(frag, pgm, e->fragUniforms); + + // Only populate this list if we have arrays in our uniforms + UniformQueryData **uniformList = NULL; + GLint numUniforms = 0; + bool hasArrays = hasArrayUniforms(vtx, frag); + if (hasArrays) { + // Get the number of active uniforms and the length of the longest name + glGetProgramiv(pgm, GL_ACTIVE_UNIFORMS, &numUniforms); + GLint maxNameLength = 0; + glGetProgramiv(pgm, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength); + if (numUniforms > 0 && maxNameLength > 0) { + uniformList = new UniformQueryData*[numUniforms]; + // Iterate over all the uniforms and build the list we + // can later use to match our uniforms to + for (uint32_t ct = 0; ct < (uint32_t)numUniforms; ct++) { + uniformList[ct] = new UniformQueryData(maxNameLength); + glGetActiveUniform(pgm, ct, maxNameLength, &uniformList[ct]->writtenLength, + &uniformList[ct]->arraySize, &uniformList[ct]->type, + uniformList[ct]->name); + //LOGE("GL UNI idx=%u, arraySize=%u, name=%s", ct, + // uniformList[ct]->arraySize, uniformList[ct]->name); + } } } - for (uint32_t ct=0; ct < frag->getUniformCount(); ct++) { - e->mFragUniformSlots[ct] = glGetUniformLocation(pgm, frag->getUniformName(ct)); - if (rsc->props.mLogShaders) { - LOGV("frag U, %s = %d\n", frag->getUniformName(ct).string(), e->mFragUniformSlots[ct]); + + // We now know the highest index of all of the array uniforms + // and we need to update our cache to reflect that + // we may have declared [n], but only m < n elements are used + updateUniformArrayData(rsc, vtx, pgm, e->vtxUniforms, "vtx", + uniformList, (uint32_t)numUniforms); + updateUniformArrayData(rsc, frag, pgm, e->fragUniforms, "frag", + uniformList, (uint32_t)numUniforms); + + // Clean up the uniform data from GL + if (uniformList != NULL) { + for (uint32_t ct = 0; ct < (uint32_t)numUniforms; ct++) { + delete uniformList[ct]; } + delete[] uniformList; + uniformList = NULL; } } - e->mIsValid = true; //LOGV("SC made program %i", e->program); glUseProgram(e->program); - mEntryCount++; rsc->checkError("ShaderCache::lookup (miss)"); return true; } -void ShaderCache::cleanupVertex(uint32_t id) -{ +int32_t ShaderCache::vtxAttribSlot(const String8 &attrName) const { + for (uint32_t ct=0; ct < mCurrent->vtxAttrCount; ct++) { + if (attrName == mCurrent->vtxAttrs[ct].name) { + return mCurrent->vtxAttrs[ct].slot; + } + } + return -1; } -void ShaderCache::cleanupFragment(uint32_t id) -{ +void ShaderCache::cleanupVertex(uint32_t id) { + int32_t numEntries = (int32_t)mEntries.size(); + for (int32_t ct = 0; ct < numEntries; ct ++) { + if (mEntries[ct]->vtx == id) { + glDeleteProgram(mEntries[ct]->program); + + delete mEntries[ct]; + mEntries.removeAt(ct); + numEntries = (int32_t)mEntries.size(); + ct --; + } + } } -void ShaderCache::cleanupAll() -{ +void ShaderCache::cleanupFragment(uint32_t id) { + int32_t numEntries = (int32_t)mEntries.size(); + for (int32_t ct = 0; ct < numEntries; ct ++) { + if (mEntries[ct]->frag == id) { + glDeleteProgram(mEntries[ct]->program); + + delete mEntries[ct]; + mEntries.removeAt(ct); + numEntries = (int32_t)mEntries.size(); + ct --; + } + } +} + +void ShaderCache::cleanupAll() { + for (uint32_t ct=0; ct < mEntries.size(); ct++) { + glDeleteProgram(mEntries[ct]->program); + free(mEntries[ct]); + } + mEntries.clear(); } diff --git a/libs/rs/rsShaderCache.h b/libs/rs/rsShaderCache.h index df99ccc293ce..354036697e90 100644 --- a/libs/rs/rsShaderCache.h +++ b/libs/rs/rsShaderCache.h @@ -27,8 +27,7 @@ namespace renderscript { // An element is a group of Components that occupies one cell in a structure. -class ShaderCache -{ +class ShaderCache { public: ShaderCache(); virtual ~ShaderCache(); @@ -40,30 +39,86 @@ public: void cleanupAll(); - int32_t vtxAttribSlot(uint32_t a) const {return mCurrent->mVtxAttribSlots[a];} - int32_t vtxUniformSlot(uint32_t a) const {return mCurrent->mVtxUniformSlots[a];} - int32_t fragAttribSlot(uint32_t a) const {return mCurrent->mFragAttribSlots[a];} - int32_t fragUniformSlot(uint32_t a) const {return mCurrent->mFragUniformSlots[a];} - bool isUserVertexProgram() const {return mCurrent->mUserVertexProgram;} + int32_t vtxAttribSlot(const String8 &attrName) const; + int32_t vtxUniformSlot(uint32_t a) const {return mCurrent->vtxUniforms[a].slot;} + uint32_t vtxUniformSize(uint32_t a) const {return mCurrent->vtxUniforms[a].arraySize;} + int32_t fragUniformSlot(uint32_t a) const {return mCurrent->fragUniforms[a].slot;} + uint32_t fragUniformSize(uint32_t a) const {return mCurrent->fragUniforms[a].arraySize;} protected: - typedef struct { + struct UniformQueryData { + char *name; + uint32_t nameLength; + int32_t writtenLength; + int32_t arraySize; + uint32_t type; + UniformQueryData(uint32_t maxName) { + name = NULL; + nameLength = maxName; + if (nameLength > 0 ) { + name = new char[nameLength]; + } + } + ~UniformQueryData() { + if (name != NULL) { + delete[] name; + name = NULL; + } + } + }; + struct UniformData { + int32_t slot; + uint32_t arraySize; + }; + struct AttrData { + int32_t slot; + const char* name; + }; + struct ProgramEntry { + ProgramEntry(uint32_t numVtxAttr, uint32_t numVtxUnis, + uint32_t numFragUnis) : vtx(0), frag(0), program(0), vtxAttrCount(0), + vtxAttrs(0), vtxUniforms(0), fragUniforms(0) { + vtxAttrCount = numVtxAttr; + if (numVtxAttr) { + vtxAttrs = new AttrData[numVtxAttr]; + } + if (numVtxUnis) { + vtxUniforms = new UniformData[numVtxUnis]; + } + if (numFragUnis) { + fragUniforms = new UniformData[numFragUnis]; + } + } + ~ProgramEntry() { + if (vtxAttrs) { + delete[] vtxAttrs; + vtxAttrs = NULL; + } + if (vtxUniforms) { + delete[] vtxUniforms; + vtxUniforms = NULL; + } + if (fragUniforms) { + delete[] fragUniforms; + fragUniforms = NULL; + } + } uint32_t vtx; uint32_t frag; uint32_t program; - int32_t mVtxAttribSlots[Program::MAX_ATTRIBS]; - int32_t mVtxUniformSlots[Program::MAX_UNIFORMS]; - int32_t mFragAttribSlots[Program::MAX_ATTRIBS]; - int32_t mFragUniformSlots[Program::MAX_UNIFORMS]; - bool mUserVertexProgram; - bool mIsValid; - } entry_t; - entry_t *mEntries; - entry_t *mCurrent; - - uint32_t mEntryCount; - uint32_t mEntryAllocationCount; - + uint32_t vtxAttrCount; + AttrData *vtxAttrs; + UniformData *vtxUniforms; + UniformData *fragUniforms; + }; + Vector<ProgramEntry*> mEntries; + ProgramEntry *mCurrent; + + bool hasArrayUniforms(ProgramVertex *vtx, ProgramFragment *frag); + void populateUniformData(Program *prog, uint32_t linkedID, UniformData *data); + void updateUniformArrayData(Context *rsc, Program *prog, uint32_t linkedID, + UniformData *data, const char* logTag, + UniformQueryData **uniformList, uint32_t uniListSize); }; diff --git a/libs/rs/rsSignal.cpp b/libs/rs/rsSignal.cpp new file mode 100644 index 000000000000..ccd20b95c100 --- /dev/null +++ b/libs/rs/rsSignal.cpp @@ -0,0 +1,93 @@ +/* + * 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. + */ + +#include "rsSignal.h" + +using namespace android; +using namespace android::renderscript; + + +Signal::Signal() { + mSet = true; +} + +Signal::~Signal() { + pthread_mutex_destroy(&mMutex); + pthread_cond_destroy(&mCondition); +} + +bool Signal::init() { + int status = pthread_mutex_init(&mMutex, NULL); + if (status) { + LOGE("LocklessFifo mutex init failure"); + return false; + } + + status = pthread_cond_init(&mCondition, NULL); + if (status) { + LOGE("LocklessFifo condition init failure"); + pthread_mutex_destroy(&mMutex); + return false; + } + + return true; +} + +void Signal::set() { + int status; + + status = pthread_mutex_lock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i locking for set condition.", status); + return; + } + + mSet = true; + + status = pthread_cond_signal(&mCondition); + if (status) { + LOGE("LocklessCommandFifo: error %i on set condition.", status); + } + + status = pthread_mutex_unlock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status); + } +} + +void Signal::wait() { + int status; + + status = pthread_mutex_lock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i locking for condition.", status); + return; + } + + if (!mSet) { + status = pthread_cond_wait(&mCondition, &mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i waiting on condition.", status); + } + } + mSet = false; + + status = pthread_mutex_unlock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i unlocking for condition.", status); + } +} + diff --git a/libs/rs/rsFileA3DDecls.h b/libs/rs/rsSignal.h index 2a08bd368837..2e760f158e53 100644 --- a/libs/rs/rsFileA3DDecls.h +++ b/libs/rs/rsSignal.h @@ -14,31 +14,33 @@ * limitations under the License. */ -#ifndef ANDROID_RS_FILE_A3D_DECLS_H -#define ANDROID_RS_FILE_A3D_DECLS_H +#ifndef ANDROID_RS_SIGNAL_H +#define ANDROID_RS_SIGNAL_H -#define A3D_MAGIC_KEY "Android3D_ff" +#include "rsUtils.h" namespace android { namespace renderscript { - enum A3DChunkType { - CHUNK_EMPTY, +class Signal { +public: + Signal(); + ~Signal(); - CHUNK_ELEMENT, - CHUNK_ELEMENT_SOURCE, - CHUNK_VERTICIES, - CHUNK_MESH, - CHUNK_PRIMITIVE, + bool init(); - CHUNK_LAST - }; + void set(); + void wait(); +protected: + bool mSet; + pthread_mutex_t mMutex; + pthread_cond_t mCondition; +}; } } -#endif //ANDROID_RS_FILE_A3D_H - +#endif diff --git a/libs/rs/rsSimpleMesh.cpp b/libs/rs/rsSimpleMesh.cpp deleted file mode 100644 index 53ce5cdbd444..000000000000 --- a/libs/rs/rsSimpleMesh.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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. - */ - -#include "rsContext.h" - -using namespace android; -using namespace android::renderscript; - -#include <GLES/gl.h> -#include <GLES/glext.h> - -SimpleMesh::SimpleMesh(Context *rsc) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; -} - -SimpleMesh::~SimpleMesh() -{ - delete[] mVertexTypes; - delete[] mVertexBuffers; -} - -void SimpleMesh::render(Context *rsc) const -{ - if (mPrimitiveType.get()) { - renderRange(rsc, 0, mPrimitiveType->getDimX()); - return; - } - - if (mIndexType.get()) { - renderRange(rsc, 0, mIndexType->getDimX()); - return; - } - - renderRange(rsc, 0, mVertexTypes[0]->getDimX()); -} - -void SimpleMesh::renderRange(Context *rsc, uint32_t start, uint32_t len) const -{ - if (len < 1) { - return; - } - - rsc->checkError("SimpleMesh::renderRange 1"); - VertexArray va; - if (rsc->checkVersion2_0()) { - for (uint32_t ct=0; ct < mVertexTypeCount; ct++) { - mVertexBuffers[ct]->uploadCheck(rsc); - va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID()); - mVertexTypes[ct]->enableGLVertexBuffer2(&va); - } - va.setupGL2(rsc, &rsc->mStateVertexArray, &rsc->mShaderCache); - } else { - for (uint32_t ct=0; ct < mVertexTypeCount; ct++) { - mVertexBuffers[ct]->uploadCheck(rsc); - va.setActiveBuffer(mVertexBuffers[ct]->getBufferObjectID()); - mVertexTypes[ct]->enableGLVertexBuffer(&va); - } - va.setupGL(rsc, 0); - } - - rsc->checkError("SimpleMesh::renderRange 2"); - if (mIndexType.get()) { - mIndexBuffer->uploadCheck(rsc); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID()); - glDrawElements(mGLPrimitive, len, GL_UNSIGNED_SHORT, (uint16_t *)(start * 2)); - } else { - glDrawArrays(mGLPrimitive, start, len); - } - - rsc->checkError("SimpleMesh::renderRange"); -} - -void SimpleMesh::uploadAll(Context *rsc) -{ - for (uint32_t ct=0; ct < mVertexTypeCount; ct++) { - if (mVertexBuffers[ct].get()) { - mVertexBuffers[ct]->deferedUploadToBufferObject(rsc); - } - } - if (mIndexBuffer.get()) { - mIndexBuffer->deferedUploadToBufferObject(rsc); - } - if (mPrimitiveBuffer.get()) { - mPrimitiveBuffer->deferedUploadToBufferObject(rsc); - } - rsc->checkError("SimpleMesh::uploadAll"); -} - - -SimpleMeshContext::SimpleMeshContext() -{ -} - -SimpleMeshContext::~SimpleMeshContext() -{ -} - - -namespace android { -namespace renderscript { - - -RsSimpleMesh rsi_SimpleMeshCreate(Context *rsc, RsType prim, RsType idx, RsType *vtx, uint32_t vtxCount, uint32_t primType) -{ - SimpleMesh *sm = new SimpleMesh(rsc); - sm->incUserRef(); - - sm->mIndexType.set((const Type *)idx); - sm->mPrimitiveType.set((const Type *)prim); - - sm->mVertexTypeCount = vtxCount; - sm->mVertexTypes = new ObjectBaseRef<const Type>[vtxCount]; - sm->mVertexBuffers = new ObjectBaseRef<Allocation>[vtxCount]; - for (uint32_t ct=0; ct < vtxCount; ct++) { - sm->mVertexTypes[ct].set((const Type *)vtx[ct]); - } - - sm->mPrimitive = (RsPrimitive)primType; - switch(sm->mPrimitive) { - case RS_PRIMITIVE_POINT: sm->mGLPrimitive = GL_POINTS; break; - case RS_PRIMITIVE_LINE: sm->mGLPrimitive = GL_LINES; break; - case RS_PRIMITIVE_LINE_STRIP: sm->mGLPrimitive = GL_LINE_STRIP; break; - case RS_PRIMITIVE_TRIANGLE: sm->mGLPrimitive = GL_TRIANGLES; break; - case RS_PRIMITIVE_TRIANGLE_STRIP: sm->mGLPrimitive = GL_TRIANGLE_STRIP; break; - case RS_PRIMITIVE_TRIANGLE_FAN: sm->mGLPrimitive = GL_TRIANGLE_FAN; break; - } - return sm; -} - -void rsi_SimpleMeshBindVertex(Context *rsc, RsSimpleMesh mv, RsAllocation va, uint32_t slot) -{ - SimpleMesh *sm = static_cast<SimpleMesh *>(mv); - rsAssert(slot < sm->mVertexTypeCount); - - sm->mVertexBuffers[slot].set((Allocation *)va); -} - -void rsi_SimpleMeshBindIndex(Context *rsc, RsSimpleMesh mv, RsAllocation va) -{ - SimpleMesh *sm = static_cast<SimpleMesh *>(mv); - sm->mIndexBuffer.set((Allocation *)va); -} - -void rsi_SimpleMeshBindPrimitive(Context *rsc, RsSimpleMesh mv, RsAllocation va) -{ - SimpleMesh *sm = static_cast<SimpleMesh *>(mv); - sm->mPrimitiveBuffer.set((Allocation *)va); -} - - - - -}} - diff --git a/libs/rs/rsSimpleMesh.h b/libs/rs/rsSimpleMesh.h deleted file mode 100644 index 6defbdaadea2..000000000000 --- a/libs/rs/rsSimpleMesh.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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. - */ - -#ifndef ANDROID_RS_SIMPLE_MESH_H -#define ANDROID_RS_SIMPLE_MESH_H - - -#include "RenderScript.h" - -// --------------------------------------------------------------------------- -namespace android { -namespace renderscript { - - -// An element is a group of Components that occupies one cell in a structure. -class SimpleMesh : public ObjectBase -{ -public: - SimpleMesh(Context *); - ~SimpleMesh(); - - ObjectBaseRef<const Type> mIndexType; - ObjectBaseRef<const Type> mPrimitiveType; - ObjectBaseRef<const Type> *mVertexTypes; - uint32_t mVertexTypeCount; - - ObjectBaseRef<Allocation> mIndexBuffer; - ObjectBaseRef<Allocation> mPrimitiveBuffer; - ObjectBaseRef<Allocation> *mVertexBuffers; - - RsPrimitive mPrimitive; - uint32_t mGLPrimitive; - - - void render(Context *) const; - void renderRange(Context *, uint32_t start, uint32_t len) const; - void uploadAll(Context *); - - -protected: -}; - -class SimpleMeshContext -{ -public: - SimpleMeshContext(); - ~SimpleMeshContext(); - - -}; - - -} -} -#endif //ANDROID_RS_SIMPLE_MESH_H - diff --git a/libs/rs/rsStream.cpp b/libs/rs/rsStream.cpp new file mode 100644 index 000000000000..b9df0ccfac6a --- /dev/null +++ b/libs/rs/rsStream.cpp @@ -0,0 +1,113 @@ + +/* + * 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. + */ + +#include "rsContext.h" +#include "rsStream.h" + +using namespace android; +using namespace android::renderscript; + +IStream::IStream(const uint8_t *buf, bool use64) { + mData = buf; + mPos = 0; + mUse64 = use64; +} + +void IStream::loadByteArray(void *dest, size_t numBytes) { + memcpy(dest, mData + mPos, numBytes); + mPos += numBytes; +} + +uint64_t IStream::loadOffset() { + uint64_t tmp; + if (mUse64) { + mPos = (mPos + 7) & (~7); + tmp = reinterpret_cast<const uint64_t *>(&mData[mPos])[0]; + mPos += sizeof(uint64_t); + return tmp; + } + return loadU32(); +} + +void IStream::loadString(String8 *s) { + uint32_t len = loadU32(); + s->setTo((const char *)&mData[mPos], len); + mPos += len; +} + +// Output stream implementation +OStream::OStream(uint64_t len, bool use64) { + mData = (uint8_t*)malloc(len); + mLength = len; + mPos = 0; + mUse64 = use64; +} + +OStream::~OStream() { + free(mData); +} + +void OStream::addByteArray(const void *src, size_t numBytes) { + // We need to potentially grow more than once if the number of byes we write is substantial + while (mPos + numBytes >= mLength) { + growSize(); + } + memcpy(mData + mPos, src, numBytes); + mPos += numBytes; +} + +void OStream::addOffset(uint64_t v) { + if (mUse64) { + mPos = (mPos + 7) & (~7); + if (mPos + sizeof(v) >= mLength) { + growSize(); + } + mData[mPos++] = (uint8_t)(v & 0xff); + mData[mPos++] = (uint8_t)((v >> 8) & 0xff); + mData[mPos++] = (uint8_t)((v >> 16) & 0xff); + mData[mPos++] = (uint8_t)((v >> 24) & 0xff); + mData[mPos++] = (uint8_t)((v >> 32) & 0xff); + mData[mPos++] = (uint8_t)((v >> 40) & 0xff); + mData[mPos++] = (uint8_t)((v >> 48) & 0xff); + mData[mPos++] = (uint8_t)((v >> 56) & 0xff); + } else { + addU32(v); + } +} + +void OStream::addString(String8 *s) { + uint32_t len = s->size(); + addU32(len); + if (mPos + len*sizeof(char) >= mLength) { + growSize(); + } + char *stringData = reinterpret_cast<char *>(&mData[mPos]); + for (uint32_t i = 0; i < len; i ++) { + stringData[i] = s->string()[i]; + } + mPos += len*sizeof(char); +} + +void OStream::growSize() { + uint8_t *newData = (uint8_t*)malloc(mLength*2); + memcpy(newData, mData, mLength*sizeof(uint8_t)); + mLength = mLength * 2; + free(mData); + mData = newData; +} + + diff --git a/libs/rs/rsStream.h b/libs/rs/rsStream.h new file mode 100644 index 000000000000..62bcf941dcbf --- /dev/null +++ b/libs/rs/rsStream.h @@ -0,0 +1,161 @@ +/* + * 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. + */ + +#ifndef ANDROID_RS_STREAM_H +#define ANDROID_RS_STREAM_H + +#include <utils/String8.h> +#include <stdio.h> + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + +class IStream { +public: + IStream(const uint8_t *, bool use64); + + float loadF() { + mPos = (mPos + 3) & (~3); + float tmp = reinterpret_cast<const float *>(&mData[mPos])[0]; + mPos += sizeof(float); + return tmp; + } + int32_t loadI32() { + mPos = (mPos + 3) & (~3); + int32_t tmp = reinterpret_cast<const int32_t *>(&mData[mPos])[0]; + mPos += sizeof(int32_t); + return tmp; + } + uint32_t loadU32() { + mPos = (mPos + 3) & (~3); + uint32_t tmp = reinterpret_cast<const uint32_t *>(&mData[mPos])[0]; + mPos += sizeof(uint32_t); + return tmp; + } + uint16_t loadU16() { + mPos = (mPos + 1) & (~1); + uint16_t tmp = reinterpret_cast<const uint16_t *>(&mData[mPos])[0]; + mPos += sizeof(uint16_t); + return tmp; + } + inline uint8_t loadU8() { + uint8_t tmp = reinterpret_cast<const uint8_t *>(&mData[mPos])[0]; + mPos += sizeof(uint8_t); + return tmp; + } + void loadByteArray(void *dest, size_t numBytes); + uint64_t loadOffset(); + void loadString(String8 *s); + uint64_t getPos() const { + return mPos; + } + void reset(uint64_t pos) { + mPos = pos; + } + void reset() { + mPos = 0; + } + + const uint8_t * getPtr() const { + return mData; + } +protected: + const uint8_t * mData; + uint64_t mPos; + bool mUse64; +}; + +class OStream { +public: + OStream(uint64_t length, bool use64); + ~OStream(); + + void align(uint32_t bytes) { + mPos = (mPos + (bytes - 1)) & (~(bytes - 1)); + if (mPos >= mLength) { + growSize(); + } + } + + void addF(float v) { + uint32_t uintV = *reinterpret_cast<uint32_t*> (&v); + addU32(uintV); + } + void addI32(int32_t v) { + mPos = (mPos + 3) & (~3); + if (mPos + sizeof(v) >= mLength) { + growSize(); + } + mData[mPos++] = (uint8_t)(v & 0xff); + mData[mPos++] = (uint8_t)((v >> 8) & 0xff); + mData[mPos++] = (uint8_t)((v >> 16) & 0xff); + mData[mPos++] = (uint8_t)((v >> 24) & 0xff); + } + void addU32(uint32_t v) { + mPos = (mPos + 3) & (~3); + if (mPos + sizeof(v) >= mLength) { + growSize(); + } + mData[mPos++] = (uint8_t)(v & 0xff); + mData[mPos++] = (uint8_t)((v >> 8) & 0xff); + mData[mPos++] = (uint8_t)((v >> 16) & 0xff); + mData[mPos++] = (uint8_t)((v >> 24) & 0xff); + } + void addU16(uint16_t v) { + mPos = (mPos + 1) & (~1); + if (mPos + sizeof(v) >= mLength) { + growSize(); + } + mData[mPos++] = (uint8_t)(v & 0xff); + mData[mPos++] = (uint8_t)(v >> 8); + } + inline void addU8(uint8_t v) { + if (mPos + 1 >= mLength) { + growSize(); + } + reinterpret_cast<uint8_t *>(&mData[mPos])[0] = v; + mPos ++; + } + void addByteArray(const void *src, size_t numBytes); + void addOffset(uint64_t v); + void addString(String8 *s); + uint64_t getPos() const { + return mPos; + } + void reset(uint64_t pos) { + mPos = pos; + } + void reset() { + mPos = 0; + } + const uint8_t * getPtr() const { + return mData; + } +protected: + void growSize(); + uint8_t * mData; + uint64_t mLength; + uint64_t mPos; + bool mUse64; +}; + + +} // renderscript +} // android +#endif //ANDROID_RS_STREAM_H + + diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp index 527b3d7dbabb..6cf07de7920b 100644 --- a/libs/rs/rsThreadIO.cpp +++ b/libs/rs/rsThreadIO.cpp @@ -21,25 +21,21 @@ using namespace android; using namespace android::renderscript; -ThreadIO::ThreadIO() -{ +ThreadIO::ThreadIO() { mToCore.init(16 * 1024); mToClient.init(1024); } -ThreadIO::~ThreadIO() -{ +ThreadIO::~ThreadIO() { } -void ThreadIO::shutdown() -{ +void ThreadIO::shutdown() { mToCore.shutdown(); } -bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand) -{ +bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand) { bool ret = false; - while(!mToCore.isEmpty() || waitForCommand) { + while (!mToCore.isEmpty() || waitForCommand) { uint32_t cmdID = 0; uint32_t cmdSize = 0; ret = true; @@ -57,6 +53,11 @@ bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand) waitForCommand = false; //LOGV("playCoreCommands 3 %i %i", cmdID, cmdSize); + if (cmdID >= (sizeof(gPlaybackFuncs) / sizeof(void *))) { + rsAssert(cmdID < (sizeof(gPlaybackFuncs) / sizeof(void *))); + LOGE("playCoreCommands error con %p, cmd %i", con, cmdID); + mToCore.printDebugData(); + } gPlaybackFuncs[cmdID](con, data); mToCore.next(); } diff --git a/libs/rs/rsThreadIO.h b/libs/rs/rsThreadIO.h index 95270f594302..f9d0de746347 100644 --- a/libs/rs/rsThreadIO.h +++ b/libs/rs/rsThreadIO.h @@ -42,7 +42,6 @@ public: LocklessCommandFifo mToClient; intptr_t mToCoreRet; - }; diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp index c09e9798c64b..cd2be94979a8 100644 --- a/libs/rs/rsType.cpp +++ b/libs/rs/rsType.cpp @@ -15,35 +15,33 @@ */ #include "rsContext.h" -#include <GLES/gl.h> using namespace android; using namespace android::renderscript; -Type::Type(Context *rsc) : ObjectBase(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; +Type::Type(Context *rsc) : ObjectBase(rsc) { mLODs = 0; mLODCount = 0; clear(); } -Type::~Type() -{ +void Type::preDestroy() { for (uint32_t ct = 0; ct < mRSC->mStateType.mTypes.size(); ct++) { if (mRSC->mStateType.mTypes[ct] == this) { mRSC->mStateType.mTypes.removeAt(ct); break; } } +} + +Type::~Type() { if (mLODs) { delete [] mLODs; + mLODs = NULL; } } -void Type::clear() -{ +void Type::clear() { if (mLODs) { delete [] mLODs; mLODs = NULL; @@ -56,22 +54,18 @@ void Type::clear() mElement.clear(); } -TypeState::TypeState() -{ +TypeState::TypeState() { } -TypeState::~TypeState() -{ +TypeState::~TypeState() { } -size_t Type::getOffsetForFace(uint32_t face) const -{ +size_t Type::getOffsetForFace(uint32_t face) const { rsAssert(mFaces); return 0; } -void Type::compute() -{ +void Type::compute() { uint32_t oldLODCount = mLODCount; if (mDimLOD) { uint32_t l2x = rsFindHighBit(mDimX) + 1; @@ -84,7 +78,9 @@ void Type::compute() mLODCount = 1; } if (mLODCount != oldLODCount) { - delete [] mLODs; + if (mLODs){ + delete [] mLODs; + } mLODs = new LOD[mLODCount]; } @@ -110,181 +106,87 @@ void Type::compute() offset *= 6; } mTotalSizeBytes = offset; - - makeGLComponents(); } -uint32_t Type::getLODOffset(uint32_t lod, uint32_t x) const -{ +uint32_t Type::getLODOffset(uint32_t lod, uint32_t x) const { uint32_t offset = mLODs[lod].mOffset; offset += x * mElement->getSizeBytes(); return offset; } -uint32_t Type::getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const -{ +uint32_t Type::getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const { uint32_t offset = mLODs[lod].mOffset; offset += (x + y * mLODs[lod].mX) * mElement->getSizeBytes(); return offset; } -uint32_t Type::getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const -{ +uint32_t Type::getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const { uint32_t offset = mLODs[lod].mOffset; offset += (x + y*mLODs[lod].mX + z*mLODs[lod].mX*mLODs[lod].mY) * mElement->getSizeBytes(); return offset; } +uint32_t Type::getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face, uint32_t x, uint32_t y) const { + uint32_t offset = mLODs[lod].mOffset; + offset += (x + y * mLODs[lod].mX) * mElement->getSizeBytes(); -void Type::makeGLComponents() -{ - uint32_t userNum = 0; - - for (uint32_t ct=0; ct < getElement()->getFieldCount(); ct++) { - const Component &c = getElement()->getField(ct)->getComponent(); - - switch(c.getKind()) { - case RS_KIND_USER: - mGL.mUser[userNum].size = c.getVectorSize(); - mGL.mUser[userNum].offset = mElement->getFieldOffsetBytes(ct); - mGL.mUser[userNum].type = c.getGLType(); - mGL.mUser[userNum].normalized = c.getType() != RS_TYPE_FLOAT_32;//c.getIsNormalized(); - mGL.mUser[userNum].name.setTo(getElement()->getFieldName(ct)); - userNum ++; - break; - - case RS_KIND_POSITION: - rsAssert(mGL.mVtx.size == 0); - mGL.mVtx.size = c.getVectorSize(); - mGL.mVtx.offset = mElement->getFieldOffsetBytes(ct); - mGL.mVtx.type = c.getGLType(); - mGL.mVtx.normalized = false; - mGL.mVtx.name.setTo("Position"); - break; - - case RS_KIND_COLOR: - rsAssert(mGL.mColor.size == 0); - mGL.mColor.size = c.getVectorSize(); - mGL.mColor.offset = mElement->getFieldOffsetBytes(ct); - mGL.mColor.type = c.getGLType(); - mGL.mColor.normalized = c.getType() != RS_TYPE_FLOAT_32; - mGL.mColor.name.setTo("Color"); - break; - - case RS_KIND_NORMAL: - rsAssert(mGL.mNorm.size == 0); - mGL.mNorm.size = c.getVectorSize(); - mGL.mNorm.offset = mElement->getFieldOffsetBytes(ct); - mGL.mNorm.type = c.getGLType(); - mGL.mNorm.normalized = false; - mGL.mNorm.name.setTo("Normal"); - break; - - case RS_KIND_TEXTURE: - rsAssert(mGL.mTex.size == 0); - mGL.mTex.size = c.getVectorSize(); - mGL.mTex.offset = mElement->getFieldOffsetBytes(ct); - mGL.mTex.type = c.getGLType(); - mGL.mTex.normalized = false; - mGL.mTex.name.setTo("Texture"); - break; - - case RS_KIND_POINT_SIZE: - rsAssert(!mGL.mPointSize.size); - mGL.mPointSize.size = c.getVectorSize(); - mGL.mPointSize.offset = mElement->getFieldOffsetBytes(ct); - mGL.mPointSize.type = c.getGLType(); - mGL.mPointSize.normalized = false; - mGL.mPointSize.name.setTo("PointSize"); - break; - - default: - break; - } + if (face != 0) { + uint32_t faceOffset = getSizeBytes() / 6; + offset += faceOffset * face; } + return offset; } -void Type::enableGLVertexBuffer(VertexArray *va) const -{ - // Note: We are only going to enable buffers and never disable them - // here. The reason is more than one Allocation may be used as a vertex - // source. So we cannot disable arrays that may have been in use by - // another allocation. - - uint32_t stride = mElement->getSizeBytes(); - if (mGL.mVtx.size) { - va->addLegacy(mGL.mVtx.type, - mGL.mVtx.size, - stride, - RS_KIND_POSITION, - false, - mGL.mVtx.offset); - } +void Type::dumpLOGV(const char *prefix) const { + char buf[1024]; + ObjectBase::dumpLOGV(prefix); + LOGV("%s Type: x=%zu y=%zu z=%zu mip=%i face=%i", prefix, mDimX, mDimY, mDimZ, mDimLOD, mFaces); + snprintf(buf, sizeof(buf), "%s element: ", prefix); + mElement->dumpLOGV(buf); +} - if (mGL.mNorm.size) { - va->addLegacy(mGL.mNorm.type, - 3, - stride, - RS_KIND_NORMAL, - false, - mGL.mNorm.offset); - } +void Type::serialize(OStream *stream) const { + // Need to identify ourselves + stream->addU32((uint32_t)getClassId()); - if (mGL.mColor.size) { - va->addLegacy(mGL.mColor.type, - mGL.mColor.size, - stride, - RS_KIND_COLOR, - true, - mGL.mColor.offset); - } + String8 name(getName()); + stream->addString(&name); - if (mGL.mTex.size) { - va->addLegacy(mGL.mTex.type, - mGL.mTex.size, - stride, - RS_KIND_TEXTURE, - false, - mGL.mTex.offset); - } + mElement->serialize(stream); - if (mGL.mPointSize.size) { - va->addLegacy(mGL.mPointSize.type, - 1, - stride, - RS_KIND_POINT_SIZE, - false, - mGL.mPointSize.offset); - } + stream->addU32(mDimX); + stream->addU32(mDimY); + stream->addU32(mDimZ); + stream->addU8((uint8_t)(mDimLOD ? 1 : 0)); + stream->addU8((uint8_t)(mFaces ? 1 : 0)); } -void Type::enableGLVertexBuffer2(VertexArray *va) const -{ - // Do legacy buffers - enableGLVertexBuffer(va); - - uint32_t stride = mElement->getSizeBytes(); - for (uint32_t ct=0; ct < RS_MAX_ATTRIBS; ct++) { - if (mGL.mUser[ct].size) { - va->addUser(mGL.mUser[ct], stride); - } +Type *Type::createFromStream(Context *rsc, IStream *stream) { + // First make sure we are reading the correct object + RsA3DClassID classID = (RsA3DClassID)stream->loadU32(); + if (classID != RS_A3D_CLASS_ID_TYPE) { + LOGE("type loading skipped due to invalid class id\n"); + return NULL; } -} + String8 name; + stream->loadString(&name); + Element *elem = Element::createFromStream(rsc, stream); + if (!elem) { + return NULL; + } -void Type::dumpLOGV(const char *prefix) const -{ - char buf[1024]; - ObjectBase::dumpLOGV(prefix); - LOGV("%s Type: x=%i y=%i z=%i mip=%i face=%i", prefix, mDimX, mDimY, mDimZ, mDimLOD, mFaces); - sprintf(buf, "%s element: ", prefix); - mElement->dumpLOGV(buf); + uint32_t x = stream->loadU32(); + uint32_t y = stream->loadU32(); + uint32_t z = stream->loadU32(); + uint8_t lod = stream->loadU8(); + uint8_t faces = stream->loadU8(); + return Type::getType(rsc, elem, x, y, z, lod != 0, faces !=0 ); } -bool Type::getIsNp2() const -{ +bool Type::getIsNp2() const { uint32_t x = getDimX(); uint32_t y = getDimY(); uint32_t z = getDimZ(); @@ -301,97 +203,99 @@ bool Type::getIsNp2() const return false; } - -////////////////////////////////////////////////// -// -namespace android { -namespace renderscript { - -void rsi_TypeBegin(Context *rsc, RsElement vse) -{ - TypeState * stc = &rsc->mStateType; - - stc->mX = 0; - stc->mY = 0; - stc->mZ = 0; - stc->mLOD = false; - stc->mFaces = false; - stc->mElement.set(static_cast<const Element *>(vse)); +bool Type::isEqual(const Type *other) const { + if (other == NULL) { + return false; + } + if (other->getElement()->isEqual(getElement()) && + other->getDimX() == mDimX && + other->getDimY() == mDimY && + other->getDimZ() == mDimZ && + other->getDimLOD() == mDimLOD && + other->getDimFaces() == mFaces) { + return true; + } + return false; } -void rsi_TypeAdd(Context *rsc, RsDimension dim, size_t value) -{ +Type * Type::getType(Context *rsc, const Element *e, + uint32_t dimX, uint32_t dimY, uint32_t dimZ, + bool dimLOD, bool dimFaces) { TypeState * stc = &rsc->mStateType; - if (dim < 0) { - //error - return; + ObjectBase::asyncLock(); + for (uint32_t ct=0; ct < stc->mTypes.size(); ct++) { + Type *t = stc->mTypes[ct]; + if (t->getElement() != e) continue; + if (t->getDimX() != dimX) continue; + if (t->getDimY() != dimY) continue; + if (t->getDimZ() != dimZ) continue; + if (t->getDimLOD() != dimLOD) continue; + if (t->getDimFaces() != dimFaces) continue; + t->incUserRef(); + ObjectBase::asyncUnlock(); + return t; } + ObjectBase::asyncUnlock(); - switch (dim) { - case RS_DIMENSION_X: - stc->mX = value; - return; - case RS_DIMENSION_Y: - stc->mY = value; - return; - case RS_DIMENSION_Z: - stc->mZ = value; - return; - case RS_DIMENSION_FACE: - stc->mFaces = (value != 0); - return; - case RS_DIMENSION_LOD: - stc->mLOD = (value != 0); - return; - default: - break; - } + Type *nt = new Type(rsc); + nt->mElement.set(e); + nt->mDimX = dimX; + nt->mDimY = dimY; + nt->mDimZ = dimZ; + nt->mDimLOD = dimLOD; + nt->mFaces = dimFaces; + nt->compute(); + nt->incUserRef(); + ObjectBase::asyncLock(); + stc->mTypes.push(nt); + ObjectBase::asyncUnlock(); - int32_t arrayNum = dim - RS_DIMENSION_ARRAY_0; - if ((dim < 0) || (dim > RS_DIMENSION_MAX)) { - LOGE("rsTypeAdd: Bad dimension"); - //error - return; - } + return nt; +} - // todo: implement array support +Type * Type::cloneAndResize1D(Context *rsc, uint32_t dimX) const { + return getType(rsc, mElement.get(), dimX, + mDimY, mDimZ, mDimLOD, mFaces); +} +Type * Type::cloneAndResize2D(Context *rsc, + uint32_t dimX, + uint32_t dimY) const { + return getType(rsc, mElement.get(), dimX, dimY, + mDimZ, mDimLOD, mFaces); } -RsType rsi_TypeCreate(Context *rsc) -{ - TypeState * stc = &rsc->mStateType; - for (uint32_t ct=0; ct < stc->mTypes.size(); ct++) { - Type *t = stc->mTypes[ct]; - if (t->getElement() != stc->mElement.get()) continue; - if (t->getDimX() != stc->mX) continue; - if (t->getDimY() != stc->mY) continue; - if (t->getDimZ() != stc->mZ) continue; - if (t->getDimLOD() != stc->mLOD) continue; - if (t->getDimFaces() != stc->mFaces) continue; - t->incUserRef(); - return t; - } +////////////////////////////////////////////////// +// +namespace android { +namespace renderscript { - Type * st = new Type(rsc); - st->incUserRef(); - st->setDimX(stc->mX); - st->setDimY(stc->mY); - st->setDimZ(stc->mZ); - st->setElement(stc->mElement.get()); - st->setDimLOD(stc->mLOD); - st->setDimFaces(stc->mFaces); - st->compute(); - stc->mElement.clear(); - stc->mTypes.push(st); - return st; +} } +RsType rsaTypeCreate(RsContext con, RsElement _e, uint32_t dimX, + uint32_t dimY, uint32_t dimZ, bool mips, bool faces) { + Context *rsc = static_cast<Context *>(con); + Element *e = static_cast<Element *>(_e); -} + return Type::getType(rsc, e, dimX, dimY, dimZ, mips, faces); } +void rsaTypeGetNativeData(RsContext con, RsType type, uint32_t *typeData, uint32_t typeDataSize) { + rsAssert(typeDataSize == 6); + // Pack the data in the follofing way mDimX; mDimY; mDimZ; + // mDimLOD; mDimFaces; mElement; into typeData + Type *t = static_cast<Type *>(type); + + (*typeData++) = t->getDimX(); + (*typeData++) = t->getDimY(); + (*typeData++) = t->getDimZ(); + (*typeData++) = t->getDimLOD(); + (*typeData++) = t->getDimFaces() ? 1 : 0; + (*typeData++) = (uint32_t)t->getElement(); + t->getElement()->incUserRef(); +} diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h index c25577cd8b4d..90ae039da3e3 100644 --- a/libs/rs/rsType.h +++ b/libs/rs/rsType.h @@ -25,15 +25,10 @@ namespace android { namespace renderscript { -class Type : public ObjectBase -{ +class Type : public ObjectBase { public: - Type(Context *); - virtual ~Type(); - Type * createTex2D(const Element *, size_t w, size_t h, bool mip); - size_t getOffsetForFace(uint32_t face) const; size_t getSizeBytes() const {return mTotalSizeBytes;} @@ -49,31 +44,33 @@ public: uint32_t getLODDimX(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mX;} uint32_t getLODDimY(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mY;} uint32_t getLODDimZ(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mZ;} - uint32_t getLODOffset(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mOffset;} + uint32_t getLODOffset(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mOffset;} uint32_t getLODOffset(uint32_t lod, uint32_t x) const; uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const; uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const; + uint32_t getLODFaceOffset(uint32_t lod, RsAllocationCubemapFace face, uint32_t x, uint32_t y) const; + uint32_t getLODCount() const {return mLODCount;} bool getIsNp2() const; - - void setElement(const Element *e) {mElement.set(e);} - void setDimX(uint32_t v) {mDimX = v;} - void setDimY(uint32_t v) {mDimY = v;} - void setDimZ(uint32_t v) {mDimZ = v;} - void setDimFaces(bool v) {mFaces = v;} - void setDimLOD(bool v) {mDimLOD = v;} - - void clear(); void compute(); - void enableGLVertexBuffer(class VertexArray *) const; - void enableGLVertexBuffer2(class VertexArray *) const; - void dumpLOGV(const char *prefix) const; + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_TYPE; } + static Type *createFromStream(Context *rsc, IStream *stream); + + bool isEqual(const Type *other) const; + + Type * cloneAndResize1D(Context *rsc, uint32_t dimX) const; + Type * cloneAndResize2D(Context *rsc, uint32_t dimX, uint32_t dimY) const; + + static Type * getType(Context *rsc, const Element *e, + uint32_t dimX, uint32_t dimY, uint32_t dimZ, + bool dimLOD, bool dimFaces); protected: struct LOD { @@ -101,10 +98,6 @@ protected: bool mDimLOD; bool mFaces; - // A list of array dimensions. The count is the number of array dimensions and the - // sizes is a per array size. - //Vector<size_t> mDimArraysSizes; - // count of mipmap levels, 0 indicates no mipmapping size_t mMipChainSizeBytes; @@ -112,18 +105,12 @@ protected: LOD *mLODs; uint32_t mLODCount; - struct GLState_t { - VertexArray::Attrib mUser[RS_MAX_ATTRIBS]; - VertexArray::Attrib mVtx; - VertexArray::Attrib mNorm; - VertexArray::Attrib mColor; - VertexArray::Attrib mTex; - VertexArray::Attrib mPointSize; - }; - GLState_t mGL; - void makeGLComponents(); +protected: + virtual void preDestroy(); + virtual ~Type(); private: + Type(Context *); Type(const Type &); }; @@ -133,14 +120,6 @@ public: TypeState(); ~TypeState(); - size_t mX; - size_t mY; - size_t mZ; - uint32_t mLOD; - bool mFaces; - ObjectBaseRef<const Element> mElement; - - // Cache of all existing types. Vector<Type *> mTypes; }; diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h index 07f893338bfa..3b60af58fb4d 100644 --- a/libs/rs/rsUtils.h +++ b/libs/rs/rsUtils.h @@ -19,15 +19,23 @@ #define LOG_NDEBUG 0 #define LOG_TAG "RenderScript" + #include <utils/Log.h> -#include <utils/Vector.h> -#include <utils/KeyedVector.h> + +#include "rsStream.h" + #include <utils/String8.h> +#include <utils/Vector.h> + #include <stdlib.h> #include <pthread.h> #include <time.h> +#include <cutils/atomic.h> +#ifndef ANDROID_RS_SERIALIZE #include <EGL/egl.h> +#endif + #include <math.h> #include "RenderScript.h" @@ -36,11 +44,31 @@ namespace android { namespace renderscript { #if 1 -#define rsAssert(v) do {if(!(v)) LOGE("rsAssert failed: %s, in %s at %i", #v, __FILE__, __LINE__);} while(0) +#define rsAssert(v) do {if(!(v)) LOGE("rsAssert failed: %s, in %s at %i", #v, __FILE__, __LINE__);} while (0) #else -#define rsAssert(v) while(0) +#define rsAssert(v) while (0) #endif +typedef float rsvF_2 __attribute__ ((vector_size (8))); +typedef float rsvF_4 __attribute__ ((vector_size (16))); +typedef uint8_t rsvU8_4 __attribute__ ((vector_size (4))); + +union float2 { + rsvF_2 v; + float f[2]; +}; + +union float4 { + rsvF_4 v; + float f[4]; +}; + +union uchar4 { + rsvU8_4 v; + uint8_t f[4]; + uint32_t packed; +}; + template<typename T> T rsMin(T in1, T in2) { @@ -51,8 +79,7 @@ T rsMin(T in1, T in2) } template<typename T> -T rsMax(T in1, T in2) -{ +T rsMax(T in1, T in2) { if (in1 < in2) { return in2; } @@ -60,10 +87,9 @@ T rsMax(T in1, T in2) } template<typename T> -T rsFindHighBit(T val) -{ +T rsFindHighBit(T val) { uint32_t bit = 0; - while(val > 1) { + while (val > 1) { bit++; val>>=1; } @@ -71,14 +97,12 @@ T rsFindHighBit(T val) } template<typename T> -bool rsIsPow2(T val) -{ +bool rsIsPow2(T val) { return (val & (val-1)) == 0; } template<typename T> -T rsHigherPow2(T v) -{ +T rsHigherPow2(T v) { if (rsIsPow2(v)) { return v; } @@ -86,17 +110,14 @@ T rsHigherPow2(T v) } template<typename T> -T rsLowerPow2(T v) -{ +T rsLowerPow2(T v) { if (rsIsPow2(v)) { return v; } return 1 << rsFindHighBit(v); } - -static inline uint16_t rs888to565(uint32_t r, uint32_t g, uint32_t b) -{ +static inline uint16_t rs888to565(uint32_t r, uint32_t g, uint32_t b) { uint16_t t = 0; t |= b >> 3; t |= (g >> 2) << 5; @@ -104,16 +125,14 @@ static inline uint16_t rs888to565(uint32_t r, uint32_t g, uint32_t b) return t; } -static inline uint16_t rsBoxFilter565(uint16_t i1, uint16_t i2, uint16_t i3, uint16_t i4) -{ +static inline uint16_t rsBoxFilter565(uint16_t i1, uint16_t i2, uint16_t i3, uint16_t i4) { uint32_t r = ((i1 & 0x1f) + (i2 & 0x1f) + (i3 & 0x1f) + (i4 & 0x1f)); uint32_t g = ((i1 >> 5) & 0x3f) + ((i2 >> 5) & 0x3f) + ((i3 >> 5) & 0x3f) + ((i4 >> 5) & 0x3f); uint32_t b = ((i1 >> 11) + (i2 >> 11) + (i3 >> 11) + (i4 >> 11)); return (r >> 2) | ((g >> 2) << 5) | ((b >> 2) << 11); } -static inline uint32_t rsBoxFilter8888(uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4) -{ +static inline uint32_t rsBoxFilter8888(uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4) { uint32_t r = (i1 & 0xff) + (i2 & 0xff) + (i3 & 0xff) + (i4 & 0xff); uint32_t g = ((i1 >> 8) & 0xff) + ((i2 >> 8) & 0xff) + ((i3 >> 8) & 0xff) + ((i4 >> 8) & 0xff); uint32_t b = ((i1 >> 16) & 0xff) + ((i2 >> 16) & 0xff) + ((i3 >> 16) & 0xff) + ((i4 >> 16) & 0xff); @@ -121,8 +140,6 @@ static inline uint32_t rsBoxFilter8888(uint32_t i1, uint32_t i2, uint32_t i3, ui return (r >> 2) | ((g >> 2) << 8) | ((b >> 2) << 16) | ((a >> 2) << 24); } - - } } diff --git a/libs/rs/rsVertexArray.cpp b/libs/rs/rsVertexArray.cpp index 6c2002d79e10..354ee89f7c5b 100644 --- a/libs/rs/rsVertexArray.cpp +++ b/libs/rs/rsVertexArray.cpp @@ -15,205 +15,115 @@ */ #include "rsContext.h" - +#ifndef ANDROID_RS_SERIALIZE #include <GLES/gl.h> #include <GLES2/gl2.h> +#endif using namespace android; using namespace android::renderscript; - -VertexArray::VertexArray() -{ - clearAll(); +VertexArray::VertexArray(const Attrib *attribs, uint32_t numAttribs) { + mAttribs = attribs; + mCount = numAttribs; } -VertexArray::~VertexArray() -{ +VertexArray::~VertexArray() { } - -void VertexArray::clearAll() -{ - for (uint32_t ct=0; ct < RS_MAX_ATTRIBS; ct++) { - mAttribs[ct].clear(); - } - mActiveBuffer = 0; - mCount = 0; -} - -VertexArray::Attrib::Attrib() -{ +VertexArray::Attrib::Attrib() { clear(); } -void VertexArray::Attrib::set(const Attrib &a) -{ - buffer = a.buffer; - offset = a.offset; - type = a.type; - size = a.size; - stride = a.stride; - normalized = a.normalized; - kind = RS_KIND_USER; - name.setTo(a.name); -} - -void VertexArray::Attrib::clear() -{ +void VertexArray::Attrib::clear() { buffer = 0; offset = 0; type = 0; size = 0; stride = 0; + ptr = NULL; normalized = false; name.setTo(""); } -void VertexArray::clear(uint32_t n) -{ - mAttribs[n].clear(); -} - -void VertexArray::addUser(const Attrib &a, uint32_t stride) -{ - assert(mCount < RS_MAX_ATTRIBS); - mAttribs[mCount].set(a); - mAttribs[mCount].buffer = mActiveBuffer; - mAttribs[mCount].stride = stride; - mAttribs[mCount].kind = RS_KIND_USER; - mCount ++; -} - -void VertexArray::addLegacy(uint32_t type, uint32_t size, uint32_t stride, RsDataKind kind, bool normalized, uint32_t offset) -{ - assert(mCount < RS_MAX_ATTRIBS); - mAttribs[mCount].clear(); - mAttribs[mCount].type = type; - mAttribs[mCount].size = size; - mAttribs[mCount].offset = offset; - mAttribs[mCount].normalized = normalized; - mAttribs[mCount].buffer = mActiveBuffer; - mAttribs[mCount].stride = stride; - mAttribs[mCount].kind = kind; - mCount ++; +void VertexArray::Attrib::set(uint32_t type, uint32_t size, uint32_t stride, + bool normalized, uint32_t offset, + const char *name) { + clear(); + this->type = type; + this->size = size; + this->offset = offset; + this->normalized = normalized; + this->stride = stride; + this->name.setTo(name); } void VertexArray::logAttrib(uint32_t idx, uint32_t slot) const { - LOGE("va %i: slot=%i name=%s buf=%i size=%i type=0x%x kind=%i stride=0x%x norm=%i offset=0x%x", idx, slot, + if (idx == 0) { + LOGV("Starting vertex attribute binding"); + } + LOGV("va %i: slot=%i name=%s buf=%i ptr=%p size=%i type=0x%x stride=0x%x norm=%i offset=0x%x", + idx, slot, mAttribs[idx].name.string(), mAttribs[idx].buffer, + mAttribs[idx].ptr, mAttribs[idx].size, mAttribs[idx].type, - mAttribs[idx].kind, mAttribs[idx].stride, mAttribs[idx].normalized, mAttribs[idx].offset); } -void VertexArray::setupGL(const Context *rsc, class VertexArrayState *state) const -{ - glClientActiveTexture(GL_TEXTURE0); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_POINT_SIZE_ARRAY_OES); - - for (uint32_t ct=0; ct < mCount; ct++) { - switch(mAttribs[ct].kind) { - case RS_KIND_POSITION: - //logAttrib(POSITION); - glEnableClientState(GL_VERTEX_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer); - glVertexPointer(mAttribs[ct].size, - mAttribs[ct].type, - mAttribs[ct].stride, - (void *)mAttribs[ct].offset); - break; - - case RS_KIND_NORMAL: - //logAttrib(NORMAL); - glEnableClientState(GL_NORMAL_ARRAY); - rsAssert(mAttribs[ct].size == 3); - glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer); - glNormalPointer(mAttribs[ct].type, - mAttribs[ct].stride, - (void *)mAttribs[ct].offset); - break; - - case RS_KIND_COLOR: - //logAttrib(COLOR); - glEnableClientState(GL_COLOR_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer); - glColorPointer(mAttribs[ct].size, - mAttribs[ct].type, - mAttribs[ct].stride, - (void *)mAttribs[ct].offset); - break; - - case RS_KIND_TEXTURE: - //logAttrib(TEXTURE); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer); - glTexCoordPointer(mAttribs[ct].size, - mAttribs[ct].type, - mAttribs[ct].stride, - (void *)mAttribs[ct].offset); - break; - - case RS_KIND_POINT_SIZE: - //logAttrib(POINT_SIZE); - glEnableClientState(GL_POINT_SIZE_ARRAY_OES); - glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer); - glPointSizePointerOES(mAttribs[ct].type, - mAttribs[ct].stride, - (void *)mAttribs[ct].offset); - break; - - default: - rsAssert(0); - } - } - - rsc->checkError("VertexArray::setupGL"); -} - -void VertexArray::setupGL2(const Context *rsc, class VertexArrayState *state, ShaderCache *sc) const -{ +void VertexArray::setupGL2(const Context *rsc, + class VertexArrayState *state, + ShaderCache *sc) const { rsc->checkError("VertexArray::setupGL2 start"); - for (uint32_t ct=1; ct <= state->mLastEnableCount; ct++) { - glDisableVertexAttribArray(ct); + uint32_t maxAttrs = state->mAttrsEnabledSize; + for (uint32_t ct=1; ct < maxAttrs; ct++) { + if(state->mAttrsEnabled[ct]) { + glDisableVertexAttribArray(ct); + state->mAttrsEnabled[ct] = false; + } } rsc->checkError("VertexArray::setupGL2 disabled"); for (uint32_t ct=0; ct < mCount; ct++) { - uint32_t slot = 0; - if (sc->isUserVertexProgram()) { - slot = sc->vtxAttribSlot(ct); - } else { - if (mAttribs[ct].kind == RS_KIND_USER) { - continue; - } - slot = sc->vtxAttribSlot(mAttribs[ct].kind); + int32_t slot = sc->vtxAttribSlot(mAttribs[ct].name); + if (rsc->props.mLogShadersAttr) { + logAttrib(ct, slot); + } + if (slot < 0 || slot >= (int32_t)maxAttrs) { + continue; } - - //logAttrib(ct, slot); glEnableVertexAttribArray(slot); + state->mAttrsEnabled[slot] = true; glBindBuffer(GL_ARRAY_BUFFER, mAttribs[ct].buffer); - glVertexAttribPointer(slot, mAttribs[ct].size, mAttribs[ct].type, mAttribs[ct].normalized, mAttribs[ct].stride, - (void *)mAttribs[ct].offset); + mAttribs[ct].ptr + mAttribs[ct].offset); } - state->mLastEnableCount = mCount; rsc->checkError("VertexArray::setupGL2 done"); } //////////////////////////////////////////// +VertexArrayState::VertexArrayState() { + mAttrsEnabled = NULL; + mAttrsEnabledSize = 0; +} -void VertexArrayState::init(Context *) { - mLastEnableCount = 0; +VertexArrayState::~VertexArrayState() { + if (mAttrsEnabled) { + delete[] mAttrsEnabled; + mAttrsEnabled = NULL; + } +} +void VertexArrayState::init(Context *rsc) { + mAttrsEnabledSize = rsc->getMaxVertexAttributes(); + mAttrsEnabled = new bool[mAttrsEnabledSize]; + for (uint32_t ct = 0; ct < mAttrsEnabledSize; ct++) { + mAttrsEnabled[ct] = false; + } } diff --git a/libs/rs/rsVertexArray.h b/libs/rs/rsVertexArray.h index 3904cb69c231..45d9e82ab108 100644 --- a/libs/rs/rsVertexArray.h +++ b/libs/rs/rsVertexArray.h @@ -27,60 +27,54 @@ namespace renderscript { class ShaderCache; // An element is a group of Components that occupies one cell in a structure. -class VertexArray -{ +class VertexArray { public: - VertexArray(); - virtual ~VertexArray(); - - class Attrib { public: uint32_t buffer; + const uint8_t * ptr; uint32_t offset; uint32_t type; uint32_t size; uint32_t stride; bool normalized; String8 name; - RsDataKind kind; Attrib(); - void set(const Attrib &); void clear(); + void set(uint32_t type, uint32_t size, uint32_t stride, bool normalized, uint32_t offset, const char *name); }; + VertexArray(const Attrib *attribs, uint32_t numAttribs); + virtual ~VertexArray(); - void clearAll(); - void setActiveBuffer(uint32_t id) {mActiveBuffer = id;} - void addUser(const Attrib &, uint32_t stride); - void addLegacy(uint32_t type, uint32_t size, uint32_t stride, RsDataKind kind, bool normalized, uint32_t offset); - - void setupGL(const Context *rsc, class VertexArrayState *) const; void setupGL2(const Context *rsc, class VertexArrayState *, ShaderCache *) const; void logAttrib(uint32_t idx, uint32_t slot) const; protected: void clear(uint32_t index); uint32_t mActiveBuffer; + const uint8_t * mActivePointer; uint32_t mCount; - Attrib mAttribs[RS_MAX_ATTRIBS]; + const Attrib *mAttribs; }; class VertexArrayState { public: + VertexArrayState(); + ~VertexArrayState(); void init(Context *); - uint32_t mLastEnableCount; - //VertexArray::Attrib mAttribs[VertexArray::_LAST]; + bool *mAttrsEnabled; + uint32_t mAttrsEnabledSize; }; } } -#endif //ANDROID_LIGHT_H +#endif //ANDROID_VERTEX_ARRAY_H diff --git a/libs/rs/rsg_ScriptJavaClass.cpp b/libs/rs/rsg_ScriptJavaClass.cpp deleted file mode 100644 index cee9f52ea1df..000000000000 --- a/libs/rs/rsg_ScriptJavaClass.cpp +++ /dev/null @@ -1,254 +0,0 @@ -#define NO_RS_FUNCS 1 - -#include "stdio.h" -#include "RenderScript.h" -#include <vector> - -struct Element; - -struct ElementField { - const char *name; - Element *e; - ElementField(const char *n, Element *_e) { - name = n; - e = _e; - } - ElementField() { - name = NULL; - e = NULL; - } -}; - -struct Element { - ElementField *fields; - size_t fieldCount; - const char *name; - bool generated; - - RsDataType compType; - uint32_t compVectorSize; - - Element() { - fields = NULL; - fieldCount = 0; - name = NULL; - generated = false; - compType = RS_TYPE_ELEMENT; - compVectorSize = 0; - } - - Element(uint32_t _fieldCount, const char *_name) { - fields = new ElementField[_fieldCount]; - fieldCount = _fieldCount; - name = _name; - generated = false; - compType = RS_TYPE_ELEMENT; - compVectorSize = 0; - } - - Element(RsDataType t, uint32_t s) { - fields = NULL; - fieldCount = 0; - name = NULL; - generated = false; - compType = t; - compVectorSize = s; - } - -}; - - -static void genHeader(FILE *f, const char *packageName) -{ - fprintf(f, "package %s;\n", packageName); - fprintf(f, "\n"); - fprintf(f, "import android.renderscript.*;\n"); - fprintf(f, "\n"); - fprintf(f, "\n"); -} - -static const char * RSTypeToJava(RsDataType dt) -{ - switch(dt) { - //case RS_TYPE_FLOAT_16: return "float"; - case RS_TYPE_FLOAT_32: return "float"; - //case RS_TYPE_FLOAT_64: return "double"; - - case RS_TYPE_SIGNED_8: return "byte"; - case RS_TYPE_SIGNED_16: return "short"; - case RS_TYPE_SIGNED_32: return "int"; - //case RS_TYPE_SIGNED_64: return "long"; - - case RS_TYPE_UNSIGNED_8: return "short"; - case RS_TYPE_UNSIGNED_16: return "int"; - case RS_TYPE_UNSIGNED_32: return "long"; - //case RS_TYPE_UNSIGNED_64: return NULL; - - //case RS_TYPE_ELEMENT: return "android.renderscript.Element"; - //case RS_TYPE_TYPE: return "android.renderscript.Type"; - //case RS_TYPE_ALLOCATION: return "android.renderscript.Allocation"; - //case RS_TYPE_SAMPLER: return "android.renderscript.Sampler"; - //case RS_TYPE_SCRIPT: return "android.renderscript.Script"; - //case RS_TYPE_MESH: return "android.renderscript.Mesh"; - //case RS_TYPE_PROGRAM_FRAGMENT: return "android.renderscript.ProgramFragment"; - //case RS_TYPE_PROGRAM_VERTEX: return "android.renderscript.ProgramVertex"; - //case RS_TYPE_PROGRAM_RASTER: return "android.renderscript.ProgramRaster"; - //case RS_TYPE_PROGRAM_STORE: return "android.renderscript.ProgramStore"; - default: return NULL; - } - return NULL; -} - -static const char * RSTypeToString(RsDataType dt) -{ - switch(dt) { - case RS_TYPE_FLOAT_16: return "F16"; - case RS_TYPE_FLOAT_32: return "F32"; - case RS_TYPE_FLOAT_64: return "F64"; - - case RS_TYPE_SIGNED_8: return "I8"; - case RS_TYPE_SIGNED_16: return "I16"; - case RS_TYPE_SIGNED_32: return "I32"; - case RS_TYPE_SIGNED_64: return "I64"; - - case RS_TYPE_UNSIGNED_8: return "U8"; - case RS_TYPE_UNSIGNED_16: return "U16"; - case RS_TYPE_UNSIGNED_32: return "U32"; - case RS_TYPE_UNSIGNED_64: return "U64"; - - //case RS_TYPE_ELEMENT: return "android.renderscript.Element"; - //case RS_TYPE_TYPE: return "android.renderscript.Type"; - //case RS_TYPE_ALLOCATION: return "android.renderscript.Allocation"; - //case RS_TYPE_SAMPLER: return "android.renderscript.Sampler"; - //case RS_TYPE_SCRIPT: return "android.renderscript.Script"; - //case RS_TYPE_MESH: return "android.renderscript.Mesh"; - //case RS_TYPE_PROGRAM_FRAGMENT: return "android.renderscript.ProgramFragment"; - //case RS_TYPE_PROGRAM_VERTEX: return "android.renderscript.ProgramVertex"; - //case RS_TYPE_PROGRAM_RASTER: return "android.renderscript.ProgramRaster"; - //case RS_TYPE_PROGRAM_STORE: return "android.renderscript.ProgramStore"; - default: return NULL; - } - return NULL; -} - -bool rsGenerateElementClass(const Element *e, const char *packageName, FILE *f) -{ - genHeader(f, packageName); - - fprintf(f, "class Element_%s {\n", e->name); - - for (size_t ct=0; ct < e->fieldCount; ct++) { - const char *ts = RSTypeToJava(e->fields[ct].e->compType); - if (ts == NULL) { - return false; - } - fprintf(f, " public %s %s;\n", ts, e->fields[ct].name); - } - - fprintf(f, "\n"); - fprintf(f, " static Element getElement(RenderScript rs) {\n"); - fprintf(f, " Element.Builder eb = new Element.Builder(rs);\n"); - for (size_t ct=0; ct < e->fieldCount; ct++) { - const char *ts = RSTypeToString(e->fields[ct].e->compType); - fprintf(f, " eb.add(Element.USER_%s(rs), \"%s\");\n", ts, e->fields[ct].name); - } - fprintf(f, " return eb.create();\n"); - fprintf(f, " }\n"); - - fprintf(f, " static Allocation createAllocation(RenderScript rs) {\n"); - fprintf(f, " Element e = getElement(rs);\n"); - fprintf(f, " Allocation a = Allocation.createSized(rs, e, 1);\n"); - fprintf(f, " return a;\n"); - fprintf(f, " }\n"); - - - fprintf(f, " void copyToAllocation(Allocation a) {\n"); - fprintf(f, " mIOBuffer.reset();\n"); - for (size_t ct=0; ct < e->fieldCount; ct++) { - const char *ts = RSTypeToString(e->fields[ct].e->compType); - fprintf(f, " mIOBuffer.add%s(%s);\n", ts, e->fields[ct].name); - } - fprintf(f, " a.data(mIOBuffer.getData());\n"); - fprintf(f, " }\n"); - - - - fprintf(f, " private FieldPacker mIOBuffer[];\n"); - fprintf(f, " public Element_%s() {\n", e->name); - fprintf(f, " mIOBuffer = new FieldPacker(%i);\n", 100/*element->getSizeBytes()*/); - fprintf(f, " }\n"); - - - fprintf(f, "}\n"); - - return true; -} - -bool rsGenerateElementClassFile(Element *e, const char *packageName) -{ - char buf[1024]; - sprintf(buf, "Element_%s.java", e->name); - printf("Creating file %s \n", buf); - FILE *f = fopen(buf, "w"); - bool ret = rsGenerateElementClass(e, packageName, f); - fclose(f); - return ret; -} - - - - -/* -bool rsGenerateScriptClass(const ScriptC *script, const char *packageName, FILE *f) -{ - genHeader(f, packageName); - - fprintf(f, "class ScriptC_%s {\n", script->getName()); - - - - ObjectBaseRef<const Type> mTypes[MAX_SCRIPT_BANKS]; - String8 mSlotNames[MAX_SCRIPT_BANKS]; - bool mSlotWritable[MAX_SCRIPT_BANKS]; - - -} -*/ - - - -int main(int argc, const char *argv) -{ - Element *u8 = new Element(RS_TYPE_UNSIGNED_8, 1); - Element *i32 = new Element(RS_TYPE_SIGNED_32, 1); - Element *f32 = new Element(RS_TYPE_FLOAT_32, 1); - - Element *e_Pixel = new Element(4, "Pixel"); - e_Pixel->fields[0].e = u8; - e_Pixel->fields[0].name = "a"; - e_Pixel->fields[1].e = u8; - e_Pixel->fields[1].name = "b"; - e_Pixel->fields[2].e = u8; - e_Pixel->fields[2].name = "g"; - e_Pixel->fields[3].e = u8; - e_Pixel->fields[3].name = "r"; - - Element *e_Params = new Element(5, "Params"); - e_Params->fields[0].e = i32; - e_Params->fields[0].name = "inHeight"; - e_Params->fields[1].e = i32; - e_Params->fields[1].name = "inWidth"; - e_Params->fields[2].e = i32; - e_Params->fields[2].name = "outHeight"; - e_Params->fields[3].e = i32; - e_Params->fields[3].name = "outWidth"; - e_Params->fields[4].e = f32; - e_Params->fields[4].name = "threshold"; - - - printf("1\n"); - rsGenerateElementClassFile(e_Pixel, "android"); - rsGenerateElementClassFile(e_Params, "android"); - -} - diff --git a/libs/rs/rsg_generator.c b/libs/rs/rsg_generator.c index f4e8c685e1d1..4ac5b7fce136 100644 --- a/libs/rs/rsg_generator.c +++ b/libs/rs/rsg_generator.c @@ -2,8 +2,7 @@ #include "spec.h" #include <stdio.h> -void printFileHeader(FILE *f) -{ +void printFileHeader(FILE *f) { fprintf(f, "/*\n"); fprintf(f, " * Copyright (C) 2010 The Android Open Source Project\n"); fprintf(f, " *\n"); @@ -21,14 +20,13 @@ void printFileHeader(FILE *f) fprintf(f, " */\n\n"); } -void printVarType(FILE *f, const VarType *vt) -{ +void printVarType(FILE *f, const VarType *vt) { int ct; if (vt->isConst) { fprintf(f, "const "); } - switch(vt->type) { + switch (vt->type) { case 0: fprintf(f, "void"); break; @@ -49,22 +47,21 @@ void printVarType(FILE *f, const VarType *vt) break; } - if(vt->ptrLevel) { + if (vt->ptrLevel) { fprintf(f, " "); - for(ct=0; ct < vt->ptrLevel; ct++) { + for (ct=0; ct < vt->ptrLevel; ct++) { fprintf(f, "*"); } } - if(vt->name[0]) { + if (vt->name[0]) { fprintf(f, " %s", vt->name); } } -void printArgList(FILE *f, const ApiEntry * api, int assumePrevious) -{ +void printArgList(FILE *f, const ApiEntry * api, int assumePrevious) { int ct; - for(ct=0; ct < api->paramCount; ct++) { + for (ct=0; ct < api->paramCount; ct++) { if (ct || assumePrevious) { fprintf(f, ", "); } @@ -72,23 +69,22 @@ void printArgList(FILE *f, const ApiEntry * api, int assumePrevious) } } -void printStructures(FILE *f) -{ +void printStructures(FILE *f) { int ct; int ct2; - for(ct=0; ct < apiCount; ct++) { + for (ct=0; ct < apiCount; ct++) { fprintf(f, "typedef struct RS_CMD_%s_rec RS_CMD_%s;\n", apis[ct].name, apis[ct].name); } fprintf(f, "\n"); - for(ct=0; ct < apiCount; ct++) { + for (ct=0; ct < apiCount; ct++) { const ApiEntry * api = &apis[ct]; fprintf(f, "#define RS_CMD_ID_%s %i\n", api->name, ct+1); fprintf(f, "struct RS_CMD_%s_rec {\n", api->name); //fprintf(f, " RsCommandHeader _hdr;\n"); - for(ct2=0; ct2 < api->paramCount; ct2++) { + for (ct2=0; ct2 < api->paramCount; ct2++) { fprintf(f, " "); printVarType(f, &api->params[ct2]); fprintf(f, ";\n"); @@ -97,8 +93,7 @@ void printStructures(FILE *f) } } -void printFuncDecl(FILE *f, const ApiEntry *api, const char *prefix, int addContext) -{ +void printFuncDecl(FILE *f, const ApiEntry *api, const char *prefix, int addContext) { printVarType(f, &api->ret); fprintf(f, " %s%s (", prefix, api->name); if (addContext) { @@ -110,26 +105,23 @@ void printFuncDecl(FILE *f, const ApiEntry *api, const char *prefix, int addCont fprintf(f, ")"); } -void printFuncDecls(FILE *f, const char *prefix, int addContext) -{ +void printFuncDecls(FILE *f, const char *prefix, int addContext) { int ct; - for(ct=0; ct < apiCount; ct++) { + for (ct=0; ct < apiCount; ct++) { printFuncDecl(f, &apis[ct], prefix, addContext); fprintf(f, ";\n"); } fprintf(f, "\n\n"); } -void printPlaybackFuncs(FILE *f, const char *prefix) -{ +void printPlaybackFuncs(FILE *f, const char *prefix) { int ct; - for(ct=0; ct < apiCount; ct++) { + for (ct=0; ct < apiCount; ct++) { fprintf(f, "void %s%s (Context *, const void *);\n", prefix, apis[ct].name); } } -void printApiCpp(FILE *f) -{ +void printApiCpp(FILE *f) { int ct; int ct2; @@ -144,7 +136,7 @@ void printApiCpp(FILE *f) fprintf(f, "#include \"rsHandcode.h\"\n"); fprintf(f, "\n"); - for(ct=0; ct < apiCount; ct++) { + for (ct=0; ct < apiCount; ct++) { int needFlush = 0; const ApiEntry * api = &apis[ct]; @@ -152,7 +144,7 @@ void printApiCpp(FILE *f) fprintf(f, "\n{\n"); if (api->handcodeApi) { fprintf(f, " rsHCAPI_%s(rsc", api->name); - for(ct2=0; ct2 < api->paramCount; ct2++) { + for (ct2=0; ct2 < api->paramCount; ct2++) { const VarType *vt = &api->params[ct2]; fprintf(f, ", %s", vt->name); } @@ -163,7 +155,7 @@ void printApiCpp(FILE *f) fprintf(f, " RS_CMD_%s *cmd = static_cast<RS_CMD_%s *>(io->mToCore.reserve(sizeof(RS_CMD_%s)));\n", api->name, api->name, api->name); fprintf(f, " uint32_t size = sizeof(RS_CMD_%s);\n", api->name); - for(ct2=0; ct2 < api->paramCount; ct2++) { + for (ct2=0; ct2 < api->paramCount; ct2++) { const VarType *vt = &api->params[ct2]; needFlush += vt->ptrLevel; fprintf(f, " cmd->%s = %s;\n", vt->name, vt->name); @@ -188,8 +180,7 @@ void printApiCpp(FILE *f) } } -void printPlaybackCpp(FILE *f) -{ +void printPlaybackCpp(FILE *f) { int ct; int ct2; @@ -204,7 +195,7 @@ void printPlaybackCpp(FILE *f) fprintf(f, "#include \"rsHandcode.h\"\n"); fprintf(f, "\n"); - for(ct=0; ct < apiCount; ct++) { + for (ct=0; ct < apiCount; ct++) { const ApiEntry * api = &apis[ct]; fprintf(f, "void rsp_%s(Context *con, const void *vp)\n", api->name); @@ -219,7 +210,7 @@ void printPlaybackCpp(FILE *f) fprintf(f, "con->mIO.mToCoreRet = (intptr_t)"); } fprintf(f, "rsi_%s(con", api->name); - for(ct2=0; ct2 < api->paramCount; ct2++) { + for (ct2=0; ct2 < api->paramCount; ct2++) { const VarType *vt = &api->params[ct2]; fprintf(f, ",\n cmd->%s", vt->name); } @@ -228,9 +219,9 @@ void printPlaybackCpp(FILE *f) fprintf(f, "};\n\n"); } - fprintf(f, "RsPlaybackFunc gPlaybackFuncs[] = {\n"); + fprintf(f, "RsPlaybackFunc gPlaybackFuncs[%i] = {\n", apiCount + 1); fprintf(f, " NULL,\n"); - for(ct=0; ct < apiCount; ct++) { + for (ct=0; ct < apiCount; ct++) { fprintf(f, " %s%s,\n", "rsp_", apis[ct].name); } fprintf(f, "};\n"); @@ -239,8 +230,7 @@ void printPlaybackCpp(FILE *f) fprintf(f, "};\n"); } -int main(int argc, char **argv) -{ +int main(int argc, char **argv) { if (argc != 3) { fprintf(stderr, "usage: %s commandFile outFile\n", argv[0]); return 1; @@ -263,7 +253,7 @@ int main(int argc, char **argv) FILE *f = fopen(outFile, "w"); printFileHeader(f); - switch(choice) { + switch (choice) { case '0': // rsgApiStructs.h { fprintf(f, "\n"); @@ -275,7 +265,7 @@ int main(int argc, char **argv) printFuncDecls(f, "rsi_", 1); printPlaybackFuncs(f, "rsp_"); fprintf(f, "\n\ntypedef void (*RsPlaybackFunc)(Context *, const void *);\n"); - fprintf(f, "extern RsPlaybackFunc gPlaybackFuncs[];\n"); + fprintf(f, "extern RsPlaybackFunc gPlaybackFuncs[%i];\n", apiCount + 1); fprintf(f, "}\n"); fprintf(f, "}\n"); diff --git a/libs/rs/scriptc/rs_cl.rsh b/libs/rs/scriptc/rs_cl.rsh new file mode 100644 index 000000000000..d78e62e1d54a --- /dev/null +++ b/libs/rs/scriptc/rs_cl.rsh @@ -0,0 +1,449 @@ +#ifndef __RS_CL_RSH__ +#define __RS_CL_RSH__ + +#define _RS_RUNTIME extern + +// Conversions +#define CVT_FUNC_2(typeout, typein) \ +_RS_RUNTIME typeout##2 __attribute__((overloadable)) \ + convert_##typeout##2(typein##2 v); \ +_RS_RUNTIME typeout##3 __attribute__((overloadable)) \ + convert_##typeout##3(typein##3 v); \ +_RS_RUNTIME typeout##4 __attribute__((overloadable)) \ + convert_##typeout##4(typein##4 v); + + +#define CVT_FUNC(type) CVT_FUNC_2(type, uchar) \ + CVT_FUNC_2(type, char) \ + CVT_FUNC_2(type, ushort) \ + CVT_FUNC_2(type, short) \ + CVT_FUNC_2(type, uint) \ + CVT_FUNC_2(type, int) \ + CVT_FUNC_2(type, float) + +CVT_FUNC(char) +CVT_FUNC(uchar) +CVT_FUNC(short) +CVT_FUNC(ushort) +CVT_FUNC(int) +CVT_FUNC(uint) +CVT_FUNC(float) + +// Float ops, 6.11.2 + +#define FN_FUNC_FN(fnc) \ +_RS_RUNTIME float2 __attribute__((overloadable)) fnc(float2 v); \ +_RS_RUNTIME float3 __attribute__((overloadable)) fnc(float3 v); \ +_RS_RUNTIME float4 __attribute__((overloadable)) fnc(float4 v); + +#define IN_FUNC_FN(fnc) \ +_RS_RUNTIME int2 __attribute__((overloadable)) fnc(float2 v); \ +_RS_RUNTIME int3 __attribute__((overloadable)) fnc(float3 v); \ +_RS_RUNTIME int4 __attribute__((overloadable)) fnc(float4 v); + +#define FN_FUNC_FN_FN(fnc) \ +_RS_RUNTIME float2 __attribute__((overloadable)) fnc(float2 v1, float2 v2); \ +_RS_RUNTIME float3 __attribute__((overloadable)) fnc(float3 v1, float3 v2); \ +_RS_RUNTIME float4 __attribute__((overloadable)) fnc(float4 v1, float4 v2); + +#define FN_FUNC_FN_F(fnc) \ +_RS_RUNTIME float2 __attribute__((overloadable)) fnc(float2 v1, float v2); \ +_RS_RUNTIME float3 __attribute__((overloadable)) fnc(float3 v1, float v2); \ +_RS_RUNTIME float4 __attribute__((overloadable)) fnc(float4 v1, float v2); + +#define FN_FUNC_FN_IN(fnc) \ +_RS_RUNTIME float2 __attribute__((overloadable)) fnc(float2 v1, int2 v2); \ +_RS_RUNTIME float3 __attribute__((overloadable)) fnc(float3 v1, int3 v2); \ +_RS_RUNTIME float4 __attribute__((overloadable)) fnc(float4 v1, int4 v2); \ + +#define FN_FUNC_FN_I(fnc) \ +_RS_RUNTIME float2 __attribute__((overloadable)) fnc(float2 v1, int v2); \ +_RS_RUNTIME float3 __attribute__((overloadable)) fnc(float3 v1, int v2); \ +_RS_RUNTIME float4 __attribute__((overloadable)) fnc(float4 v1, int v2); + +#define FN_FUNC_FN_PFN(fnc) \ +_RS_RUNTIME float2 __attribute__((overloadable)) \ + fnc(float2 v1, float2 *v2); \ +_RS_RUNTIME float3 __attribute__((overloadable)) \ + fnc(float3 v1, float3 *v2); \ +_RS_RUNTIME float4 __attribute__((overloadable)) \ + fnc(float4 v1, float4 *v2); + +#define FN_FUNC_FN_PIN(fnc) \ +_RS_RUNTIME float2 __attribute__((overloadable)) fnc(float2 v1, int2 *v2); \ +_RS_RUNTIME float3 __attribute__((overloadable)) fnc(float3 v1, int3 *v2); \ +_RS_RUNTIME float4 __attribute__((overloadable)) fnc(float4 v1, int4 *v2); + +#define FN_FUNC_FN_FN_FN(fnc) \ +_RS_RUNTIME float2 __attribute__((overloadable)) \ + fnc(float2 v1, float2 v2, float2 v3); \ +_RS_RUNTIME float3 __attribute__((overloadable)) \ + fnc(float3 v1, float3 v2, float3 v3); \ +_RS_RUNTIME float4 __attribute__((overloadable)) \ + fnc(float4 v1, float4 v2, float4 v3); + +#define FN_FUNC_FN_FN_PIN(fnc) \ +_RS_RUNTIME float2 __attribute__((overloadable)) \ + fnc(float2 v1, float2 v2, int2 *v3); \ +_RS_RUNTIME float3 __attribute__((overloadable)) \ + fnc(float3 v1, float3 v2, int3 *v3); \ +_RS_RUNTIME float4 __attribute__((overloadable)) \ + fnc(float4 v1, float4 v2, int4 *v3); + + +extern float __attribute__((overloadable)) acos(float); +FN_FUNC_FN(acos) + +extern float __attribute__((overloadable)) acosh(float); +FN_FUNC_FN(acosh) + +_RS_RUNTIME float __attribute__((overloadable)) acospi(float v); + + +FN_FUNC_FN(acospi) + +extern float __attribute__((overloadable)) asin(float); +FN_FUNC_FN(asin) + +extern float __attribute__((overloadable)) asinh(float); +FN_FUNC_FN(asinh) + + +_RS_RUNTIME float __attribute__((overloadable)) asinpi(float v); +FN_FUNC_FN(asinpi) + +extern float __attribute__((overloadable)) atan(float); +FN_FUNC_FN(atan) + +extern float __attribute__((overloadable)) atan2(float, float); +FN_FUNC_FN_FN(atan2) + +extern float __attribute__((overloadable)) atanh(float); +FN_FUNC_FN(atanh) + + +_RS_RUNTIME float __attribute__((overloadable)) atanpi(float v); +FN_FUNC_FN(atanpi) + + +_RS_RUNTIME float __attribute__((overloadable)) atan2pi(float y, float x); +FN_FUNC_FN_FN(atan2pi) + +extern float __attribute__((overloadable)) cbrt(float); +FN_FUNC_FN(cbrt) + +extern float __attribute__((overloadable)) ceil(float); +FN_FUNC_FN(ceil) + +extern float __attribute__((overloadable)) copysign(float, float); +FN_FUNC_FN_FN(copysign) + +extern float __attribute__((overloadable)) cos(float); +FN_FUNC_FN(cos) + +extern float __attribute__((overloadable)) cosh(float); +FN_FUNC_FN(cosh) + + +_RS_RUNTIME float __attribute__((overloadable)) cospi(float v); +FN_FUNC_FN(cospi) + +extern float __attribute__((overloadable)) erfc(float); +FN_FUNC_FN(erfc) + +extern float __attribute__((overloadable)) erf(float); +FN_FUNC_FN(erf) + +extern float __attribute__((overloadable)) exp(float); +FN_FUNC_FN(exp) + +extern float __attribute__((overloadable)) exp2(float); +FN_FUNC_FN(exp2) + +extern float __attribute__((overloadable)) pow(float, float); + +_RS_RUNTIME float __attribute__((overloadable)) exp10(float v); +FN_FUNC_FN(exp10) + +extern float __attribute__((overloadable)) expm1(float); +FN_FUNC_FN(expm1) + +extern float __attribute__((overloadable)) fabs(float); +FN_FUNC_FN(fabs) + +extern float __attribute__((overloadable)) fdim(float, float); +FN_FUNC_FN_FN(fdim) + +extern float __attribute__((overloadable)) floor(float); +FN_FUNC_FN(floor) + +extern float __attribute__((overloadable)) fma(float, float, float); +FN_FUNC_FN_FN_FN(fma) + +extern float __attribute__((overloadable)) fmax(float, float); +FN_FUNC_FN_FN(fmax); +FN_FUNC_FN_F(fmax); + +extern float __attribute__((overloadable)) fmin(float, float); +FN_FUNC_FN_FN(fmin); +FN_FUNC_FN_F(fmin); + +extern float __attribute__((overloadable)) fmod(float, float); +FN_FUNC_FN_FN(fmod) + + +_RS_RUNTIME float __attribute__((overloadable)) fract(float v, float *iptr); +FN_FUNC_FN_PFN(fract) + +extern float __attribute__((overloadable)) frexp(float, int *); +FN_FUNC_FN_PIN(frexp) + +extern float __attribute__((overloadable)) hypot(float, float); +FN_FUNC_FN_FN(hypot) + +extern int __attribute__((overloadable)) ilogb(float); +IN_FUNC_FN(ilogb) + +extern float __attribute__((overloadable)) ldexp(float, int); +FN_FUNC_FN_IN(ldexp) +FN_FUNC_FN_I(ldexp) + +extern float __attribute__((overloadable)) lgamma(float); +FN_FUNC_FN(lgamma) +extern float __attribute__((overloadable)) lgamma(float, int*); +FN_FUNC_FN_PIN(lgamma) + +extern float __attribute__((overloadable)) log(float); +FN_FUNC_FN(log) + + +extern float __attribute__((overloadable)) log10(float); +FN_FUNC_FN(log10) + + +_RS_RUNTIME float __attribute__((overloadable)) log2(float v); +FN_FUNC_FN(log2) + +extern float __attribute__((overloadable)) log1p(float); +FN_FUNC_FN(log1p) + +extern float __attribute__((overloadable)) logb(float); +FN_FUNC_FN(logb) + +extern float __attribute__((overloadable)) mad(float, float, float); +FN_FUNC_FN_FN_FN(mad) + +extern float __attribute__((overloadable)) modf(float, float *); +FN_FUNC_FN_PFN(modf); + +//extern float __attribute__((overloadable)) nan(uint); + +extern float __attribute__((overloadable)) nextafter(float, float); +FN_FUNC_FN_FN(nextafter) + +FN_FUNC_FN_FN(pow) + +_RS_RUNTIME float __attribute__((overloadable)) pown(float v, int p); +_RS_RUNTIME float2 __attribute__((overloadable)) pown(float2 v, int2 p); +_RS_RUNTIME float3 __attribute__((overloadable)) pown(float3 v, int3 p); +_RS_RUNTIME float4 __attribute__((overloadable)) pown(float4 v, int4 p); + +_RS_RUNTIME float __attribute__((overloadable)) powr(float v, float p); +_RS_RUNTIME float2 __attribute__((overloadable)) powr(float2 v, float2 p); +_RS_RUNTIME float3 __attribute__((overloadable)) powr(float3 v, float3 p); +_RS_RUNTIME float4 __attribute__((overloadable)) powr(float4 v, float4 p); + +extern float __attribute__((overloadable)) remainder(float, float); +FN_FUNC_FN_FN(remainder) + +extern float __attribute__((overloadable)) remquo(float, float, int *); +FN_FUNC_FN_FN_PIN(remquo) + +extern float __attribute__((overloadable)) rint(float); +FN_FUNC_FN(rint) + + +_RS_RUNTIME float __attribute__((overloadable)) rootn(float v, int r); +_RS_RUNTIME float2 __attribute__((overloadable)) rootn(float2 v, int2 r); +_RS_RUNTIME float3 __attribute__((overloadable)) rootn(float3 v, int3 r); +_RS_RUNTIME float4 __attribute__((overloadable)) rootn(float4 v, int4 r); + + +extern float __attribute__((overloadable)) round(float); +FN_FUNC_FN(round) + + +extern float __attribute__((overloadable)) sqrt(float); +_RS_RUNTIME float __attribute__((overloadable)) rsqrt(float v); +FN_FUNC_FN(rsqrt) + +extern float __attribute__((overloadable)) sin(float); +FN_FUNC_FN(sin) + +_RS_RUNTIME float __attribute__((overloadable)) sincos(float v, float *cosptr); +_RS_RUNTIME float2 __attribute__((overloadable)) sincos(float2 v, float2 *cosptr); +_RS_RUNTIME float3 __attribute__((overloadable)) sincos(float3 v, float3 *cosptr); +_RS_RUNTIME float4 __attribute__((overloadable)) sincos(float4 v, float4 *cosptr); + +extern float __attribute__((overloadable)) sinh(float); +FN_FUNC_FN(sinh) + +_RS_RUNTIME float __attribute__((overloadable)) sinpi(float v); +FN_FUNC_FN(sinpi) + +FN_FUNC_FN(sqrt) + +extern float __attribute__((overloadable)) tan(float); +FN_FUNC_FN(tan) + +extern float __attribute__((overloadable)) tanh(float); +FN_FUNC_FN(tanh) + +_RS_RUNTIME float __attribute__((overloadable)) tanpi(float v); +FN_FUNC_FN(tanpi) + + +extern float __attribute__((overloadable)) tgamma(float); +FN_FUNC_FN(tgamma) + +extern float __attribute__((overloadable)) trunc(float); +FN_FUNC_FN(trunc) + +// Int ops (partial), 6.11.3 + +#define XN_FUNC_YN(typeout, fnc, typein) \ +extern typeout __attribute__((overloadable)) fnc(typein); \ +_RS_RUNTIME typeout##2 __attribute__((overloadable)) fnc(typein##2 v); \ +_RS_RUNTIME typeout##3 __attribute__((overloadable)) fnc(typein##3 v); \ +_RS_RUNTIME typeout##4 __attribute__((overloadable)) fnc(typein##4 v); + +#define UIN_FUNC_IN(fnc) \ +XN_FUNC_YN(uchar, fnc, char) \ +XN_FUNC_YN(ushort, fnc, short) \ +XN_FUNC_YN(uint, fnc, int) + +#define IN_FUNC_IN(fnc) \ +XN_FUNC_YN(uchar, fnc, uchar) \ +XN_FUNC_YN(char, fnc, char) \ +XN_FUNC_YN(ushort, fnc, ushort) \ +XN_FUNC_YN(short, fnc, short) \ +XN_FUNC_YN(uint, fnc, uint) \ +XN_FUNC_YN(int, fnc, int) + + +#define XN_FUNC_XN_XN_BODY(type, fnc, body) \ +_RS_RUNTIME type __attribute__((overloadable)) \ + fnc(type v1, type v2); \ +_RS_RUNTIME type##2 __attribute__((overloadable)) \ + fnc(type##2 v1, type##2 v2); \ +_RS_RUNTIME type##3 __attribute__((overloadable)) \ + fnc(type##3 v1, type##3 v2); \ +_RS_RUNTIME type##4 __attribute__((overloadable)) \ + fnc(type##4 v1, type##4 v2); + +#define IN_FUNC_IN_IN_BODY(fnc, body) \ +XN_FUNC_XN_XN_BODY(uchar, fnc, body) \ +XN_FUNC_XN_XN_BODY(char, fnc, body) \ +XN_FUNC_XN_XN_BODY(ushort, fnc, body) \ +XN_FUNC_XN_XN_BODY(short, fnc, body) \ +XN_FUNC_XN_XN_BODY(uint, fnc, body) \ +XN_FUNC_XN_XN_BODY(int, fnc, body) \ +XN_FUNC_XN_XN_BODY(float, fnc, body) + +UIN_FUNC_IN(abs) +IN_FUNC_IN(clz) + +IN_FUNC_IN_IN_BODY(min, (v1 < v2 ? v1 : v2)) +FN_FUNC_FN_F(min) + +IN_FUNC_IN_IN_BODY(max, (v1 > v2 ? v1 : v2)) +FN_FUNC_FN_F(max) + +// 6.11.4 + +_RS_RUNTIME float __attribute__((overloadable)) clamp(float amount, float low, float high); +_RS_RUNTIME float2 __attribute__((overloadable)) clamp(float2 amount, float2 low, float2 high); +_RS_RUNTIME float3 __attribute__((overloadable)) clamp(float3 amount, float3 low, float3 high); +_RS_RUNTIME float4 __attribute__((overloadable)) clamp(float4 amount, float4 low, float4 high); +_RS_RUNTIME float2 __attribute__((overloadable)) clamp(float2 amount, float low, float high); +_RS_RUNTIME float3 __attribute__((overloadable)) clamp(float3 amount, float low, float high); +_RS_RUNTIME float4 __attribute__((overloadable)) clamp(float4 amount, float low, float high); + +_RS_RUNTIME float __attribute__((overloadable)) degrees(float radians); +FN_FUNC_FN(degrees) + +_RS_RUNTIME float __attribute__((overloadable)) mix(float start, float stop, float amount); +_RS_RUNTIME float2 __attribute__((overloadable)) mix(float2 start, float2 stop, float2 amount); +_RS_RUNTIME float3 __attribute__((overloadable)) mix(float3 start, float3 stop, float3 amount); +_RS_RUNTIME float4 __attribute__((overloadable)) mix(float4 start, float4 stop, float4 amount); +_RS_RUNTIME float2 __attribute__((overloadable)) mix(float2 start, float2 stop, float amount); +_RS_RUNTIME float3 __attribute__((overloadable)) mix(float3 start, float3 stop, float amount); +_RS_RUNTIME float4 __attribute__((overloadable)) mix(float4 start, float4 stop, float amount); + +_RS_RUNTIME float __attribute__((overloadable)) radians(float degrees); +FN_FUNC_FN(radians) + +_RS_RUNTIME float __attribute__((overloadable)) step(float edge, float v); +_RS_RUNTIME float2 __attribute__((overloadable)) step(float2 edge, float2 v); +_RS_RUNTIME float3 __attribute__((overloadable)) step(float3 edge, float3 v); +_RS_RUNTIME float4 __attribute__((overloadable)) step(float4 edge, float4 v); +_RS_RUNTIME float2 __attribute__((overloadable)) step(float2 edge, float v); +_RS_RUNTIME float3 __attribute__((overloadable)) step(float3 edge, float v); +_RS_RUNTIME float4 __attribute__((overloadable)) step(float4 edge, float v); + +extern float __attribute__((overloadable)) smoothstep(float, float, float); +extern float2 __attribute__((overloadable)) smoothstep(float2, float2, float2); +extern float3 __attribute__((overloadable)) smoothstep(float3, float3, float3); +extern float4 __attribute__((overloadable)) smoothstep(float4, float4, float4); +extern float2 __attribute__((overloadable)) smoothstep(float, float, float2); +extern float3 __attribute__((overloadable)) smoothstep(float, float, float3); +extern float4 __attribute__((overloadable)) smoothstep(float, float, float4); + +_RS_RUNTIME float __attribute__((overloadable)) sign(float v); +FN_FUNC_FN(sign) + +// 6.11.5 +_RS_RUNTIME float3 __attribute__((overloadable)) cross(float3 lhs, float3 rhs); + +_RS_RUNTIME float4 __attribute__((overloadable)) cross(float4 lhs, float4 rhs); + +_RS_RUNTIME float __attribute__((overloadable)) dot(float lhs, float rhs); +_RS_RUNTIME float __attribute__((overloadable)) dot(float2 lhs, float2 rhs); +_RS_RUNTIME float __attribute__((overloadable)) dot(float3 lhs, float3 rhs); +_RS_RUNTIME float __attribute__((overloadable)) dot(float4 lhs, float4 rhs); + +_RS_RUNTIME float __attribute__((overloadable)) length(float v); +_RS_RUNTIME float __attribute__((overloadable)) length(float2 v); +_RS_RUNTIME float __attribute__((overloadable)) length(float3 v); +_RS_RUNTIME float __attribute__((overloadable)) length(float4 v); + +_RS_RUNTIME float __attribute__((overloadable)) distance(float lhs, float rhs); +_RS_RUNTIME float __attribute__((overloadable)) distance(float2 lhs, float2 rhs); +_RS_RUNTIME float __attribute__((overloadable)) distance(float3 lhs, float3 rhs); +_RS_RUNTIME float __attribute__((overloadable)) distance(float4 lhs, float4 rhs); + +_RS_RUNTIME float __attribute__((overloadable)) normalize(float v); +_RS_RUNTIME float2 __attribute__((overloadable)) normalize(float2 v); +_RS_RUNTIME float3 __attribute__((overloadable)) normalize(float3 v); +_RS_RUNTIME float4 __attribute__((overloadable)) normalize(float4 v); + +#undef CVT_FUNC +#undef CVT_FUNC_2 +#undef FN_FUNC_FN +#undef IN_FUNC_FN +#undef FN_FUNC_FN_FN +#undef FN_FUNC_FN_F +#undef FN_FUNC_FN_IN +#undef FN_FUNC_FN_I +#undef FN_FUNC_FN_PFN +#undef FN_FUNC_FN_PIN +#undef FN_FUNC_FN_FN_FN +#undef FN_FUNC_FN_FN_PIN +#undef XN_FUNC_YN +#undef UIN_FUNC_IN +#undef IN_FUNC_IN +#undef XN_FUNC_XN_XN_BODY +#undef IN_FUNC_IN_IN_BODY +#undef _RS_RUNTIME + +#endif diff --git a/libs/rs/scriptc/rs_core.rsh b/libs/rs/scriptc/rs_core.rsh new file mode 100644 index 000000000000..4768bbed1f14 --- /dev/null +++ b/libs/rs/scriptc/rs_core.rsh @@ -0,0 +1,560 @@ +#ifndef __RS_CORE_RSH__ +#define __RS_CORE_RSH__ + +#define _RS_RUNTIME extern + +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, float); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, float, float); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, float, float, float); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, float, float, float, float); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, double); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, const rs_matrix4x4 *); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, const rs_matrix3x3 *); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, const rs_matrix2x2 *); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, int); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, uint); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, long); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, unsigned long); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, long long); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, unsigned long long); +/** + * Debug function. Prints a string and value to the log. + */ +extern void __attribute__((overloadable)) + rsDebug(const char *, const void *); +#define RS_DEBUG(a) rsDebug(#a, a) +#define RS_DEBUG_MARKER rsDebug(__FILE__, __LINE__) + + +/** + * Debug function. Prints a string and value to the log. + */ +_RS_RUNTIME void __attribute__((overloadable)) rsDebug(const char *s, float2 v); +/** + * Debug function. Prints a string and value to the log. + */ +_RS_RUNTIME void __attribute__((overloadable)) rsDebug(const char *s, float3 v); +/** + * Debug function. Prints a string and value to the log. + */ +_RS_RUNTIME void __attribute__((overloadable)) rsDebug(const char *s, float4 v); + + +/** + * Pack floating point (0-1) RGB values into a uchar4. The alpha component is + * set to 255 (1.0). + * + * @param r + * @param g + * @param b + * + * @return uchar4 + */ +_RS_RUNTIME uchar4 __attribute__((overloadable)) rsPackColorTo8888(float r, float g, float b); + +/** + * Pack floating point (0-1) RGBA values into a uchar4. + * + * @param r + * @param g + * @param b + * @param a + * + * @return uchar4 + */ +_RS_RUNTIME uchar4 __attribute__((overloadable)) rsPackColorTo8888(float r, float g, float b, float a); + +/** + * Pack floating point (0-1) RGB values into a uchar4. The alpha component is + * set to 255 (1.0). + * + * @param color + * + * @return uchar4 + */ +_RS_RUNTIME uchar4 __attribute__((overloadable)) rsPackColorTo8888(float3 color); + +/** + * Pack floating point (0-1) RGBA values into a uchar4. + * + * @param color + * + * @return uchar4 + */ +_RS_RUNTIME uchar4 __attribute__((overloadable)) rsPackColorTo8888(float4 color); + +/** + * Unpack a uchar4 color to float4. The resulting float range will be (0-1). + * + * @param c + * + * @return float4 + */ +_RS_RUNTIME float4 rsUnpackColor8888(uchar4 c); + + +///////////////////////////////////////////////////// +// Matrix ops +///////////////////////////////////////////////////// + +/** + * Set one element of a matrix. + * + * @param m The matrix to be set + * @param row + * @param col + * @param v + * + * @return void + */ +_RS_RUNTIME void __attribute__((overloadable)) +rsMatrixSet(rs_matrix4x4 *m, uint32_t row, uint32_t col, float v); +_RS_RUNTIME void __attribute__((overloadable)) +rsMatrixSet(rs_matrix3x3 *m, uint32_t row, uint32_t col, float v); +_RS_RUNTIME void __attribute__((overloadable)) +rsMatrixSet(rs_matrix2x2 *m, uint32_t row, uint32_t col, float v); + +/** + * Get one element of a matrix. + * + * @param m The matrix to read from + * @param row + * @param col + * + * @return float + */ +_RS_RUNTIME float __attribute__((overloadable)) +rsMatrixGet(const rs_matrix4x4 *m, uint32_t row, uint32_t col); +_RS_RUNTIME float __attribute__((overloadable)) +rsMatrixGet(const rs_matrix3x3 *m, uint32_t row, uint32_t col); +_RS_RUNTIME float __attribute__((overloadable)) +rsMatrixGet(const rs_matrix2x2 *m, uint32_t row, uint32_t col); + +/** + * Set the elements of a matrix to the identity matrix. + * + * @param m + */ +extern void __attribute__((overloadable)) rsMatrixLoadIdentity(rs_matrix4x4 *m); +extern void __attribute__((overloadable)) rsMatrixLoadIdentity(rs_matrix3x3 *m); +extern void __attribute__((overloadable)) rsMatrixLoadIdentity(rs_matrix2x2 *m); + +/** + * Set the elements of a matrix from an array of floats. + * + * @param m + */ +extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix4x4 *m, const float *v); +extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix3x3 *m, const float *v); +extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix2x2 *m, const float *v); + +/** + * Set the elements of a matrix from another matrix. + * + * @param m + */ +extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix4x4 *m, const rs_matrix4x4 *v); +extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix4x4 *m, const rs_matrix3x3 *v); +extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix4x4 *m, const rs_matrix2x2 *v); +extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix3x3 *m, const rs_matrix3x3 *v); +extern void __attribute__((overloadable)) rsMatrixLoad(rs_matrix2x2 *m, const rs_matrix2x2 *v); + +/** + * Load a rotation matrix. + * + * @param m + * @param rot + * @param x + * @param y + * @param z + */ +extern void __attribute__((overloadable)) +rsMatrixLoadRotate(rs_matrix4x4 *m, float rot, float x, float y, float z); + +extern void __attribute__((overloadable)) +rsMatrixLoadScale(rs_matrix4x4 *m, float x, float y, float z); + +extern void __attribute__((overloadable)) +rsMatrixLoadTranslate(rs_matrix4x4 *m, float x, float y, float z); + +extern void __attribute__((overloadable)) +rsMatrixLoadMultiply(rs_matrix4x4 *m, const rs_matrix4x4 *lhs, const rs_matrix4x4 *rhs); + +extern void __attribute__((overloadable)) +rsMatrixMultiply(rs_matrix4x4 *m, const rs_matrix4x4 *rhs); + +extern void __attribute__((overloadable)) +rsMatrixLoadMultiply(rs_matrix3x3 *m, const rs_matrix3x3 *lhs, const rs_matrix3x3 *rhs); + +extern void __attribute__((overloadable)) +rsMatrixMultiply(rs_matrix3x3 *m, const rs_matrix3x3 *rhs); + +extern void __attribute__((overloadable)) +rsMatrixLoadMultiply(rs_matrix2x2 *m, const rs_matrix2x2 *lhs, const rs_matrix2x2 *rhs); + +extern void __attribute__((overloadable)) +rsMatrixMultiply(rs_matrix2x2 *m, const rs_matrix2x2 *rhs); + +extern void __attribute__((overloadable)) +rsMatrixRotate(rs_matrix4x4 *m, float rot, float x, float y, float z); + +extern void __attribute__((overloadable)) +rsMatrixScale(rs_matrix4x4 *m, float x, float y, float z); + +extern void __attribute__((overloadable)) +rsMatrixTranslate(rs_matrix4x4 *m, float x, float y, float z); + +extern void __attribute__((overloadable)) +rsMatrixLoadOrtho(rs_matrix4x4 *m, float left, float right, float bottom, float top, float near, float far); + +extern void __attribute__((overloadable)) +rsMatrixLoadFrustum(rs_matrix4x4 *m, float left, float right, float bottom, float top, float near, float far); + +extern void __attribute__((overloadable)) +rsMatrixLoadPerspective(rs_matrix4x4* m, float fovy, float aspect, float near, float far); + +_RS_RUNTIME float4 __attribute__((overloadable)) +rsMatrixMultiply(rs_matrix4x4 *m, float4 in); + +_RS_RUNTIME float4 __attribute__((overloadable)) +rsMatrixMultiply(rs_matrix4x4 *m, float3 in); + +_RS_RUNTIME float4 __attribute__((overloadable)) +rsMatrixMultiply(rs_matrix4x4 *m, float2 in); + +_RS_RUNTIME float3 __attribute__((overloadable)) +rsMatrixMultiply(rs_matrix3x3 *m, float3 in); + +_RS_RUNTIME float3 __attribute__((overloadable)) +rsMatrixMultiply(rs_matrix3x3 *m, float2 in); + +_RS_RUNTIME float2 __attribute__((overloadable)) +rsMatrixMultiply(rs_matrix2x2 *m, float2 in); + +// Returns true if the matrix was successfully inversed +extern bool __attribute__((overloadable)) rsMatrixInverse(rs_matrix4x4 *m); +extern bool __attribute__((overloadable)) rsMatrixInverseTranspose(rs_matrix4x4 *m); +extern void __attribute__((overloadable)) rsMatrixTranspose(rs_matrix4x4 *m); +extern void __attribute__((overloadable)) rsMatrixTranspose(rs_matrix3x3 *m); +extern void __attribute__((overloadable)) rsMatrixTranspose(rs_matrix2x2 *m); + +///////////////////////////////////////////////////// +// quaternion ops +///////////////////////////////////////////////////// + +static void __attribute__((overloadable)) +rsQuaternionSet(rs_quaternion *q, float w, float x, float y, float z) { + q->w = w; + q->x = x; + q->y = y; + q->z = z; +} + +static void __attribute__((overloadable)) +rsQuaternionSet(rs_quaternion *q, const rs_quaternion *rhs) { + q->w = rhs->w; + q->x = rhs->x; + q->y = rhs->y; + q->z = rhs->z; +} + +static void __attribute__((overloadable)) +rsQuaternionMultiply(rs_quaternion *q, float s) { + q->w *= s; + q->x *= s; + q->y *= s; + q->z *= s; +} + +static void __attribute__((overloadable)) +rsQuaternionMultiply(rs_quaternion *q, const rs_quaternion *rhs) { + q->w = -q->x*rhs->x - q->y*rhs->y - q->z*rhs->z + q->w*rhs->w; + q->x = q->x*rhs->w + q->y*rhs->z - q->z*rhs->y + q->w*rhs->x; + q->y = -q->x*rhs->z + q->y*rhs->w + q->z*rhs->x + q->w*rhs->y; + q->z = q->x*rhs->y - q->y*rhs->x + q->z*rhs->w + q->w*rhs->z; +} + +static void +rsQuaternionAdd(rs_quaternion *q, const rs_quaternion *rhs) { + q->w *= rhs->w; + q->x *= rhs->x; + q->y *= rhs->y; + q->z *= rhs->z; +} + +static void +rsQuaternionLoadRotateUnit(rs_quaternion *q, float rot, float x, float y, float z) { + rot *= (float)(M_PI / 180.0f) * 0.5f; + float c = cos(rot); + float s = sin(rot); + + q->w = c; + q->x = x * s; + q->y = y * s; + q->z = z * s; +} + +static void +rsQuaternionLoadRotate(rs_quaternion *q, float rot, float x, float y, float z) { + const float len = x*x + y*y + z*z; + if (len != 1) { + const float recipLen = 1.f / sqrt(len); + x *= recipLen; + y *= recipLen; + z *= recipLen; + } + rsQuaternionLoadRotateUnit(q, rot, x, y, z); +} + +static void +rsQuaternionConjugate(rs_quaternion *q) { + q->x = -q->x; + q->y = -q->y; + q->z = -q->z; +} + +static float +rsQuaternionDot(const rs_quaternion *q0, const rs_quaternion *q1) { + return q0->w*q1->w + q0->x*q1->x + q0->y*q1->y + q0->z*q1->z; +} + +static void +rsQuaternionNormalize(rs_quaternion *q) { + const float len = rsQuaternionDot(q, q); + if (len != 1) { + const float recipLen = 1.f / sqrt(len); + rsQuaternionMultiply(q, recipLen); + } +} + +static void +rsQuaternionSlerp(rs_quaternion *q, const rs_quaternion *q0, const rs_quaternion *q1, float t) { + if (t <= 0.0f) { + rsQuaternionSet(q, q0); + return; + } + if (t >= 1.0f) { + rsQuaternionSet(q, q1); + return; + } + + rs_quaternion tempq0, tempq1; + rsQuaternionSet(&tempq0, q0); + rsQuaternionSet(&tempq1, q1); + + float angle = rsQuaternionDot(q0, q1); + if (angle < 0) { + rsQuaternionMultiply(&tempq0, -1.0f); + angle *= -1.0f; + } + + float scale, invScale; + if (angle + 1.0f > 0.05f) { + if (1.0f - angle >= 0.05f) { + float theta = acos(angle); + float invSinTheta = 1.0f / sin(theta); + scale = sin(theta * (1.0f - t)) * invSinTheta; + invScale = sin(theta * t) * invSinTheta; + } else { + scale = 1.0f - t; + invScale = t; + } + } else { + rsQuaternionSet(&tempq1, tempq0.z, -tempq0.y, tempq0.x, -tempq0.w); + scale = sin(M_PI * (0.5f - t)); + invScale = sin(M_PI * t); + } + + rsQuaternionSet(q, tempq0.w*scale + tempq1.w*invScale, tempq0.x*scale + tempq1.x*invScale, + tempq0.y*scale + tempq1.y*invScale, tempq0.z*scale + tempq1.z*invScale); +} + +static void rsQuaternionGetMatrixUnit(rs_matrix4x4 *m, const rs_quaternion *q) { + float x2 = 2.0f * q->x * q->x; + float y2 = 2.0f * q->y * q->y; + float z2 = 2.0f * q->z * q->z; + float xy = 2.0f * q->x * q->y; + float wz = 2.0f * q->w * q->z; + float xz = 2.0f * q->x * q->z; + float wy = 2.0f * q->w * q->y; + float wx = 2.0f * q->w * q->x; + float yz = 2.0f * q->y * q->z; + + m->m[0] = 1.0f - y2 - z2; + m->m[1] = xy - wz; + m->m[2] = xz + wy; + m->m[3] = 0.0f; + + m->m[4] = xy + wz; + m->m[5] = 1.0f - x2 - z2; + m->m[6] = yz - wx; + m->m[7] = 0.0f; + + m->m[8] = xz - wy; + m->m[9] = yz - wx; + m->m[10] = 1.0f - x2 - y2; + m->m[11] = 0.0f; + + m->m[12] = 0.0f; + m->m[13] = 0.0f; + m->m[14] = 0.0f; + m->m[15] = 1.0f; +} + +///////////////////////////////////////////////////// +// utility funcs +///////////////////////////////////////////////////// +__inline__ static void __attribute__((overloadable, always_inline)) +rsExtractFrustumPlanes(const rs_matrix4x4 *modelViewProj, + float4 *left, float4 *right, + float4 *top, float4 *bottom, + float4 *near, float4 *far) { + // x y z w = a b c d in the plane equation + left->x = modelViewProj->m[3] + modelViewProj->m[0]; + left->y = modelViewProj->m[7] + modelViewProj->m[4]; + left->z = modelViewProj->m[11] + modelViewProj->m[8]; + left->w = modelViewProj->m[15] + modelViewProj->m[12]; + + right->x = modelViewProj->m[3] - modelViewProj->m[0]; + right->y = modelViewProj->m[7] - modelViewProj->m[4]; + right->z = modelViewProj->m[11] - modelViewProj->m[8]; + right->w = modelViewProj->m[15] - modelViewProj->m[12]; + + top->x = modelViewProj->m[3] - modelViewProj->m[1]; + top->y = modelViewProj->m[7] - modelViewProj->m[5]; + top->z = modelViewProj->m[11] - modelViewProj->m[9]; + top->w = modelViewProj->m[15] - modelViewProj->m[13]; + + bottom->x = modelViewProj->m[3] + modelViewProj->m[1]; + bottom->y = modelViewProj->m[7] + modelViewProj->m[5]; + bottom->z = modelViewProj->m[11] + modelViewProj->m[9]; + bottom->w = modelViewProj->m[15] + modelViewProj->m[13]; + + near->x = modelViewProj->m[3] + modelViewProj->m[2]; + near->y = modelViewProj->m[7] + modelViewProj->m[6]; + near->z = modelViewProj->m[11] + modelViewProj->m[10]; + near->w = modelViewProj->m[15] + modelViewProj->m[14]; + + far->x = modelViewProj->m[3] - modelViewProj->m[2]; + far->y = modelViewProj->m[7] - modelViewProj->m[6]; + far->z = modelViewProj->m[11] - modelViewProj->m[10]; + far->w = modelViewProj->m[15] - modelViewProj->m[14]; + + float len = length(left->xyz); + *left /= len; + len = length(right->xyz); + *right /= len; + len = length(top->xyz); + *top /= len; + len = length(bottom->xyz); + *bottom /= len; + len = length(near->xyz); + *near /= len; + len = length(far->xyz); + *far /= len; +} + +__inline__ static bool __attribute__((overloadable, always_inline)) +rsIsSphereInFrustum(float4 *sphere, + float4 *left, float4 *right, + float4 *top, float4 *bottom, + float4 *near, float4 *far) { + + float distToCenter = dot(left->xyz, sphere->xyz) + left->w; + if (distToCenter < -sphere->w) { + return false; + } + distToCenter = dot(right->xyz, sphere->xyz) + right->w; + if (distToCenter < -sphere->w) { + return false; + } + distToCenter = dot(top->xyz, sphere->xyz) + top->w; + if (distToCenter < -sphere->w) { + return false; + } + distToCenter = dot(bottom->xyz, sphere->xyz) + bottom->w; + if (distToCenter < -sphere->w) { + return false; + } + distToCenter = dot(near->xyz, sphere->xyz) + near->w; + if (distToCenter < -sphere->w) { + return false; + } + distToCenter = dot(far->xyz, sphere->xyz) + far->w; + if (distToCenter < -sphere->w) { + return false; + } + return true; +} + + +///////////////////////////////////////////////////// +// int ops +///////////////////////////////////////////////////// + +_RS_RUNTIME uint __attribute__((overloadable, always_inline)) rsClamp(uint amount, uint low, uint high); +_RS_RUNTIME int __attribute__((overloadable, always_inline)) rsClamp(int amount, int low, int high); +_RS_RUNTIME ushort __attribute__((overloadable, always_inline)) rsClamp(ushort amount, ushort low, ushort high); +_RS_RUNTIME short __attribute__((overloadable, always_inline)) rsClamp(short amount, short low, short high); +_RS_RUNTIME uchar __attribute__((overloadable, always_inline)) rsClamp(uchar amount, uchar low, uchar high); +_RS_RUNTIME char __attribute__((overloadable, always_inline)) rsClamp(char amount, char low, char high); + +#undef _RS_RUNTIME + +#endif diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh index 70cd56266dd1..67ffc3d9e56b 100644 --- a/libs/rs/scriptc/rs_graphics.rsh +++ b/libs/rs/scriptc/rs_graphics.rsh @@ -1,65 +1,252 @@ - - -extern float2 vec2Rand(float len); - -extern float3 float3Norm(float3); -extern float float3Length(float3); -extern float3 float3Add(float3 lhs, float3 rhs); -extern float3 float3Sub(float3 lhs, float3 rhs); -extern float3 float3Cross(float3 lhs, float3 rhs); -extern float float3Dot(float3 lhs, float3 rhs); -extern float3 float3Scale(float3 v, float scale); - -extern float4 float4Add(float4 lhs, float4 rhs); -extern float4 float4Sub(float4 lhs, float4 rhs); -extern float4 float4Cross(float4 lhs, float4 rhs); -extern float float4Dot(float4 lhs, float4 rhs); -extern float4 float4Scale(float4 v, float scale); - - // context -extern void bindProgramFragment(rs_program_fragment); -extern void bindProgramStore(rs_program_store); -extern void bindProgramVertex(rs_program_vertex); - -extern void bindSampler(rs_program_fragment, int slot, rs_sampler); -extern void bindSampler(rs_program_fragment, int slot, rs_allocation); - -extern void vpLoadModelMatrix(const float *); -extern void vpLoadTextureMatrix(const float *); - - -// drawing -extern void drawRect(float x1, float y1, float x2, float y2, float z); -extern void drawQuad(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4); -extern void drawQuadTexCoords(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); -extern void drawSprite(float x, float y, float z, float w, float h); -extern void drawSpriteScreenspace(float x, float y, float z, float w, float h); -extern void drawLine(float x1, float y1, float z1, float x2, float y2, float z2); -extern void drawPoint(float x1, float y1, float z1); -extern void drawSimpleMesh(int ism); -extern void drawSimpleMeshRange(int ism, int start, int len); - -// misc -extern void pfClearColor(float, float, float, float); -extern void color(float, float, float, float); -extern void hsb(float, float, float, float); -extern void hsbToRgb(float, float, float, float*); -extern int hsbToAbgr(float, float, float, float); - -extern void uploadToTexture(int, int); -extern void uploadToBufferObject(int); - -extern int colorFloatRGBAtoUNorm8(float, float, float, float); -extern int colorFloatRGBto565(float, float, float); - -extern int getWidth(); -extern int getHeight(); - -extern int sendToClient(void *data, int cmdID, int len, int waitForSpace); - -extern void debugF(const char *, float); -extern void debugI32(const char *, int); -extern void debugHexI32(const char *, int); - - +#ifndef __RS_GRAPHICS_RSH__ +#define __RS_GRAPHICS_RSH__ + + +/** + * Bind a new ProgramFragment to the rendering context. + * + * @param pf + */ +extern void __attribute__((overloadable)) + rsgBindProgramFragment(rs_program_fragment pf); + +/** + * Bind a new ProgramStore to the rendering context. + * + * @param ps + */ +extern void __attribute__((overloadable)) + rsgBindProgramStore(rs_program_store ps); + +/** + * Bind a new ProgramVertex to the rendering context. + * + * @param pv + */ +extern void __attribute__((overloadable)) + rsgBindProgramVertex(rs_program_vertex pv); + +/** + * Bind a new ProgramRaster to the rendering context. + * + * @param pr + */ +extern void __attribute__((overloadable)) + rsgBindProgramRaster(rs_program_raster pr); + +/** + * Bind a new Sampler object to a ProgramFragment. The sampler will + * operate on the texture bound at the matching slot. + * + * @param slot + */ +extern void __attribute__((overloadable)) + rsgBindSampler(rs_program_fragment, uint slot, rs_sampler); + +/** + * Bind a new Allocation object to a ProgramFragment. The + * Allocation must be a valid texture for the Program. The sampling + * of the texture will be controled by the Sampler bound at the + * matching slot. + * + * @param slot + */ +extern void __attribute__((overloadable)) + rsgBindTexture(rs_program_fragment, uint slot, rs_allocation); + + +extern void __attribute__((overloadable)) + rsgProgramVertexLoadProjectionMatrix(const rs_matrix4x4 *); +extern void __attribute__((overloadable)) + rsgProgramVertexLoadModelMatrix(const rs_matrix4x4 *); +extern void __attribute__((overloadable)) + rsgProgramVertexLoadTextureMatrix(const rs_matrix4x4 *); + +extern void __attribute__((overloadable)) + rsgProgramVertexGetProjectionMatrix(rs_matrix4x4 *); + +/** + * Set the constant color for a fixed function emulation program. + * + * @param pf + * @param r + * @param g + * @param b + * @param a + */ +extern void __attribute__((overloadable)) + rsgProgramFragmentConstantColor(rs_program_fragment pf, float r, float g, float b, float a); + +/** + * Get the width of the current rendering surface. + * + * @return uint + */ +extern uint __attribute__((overloadable)) + rsgGetWidth(void); + +/** + * Get the height of the current rendering surface. + * + * @return uint + */ +extern uint __attribute__((overloadable)) + rsgGetHeight(void); + + +/** + * Sync the contents of an allocation from its SCRIPT memory space to its HW + * memory spaces. + * + * @param alloc + */ +extern void __attribute__((overloadable)) + rsgAllocationSyncAll(rs_allocation alloc); + +/** + * Low performance utility function for drawing a simple rectangle. Not + * intended for drawing large quantities of geometry. + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @param z + */ +extern void __attribute__((overloadable)) + rsgDrawRect(float x1, float y1, float x2, float y2, float z); + +/** + * Low performance utility function for drawing a simple quad. Not intended for + * drawing large quantities of geometry. + * + * @param x1 + * @param y1 + * @param z1 + * @param x2 + * @param y2 + * @param z2 + * @param x3 + * @param y3 + * @param z3 + * @param x4 + * @param y4 + * @param z4 + */ +extern void __attribute__((overloadable)) + rsgDrawQuad(float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4); + + +/** + * Low performance utility function for drawing a textured quad. Not intended + * for drawing large quantities of geometry. + * + * @param x1 + * @param y1 + * @param z1 + * @param u1 + * @param v1 + * @param x2 + * @param y2 + * @param z2 + * @param u2 + * @param v2 + * @param x3 + * @param y3 + * @param z3 + * @param u3 + * @param v3 + * @param x4 + * @param y4 + * @param z4 + * @param u4 + * @param v4 + */ +extern void __attribute__((overloadable)) + rsgDrawQuadTexCoords(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); + + +/** + * Low performance function for drawing rectangles in screenspace. This + * function uses the default passthough ProgramVertex. Any bound ProgramVertex + * is ignored. This function has considerable overhead and should not be used + * for drawing in shipping applications. + * + * @param x + * @param y + * @param z + * @param w + * @param h + */ +extern void __attribute__((overloadable)) + rsgDrawSpriteScreenspace(float x, float y, float z, float w, float h); + +/** + * Draw a mesh of geometry using the current context state. The whole mesh is + * rendered. + * + * @param ism + */ +extern void __attribute__((overloadable)) + rsgDrawMesh(rs_mesh ism); +extern void __attribute__((overloadable)) + rsgDrawMesh(rs_mesh ism, uint primitiveIndex); +extern void __attribute__((overloadable)) + rsgDrawMesh(rs_mesh ism, uint primitiveIndex, uint start, uint len); + +/** + * Clears the rendering surface to the specified color. + * + * @param r + * @param g + * @param b + * @param a + */ +extern void __attribute__((overloadable)) + rsgClearColor(float r, float g, float b, float a); + +/** + * Clears the depth suface to the specified value. + * + */ +extern void __attribute__((overloadable)) + rsgClearDepth(float value); + +extern void __attribute__((overloadable)) + rsgDrawText(const char *, int x, int y); +extern void __attribute__((overloadable)) + rsgDrawText(rs_allocation, int x, int y); +extern void __attribute__((overloadable)) + rsgBindFont(rs_font); +extern void __attribute__((overloadable)) + rsgFontColor(float, float, float, float); +// Returns the bounding box of the text relative to (0, 0) +// Any of left, right, top, bottom could be NULL +extern void __attribute__((overloadable)) + rsgMeasureText(const char *, int *left, int *right, int *top, int *bottom); +extern void __attribute__((overloadable)) + rsgMeasureText(rs_allocation, int *left, int *right, int *top, int *bottom); + +extern void __attribute__((overloadable)) + rsgMeshComputeBoundingBox(rs_mesh mesh, float *minX, float *minY, float *minZ, + float *maxX, float *maxY, float *maxZ); +__inline__ static void __attribute__((overloadable, always_inline)) +rsgMeshComputeBoundingBox(rs_mesh mesh, float3 *bBoxMin, float3 *bBoxMax) { + float x1, y1, z1, x2, y2, z2; + rsgMeshComputeBoundingBox(mesh, &x1, &y1, &z1, &x2, &y2, &z2); + bBoxMin->x = x1; + bBoxMin->y = y1; + bBoxMin->z = z1; + bBoxMax->x = x2; + bBoxMax->y = y2; + bBoxMax->z = z2; +} + +#endif diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh index 613c7caa0c88..6e3cfdb77529 100644 --- a/libs/rs/scriptc/rs_math.rsh +++ b/libs/rs/scriptc/rs_math.rsh @@ -1,287 +1,209 @@ -// Float ops - -extern float __attribute__((overloadable)) abs(float); -extern float2 __attribute__((overloadable)) abs(float2); -extern float3 __attribute__((overloadable)) abs(float3); -extern float4 __attribute__((overloadable)) abs(float4); -extern float8 __attribute__((overloadable)) abs(float8); -extern float16 __attribute__((overloadable)) abs(float16); - -extern float __attribute__((overloadable)) acos(float); -extern float2 __attribute__((overloadable)) acos(float2); -extern float3 __attribute__((overloadable)) acos(float3); -extern float4 __attribute__((overloadable)) acos(float4); -extern float8 __attribute__((overloadable)) acos(float8); -extern float16 __attribute__((overloadable)) acos(float16); - -extern float __attribute__((overloadable)) asin(float); -extern float2 __attribute__((overloadable)) asin(float2); -extern float3 __attribute__((overloadable)) asin(float3); -extern float4 __attribute__((overloadable)) asin(float4); -extern float8 __attribute__((overloadable)) asin(float8); -extern float16 __attribute__((overloadable)) asin(float16); - -extern float __attribute__((overloadable)) atan(float); -extern float2 __attribute__((overloadable)) atan(float2); -extern float3 __attribute__((overloadable)) atan(float3); -extern float4 __attribute__((overloadable)) atan(float4); -extern float8 __attribute__((overloadable)) atan(float8); -extern float16 __attribute__((overloadable)) atan(float16); - -extern float __attribute__((overloadable)) atan2(float, float); -extern float2 __attribute__((overloadable)) atan2(float2, float2); -extern float3 __attribute__((overloadable)) atan2(float3, float3); -extern float4 __attribute__((overloadable)) atan2(float4, float4); -extern float8 __attribute__((overloadable)) atan2(float8, float8); -extern float16 __attribute__((overloadable)) atan2(float16, float16); - -extern float __attribute__((overloadable)) ceil(float); -extern float2 __attribute__((overloadable)) ceil(float2); -extern float3 __attribute__((overloadable)) ceil(float3); -extern float4 __attribute__((overloadable)) ceil(float4); -extern float8 __attribute__((overloadable)) ceil(float8); -extern float16 __attribute__((overloadable)) ceil(float16); - -extern float __attribute__((overloadable)) clamp(float, float, float); -extern float2 __attribute__((overloadable)) clamp(float2, float2, float2); -extern float3 __attribute__((overloadable)) clamp(float3, float3, float3); -extern float4 __attribute__((overloadable)) clamp(float4, float4, float4); -extern float8 __attribute__((overloadable)) clamp(float8, float8, float8); -extern float16 __attribute__((overloadable)) clamp(float16, float16, float16); -extern float __attribute__((overloadable)) clamp(float, float, float); -extern float2 __attribute__((overloadable)) clamp(float2, float, float); -extern float3 __attribute__((overloadable)) clamp(float3, float, float); -extern float4 __attribute__((overloadable)) clamp(float4, float, float); -extern float8 __attribute__((overloadable)) clamp(float8, float, float); -extern float16 __attribute__((overloadable)) clamp(float16, float, float); - -extern float __attribute__((overloadable)) copysign(float, float); -extern float2 __attribute__((overloadable)) copysign(float2, float2); -extern float3 __attribute__((overloadable)) copysign(float3, float3); -extern float4 __attribute__((overloadable)) copysign(float4, float4); -extern float8 __attribute__((overloadable)) copysign(float8, float8); -extern float16 __attribute__((overloadable)) copysign(float16, float16); - -extern float __attribute__((overloadable)) cos(float); -extern float2 __attribute__((overloadable)) cos(float2); -extern float3 __attribute__((overloadable)) cos(float3); -extern float4 __attribute__((overloadable)) cos(float4); -extern float8 __attribute__((overloadable)) cos(float8); -extern float16 __attribute__((overloadable)) cos(float16); - -extern float __attribute__((overloadable)) degrees(float); -extern float2 __attribute__((overloadable)) degrees(float2); -extern float3 __attribute__((overloadable)) degrees(float3); -extern float4 __attribute__((overloadable)) degrees(float4); -extern float8 __attribute__((overloadable)) degrees(float8); -extern float16 __attribute__((overloadable)) degrees(float16); - -extern float __attribute__((overloadable)) exp(float); -extern float2 __attribute__((overloadable)) exp(float2); -extern float3 __attribute__((overloadable)) exp(float3); -extern float4 __attribute__((overloadable)) exp(float4); -extern float8 __attribute__((overloadable)) exp(float8); -extern float16 __attribute__((overloadable)) exp(float16); - -extern float __attribute__((overloadable)) exp2(float); -extern float2 __attribute__((overloadable)) exp2(float2); -extern float3 __attribute__((overloadable)) exp2(float3); -extern float4 __attribute__((overloadable)) exp2(float4); -extern float8 __attribute__((overloadable)) exp2(float8); -extern float16 __attribute__((overloadable)) exp2(float16); - -extern float __attribute__((overloadable)) exp10(float); -extern float2 __attribute__((overloadable)) exp10(float2); -extern float3 __attribute__((overloadable)) exp10(float3); -extern float4 __attribute__((overloadable)) exp10(float4); -extern float8 __attribute__((overloadable)) exp10(float8); -extern float16 __attribute__((overloadable)) exp10(float16); - -extern float __attribute__((overloadable)) fabs(float); -extern float2 __attribute__((overloadable)) fabs(float2); -extern float3 __attribute__((overloadable)) fabs(float3); -extern float4 __attribute__((overloadable)) fabs(float4); -extern float8 __attribute__((overloadable)) fabs(float8); -extern float16 __attribute__((overloadable)) fabs(float16); - -extern float __attribute__((overloadable)) floor(float); -extern float2 __attribute__((overloadable)) floor(float2); -extern float3 __attribute__((overloadable)) floor(float3); -extern float4 __attribute__((overloadable)) floor(float4); -extern float8 __attribute__((overloadable)) floor(float8); -extern float16 __attribute__((overloadable)) floor(float16); - -extern float __attribute__((overloadable)) fmax(float, float); -extern float2 __attribute__((overloadable)) fmax(float2, float2); -extern float3 __attribute__((overloadable)) fmax(float3, float3); -extern float4 __attribute__((overloadable)) fmax(float4, float4); -extern float8 __attribute__((overloadable)) fmax(float8, float8); -extern float16 __attribute__((overloadable)) fmax(float16, float16); -extern float2 __attribute__((overloadable)) fmax(float2, float); -extern float3 __attribute__((overloadable)) fmax(float3, float); -extern float4 __attribute__((overloadable)) fmax(float4, float); -extern float8 __attribute__((overloadable)) fmax(float8, float); -extern float16 __attribute__((overloadable)) fmax(float16, float); - -extern float __attribute__((overloadable)) fmin(float, float); -extern float2 __attribute__((overloadable)) fmin(float2, float2); -extern float3 __attribute__((overloadable)) fmin(float3, float3); -extern float4 __attribute__((overloadable)) fmin(float4, float4); -extern float8 __attribute__((overloadable)) fmin(float8, float8); -extern float16 __attribute__((overloadable)) fmin(float16, float16); -extern float2 __attribute__((overloadable)) fmin(float2, float); -extern float3 __attribute__((overloadable)) fmin(float3, float); -extern float4 __attribute__((overloadable)) fmin(float4, float); -extern float8 __attribute__((overloadable)) fmin(float8, float); -extern float16 __attribute__((overloadable)) fmin(float16, float); - -extern float __attribute__((overloadable)) fmod(float, float); -extern float2 __attribute__((overloadable)) fmod(float2, float2); -extern float3 __attribute__((overloadable)) fmod(float3, float3); -extern float4 __attribute__((overloadable)) fmod(float4, float4); -extern float8 __attribute__((overloadable)) fmod(float8, float8); -extern float16 __attribute__((overloadable)) fmod(float16, float16); - -extern float __attribute__((overloadable)) log(float); -extern float2 __attribute__((overloadable)) log(float2); -extern float3 __attribute__((overloadable)) log(float3); -extern float4 __attribute__((overloadable)) log(float4); -extern float8 __attribute__((overloadable)) log(float8); -extern float16 __attribute__((overloadable)) log(float16); - -extern float __attribute__((overloadable)) log2(float); -extern float2 __attribute__((overloadable)) log2(float2); -extern float3 __attribute__((overloadable)) log2(float3); -extern float4 __attribute__((overloadable)) log2(float4); -extern float8 __attribute__((overloadable)) log2(float8); -extern float16 __attribute__((overloadable)) log2(float16); - -extern float __attribute__((overloadable)) log10(float); -extern float2 __attribute__((overloadable)) log10(float2); -extern float3 __attribute__((overloadable)) log10(float3); -extern float4 __attribute__((overloadable)) log10(float4); -extern float8 __attribute__((overloadable)) log10(float8); -extern float16 __attribute__((overloadable)) log10(float16); - -extern float __attribute__((overloadable)) max(float, float); -extern float2 __attribute__((overloadable)) max(float2, float2); -extern float3 __attribute__((overloadable)) max(float3, float3); -extern float4 __attribute__((overloadable)) max(float4, float4); -extern float8 __attribute__((overloadable)) max(float8, float8); -extern float16 __attribute__((overloadable)) max(float16, float16); - -extern float __attribute__((overloadable)) min(float, float); -extern float2 __attribute__((overloadable)) min(float2, float2); -extern float3 __attribute__((overloadable)) min(float3, float3); -extern float4 __attribute__((overloadable)) min(float4, float4); -extern float8 __attribute__((overloadable)) min(float8, float8); -extern float16 __attribute__((overloadable)) min(float16, float16); - -extern float __attribute__((overloadable)) mix(float, float, float); -extern float2 __attribute__((overloadable)) mix(float2, float2, float2); -extern float3 __attribute__((overloadable)) mix(float3, float3, float3); -extern float4 __attribute__((overloadable)) mix(float4, float4, float4); -extern float8 __attribute__((overloadable)) mix(float8, float8, float8); -extern float16 __attribute__((overloadable)) mix(float16, float16, float16); -extern float __attribute__((overloadable)) mix(float, float, float); -extern float2 __attribute__((overloadable)) mix(float2, float2, float); -extern float3 __attribute__((overloadable)) mix(float3, float3, float); -extern float4 __attribute__((overloadable)) mix(float4, float4, float); -extern float8 __attribute__((overloadable)) mix(float8, float8, float); -extern float16 __attribute__((overloadable)) mix(float16, float16, float); - -extern float __attribute__((overloadable)) pow(float, float); -extern float2 __attribute__((overloadable)) pow(float2, float2); -extern float3 __attribute__((overloadable)) pow(float3, float3); -extern float4 __attribute__((overloadable)) pow(float4, float4); -extern float8 __attribute__((overloadable)) pow(float8, float8); -extern float16 __attribute__((overloadable)) pow(float16, float16); - -extern float __attribute__((overloadable)) radians(float); -extern float2 __attribute__((overloadable)) radians(float2); -extern float3 __attribute__((overloadable)) radians(float3); -extern float4 __attribute__((overloadable)) radians(float4); -extern float8 __attribute__((overloadable)) radians(float8); -extern float16 __attribute__((overloadable)) radians(float16); - -extern float __attribute__((overloadable)) rint(float); -extern float2 __attribute__((overloadable)) rint(float2); -extern float3 __attribute__((overloadable)) rint(float3); -extern float4 __attribute__((overloadable)) rint(float4); -extern float8 __attribute__((overloadable)) rint(float8); -extern float16 __attribute__((overloadable)) rint(float16); - -extern float __attribute__((overloadable)) round(float); -extern float2 __attribute__((overloadable)) round(float2); -extern float3 __attribute__((overloadable)) round(float3); -extern float4 __attribute__((overloadable)) round(float4); -extern float8 __attribute__((overloadable)) round(float8); -extern float16 __attribute__((overloadable)) round(float16); - -extern float __attribute__((overloadable)) rsqrt(float); -extern float2 __attribute__((overloadable)) rsqrt(float2); -extern float3 __attribute__((overloadable)) rsqrt(float3); -extern float4 __attribute__((overloadable)) rsqrt(float4); -extern float8 __attribute__((overloadable)) rsqrt(float8); -extern float16 __attribute__((overloadable)) rsqrt(float16); - -extern float __attribute__((overloadable)) sign(float); -extern float2 __attribute__((overloadable)) sign(float2); -extern float3 __attribute__((overloadable)) sign(float3); -extern float4 __attribute__((overloadable)) sign(float4); -extern float8 __attribute__((overloadable)) sign(float8); -extern float16 __attribute__((overloadable)) sign(float16); - -extern float __attribute__((overloadable)) sin(float); -extern float2 __attribute__((overloadable)) sin(float2); -extern float3 __attribute__((overloadable)) sin(float3); -extern float4 __attribute__((overloadable)) sin(float4); -extern float8 __attribute__((overloadable)) sin(float8); -extern float16 __attribute__((overloadable)) sin(float16); - -extern float __attribute__((overloadable)) sqrt(float); -extern float2 __attribute__((overloadable)) sqrt(float2); -extern float3 __attribute__((overloadable)) sqrt(float3); -extern float4 __attribute__((overloadable)) sqrt(float4); -extern float8 __attribute__((overloadable)) sqrt(float8); -extern float16 __attribute__((overloadable)) sqrt(float16); - -extern float __attribute__((overloadable)) tan(float); -extern float2 __attribute__((overloadable)) tan(float2); -extern float3 __attribute__((overloadable)) tan(float3); -extern float4 __attribute__((overloadable)) tan(float4); -extern float8 __attribute__((overloadable)) tan(float8); -extern float16 __attribute__((overloadable)) tan(float16); - -extern float __attribute__((overloadable)) trunc(float); -extern float2 __attribute__((overloadable)) trunc(float2); -extern float3 __attribute__((overloadable)) trunc(float3); -extern float4 __attribute__((overloadable)) trunc(float4); -extern float8 __attribute__((overloadable)) trunc(float8); -extern float16 __attribute__((overloadable)) trunc(float16); - - - - - - -// Int ops - -extern int __attribute__((overloadable)) abs(int); -extern int2 __attribute__((overloadable)) abs(int2); -extern int3 __attribute__((overloadable)) abs(int3); -extern int4 __attribute__((overloadable)) abs(int4); -extern int8 __attribute__((overloadable)) abs(int8); -extern int16 __attribute__((overloadable)) abs(int16); - - - -/* -extern float modf(float, float); -extern float randf(float); -extern float randf2(float, float); -extern float fracf(float); -extern float lerpf(float, float, float); -extern float mapf(float, float, float, float, float); -*/ - +#ifndef __RS_MATH_RSH__ +#define __RS_MATH_RSH__ + +/** + * Copy reference to the specified object. + * + * @param dst + * @param src + */ +extern void __attribute__((overloadable)) + rsSetObject(rs_element *dst, rs_element src); +extern void __attribute__((overloadable)) + rsSetObject(rs_type *dst, rs_type src); +extern void __attribute__((overloadable)) + rsSetObject(rs_allocation *dst, rs_allocation src); +extern void __attribute__((overloadable)) + rsSetObject(rs_sampler *dst, rs_sampler src); +extern void __attribute__((overloadable)) + rsSetObject(rs_script *dst, rs_script src); +extern void __attribute__((overloadable)) + rsSetObject(rs_mesh *dst, rs_mesh src); +extern void __attribute__((overloadable)) + rsSetObject(rs_program_fragment *dst, rs_program_fragment src); +extern void __attribute__((overloadable)) + rsSetObject(rs_program_vertex *dst, rs_program_vertex src); +extern void __attribute__((overloadable)) + rsSetObject(rs_program_raster *dst, rs_program_raster src); +extern void __attribute__((overloadable)) + rsSetObject(rs_program_store *dst, rs_program_store src); +extern void __attribute__((overloadable)) + rsSetObject(rs_font *dst, rs_font src); + +/** + * Sets the object to NULL. + * + * @return bool + */ +extern void __attribute__((overloadable)) + rsClearObject(rs_element *dst); +extern void __attribute__((overloadable)) + rsClearObject(rs_type *dst); +extern void __attribute__((overloadable)) + rsClearObject(rs_allocation *dst); +extern void __attribute__((overloadable)) + rsClearObject(rs_sampler *dst); +extern void __attribute__((overloadable)) + rsClearObject(rs_script *dst); +extern void __attribute__((overloadable)) + rsClearObject(rs_mesh *dst); +extern void __attribute__((overloadable)) + rsClearObject(rs_program_fragment *dst); +extern void __attribute__((overloadable)) + rsClearObject(rs_program_vertex *dst); +extern void __attribute__((overloadable)) + rsClearObject(rs_program_raster *dst); +extern void __attribute__((overloadable)) + rsClearObject(rs_program_store *dst); +extern void __attribute__((overloadable)) + rsClearObject(rs_font *dst); + +/** + * Tests if the object is valid. Returns true if the object is valid, false if + * it is NULL. + * + * @return bool + */ +extern bool __attribute__((overloadable)) + rsIsObject(rs_element); +extern bool __attribute__((overloadable)) + rsIsObject(rs_type); +extern bool __attribute__((overloadable)) + rsIsObject(rs_allocation); +extern bool __attribute__((overloadable)) + rsIsObject(rs_sampler); +extern bool __attribute__((overloadable)) + rsIsObject(rs_script); +extern bool __attribute__((overloadable)) + rsIsObject(rs_mesh); +extern bool __attribute__((overloadable)) + rsIsObject(rs_program_fragment); +extern bool __attribute__((overloadable)) + rsIsObject(rs_program_vertex); +extern bool __attribute__((overloadable)) + rsIsObject(rs_program_raster); +extern bool __attribute__((overloadable)) + rsIsObject(rs_program_store); +extern bool __attribute__((overloadable)) + rsIsObject(rs_font); + + +/** + * Returns the Allocation for a given pointer. The pointer should point within + * a valid allocation. The results are undefined if the pointer is not from a + * valid allocation. + */ +extern rs_allocation __attribute__((overloadable)) + rsGetAllocation(const void *); + +/** + * Query the dimension of an allocation. + * + * @return uint32_t The X dimension of the allocation. + */ +extern uint32_t __attribute__((overloadable)) + rsAllocationGetDimX(rs_allocation); + +/** + * Query the dimension of an allocation. + * + * @return uint32_t The Y dimension of the allocation. + */ +extern uint32_t __attribute__((overloadable)) + rsAllocationGetDimY(rs_allocation); + +/** + * Query the dimension of an allocation. + * + * @return uint32_t The Z dimension of the allocation. + */ +extern uint32_t __attribute__((overloadable)) + rsAllocationGetDimZ(rs_allocation); + +/** + * Query an allocation for the presence of more than one LOD. + * + * @return uint32_t Returns 1 if more than one LOD is present, 0 otherwise. + */ +extern uint32_t __attribute__((overloadable)) + rsAllocationGetDimLOD(rs_allocation); + +/** + * Query an allocation for the presence of more than one face. + * + * @return uint32_t Returns 1 if more than one face is present, 0 otherwise. + */ +extern uint32_t __attribute__((overloadable)) + rsAllocationGetDimFaces(rs_allocation); + +// Extract a single element from an allocation. +extern const void * __attribute__((overloadable)) + rsGetElementAt(rs_allocation, uint32_t x); +extern const void * __attribute__((overloadable)) + rsGetElementAt(rs_allocation, uint32_t x, uint32_t y); +extern const void * __attribute__((overloadable)) + rsGetElementAt(rs_allocation, uint32_t x, uint32_t y, uint32_t z); + +// Return a random value between 0 (or min_value) and max_malue. +extern int __attribute__((overloadable)) + rsRand(int max_value); +extern int __attribute__((overloadable)) + rsRand(int min_value, int max_value); +extern float __attribute__((overloadable)) + rsRand(float max_value); +extern float __attribute__((overloadable)) + rsRand(float min_value, float max_value); + +// return the fractional part of a float +// min(v - ((int)floor(v)), 0x1.fffffep-1f); +extern float __attribute__((overloadable)) + rsFrac(float); + +// Send a message back to the client. Will not block and returns true +// if the message was sendable and false if the fifo was full. +// A message ID is required. Data payload is optional. +extern bool __attribute__((overloadable)) + rsSendToClient(int cmdID); +extern bool __attribute__((overloadable)) + rsSendToClient(int cmdID, const void *data, uint len); + +// Send a message back to the client, blocking until the message is queued. +// A message ID is required. Data payload is optional. +extern void __attribute__((overloadable)) + rsSendToClientBlocking(int cmdID); +extern void __attribute__((overloadable)) + rsSendToClientBlocking(int cmdID, const void *data, uint len); + + +// Script to Script +enum rs_for_each_strategy { + RS_FOR_EACH_STRATEGY_SERIAL, + RS_FOR_EACH_STRATEGY_DONT_CARE, + RS_FOR_EACH_STRATEGY_DST_LINEAR, + RS_FOR_EACH_STRATEGY_TILE_SMALL, + RS_FOR_EACH_STRATEGY_TILE_MEDIUM, + RS_FOR_EACH_STRATEGY_TILE_LARGE +}; + +typedef struct rs_script_call { + enum rs_for_each_strategy strategy; + uint32_t xStart; + uint32_t xEnd; + uint32_t yStart; + uint32_t yEnd; + uint32_t zStart; + uint32_t zEnd; + uint32_t arrayStart; + uint32_t arrayEnd; +} rs_script_call_t; + +extern void __attribute__((overloadable)) + rsForEach(rs_script script, rs_allocation input, + rs_allocation output, const void * usrData); + +extern void __attribute__((overloadable)) + rsForEach(rs_script script, rs_allocation input, + rs_allocation output, const void * usrData, + const rs_script_call_t *); + +#endif diff --git a/libs/rs/scriptc/rs_time.rsh b/libs/rs/scriptc/rs_time.rsh new file mode 100644 index 000000000000..f1abed6308e4 --- /dev/null +++ b/libs/rs/scriptc/rs_time.rsh @@ -0,0 +1,36 @@ +#ifndef __RS_TIME_RSH__ +#define __RS_TIME_RSH__ + +typedef int rs_time_t; + +typedef struct { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +} rs_tm; + +extern rs_time_t __attribute__((overloadable)) + rsTime(rs_time_t *timer); + +extern rs_tm * __attribute__((overloadable)) + rsLocaltime(rs_tm *local, const rs_time_t *timer); + +// Return the current system clock in milliseconds +extern int64_t __attribute__((overloadable)) + rsUptimeMillis(void); + +// Return the current system clock in nanoseconds +extern int64_t __attribute__((overloadable)) + rsUptimeNanos(void); + +// Return the time in seconds since function was last called in this script. +extern float __attribute__((overloadable)) + rsGetDt(void); + +#endif diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh index 4198a748c574..a010096507b1 100644 --- a/libs/rs/scriptc/rs_types.rsh +++ b/libs/rs/scriptc/rs_types.rsh @@ -1,71 +1,80 @@ +#ifndef __RS_TYPES_RSH__ +#define __RS_TYPES_RSH__ +#define M_PI 3.14159265358979323846264338327950288f /* pi */ + +#include "stdbool.h" typedef char int8_t; typedef short int16_t; typedef int int32_t; -//typedef long int64_t; +typedef long long int64_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; -//typedef long uint64_t; +typedef unsigned long long uint64_t; typedef uint8_t uchar; typedef uint16_t ushort; typedef uint32_t uint; -//typedef uint64_t ulong; - -typedef int rs_element; -typedef int rs_type; -typedef int rs_allocation; -typedef int rs_sampler; -typedef int rs_script; -typedef int rs_mesh; -typedef int rs_program_fragment; -typedef int rs_program_vertex; -typedef int rs_program_raster; -typedef int rs_program_store; +typedef uint64_t ulong; + +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_element; +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_type; +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_allocation; +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_sampler; +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_script; +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_mesh; +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_fragment; +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_vertex; +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_raster; +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_program_store; +typedef struct { const int* const p; } __attribute__((packed, aligned(4))) rs_font; + typedef float float2 __attribute__((ext_vector_type(2))); typedef float float3 __attribute__((ext_vector_type(3))); typedef float float4 __attribute__((ext_vector_type(4))); -typedef float float8 __attribute__((ext_vector_type(8))); -typedef float float16 __attribute__((ext_vector_type(16))); typedef uchar uchar2 __attribute__((ext_vector_type(2))); typedef uchar uchar3 __attribute__((ext_vector_type(3))); typedef uchar uchar4 __attribute__((ext_vector_type(4))); -typedef uchar uchar8 __attribute__((ext_vector_type(8))); -typedef uchar uchar16 __attribute__((ext_vector_type(16))); typedef ushort ushort2 __attribute__((ext_vector_type(2))); typedef ushort ushort3 __attribute__((ext_vector_type(3))); typedef ushort ushort4 __attribute__((ext_vector_type(4))); -typedef ushort ushort8 __attribute__((ext_vector_type(8))); -typedef ushort ushort16 __attribute__((ext_vector_type(16))); typedef uint uint2 __attribute__((ext_vector_type(2))); typedef uint uint3 __attribute__((ext_vector_type(3))); typedef uint uint4 __attribute__((ext_vector_type(4))); -typedef uint uint8 __attribute__((ext_vector_type(8))); -typedef uint uint16 __attribute__((ext_vector_type(16))); typedef char char2 __attribute__((ext_vector_type(2))); typedef char char3 __attribute__((ext_vector_type(3))); typedef char char4 __attribute__((ext_vector_type(4))); -typedef char char8 __attribute__((ext_vector_type(8))); -typedef char char16 __attribute__((ext_vector_type(16))); typedef short short2 __attribute__((ext_vector_type(2))); typedef short short3 __attribute__((ext_vector_type(3))); typedef short short4 __attribute__((ext_vector_type(4))); -typedef short short8 __attribute__((ext_vector_type(8))); -typedef short short16 __attribute__((ext_vector_type(16))); typedef int int2 __attribute__((ext_vector_type(2))); typedef int int3 __attribute__((ext_vector_type(3))); typedef int int4 __attribute__((ext_vector_type(4))); -typedef int int8 __attribute__((ext_vector_type(8))); -typedef int int16 __attribute__((ext_vector_type(16))); +typedef struct { + float m[16]; +} rs_matrix4x4; + +typedef struct { + float m[9]; +} rs_matrix3x3; + +typedef struct { + float m[4]; +} rs_matrix2x2; + +typedef float4 rs_quaternion; + +#define RS_PACKED __attribute__((packed, aligned(4))) +#endif diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp index 9ff693004c0f..7fbf67a02764 100644 --- a/libs/storage/IMountService.cpp +++ b/libs/storage/IMountService.cpp @@ -47,6 +47,9 @@ enum { TRANSACTION_unmountObb, TRANSACTION_isObbMounted, TRANSACTION_getMountedObbPath, + TRANSACTION_isExternalStorageEmulated, + TRANSACTION_decryptStorage, + TRANSACTION_encryptStorage, }; class BpMountService: public BpInterface<IMountService> @@ -503,6 +506,40 @@ public: path = reply.readString16(); return true; } + + int32_t decryptStorage(const String16& password) + { + Parcel data, reply; + data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); + data.writeString16(password); + if (remote()->transact(TRANSACTION_decryptStorage, data, &reply) != NO_ERROR) { + LOGD("decryptStorage could not contact remote\n"); + return -1; + } + int32_t err = reply.readExceptionCode(); + if (err < 0) { + LOGD("decryptStorage caught exception %d\n", err); + return err; + } + return reply.readInt32(); + } + + int32_t encryptStorage(const String16& password) + { + Parcel data, reply; + data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); + data.writeString16(password); + if (remote()->transact(TRANSACTION_encryptStorage, data, &reply) != NO_ERROR) { + LOGD("encryptStorage could not contact remote\n"); + return -1; + } + int32_t err = reply.readExceptionCode(); + if (err < 0) { + LOGD("encryptStorage caught exception %d\n", err); + return err; + } + return reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(MountService, "IMountService"); diff --git a/libs/surfaceflinger_client/Android.mk b/libs/surfaceflinger_client/Android.mk index ce3c71a7d9bb..4a0faf06a12c 100644 --- a/libs/surfaceflinger_client/Android.mk +++ b/libs/surfaceflinger_client/Android.mk @@ -5,6 +5,7 @@ LOCAL_SRC_FILES:= \ ISurfaceComposer.cpp \ ISurface.cpp \ ISurfaceComposerClient.cpp \ + IGraphicBufferAlloc.cpp \ LayerState.cpp \ SharedBufferStack.cpp \ Surface.cpp \ diff --git a/libs/surfaceflinger_client/IGraphicBufferAlloc.cpp b/libs/surfaceflinger_client/IGraphicBufferAlloc.cpp new file mode 100644 index 000000000000..e05da725c47f --- /dev/null +++ b/libs/surfaceflinger_client/IGraphicBufferAlloc.cpp @@ -0,0 +1,108 @@ +/* + * 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. + */ + +// tag as surfaceflinger +#define LOG_TAG "SurfaceFlinger" + +#include <stdint.h> +#include <sys/types.h> + +#include <binder/Parcel.h> + +#include <ui/GraphicBuffer.h> + +#include <surfaceflinger/IGraphicBufferAlloc.h> + +// --------------------------------------------------------------------------- + +namespace android { + +enum { + CREATE_GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + FREE_ALL_GRAPHIC_BUFFERS_EXCEPT, +}; + +class BpGraphicBufferAlloc : public BpInterface<IGraphicBufferAlloc> +{ +public: + BpGraphicBufferAlloc(const sp<IBinder>& impl) + : BpInterface<IGraphicBufferAlloc>(impl) + { + } + + virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t w, uint32_t h, + PixelFormat format, uint32_t usage) { + Parcel data, reply; + data.writeInterfaceToken( + IGraphicBufferAlloc::getInterfaceDescriptor()); + data.writeInt32(w); + data.writeInt32(h); + data.writeInt32(format); + data.writeInt32(usage); + remote()->transact(CREATE_GRAPHIC_BUFFER, data, &reply); + sp<GraphicBuffer> graphicBuffer; + bool nonNull = (bool)reply.readInt32(); + if (nonNull) { + graphicBuffer = new GraphicBuffer(); + reply.read(*graphicBuffer); + } + return graphicBuffer; + } + + virtual void freeAllGraphicBuffersExcept(int bufIdx) { + Parcel data, reply; + data.writeInterfaceToken( + IGraphicBufferAlloc::getInterfaceDescriptor()); + data.writeInt32(bufIdx); + remote()->transact(FREE_ALL_GRAPHIC_BUFFERS_EXCEPT, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(GraphicBufferAlloc, "android.ui.IGraphicBufferAlloc"); + +// ---------------------------------------------------------------------- + +status_t BnGraphicBufferAlloc::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + // codes that don't require permission check + + switch(code) { + case CREATE_GRAPHIC_BUFFER: { + CHECK_INTERFACE(IGraphicBufferAlloc, data, reply); + uint32_t w = data.readInt32(); + uint32_t h = data.readInt32(); + PixelFormat format = data.readInt32(); + uint32_t usage = data.readInt32(); + sp<GraphicBuffer> result(createGraphicBuffer(w, h, format, usage)); + reply->writeInt32(result != 0); + if (result != 0) { + reply->write(*result); + } + return NO_ERROR; + } break; + case FREE_ALL_GRAPHIC_BUFFERS_EXCEPT: { + CHECK_INTERFACE(IGraphicBufferAlloc, data, reply); + int bufIdx = data.readInt32(); + freeAllGraphicBuffersExcept(bufIdx); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/surfaceflinger_client/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp index 7049d9e42d16..23b90af31143 100644 --- a/libs/surfaceflinger_client/ISurface.cpp +++ b/libs/surfaceflinger_client/ISurface.cpp @@ -21,9 +21,7 @@ #include <sys/types.h> #include <binder/Parcel.h> -#include <binder/IMemory.h> -#include <ui/Overlay.h> #include <ui/GraphicBuffer.h> #include <surfaceflinger/Surface.h> @@ -33,36 +31,6 @@ namespace android { // ---------------------------------------------------------------------- -ISurface::BufferHeap::BufferHeap() - : w(0), h(0), hor_stride(0), ver_stride(0), format(0), - transform(0), flags(0) -{ -} - -ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, - int32_t hor_stride, int32_t ver_stride, - PixelFormat format, const sp<IMemoryHeap>& heap) - : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), - format(format), transform(0), flags(0), heap(heap) -{ -} - -ISurface::BufferHeap::BufferHeap(uint32_t w, uint32_t h, - int32_t hor_stride, int32_t ver_stride, - PixelFormat format, uint32_t transform, uint32_t flags, - const sp<IMemoryHeap>& heap) - : w(w), h(h), hor_stride(hor_stride), ver_stride(ver_stride), - format(format), transform(transform), flags(flags), heap(heap) -{ -} - - -ISurface::BufferHeap::~BufferHeap() -{ -} - -// ---------------------------------------------------------------------- - class BpSurface : public BpInterface<ISurface> { public: @@ -96,51 +64,6 @@ public: status_t err = reply.readInt32(); return err; } - - virtual status_t registerBuffers(const BufferHeap& buffers) - { - Parcel data, reply; - data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(buffers.w); - data.writeInt32(buffers.h); - data.writeInt32(buffers.hor_stride); - data.writeInt32(buffers.ver_stride); - data.writeInt32(buffers.format); - data.writeInt32(buffers.transform); - data.writeInt32(buffers.flags); - data.writeStrongBinder(buffers.heap->asBinder()); - remote()->transact(REGISTER_BUFFERS, data, &reply); - status_t result = reply.readInt32(); - return result; - } - - virtual void postBuffer(ssize_t offset) - { - Parcel data, reply; - data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(offset); - remote()->transact(POST_BUFFER, data, &reply, IBinder::FLAG_ONEWAY); - } - - virtual void unregisterBuffers() - { - Parcel data, reply; - data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - remote()->transact(UNREGISTER_BUFFERS, data, &reply); - } - - virtual sp<OverlayRef> createOverlay( - uint32_t w, uint32_t h, int32_t format, int32_t orientation) - { - Parcel data, reply; - data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); - data.writeInt32(w); - data.writeInt32(h); - data.writeInt32(format); - data.writeInt32(orientation); - remote()->transact(CREATE_OVERLAY, data, &reply); - return OverlayRef::readFromParcel(reply); - } }; IMPLEMENT_META_INTERFACE(Surface, "android.ui.ISurface"); @@ -170,41 +93,6 @@ status_t BnSurface::onTransact( reply->writeInt32(err); return NO_ERROR; } - case REGISTER_BUFFERS: { - CHECK_INTERFACE(ISurface, data, reply); - BufferHeap buffer; - buffer.w = data.readInt32(); - buffer.h = data.readInt32(); - buffer.hor_stride = data.readInt32(); - buffer.ver_stride= data.readInt32(); - buffer.format = data.readInt32(); - buffer.transform = data.readInt32(); - buffer.flags = data.readInt32(); - buffer.heap = interface_cast<IMemoryHeap>(data.readStrongBinder()); - status_t err = registerBuffers(buffer); - reply->writeInt32(err); - return NO_ERROR; - } break; - case UNREGISTER_BUFFERS: { - CHECK_INTERFACE(ISurface, data, reply); - unregisterBuffers(); - return NO_ERROR; - } break; - case POST_BUFFER: { - CHECK_INTERFACE(ISurface, data, reply); - ssize_t offset = data.readInt32(); - postBuffer(offset); - return NO_ERROR; - } break; - case CREATE_OVERLAY: { - CHECK_INTERFACE(ISurface, data, reply); - int w = data.readInt32(); - int h = data.readInt32(); - int f = data.readInt32(); - int orientation = data.readInt32(); - sp<OverlayRef> o = createOverlay(w, h, f, orientation); - return OverlayRef::writeToParcel(reply, o); - } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/surfaceflinger_client/ISurfaceComposer.cpp b/libs/surfaceflinger_client/ISurfaceComposer.cpp index a2a5455c4d31..8951c3f0453a 100644 --- a/libs/surfaceflinger_client/ISurfaceComposer.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposer.cpp @@ -25,9 +25,11 @@ #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <surfaceflinger/ISurfaceComposer.h> + #include <ui/DisplayInfo.h> -#include <surfaceflinger/ISurfaceComposer.h> +#include <utils/Log.h> // --------------------------------------------------------------------------- @@ -64,6 +66,15 @@ public: return interface_cast<ISurfaceComposerClient>(reply.readStrongBinder()); } + virtual sp<IGraphicBufferAlloc> createGraphicBufferAlloc() + { + uint32_t n; + Parcel data, reply; + data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + remote()->transact(BnSurfaceComposer::CREATE_GRAPHIC_BUFFER_ALLOC, data, &reply); + return interface_cast<IGraphicBufferAlloc>(reply.readStrongBinder()); + } + virtual sp<IMemoryHeap> getCblk() const { Parcel data, reply; @@ -127,13 +138,16 @@ public: virtual status_t captureScreen(DisplayID dpy, sp<IMemoryHeap>* heap, uint32_t* width, uint32_t* height, PixelFormat* format, - uint32_t reqWidth, uint32_t reqHeight) + uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeInt32(dpy); data.writeInt32(reqWidth); data.writeInt32(reqHeight); + data.writeInt32(minLayerZ); + data.writeInt32(maxLayerZ); remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); *heap = interface_cast<IMemoryHeap>(reply.readStrongBinder()); *width = reply.readInt32(); @@ -166,6 +180,40 @@ public: data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::SIGNAL, data, &reply, IBinder::FLAG_ONEWAY); } + + virtual bool authenticateSurface(const sp<ISurface>& surface) const + { + Parcel data, reply; + int err = NO_ERROR; + err = data.writeInterfaceToken( + ISurfaceComposer::getInterfaceDescriptor()); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error writing " + "interface descriptor: %s (%d)", strerror(-err), -err); + return false; + } + err = data.writeStrongBinder(surface->asBinder()); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error writing strong " + "binder to parcel: %s (%d)", strerror(-err), -err); + return false; + } + err = remote()->transact(BnSurfaceComposer::AUTHENTICATE_SURFACE, data, + &reply); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error performing " + "transaction: %s (%d)", strerror(-err), -err); + return false; + } + int32_t result = 0; + err = reply.readInt32(&result); + if (err != NO_ERROR) { + LOGE("ISurfaceComposer::authenticateSurface: error retrieving " + "result: %s (%d)", strerror(-err), -err); + return false; + } + return result != 0; + } }; IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); @@ -186,6 +234,11 @@ status_t BnSurfaceComposer::onTransact( sp<IBinder> b = createClientConnection()->asBinder(); reply->writeStrongBinder(b); } break; + case CREATE_GRAPHIC_BUFFER_ALLOC: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> b = createGraphicBufferAlloc()->asBinder(); + reply->writeStrongBinder(b); + } break; case OPEN_GLOBAL_TRANSACTION: { CHECK_INTERFACE(ISurfaceComposer, data, reply); openGlobalTransaction(); @@ -231,11 +284,13 @@ status_t BnSurfaceComposer::onTransact( DisplayID dpy = data.readInt32(); uint32_t reqWidth = data.readInt32(); uint32_t reqHeight = data.readInt32(); + uint32_t minLayerZ = data.readInt32(); + uint32_t maxLayerZ = data.readInt32(); sp<IMemoryHeap> heap; uint32_t w, h; PixelFormat f; status_t res = captureScreen(dpy, &heap, &w, &h, &f, - reqWidth, reqHeight); + reqWidth, reqHeight, minLayerZ, maxLayerZ); reply->writeStrongBinder(heap->asBinder()); reply->writeInt32(w); reply->writeInt32(h); @@ -254,6 +309,12 @@ status_t BnSurfaceComposer::onTransact( status_t res = turnElectronBeamOn(mode); reply->writeInt32(res); } break; + case AUTHENTICATE_SURFACE: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<ISurface> surface = interface_cast<ISurface>(data.readStrongBinder()); + int32_t result = authenticateSurface(surface) ? 1 : 0; + reply->writeInt32(result); + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/surfaceflinger_client/ISurfaceComposerClient.cpp b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp index 2cc1f8e34383..7730eb1fa91b 100644 --- a/libs/surfaceflinger_client/ISurfaceComposerClient.cpp +++ b/libs/surfaceflinger_client/ISurfaceComposerClient.cpp @@ -157,7 +157,7 @@ status_t BnSurfaceComposerClient::onTransact( const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); const int self_pid = getpid(); - if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS)) { + if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS && uid != 0)) { // we're called from a different process, do the real check if (!checkCallingPermission( String16("android.permission.ACCESS_SURFACE_FLINGER"))) diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp index b45e43fb3d85..7505d530e0c5 100644 --- a/libs/surfaceflinger_client/SharedBufferStack.cpp +++ b/libs/surfaceflinger_client/SharedBufferStack.cpp @@ -263,6 +263,14 @@ bool SharedBufferClient::LockCondition::operator()() const { return (buf != stack.index[stack.head]); } +SharedBufferServer::BuffersAvailableCondition::BuffersAvailableCondition( + SharedBufferServer* sbs, int numBuffers) : ConditionBase(sbs), + mNumBuffers(numBuffers) { +} +bool SharedBufferServer::BuffersAvailableCondition::operator()() const { + return stack.available == mNumBuffers; +} + // ---------------------------------------------------------------------------- SharedBufferClient::QueueUpdate::QueueUpdate(SharedBufferBase* sbb) @@ -358,11 +366,6 @@ ssize_t SharedBufferClient::dequeue() { SharedBufferStack& stack( *mSharedStack ); - if (stack.head == tail && stack.available == mNumBuffers) { - LOGW("dequeue: tail=%d, head=%d, avail=%d, queued=%d", - tail, stack.head, stack.available, stack.queued); - } - RWLock::AutoRLock _rd(mLock); const nsecs_t dequeueTime = systemTime(SYSTEM_TIME_THREAD); @@ -431,6 +434,7 @@ status_t SharedBufferClient::queue(int buf) const nsecs_t now = systemTime(SYSTEM_TIME_THREAD); stack.stats.totalTime = ns2us(now - mDequeueTime[buf]); + return err; } @@ -475,6 +479,7 @@ status_t SharedBufferClient::setBufferCount( if (err == NO_ERROR) { mNumBuffers = bufferCount; queued_head = (stack.head + stack.queued) % mNumBuffers; + tail = computeTail(); } return err; } @@ -582,17 +587,24 @@ uint32_t SharedBufferServer::getTransform(int buf) const */ status_t SharedBufferServer::resize(int newNumBuffers) { - if (uint32_t(newNumBuffers) >= SharedBufferStack::NUM_BUFFER_MAX) + if ((unsigned int)(newNumBuffers) < SharedBufferStack::NUM_BUFFER_MIN || + (unsigned int)(newNumBuffers) > SharedBufferStack::NUM_BUFFER_MAX) { return BAD_VALUE; + } RWLock::AutoWLock _l(mLock); - // for now we're not supporting shrinking - const int numBuffers = mNumBuffers; - if (newNumBuffers < numBuffers) - return BAD_VALUE; + if (newNumBuffers < mNumBuffers) { + return shrink(newNumBuffers); + } else { + return grow(newNumBuffers); + } +} +status_t SharedBufferServer::grow(int newNumBuffers) +{ SharedBufferStack& stack( *mSharedStack ); + const int numBuffers = mNumBuffers; const int extra = newNumBuffers - numBuffers; // read the head, make sure it's valid @@ -626,6 +638,48 @@ status_t SharedBufferServer::resize(int newNumBuffers) return NO_ERROR; } +status_t SharedBufferServer::shrink(int newNumBuffers) +{ + SharedBufferStack& stack( *mSharedStack ); + + // Shrinking is only supported if there are no buffers currently dequeued. + int32_t avail = stack.available; + int32_t queued = stack.queued; + if (avail + queued != mNumBuffers) { + return INVALID_OPERATION; + } + + // Wait for any queued buffers to be displayed. + BuffersAvailableCondition condition(this, mNumBuffers); + status_t err = waitForCondition(condition); + if (err < 0) { + return err; + } + + // Reset head to index 0 and make it refer to buffer 0. The same renaming + // (head -> 0) is done in the BufferManager. + int32_t head = stack.head; + int8_t* index = const_cast<int8_t*>(stack.index); + for (int8_t i = 0; i < newNumBuffers; i++) { + index[i] = i; + } + stack.head = 0; + stack.headBuf = 0; + + // Free the buffers from the end of the list that are no longer needed. + for (int i = newNumBuffers; i < mNumBuffers; i++) { + mBufferList.remove(i); + } + + // Tell the client to reallocate all the buffers. + reallocateAll(); + + mNumBuffers = newNumBuffers; + stack.available = newNumBuffers; + + return NO_ERROR; +} + SharedBufferStack::Statistics SharedBufferServer::getStats() const { SharedBufferStack& stack( *mSharedStack ); diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp index 017e94c94fa0..21d509a6a8df 100644 --- a/libs/surfaceflinger_client/Surface.cpp +++ b/libs/surfaceflinger_client/Surface.cpp @@ -364,6 +364,13 @@ status_t Surface::writeToParcel( height = surface->mHeight; format = surface->mFormat; flags = surface->mFlags; + } else if (surface != 0 && surface->mSurface != 0) { + LOGW("Parceling invalid surface with non-NULL ISurface as NULL: " + "mSurface = %p, mIdentity = %d, mWidth = %d, mHeight = %d, " + "mFormat = %d, mFlags = 0x%08x, mInitCheck = %d", + surface->mSurface.get(), surface->mIdentity, surface->mWidth, + surface->mHeight, surface->mFormat, surface->mFlags, + surface->mInitCheck); } parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); parcel->writeInt32(identity); @@ -438,21 +445,15 @@ void Surface::init() mSharedBufferClient = new SharedBufferClient( mClient.getSharedClient(), token, 2, mIdentity); mInitCheck = mClient.getSharedClient()->validate(token); + } else { + LOGW("Not initializing the shared buffer client because token = %d", + token); } } } Surface::~Surface() { - // this is a client-side operation, the surface is destroyed, unmap - // its buffers in this process. - size_t size = mBuffers.size(); - for (size_t i=0 ; i<size ; i++) { - if (mBuffers[i] != 0 && mBuffers[i]->handle != 0) { - getBufferMapper().unregisterBuffer(mBuffers[i]->handle); - } - } - // clear all references and trigger an IPC now, to make sure things // happen without delay, since these resources are quite heavy. mBuffers.clear(); @@ -465,7 +466,7 @@ bool Surface::isValid() { return mInitCheck == NO_ERROR; } -status_t Surface::validate() const +status_t Surface::validate(bool inCancelBuffer) const { // check that we initialized ourself properly if (mInitCheck != NO_ERROR) { @@ -475,27 +476,26 @@ status_t Surface::validate() const // verify the identity of this surface uint32_t identity = mSharedBufferClient->getIdentity(); - - // this is a bit of a (temporary) special case, identity==0 means that - // no operation are allowed from the client (eg: dequeue/queue), this - // is used with PUSH_BUFFER surfaces for instance - if (identity == 0) { - LOGE("[Surface] invalid operation (identity=%u)", mIdentity); - return INVALID_OPERATION; - } - if (mIdentity != identity) { LOGE("[Surface] using an invalid surface, " "identity=%u should be %d", mIdentity, identity); - return NO_INIT; + CallStack stack; + stack.update(); + stack.dump("Surface"); + return BAD_INDEX; } // check the surface didn't become invalid status_t err = mSharedBufferClient->getStatus(); if (err != NO_ERROR) { - LOGE("surface (identity=%u) is invalid, err=%d (%s)", - mIdentity, err, strerror(-err)); + if (!inCancelBuffer) { + LOGE("surface (identity=%u) is invalid, err=%d (%s)", + mIdentity, err, strerror(-err)); + CallStack stack; + stack.update(); + stack.dump("Surface"); + } return err; } @@ -626,12 +626,12 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer) int Surface::cancelBuffer(android_native_buffer_t* buffer) { - status_t err = validate(); + status_t err = validate(true); switch (err) { case NO_ERROR: // no error, common case break; - case INVALID_OPERATION: + case BAD_INDEX: // legitimate errors here return err; default: @@ -709,6 +709,17 @@ int Surface::query(int what, int* value) case NATIVE_WINDOW_FORMAT: *value = int(mFormat); return NO_ERROR; + case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: + *value = MIN_UNDEQUEUED_BUFFERS; + return NO_ERROR; + case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + *value = sf->authenticateSurface(mSurface) ? 1 : 0; + return NO_ERROR; + } + case NATIVE_WINDOW_CONCRETE_TYPE: + *value = NATIVE_WINDOW_SURFACE; + return NO_ERROR; } return BAD_VALUE; } @@ -827,13 +838,15 @@ int Surface::disconnect(int api) int Surface::crop(Rect const* rect) { - // empty/invalid rects are not allowed - if (rect->isEmpty()) - return BAD_VALUE; - Mutex::Autolock _l(mSurfaceLock); // TODO: validate rect size - mNextBufferCrop = *rect; + + if (rect == NULL || rect->isEmpty()) { + mNextBufferCrop = Rect(0,0); + } else { + mNextBufferCrop = *rect; + } + return NO_ERROR; } @@ -854,6 +867,12 @@ int Surface::setBufferCount(int bufferCount) status_t err = mSharedBufferClient->setBufferCount(bufferCount, ipc); LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s", bufferCount, strerror(-err)); + + if (err == NO_ERROR) { + // Clear out any references to the old buffers. + mBuffers.clear(); + } + return err; } @@ -878,6 +897,9 @@ int Surface::setBuffersGeometry(int w, int h, int format) // EGLConfig validation. mFormat = format; } + + mNextBufferCrop = Rect(0,0); + return NO_ERROR; } @@ -1022,7 +1044,20 @@ void Surface::setSwapRectangle(const Rect& r) { int Surface::getBufferIndex(const sp<GraphicBuffer>& buffer) const { - return buffer->getIndex(); + int idx = buffer->getIndex(); + if (idx < 0) { + // The buffer doesn't have an index set. See if the handle the same as + // one of the buffers for which we do know the index. This can happen + // e.g. if GraphicBuffer is used to wrap an android_native_buffer_t that + // was dequeued from an ANativeWindow. + for (size_t i = 0; i < mBuffers.size(); i++) { + if (mBuffers[i] != 0 && buffer->handle == mBuffers[i]->handle) { + idx = mBuffers[i]->getIndex(); + break; + } + } + } + return idx; } status_t Surface::getBufferLocked(int index, @@ -1036,7 +1071,6 @@ status_t Surface::getBufferLocked(int index, // free the current buffer sp<GraphicBuffer>& currentBuffer(mBuffers.editItemAt(index)); if (currentBuffer != 0) { - getBufferMapper().unregisterBuffer(currentBuffer->handle); currentBuffer.clear(); } @@ -1044,7 +1078,7 @@ status_t Surface::getBufferLocked(int index, LOGE_IF(buffer==0, "ISurface::getBuffer(%d, %08x) returned NULL", index, usage); - if (buffer != 0) { // this should never happen by construction + if (buffer != 0) { // this should always happen by construction LOGE_IF(buffer->handle == NULL, "Surface (identity=%d) requestBuffer(%d, %u, %u, %u, %08x) " "returned a buffer with a null handle", @@ -1052,13 +1086,8 @@ status_t Surface::getBufferLocked(int index, err = mSharedBufferClient->getStatus(); LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err); if (!err && buffer->handle != NULL) { - err = getBufferMapper().registerBuffer(buffer->handle); - LOGW_IF(err, "registerBuffer(...) failed %d (%s)", - err, strerror(-err)); - if (err == NO_ERROR) { - currentBuffer = buffer; - currentBuffer->setIndex(index); - } + currentBuffer = buffer; + currentBuffer->setIndex(index); } else { err = err<0 ? err : status_t(NO_MEMORY); } diff --git a/libs/surfaceflinger_client/SurfaceComposerClient.cpp b/libs/surfaceflinger_client/SurfaceComposerClient.cpp index f27046160709..d3367246a119 100644 --- a/libs/surfaceflinger_client/SurfaceComposerClient.cpp +++ b/libs/surfaceflinger_client/SurfaceComposerClient.cpp @@ -555,7 +555,8 @@ status_t ScreenshotClient::update() { if (s == NULL) return NO_INIT; mHeap = 0; return s->captureScreen(0, &mHeap, - &mWidth, &mHeight, &mFormat, 0, 0); + &mWidth, &mHeight, &mFormat, 0, 0, + 0, -1UL); } status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) { @@ -563,7 +564,18 @@ status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight) { if (s == NULL) return NO_INIT; mHeap = 0; return s->captureScreen(0, &mHeap, - &mWidth, &mHeight, &mFormat, reqWidth, reqHeight); + &mWidth, &mHeight, &mFormat, reqWidth, reqHeight, + 0, -1UL); +} + +status_t ScreenshotClient::update(uint32_t reqWidth, uint32_t reqHeight, + uint32_t minLayerZ, uint32_t maxLayerZ) { + sp<ISurfaceComposer> s(ComposerService::getComposerService()); + if (s == NULL) return NO_INIT; + mHeap = 0; + return s->captureScreen(0, &mHeap, + &mWidth, &mHeight, &mFormat, reqWidth, reqHeight, + minLayerZ, maxLayerZ); } void ScreenshotClient::release() { diff --git a/libs/surfaceflinger_client/tests/Android.mk b/libs/surfaceflinger_client/tests/Android.mk index 5053e7d64389..212b8e75919a 100644 --- a/libs/surfaceflinger_client/tests/Android.mk +++ b/libs/surfaceflinger_client/tests/Android.mk @@ -1 +1,53 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +ifneq ($(TARGET_SIMULATOR),true) + +# Build the unit tests. +test_src_files := \ + Surface_test.cpp \ + +shared_libraries := \ + libcutils \ + libutils \ + libbinder \ + libsurfaceflinger_client \ + libstlport \ + +static_libraries := \ + libgtest \ + libgtest_main \ + +c_includes := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + +module_tags := tests + +$(foreach file,$(test_src_files), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ + $(eval LOCAL_C_INCLUDES := $(c_includes)) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ + $(eval include $(BUILD_EXECUTABLE)) \ +) + +# Build the manual test programs. include $(call all-subdir-makefiles) + +endif + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp index f409f4828970..7ef59269bade 100644 --- a/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp +++ b/libs/surfaceflinger_client/tests/SharedBufferStack/SharedBufferStackTest.cpp @@ -32,7 +32,8 @@ void test0(SharedBufferServer& s, SharedBufferClient& c, size_t num, int* list); int main(int argc, char** argv) { SharedClient client; - SharedBufferServer s(&client, 0, 4, 0); + sp<SharedBufferServer> ps(new SharedBufferServer(&client, 0, 4, 0)); + SharedBufferServer& s(*ps); SharedBufferClient c(&client, 0, 4, 0); printf("basic test 0\n"); @@ -67,6 +68,10 @@ int main(int argc, char** argv) int list3[6] = {3, 2, 1, 4, 5, 0}; test0(s, c, 6, list3); + c.setBufferCount(4, resize); + int list4[4] = {1, 2, 3, 0}; + test0(s, c, 4, list4); + return 0; } diff --git a/libs/surfaceflinger_client/tests/Surface_test.cpp b/libs/surfaceflinger_client/tests/Surface_test.cpp new file mode 100644 index 000000000000..fd07479a0587 --- /dev/null +++ b/libs/surfaceflinger_client/tests/Surface_test.cpp @@ -0,0 +1,141 @@ +/* + * 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 <gtest/gtest.h> + +#include <binder/IMemory.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/SurfaceComposerClient.h> +#include <utils/String8.h> + +namespace android { + +class SurfaceTest : public ::testing::Test { +protected: + virtual void SetUp() { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + mSurfaceControl = mComposerClient->createSurface(getpid(), + String8("Test Surface"), 0, 32, 32, PIXEL_FORMAT_RGB_888, 0); + + ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl->isValid()); + + ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction()); + ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000)); + ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); + ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction()); + + mSurface = mSurfaceControl->getSurface(); + ASSERT_TRUE(mSurface != NULL); + } + + virtual void TearDown() { + mComposerClient->dispose(); + } + + sp<Surface> mSurface; + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mSurfaceControl; +}; + +TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenVisible) { + sp<ANativeWindow> anw(mSurface); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(1, result); +} + +TEST_F(SurfaceTest, QueuesToWindowComposerIsTrueWhenPurgatorized) { + mSurfaceControl.clear(); + + sp<ANativeWindow> anw(mSurface); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(1, result); +} + +// This test probably doesn't belong here. +TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersFail) { + sp<ANativeWindow> anw(mSurface); + + // Verify the screenshot works with no protected buffers. + sp<IMemoryHeap> heap; + uint32_t w=0, h=0; + PixelFormat fmt=0; + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + ASSERT_EQ(NO_ERROR, sf->captureScreen(0, &heap, &w, &h, &fmt, 64, 64, 0, + 40000)); + ASSERT_TRUE(heap != NULL); + + // Set the PROTECTED usage bit and verify that the screenshot fails. Note + // that we need to dequeue a buffer in order for it to actually get + // allocated in SurfaceFlinger. + ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), + GRALLOC_USAGE_PROTECTED)); + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); + android_native_buffer_t* buf = 0; + for (int i = 0; i < 4; i++) { + // Loop to make sure SurfaceFlinger has retired a protected buffer. + ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buf)); + ASSERT_EQ(NO_ERROR, anw->lockBuffer(anw.get(), buf)); + ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf)); + } + heap = 0; + w = h = fmt = 0; + ASSERT_EQ(INVALID_OPERATION, sf->captureScreen(0, &heap, &w, &h, &fmt, + 64, 64, 0, 40000)); + ASSERT_TRUE(heap == NULL); + + // XXX: This should not be needed, but it seems that the new buffers don't + // correctly show up after the upcoming dequeue/lock/queue loop without it. + // We should look into this at some point. + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3)); + + // Un-set the PROTECTED usage bit and verify that the screenshot works + // again. Note that we have to change the buffers geometry to ensure that + // the buffers get reallocated, as the new usage bits are a subset of the + // old. + ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0)); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(anw.get(), 32, 32, 0)); + for (int i = 0; i < 4; i++) { + // Loop to make sure SurfaceFlinger has retired a protected buffer. + ASSERT_EQ(NO_ERROR, anw->dequeueBuffer(anw.get(), &buf)); + ASSERT_EQ(NO_ERROR, anw->lockBuffer(anw.get(), buf)); + ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf)); + } + heap = 0; + w = h = fmt = 0; + ASSERT_EQ(NO_ERROR, sf->captureScreen(0, &heap, &w, &h, &fmt, 64, 64, 0, + 40000)); + ASSERT_TRUE(heap != NULL); +} + +TEST_F(SurfaceTest, ConcreteTypeIsSurface) { + sp<ANativeWindow> anw(mSurface); + int result = -123; + int err = anw->query(anw.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result); + EXPECT_EQ(NO_ERROR, err); + EXPECT_EQ(NATIVE_WINDOW_SURFACE, result); +} + +} diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index c4a09d67d468..f9990bb7d9e3 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -1,24 +1,55 @@ +# 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. + LOCAL_PATH:= $(call my-dir) + +# libui is partially built for the host (used by build time keymap validation tool) +# These files are common to host and target builds. +commonSources:= \ + Input.cpp \ + Keyboard.cpp \ + KeyLayoutMap.cpp \ + KeyCharacterMap.cpp \ + VirtualKeyMap.cpp + +# For the host +# ===================================================== + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= $(commonSources) + +LOCAL_MODULE:= libui + +include $(BUILD_HOST_STATIC_LIBRARY) + + +# For the device +# ===================================================== + include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + $(commonSources) \ EGLUtils.cpp \ - EventHub.cpp \ EventRecurrence.cpp \ FramebufferNativeWindow.cpp \ GraphicBuffer.cpp \ GraphicBufferAllocator.cpp \ GraphicBufferMapper.cpp \ GraphicLog.cpp \ - KeyLayoutMap.cpp \ - KeyCharacterMap.cpp \ - Input.cpp \ - InputDispatcher.cpp \ - InputManager.cpp \ - InputReader.cpp \ InputTransport.cpp \ - IOverlay.cpp \ - Overlay.cpp \ PixelFormat.cpp \ Rect.cpp \ Region.cpp @@ -27,10 +58,14 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ libEGL \ - libbinder \ libpixelflinger \ libhardware \ - libhardware_legacy + libhardware_legacy \ + libskia \ + libbinder + +LOCAL_C_INCLUDES := \ + external/skia/include/core LOCAL_MODULE:= libui diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp deleted file mode 100644 index 41daa9ca0dbc..000000000000 --- a/libs/ui/EventHub.cpp +++ /dev/null @@ -1,1026 +0,0 @@ -// -// Copyright 2005 The Android Open Source Project -// -// Handle events, like key input and vsync. -// -// The goal is to provide an optimized solution for Linux, not an -// implementation that works well across all platforms. We expect -// events to arrive on file descriptors, so that we can use a select() -// select() call to sleep. -// -// We can't select() on anything but network sockets in Windows, so we -// provide an alternative implementation of waitEvent for that platform. -// -#define LOG_TAG "EventHub" - -//#define LOG_NDEBUG 0 - -#include <ui/EventHub.h> -#include <ui/KeycodeLabels.h> -#include <hardware_legacy/power.h> - -#include <cutils/properties.h> -#include <utils/Log.h> -#include <utils/Timers.h> -#include <utils/threads.h> -#include <utils/Errors.h> - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <fcntl.h> -#include <memory.h> -#include <errno.h> -#include <assert.h> - -#include "KeyLayoutMap.h" - -#include <string.h> -#include <stdint.h> -#include <dirent.h> -#ifdef HAVE_INOTIFY -# include <sys/inotify.h> -#endif -#ifdef HAVE_ANDROID_OS -# include <sys/limits.h> /* not part of Linux */ -#endif -#include <sys/poll.h> -#include <sys/ioctl.h> - -/* this macro is used to tell if "bit" is set in "array" - * it selects a byte from the array, and does a boolean AND - * operation with a byte that only has the relevant bit set. - * eg. to check for the 12th bit, we do (array[1] & 1<<4) - */ -#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) - -/* this macro computes the number of bytes needed to represent a bit array of the specified size */ -#define sizeof_bit_array(bits) ((bits + 7) / 8) - -#define ID_MASK 0x0000ffff -#define SEQ_MASK 0x7fff0000 -#define SEQ_SHIFT 16 - -#ifndef ABS_MT_TOUCH_MAJOR -#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ -#endif - -#ifndef ABS_MT_POSITION_X -#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */ -#endif - -#ifndef ABS_MT_POSITION_Y -#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ -#endif - -#define INDENT " " -#define INDENT2 " " -#define INDENT3 " " - -namespace android { - -static const char *WAKE_LOCK_ID = "KeyEvents"; -static const char *device_path = "/dev/input"; - -/* return the larger integer */ -static inline int max(int v1, int v2) -{ - return (v1 > v2) ? v1 : v2; -} - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - -EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name) - : id(_id), path(_path), name(name), classes(0) - , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) { -} - -EventHub::device_t::~device_t() { - delete [] keyBitmask; - delete layoutMap; -} - -EventHub::EventHub(void) - : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0) - , mDevicesById(0), mNumDevicesById(0) - , mOpeningDevices(0), mClosingDevices(0) - , mDevices(0), mFDs(0), mFDCount(0), mOpened(false), mNeedToSendFinishedDeviceScan(false) - , mInputBufferIndex(0), mInputBufferCount(0), mInputDeviceIndex(0) -{ - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); -#ifdef EV_SW - memset(mSwitches, 0, sizeof(mSwitches)); -#endif -} - -/* - * Clean up. - */ -EventHub::~EventHub(void) -{ - release_wake_lock(WAKE_LOCK_ID); - // we should free stuff here... -} - -status_t EventHub::errorCheck() const -{ - return mError; -} - -String8 EventHub::getDeviceName(int32_t deviceId) const -{ - AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); - if (device == NULL) return String8(); - return device->name; -} - -uint32_t EventHub::getDeviceClasses(int32_t deviceId) const -{ - AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); - if (device == NULL) return 0; - return device->classes; -} - -status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const { - outAxisInfo->clear(); - - AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); - if (device == NULL) return -1; - - struct input_absinfo info; - - if(ioctl(device->fd, EVIOCGABS(axis), &info)) { - LOGW("Error reading absolute controller %d for device %s fd %d\n", - axis, device->name.string(), device->fd); - return -errno; - } - - if (info.minimum != info.maximum) { - outAxisInfo->valid = true; - outAxisInfo->minValue = info.minimum; - outAxisInfo->maxValue = info.maximum; - outAxisInfo->flat = info.flat; - outAxisInfo->fuzz = info.fuzz; - } - return OK; -} - -int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { - if (scanCode >= 0 && scanCode <= KEY_MAX) { - AutoMutex _l(mLock); - - device_t* device = getDeviceLocked(deviceId); - if (device != NULL) { - return getScanCodeStateLocked(device, scanCode); - } - } - return AKEY_STATE_UNKNOWN; -} - -int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const { - uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; - memset(key_bitmask, 0, sizeof(key_bitmask)); - if (ioctl(device->fd, - EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { - return test_bit(scanCode, key_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP; - } - return AKEY_STATE_UNKNOWN; -} - -int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { - AutoMutex _l(mLock); - - device_t* device = getDeviceLocked(deviceId); - if (device != NULL) { - return getKeyCodeStateLocked(device, keyCode); - } - return AKEY_STATE_UNKNOWN; -} - -int32_t EventHub::getKeyCodeStateLocked(device_t* device, int32_t keyCode) const { - Vector<int32_t> scanCodes; - device->layoutMap->findScancodes(keyCode, &scanCodes); - - uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; - memset(key_bitmask, 0, sizeof(key_bitmask)); - if (ioctl(device->fd, EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) { - #if 0 - for (size_t i=0; i<=KEY_MAX; i++) { - LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask)); - } - #endif - const size_t N = scanCodes.size(); - for (size_t i=0; i<N && i<=KEY_MAX; i++) { - int32_t sc = scanCodes.itemAt(i); - //LOGI("Code %d: down=%d", sc, test_bit(sc, key_bitmask)); - if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, key_bitmask)) { - return AKEY_STATE_DOWN; - } - } - return AKEY_STATE_UP; - } - return AKEY_STATE_UNKNOWN; -} - -int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { -#ifdef EV_SW - if (sw >= 0 && sw <= SW_MAX) { - AutoMutex _l(mLock); - - device_t* device = getDeviceLocked(deviceId); - if (device != NULL) { - return getSwitchStateLocked(device, sw); - } - } -#endif - return AKEY_STATE_UNKNOWN; -} - -int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const { - uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)]; - memset(sw_bitmask, 0, sizeof(sw_bitmask)); - if (ioctl(device->fd, - EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) { - return test_bit(sw, sw_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP; - } - return AKEY_STATE_UNKNOWN; -} - -bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) const { - AutoMutex _l(mLock); - - device_t* device = getDeviceLocked(deviceId); - if (device != NULL) { - return markSupportedKeyCodesLocked(device, numCodes, keyCodes, outFlags); - } - return false; -} - -bool EventHub::markSupportedKeyCodesLocked(device_t* device, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) const { - if (device->layoutMap == NULL || device->keyBitmask == NULL) { - return false; - } - - Vector<int32_t> scanCodes; - for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { - scanCodes.clear(); - - status_t err = device->layoutMap->findScancodes(keyCodes[codeIndex], &scanCodes); - if (! err) { - // check the possible scan codes identified by the layout map against the - // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (test_bit(scanCodes[sc], device->keyBitmask)) { - outFlags[codeIndex] = 1; - break; - } - } - } - } - return true; -} - -status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode, - int32_t* outKeycode, uint32_t* outFlags) const -{ - AutoMutex _l(mLock); - device_t* device = getDeviceLocked(deviceId); - - if (device != NULL && device->layoutMap != NULL) { - status_t err = device->layoutMap->map(scancode, outKeycode, outFlags); - if (err == NO_ERROR) { - return NO_ERROR; - } - } - - if (mHaveFirstKeyboard) { - device = getDeviceLocked(mFirstKeyboardId); - - if (device != NULL && device->layoutMap != NULL) { - status_t err = device->layoutMap->map(scancode, outKeycode, outFlags); - if (err == NO_ERROR) { - return NO_ERROR; - } - } - } - - *outKeycode = 0; - *outFlags = 0; - return NAME_NOT_FOUND; -} - -void EventHub::addExcludedDevice(const char* deviceName) -{ - AutoMutex _l(mLock); - - String8 name(deviceName); - mExcludedDevices.push_back(name); -} - -EventHub::device_t* EventHub::getDeviceLocked(int32_t deviceId) const -{ - if (deviceId == 0) deviceId = mFirstKeyboardId; - int32_t id = deviceId & ID_MASK; - if (id >= mNumDevicesById || id < 0) return NULL; - device_t* dev = mDevicesById[id].device; - if (dev == NULL) return NULL; - if (dev->id == deviceId) { - return dev; - } - return NULL; -} - -bool EventHub::getEvent(RawEvent* outEvent) -{ - outEvent->deviceId = 0; - outEvent->type = 0; - outEvent->scanCode = 0; - outEvent->keyCode = 0; - outEvent->flags = 0; - outEvent->value = 0; - outEvent->when = 0; - - // Note that we only allow one caller to getEvent(), so don't need - // to do locking here... only when adding/removing devices. - - if (!mOpened) { - mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; - mOpened = true; - mNeedToSendFinishedDeviceScan = true; - } - - for (;;) { - // Report any devices that had last been added/removed. - if (mClosingDevices != NULL) { - device_t* device = mClosingDevices; - LOGV("Reporting device closed: id=0x%x, name=%s\n", - device->id, device->path.string()); - mClosingDevices = device->next; - if (device->id == mFirstKeyboardId) { - outEvent->deviceId = 0; - } else { - outEvent->deviceId = device->id; - } - outEvent->type = DEVICE_REMOVED; - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); - delete device; - mNeedToSendFinishedDeviceScan = true; - return true; - } - - if (mOpeningDevices != NULL) { - device_t* device = mOpeningDevices; - LOGV("Reporting device opened: id=0x%x, name=%s\n", - device->id, device->path.string()); - mOpeningDevices = device->next; - if (device->id == mFirstKeyboardId) { - outEvent->deviceId = 0; - } else { - outEvent->deviceId = device->id; - } - outEvent->type = DEVICE_ADDED; - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); - mNeedToSendFinishedDeviceScan = true; - return true; - } - - if (mNeedToSendFinishedDeviceScan) { - mNeedToSendFinishedDeviceScan = false; - outEvent->type = FINISHED_DEVICE_SCAN; - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); - return true; - } - - // Grab the next input event. - for (;;) { - // Consume buffered input events, if any. - if (mInputBufferIndex < mInputBufferCount) { - const struct input_event& iev = mInputBufferData[mInputBufferIndex++]; - const device_t* device = mDevices[mInputDeviceIndex]; - - LOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, v=%d", device->path.string(), - (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); - if (device->id == mFirstKeyboardId) { - outEvent->deviceId = 0; - } else { - outEvent->deviceId = device->id; - } - outEvent->type = iev.type; - outEvent->scanCode = iev.code; - if (iev.type == EV_KEY) { - status_t err = device->layoutMap->map(iev.code, - & outEvent->keyCode, & outEvent->flags); - LOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", - iev.code, outEvent->keyCode, outEvent->flags, err); - if (err != 0) { - outEvent->keyCode = AKEYCODE_UNKNOWN; - outEvent->flags = 0; - } - } else { - outEvent->keyCode = iev.code; - } - outEvent->value = iev.value; - - // Use an event timestamp in the same timebase as - // java.lang.System.nanoTime() and android.os.SystemClock.uptimeMillis() - // as expected by the rest of the system. - outEvent->when = systemTime(SYSTEM_TIME_MONOTONIC); - return true; - } - - // Finish reading all events from devices identified in previous poll(). - // This code assumes that mInputDeviceIndex is initially 0 and that the - // revents member of pollfd is initialized to 0 when the device is first added. - // Since mFDs[0] is used for inotify, we process regular events starting at index 1. - mInputDeviceIndex += 1; - if (mInputDeviceIndex >= mFDCount) { - break; - } - - const struct pollfd& pfd = mFDs[mInputDeviceIndex]; - if (pfd.revents & POLLIN) { - int32_t readSize = read(pfd.fd, mInputBufferData, - sizeof(struct input_event) * INPUT_BUFFER_SIZE); - if (readSize < 0) { - if (errno != EAGAIN && errno != EINTR) { - LOGW("could not get event (errno=%d)", errno); - } - } else if ((readSize % sizeof(struct input_event)) != 0) { - LOGE("could not get event (wrong size: %d)", readSize); - } else { - mInputBufferCount = readSize / sizeof(struct input_event); - mInputBufferIndex = 0; - } - } - } - -#if HAVE_INOTIFY - // readNotify() will modify mFDs and mFDCount, so this must be done after - // processing all other events. - if(mFDs[0].revents & POLLIN) { - readNotify(mFDs[0].fd); - mFDs[0].revents = 0; - continue; // report added or removed devices immediately - } -#endif - - mInputDeviceIndex = 0; - - // Poll for events. Mind the wake lock dance! - // We hold a wake lock at all times except during poll(). This works due to some - // subtle choreography. When a device driver has pending (unread) events, it acquires - // a kernel wake lock. However, once the last pending event has been read, the device - // driver will release the kernel wake lock. To prevent the system from going to sleep - // when this happens, the EventHub holds onto its own user wake lock while the client - // is processing events. Thus the system can only sleep if there are no events - // pending or currently being processed. - release_wake_lock(WAKE_LOCK_ID); - - int pollResult = poll(mFDs, mFDCount, -1); - - acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); - - if (pollResult <= 0) { - if (errno != EINTR) { - LOGW("poll failed (errno=%d)\n", errno); - usleep(100000); - } - } - } -} - -/* - * Open the platform-specific input device. - */ -bool EventHub::openPlatformInput(void) -{ - /* - * Open platform-specific input device(s). - */ - int res; - - mFDCount = 1; - mFDs = (pollfd *)calloc(1, sizeof(mFDs[0])); - mDevices = (device_t **)calloc(1, sizeof(mDevices[0])); - mFDs[0].events = POLLIN; - mFDs[0].revents = 0; - mDevices[0] = NULL; -#ifdef HAVE_INOTIFY - mFDs[0].fd = inotify_init(); - res = inotify_add_watch(mFDs[0].fd, device_path, IN_DELETE | IN_CREATE); - if(res < 0) { - LOGE("could not add watch for %s, %s\n", device_path, strerror(errno)); - } -#else - /* - * The code in EventHub::getEvent assumes that mFDs[0] is an inotify fd. - * We allocate space for it and set it to something invalid. - */ - mFDs[0].fd = -1; -#endif - - res = scanDir(device_path); - if(res < 0) { - LOGE("scan dir failed for %s\n", device_path); - } - - return true; -} - -// ---------------------------------------------------------------------------- - -static bool containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex) { - const uint8_t* end = array + endIndex; - array += startIndex; - while (array != end) { - if (*(array++) != 0) { - return true; - } - } - return false; -} - -static const int32_t GAMEPAD_KEYCODES[] = { - AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, - AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, - AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, - AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, - AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, - AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE -}; - -int EventHub::openDevice(const char *deviceName) { - int version; - int fd; - struct pollfd *new_mFDs; - device_t **new_devices; - char **new_device_names; - char name[80]; - char location[80]; - char idstr[80]; - struct input_id id; - - LOGV("Opening device: %s", deviceName); - - AutoMutex _l(mLock); - - fd = open(deviceName, O_RDWR); - if(fd < 0) { - LOGE("could not open %s, %s\n", deviceName, strerror(errno)); - return -1; - } - - if(ioctl(fd, EVIOCGVERSION, &version)) { - LOGE("could not get driver version for %s, %s\n", deviceName, strerror(errno)); - return -1; - } - if(ioctl(fd, EVIOCGID, &id)) { - LOGE("could not get driver id for %s, %s\n", deviceName, strerror(errno)); - return -1; - } - name[sizeof(name) - 1] = '\0'; - location[sizeof(location) - 1] = '\0'; - idstr[sizeof(idstr) - 1] = '\0'; - if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { - //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno)); - name[0] = '\0'; - } - - // check to see if the device is on our excluded list - List<String8>::iterator iter = mExcludedDevices.begin(); - List<String8>::iterator end = mExcludedDevices.end(); - for ( ; iter != end; iter++) { - const char* test = *iter; - if (strcmp(name, test) == 0) { - LOGI("ignoring event id %s driver %s\n", deviceName, test); - close(fd); - return -1; - } - } - - if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { - //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno)); - location[0] = '\0'; - } - if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) { - //fprintf(stderr, "could not get idstring for %s, %s\n", deviceName, strerror(errno)); - idstr[0] = '\0'; - } - - if (fcntl(fd, F_SETFL, O_NONBLOCK)) { - LOGE("Error %d making device file descriptor non-blocking.", errno); - close(fd); - return -1; - } - - int devid = 0; - while (devid < mNumDevicesById) { - if (mDevicesById[devid].device == NULL) { - break; - } - devid++; - } - if (devid >= mNumDevicesById) { - device_ent* new_devids = (device_ent*)realloc(mDevicesById, - sizeof(mDevicesById[0]) * (devid + 1)); - if (new_devids == NULL) { - LOGE("out of memory"); - return -1; - } - mDevicesById = new_devids; - mNumDevicesById = devid+1; - mDevicesById[devid].device = NULL; - mDevicesById[devid].seq = 0; - } - - mDevicesById[devid].seq = (mDevicesById[devid].seq+(1<<SEQ_SHIFT))&SEQ_MASK; - if (mDevicesById[devid].seq == 0) { - mDevicesById[devid].seq = 1<<SEQ_SHIFT; - } - - new_mFDs = (pollfd*)realloc(mFDs, sizeof(mFDs[0]) * (mFDCount + 1)); - new_devices = (device_t**)realloc(mDevices, sizeof(mDevices[0]) * (mFDCount + 1)); - if (new_mFDs == NULL || new_devices == NULL) { - LOGE("out of memory"); - return -1; - } - mFDs = new_mFDs; - mDevices = new_devices; - -#if 0 - LOGI("add device %d: %s\n", mFDCount, deviceName); - LOGI(" bus: %04x\n" - " vendor %04x\n" - " product %04x\n" - " version %04x\n", - id.bustype, id.vendor, id.product, id.version); - LOGI(" name: \"%s\"\n", name); - LOGI(" location: \"%s\"\n" - " id: \"%s\"\n", location, idstr); - LOGI(" version: %d.%d.%d\n", - version >> 16, (version >> 8) & 0xff, version & 0xff); -#endif - - device_t* device = new device_t(devid|mDevicesById[devid].seq, deviceName, name); - if (device == NULL) { - LOGE("out of memory"); - return -1; - } - - device->fd = fd; - mFDs[mFDCount].fd = fd; - mFDs[mFDCount].events = POLLIN; - mFDs[mFDCount].revents = 0; - - // Figure out the kinds of events the device reports. - - uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; - memset(key_bitmask, 0, sizeof(key_bitmask)); - - LOGV("Getting keys..."); - if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) { - //LOGI("MAP\n"); - //for (int i = 0; i < sizeof(key_bitmask); i++) { - // LOGI("%d: 0x%02x\n", i, key_bitmask[i]); - //} - - // See if this is a keyboard. Ignore everything in the button range except for - // gamepads which are also considered keyboards. - if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC)) - || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD), - sizeof_bit_array(BTN_DIGI)) - || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK), - sizeof_bit_array(KEY_MAX + 1))) { - device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; - - device->keyBitmask = new uint8_t[sizeof(key_bitmask)]; - if (device->keyBitmask != NULL) { - memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); - } else { - delete device; - LOGE("out of memory allocating key bitmask"); - return -1; - } - } - } - - // See if this is a trackball (or mouse). - if (test_bit(BTN_MOUSE, key_bitmask)) { - uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)]; - memset(rel_bitmask, 0, sizeof(rel_bitmask)); - LOGV("Getting relative controllers..."); - if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask) >= 0) { - if (test_bit(REL_X, rel_bitmask) && test_bit(REL_Y, rel_bitmask)) { - device->classes |= INPUT_DEVICE_CLASS_TRACKBALL; - } - } - } - - // See if this is a touch pad. - uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)]; - memset(abs_bitmask, 0, sizeof(abs_bitmask)); - LOGV("Getting absolute controllers..."); - if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) { - // Is this a new modern multi-touch driver? - if (test_bit(ABS_MT_POSITION_X, abs_bitmask) - && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) { - device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN | INPUT_DEVICE_CLASS_TOUCHSCREEN_MT; - - // Is this an old style single-touch driver? - } else if (test_bit(BTN_TOUCH, key_bitmask) - && test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) { - device->classes |= INPUT_DEVICE_CLASS_TOUCHSCREEN; - } - } - -#ifdef EV_SW - // figure out the switches this device reports - uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)]; - memset(sw_bitmask, 0, sizeof(sw_bitmask)); - bool hasSwitches = false; - if (ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask) >= 0) { - for (int i=0; i<EV_SW; i++) { - //LOGI("Device 0x%x sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask)); - if (test_bit(i, sw_bitmask)) { - hasSwitches = true; - if (mSwitches[i] == 0) { - mSwitches[i] = device->id; - } - } - } - } - if (hasSwitches) { - device->classes |= INPUT_DEVICE_CLASS_SWITCH; - } -#endif - - if ((device->classes & INPUT_DEVICE_CLASS_KEYBOARD) != 0) { - char tmpfn[sizeof(name)]; - char keylayoutFilename[300]; - - // a more descriptive name - device->name = name; - - // replace all the spaces with underscores - strcpy(tmpfn, name); - for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) - *p = '_'; - - // find the .kl file we need for this device - const char* root = getenv("ANDROID_ROOT"); - snprintf(keylayoutFilename, sizeof(keylayoutFilename), - "%s/usr/keylayout/%s.kl", root, tmpfn); - bool defaultKeymap = false; - if (access(keylayoutFilename, R_OK)) { - snprintf(keylayoutFilename, sizeof(keylayoutFilename), - "%s/usr/keylayout/%s", root, "qwerty.kl"); - defaultKeymap = true; - } - status_t status = device->layoutMap->load(keylayoutFilename); - if (status) { - LOGE("Error %d loading key layout.", status); - } - - // tell the world about the devname (the descriptive name) - if (!mHaveFirstKeyboard && !defaultKeymap && strstr(name, "-keypad")) { - // the built-in keyboard has a well-known device ID of 0, - // this device better not go away. - mHaveFirstKeyboard = true; - mFirstKeyboardId = device->id; - property_set("hw.keyboards.0.devname", name); - } else { - // ensure mFirstKeyboardId is set to -something-. - if (mFirstKeyboardId == 0) { - mFirstKeyboardId = device->id; - } - } - char propName[100]; - sprintf(propName, "hw.keyboards.%u.devname", device->id); - property_set(propName, name); - - // 'Q' key support = cheap test of whether this is an alpha-capable kbd - if (hasKeycodeLocked(device, AKEYCODE_Q)) { - device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY; - } - - // See if this device has a DPAD. - if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) && - hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) && - hasKeycodeLocked(device, AKEYCODE_DPAD_LEFT) && - hasKeycodeLocked(device, AKEYCODE_DPAD_RIGHT) && - hasKeycodeLocked(device, AKEYCODE_DPAD_CENTER)) { - device->classes |= INPUT_DEVICE_CLASS_DPAD; - } - - // See if this device has a gamepad. - for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES)/sizeof(GAMEPAD_KEYCODES[0]); i++) { - if (hasKeycodeLocked(device, GAMEPAD_KEYCODES[i])) { - device->classes |= INPUT_DEVICE_CLASS_GAMEPAD; - break; - } - } - - LOGI("New keyboard: device->id=0x%x devname='%s' propName='%s' keylayout='%s'\n", - device->id, name, propName, keylayoutFilename); - } - - // If the device isn't recognized as something we handle, don't monitor it. - if (device->classes == 0) { - LOGV("Dropping device %s %p, id = %d\n", deviceName, device, devid); - close(fd); - delete device; - return -1; - } - - LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", - deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes); - - LOGV("Adding device %s %p at %d, id = %d, classes = 0x%x\n", - deviceName, device, mFDCount, devid, device->classes); - - mDevicesById[devid].device = device; - device->next = mOpeningDevices; - mOpeningDevices = device; - mDevices[mFDCount] = device; - - mFDCount++; - return 0; -} - -bool EventHub::hasKeycodeLocked(device_t* device, int keycode) const -{ - if (device->keyBitmask == NULL || device->layoutMap == NULL) { - return false; - } - - Vector<int32_t> scanCodes; - device->layoutMap->findScancodes(keycode, &scanCodes); - const size_t N = scanCodes.size(); - for (size_t i=0; i<N && i<=KEY_MAX; i++) { - int32_t sc = scanCodes.itemAt(i); - if (sc >= 0 && sc <= KEY_MAX && test_bit(sc, device->keyBitmask)) { - return true; - } - } - - return false; -} - -int EventHub::closeDevice(const char *deviceName) { - AutoMutex _l(mLock); - - int i; - for(i = 1; i < mFDCount; i++) { - if(strcmp(mDevices[i]->path.string(), deviceName) == 0) { - //LOGD("remove device %d: %s\n", i, deviceName); - device_t* device = mDevices[i]; - - LOGI("Removed device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n", - device->path.string(), device->name.string(), device->id, - mNumDevicesById, mFDCount, mFDs[i].fd, device->classes); - - // Clear this device's entry. - int index = (device->id&ID_MASK); - mDevicesById[index].device = NULL; - - // Close the file descriptor and compact the fd array. - close(mFDs[i].fd); - int count = mFDCount - i - 1; - memmove(mDevices + i, mDevices + i + 1, sizeof(mDevices[0]) * count); - memmove(mFDs + i, mFDs + i + 1, sizeof(mFDs[0]) * count); - mFDCount--; - -#ifdef EV_SW - for (int j=0; j<EV_SW; j++) { - if (mSwitches[j] == device->id) { - mSwitches[j] = 0; - } - } -#endif - - device->next = mClosingDevices; - mClosingDevices = device; - - if (device->id == mFirstKeyboardId) { - LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", - device->path.string(), mFirstKeyboardId); - mFirstKeyboardId = 0; - property_set("hw.keyboards.0.devname", NULL); - } - // clear the property - char propName[100]; - sprintf(propName, "hw.keyboards.%u.devname", device->id); - property_set(propName, NULL); - return 0; - } - } - LOGE("remove device: %s not found\n", deviceName); - return -1; -} - -int EventHub::readNotify(int nfd) { -#ifdef HAVE_INOTIFY - int res; - char devname[PATH_MAX]; - char *filename; - char event_buf[512]; - int event_size; - int event_pos = 0; - struct inotify_event *event; - - LOGV("EventHub::readNotify nfd: %d\n", nfd); - res = read(nfd, event_buf, sizeof(event_buf)); - if(res < (int)sizeof(*event)) { - if(errno == EINTR) - return 0; - LOGW("could not get event, %s\n", strerror(errno)); - return 1; - } - //printf("got %d bytes of event information\n", res); - - strcpy(devname, device_path); - filename = devname + strlen(devname); - *filename++ = '/'; - - while(res >= (int)sizeof(*event)) { - event = (struct inotify_event *)(event_buf + event_pos); - //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : ""); - if(event->len) { - strcpy(filename, event->name); - if(event->mask & IN_CREATE) { - openDevice(devname); - } - else { - closeDevice(devname); - } - } - event_size = sizeof(*event) + event->len; - res -= event_size; - event_pos += event_size; - } -#endif - return 0; -} - - -int EventHub::scanDir(const char *dirname) -{ - char devname[PATH_MAX]; - char *filename; - DIR *dir; - struct dirent *de; - dir = opendir(dirname); - if(dir == NULL) - return -1; - strcpy(devname, dirname); - filename = devname + strlen(devname); - *filename++ = '/'; - while((de = readdir(dir))) { - if(de->d_name[0] == '.' && - (de->d_name[1] == '\0' || - (de->d_name[1] == '.' && de->d_name[2] == '\0'))) - continue; - strcpy(filename, de->d_name); - openDevice(devname); - } - closedir(dir); - return 0; -} - -void EventHub::dump(String8& dump) { - dump.append("Event Hub State:\n"); - - { // acquire lock - AutoMutex _l(mLock); - - dump.appendFormat(INDENT "HaveFirstKeyboard: %s\n", toString(mHaveFirstKeyboard)); - dump.appendFormat(INDENT "FirstKeyboardId: 0x%x\n", mFirstKeyboardId); - - dump.append(INDENT "Devices:\n"); - - for (int i = 0; i < mNumDevicesById; i++) { - const device_t* device = mDevicesById[i].device; - if (device) { - if (mFirstKeyboardId == device->id) { - dump.appendFormat(INDENT2 "0x%x: %s (aka device 0 - first keyboard)\n", - device->id, device->name.string()); - } else { - dump.appendFormat(INDENT2 "0x%x: %s\n", device->id, device->name.string()); - } - dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes); - dump.appendFormat(INDENT3 "Path: %s\n", device->path.string()); - dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", device->keylayoutFilename.string()); - } - } - } // release lock -} - -}; // namespace android diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index 04a01955558b..dc223f96453a 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -182,6 +182,16 @@ int FramebufferNativeWindow::setSwapInterval( return fb->setSwapInterval(fb, interval); } +void FramebufferNativeWindow::dump(String8& result) { + if (fbDev->common.version >= 1 && fbDev->dump) { + const size_t SIZE = 4096; + char buffer[SIZE]; + + fbDev->dump(fbDev, buffer, SIZE); + result.append(buffer); + } +} + // only for debugging / logging int FramebufferNativeWindow::getCurrentBufferIndex() const { @@ -276,6 +286,9 @@ int FramebufferNativeWindow::query(ANativeWindow* window, case NATIVE_WINDOW_FORMAT: *value = fb->format; return NO_ERROR; + case NATIVE_WINDOW_CONCRETE_TYPE: + *value = NATIVE_WINDOW_FRAMEBUFFER; + return NO_ERROR; } *value = 0; return BAD_VALUE; diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 367195439763..97312a6d4b29 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -45,7 +45,6 @@ GraphicBuffer::GraphicBuffer() stride = format = usage = 0; - transform = 0; handle = NULL; } @@ -58,8 +57,7 @@ GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, height = stride = format = - usage = - transform = 0; + usage = 0; handle = NULL; mInitCheck = initSize(w, h, reqFormat, reqUsage); } @@ -76,10 +74,22 @@ GraphicBuffer::GraphicBuffer(uint32_t w, uint32_t h, stride = inStride; format = inFormat; usage = inUsage; - transform = 0; handle = inHandle; } +GraphicBuffer::GraphicBuffer(android_native_buffer_t* buffer, bool keepOwnership) + : BASE(), mOwner(keepOwnership ? ownHandle : ownNone), + mBufferMapper(GraphicBufferMapper::get()), + mInitCheck(NO_ERROR), mIndex(-1), mWrappedBuffer(buffer) +{ + width = buffer->width; + height = buffer->height; + stride = buffer->stride; + format = buffer->format; + usage = buffer->usage; + handle = buffer->handle; +} + GraphicBuffer::~GraphicBuffer() { if (handle) { @@ -90,12 +100,14 @@ GraphicBuffer::~GraphicBuffer() void GraphicBuffer::free_handle() { if (mOwner == ownHandle) { + mBufferMapper.unregisterBuffer(handle); native_handle_close(handle); native_handle_delete(const_cast<native_handle*>(handle)); } else if (mOwner == ownData) { GraphicBufferAllocator& allocator(GraphicBufferAllocator::get()); allocator.free(handle); } + mWrappedBuffer = 0; } status_t GraphicBuffer::initCheck() const { @@ -185,10 +197,8 @@ status_t GraphicBuffer::lock(GGLSurface* sur, uint32_t usage) return res; } -const int kFlattenFdsOffset = 9; - size_t GraphicBuffer::getFlattenedSize() const { - return (kFlattenFdsOffset + (handle ? handle->numInts : 0))*sizeof(int); + return (8 + (handle ? handle->numInts : 0))*sizeof(int); } size_t GraphicBuffer::getFdCount() const { @@ -213,14 +223,13 @@ status_t GraphicBuffer::flatten(void* buffer, size_t size, buf[5] = usage; buf[6] = 0; buf[7] = 0; - buf[8] = transform; if (handle) { buf[6] = handle->numFds; buf[7] = handle->numInts; native_handle_t const* const h = handle; memcpy(fds, h->data, h->numFds*sizeof(int)); - memcpy(&buf[kFlattenFdsOffset], h->data + h->numFds, h->numInts*sizeof(int)); + memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int)); } return NO_ERROR; @@ -229,7 +238,7 @@ status_t GraphicBuffer::flatten(void* buffer, size_t size, status_t GraphicBuffer::unflatten(void const* buffer, size_t size, int fds[], size_t count) { - if (size < kFlattenFdsOffset*sizeof(int)) return NO_MEMORY; + if (size < 8*sizeof(int)) return NO_MEMORY; int const* buf = static_cast<int const*>(buffer); if (buf[0] != 'GBFR') return BAD_TYPE; @@ -237,7 +246,7 @@ status_t GraphicBuffer::unflatten(void const* buffer, size_t size, const size_t numFds = buf[6]; const size_t numInts = buf[7]; - const size_t sizeNeeded = (kFlattenFdsOffset + numInts) * sizeof(int); + const size_t sizeNeeded = (8 + numInts) * sizeof(int); if (size < sizeNeeded) return NO_MEMORY; size_t fdCountNeeded = 0; @@ -254,10 +263,9 @@ status_t GraphicBuffer::unflatten(void const* buffer, size_t size, stride = buf[3]; format = buf[4]; usage = buf[5]; - transform = buf[8]; native_handle* h = native_handle_create(numFds, numInts); memcpy(h->data, fds, numFds*sizeof(int)); - memcpy(h->data + numFds, &buf[kFlattenFdsOffset], numInts*sizeof(int)); + memcpy(h->data + numFds, &buf[8], numInts*sizeof(int)); handle = h; } else { width = height = stride = format = usage = 0; @@ -265,6 +273,11 @@ status_t GraphicBuffer::unflatten(void const* buffer, size_t size, } mOwner = ownHandle; + + if (handle != 0) { + mBufferMapper.registerBuffer(handle); + } + return NO_ERROR; } diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index fa46ab7b3379..33ef1fc8945c 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -24,8 +24,6 @@ #include <ui/GraphicBufferAllocator.h> -#include <private/ui/sw_gralloc_handle.h> - namespace android { // --------------------------------------------------------------------------- @@ -56,14 +54,14 @@ void GraphicBufferAllocator::dump(String8& result) const Mutex::Autolock _l(sLock); KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); size_t total = 0; - const size_t SIZE = 512; + const size_t SIZE = 4096; char buffer[SIZE]; snprintf(buffer, SIZE, "Allocated buffers:\n"); result.append(buffer); const size_t c = list.size(); for (size_t i=0 ; i<c ; i++) { const alloc_rec_t& rec(list.valueAt(i)); - snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %2d | 0x%08x\n", + snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u (%4u) x %4u | %8X | 0x%08x\n", list.keyAt(i), rec.size/1024.0f, rec.w, rec.s, rec.h, rec.format, rec.usage); result.append(buffer); @@ -71,6 +69,10 @@ void GraphicBufferAllocator::dump(String8& result) const } snprintf(buffer, SIZE, "Total allocated: %.2f KB\n", total/1024.0f); result.append(buffer); + if (mAllocDev->common.version >= 1 && mAllocDev->dump) { + mAllocDev->dump(mAllocDev, buffer, SIZE); + result.append(buffer); + } } void GraphicBufferAllocator::dumpToSystemLog() @@ -91,11 +93,7 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma // we have a h/w allocator and h/w buffer is requested status_t err; - if (usage & GRALLOC_USAGE_HW_MASK) { - err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); - } else { - err = sw_gralloc_handle_t::alloc(w, h, format, usage, handle, stride); - } + err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); LOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)", w, h, format, usage, err, strerror(-err)); @@ -119,11 +117,8 @@ status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat forma status_t GraphicBufferAllocator::free(buffer_handle_t handle) { status_t err; - if (sw_gralloc_handle_t::validate(handle) < 0) { - err = mAllocDev->free(mAllocDev, handle); - } else { - err = sw_gralloc_handle_t::free((sw_gralloc_handle_t*)handle); - } + + err = mAllocDev->free(mAllocDev, handle); LOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err)); if (err == NO_ERROR) { diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index ce2acd01dfe3..07c067474d54 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -17,15 +17,7 @@ #define LOG_TAG "GraphicBufferMapper" #include <stdint.h> -#ifdef HAVE_ANDROID_OS // just want PAGE_SIZE define -# include <asm/page.h> -#else -# include <sys/user.h> -#endif #include <errno.h> -#include <sys/mman.h> - -#include <cutils/ashmem.h> #include <utils/Errors.h> #include <utils/Log.h> @@ -35,8 +27,6 @@ #include <hardware/gralloc.h> -#include <private/ui/sw_gralloc_handle.h> - namespace android { // --------------------------------------------------------------------------- @@ -57,11 +47,9 @@ GraphicBufferMapper::GraphicBufferMapper() status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle) { status_t err; - if (sw_gralloc_handle_t::validate(handle) < 0) { - err = mAllocMod->registerBuffer(mAllocMod, handle); - } else { - err = sw_gralloc_handle_t::registerBuffer((sw_gralloc_handle_t*)handle); - } + + err = mAllocMod->registerBuffer(mAllocMod, handle); + LOGW_IF(err, "registerBuffer(%p) failed %d (%s)", handle, err, strerror(-err)); return err; @@ -70,11 +58,9 @@ status_t GraphicBufferMapper::registerBuffer(buffer_handle_t handle) status_t GraphicBufferMapper::unregisterBuffer(buffer_handle_t handle) { status_t err; - if (sw_gralloc_handle_t::validate(handle) < 0) { - err = mAllocMod->unregisterBuffer(mAllocMod, handle); - } else { - err = sw_gralloc_handle_t::unregisterBuffer((sw_gralloc_handle_t*)handle); - } + + err = mAllocMod->unregisterBuffer(mAllocMod, handle); + LOGW_IF(err, "unregisterBuffer(%p) failed %d (%s)", handle, err, strerror(-err)); return err; @@ -84,15 +70,11 @@ status_t GraphicBufferMapper::lock(buffer_handle_t handle, int usage, const Rect& bounds, void** vaddr) { status_t err; - if (sw_gralloc_handle_t::validate(handle) < 0) { - err = mAllocMod->lock(mAllocMod, handle, usage, - bounds.left, bounds.top, bounds.width(), bounds.height(), - vaddr); - } else { - err = sw_gralloc_handle_t::lock((sw_gralloc_handle_t*)handle, usage, - bounds.left, bounds.top, bounds.width(), bounds.height(), - vaddr); - } + + err = mAllocMod->lock(mAllocMod, handle, usage, + bounds.left, bounds.top, bounds.width(), bounds.height(), + vaddr); + LOGW_IF(err, "lock(...) failed %d (%s)", err, strerror(-err)); return err; } @@ -100,128 +82,11 @@ status_t GraphicBufferMapper::lock(buffer_handle_t handle, status_t GraphicBufferMapper::unlock(buffer_handle_t handle) { status_t err; - if (sw_gralloc_handle_t::validate(handle) < 0) { - err = mAllocMod->unlock(mAllocMod, handle); - } else { - err = sw_gralloc_handle_t::unlock((sw_gralloc_handle_t*)handle); - } - LOGW_IF(err, "unlock(...) failed %d (%s)", err, strerror(-err)); - return err; -} - -// --------------------------------------------------------------------------- - -status_t sw_gralloc_handle_t::alloc(uint32_t w, uint32_t h, int format, - int usage, buffer_handle_t* pHandle, int32_t* pStride) -{ - int align = 4; - int bpp = 0; - switch (format) { - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - case HAL_PIXEL_FORMAT_BGRA_8888: - bpp = 4; - break; - case HAL_PIXEL_FORMAT_RGB_888: - bpp = 3; - break; - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_RGBA_5551: - case HAL_PIXEL_FORMAT_RGBA_4444: - bpp = 2; - break; - default: - return -EINVAL; - } - size_t bpr = (w*bpp + (align-1)) & ~(align-1); - size_t size = bpr * h; - size_t stride = bpr / bpp; - size = (size + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1); - - int fd = ashmem_create_region("sw-gralloc-buffer", size); - if (fd < 0) { - LOGE("ashmem_create_region(size=%d) failed (%s)", - size, strerror(-errno)); - return -errno; - } - - int prot = PROT_READ; - if (usage & GRALLOC_USAGE_SW_WRITE_MASK) - prot |= PROT_WRITE; - - if (ashmem_set_prot_region(fd, prot) < 0) { - LOGE("ashmem_set_prot_region(fd=%d, prot=%x) failed (%s)", - fd, prot, strerror(-errno)); - close(fd); - return -errno; - } - void* base = mmap(0, size, prot, MAP_SHARED, fd, 0); - if (base == MAP_FAILED) { - LOGE("alloc mmap(fd=%d, size=%d, prot=%x) failed (%s)", - fd, size, prot, strerror(-errno)); - close(fd); - return -errno; - } - - sw_gralloc_handle_t* hnd = new sw_gralloc_handle_t(); - hnd->fd = fd; - hnd->size = size; - hnd->base = intptr_t(base); - hnd->prot = prot; - *pStride = stride; - *pHandle = hnd; - - return NO_ERROR; -} - -status_t sw_gralloc_handle_t::free(sw_gralloc_handle_t* hnd) -{ - if (hnd->base) { - munmap((void*)hnd->base, hnd->size); - } - if (hnd->fd >= 0) { - close(hnd->fd); - } - delete hnd; - return NO_ERROR; -} - -status_t sw_gralloc_handle_t::registerBuffer(sw_gralloc_handle_t* hnd) -{ - if (hnd->pid != getpid()) { - void* base = mmap(0, hnd->size, hnd->prot, MAP_SHARED, hnd->fd, 0); - if (base == MAP_FAILED) { - LOGE("registerBuffer mmap(fd=%d, size=%d, prot=%x) failed (%s)", - hnd->fd, hnd->size, hnd->prot, strerror(-errno)); - return -errno; - } - hnd->base = intptr_t(base); - } - return NO_ERROR; -} - -status_t sw_gralloc_handle_t::unregisterBuffer(sw_gralloc_handle_t* hnd) -{ - if (hnd->pid != getpid()) { - if (hnd->base) { - munmap((void*)hnd->base, hnd->size); - } - hnd->base = 0; - } - return NO_ERROR; -} + err = mAllocMod->unlock(mAllocMod, handle); -status_t sw_gralloc_handle_t::lock(sw_gralloc_handle_t* hnd, int usage, - int l, int t, int w, int h, void** vaddr) -{ - *vaddr = (void*)hnd->base; - return NO_ERROR; -} - -status_t sw_gralloc_handle_t::unlock(sw_gralloc_handle_t* hnd) -{ - return NO_ERROR; + LOGW_IF(err, "unlock(...) failed %d (%s)", err, strerror(-err)); + return err; } // --------------------------------------------------------------------------- diff --git a/libs/ui/IOverlay.cpp b/libs/ui/IOverlay.cpp deleted file mode 100644 index 65e6b4f37612..000000000000 --- a/libs/ui/IOverlay.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 <stdio.h> -#include <stdint.h> -#include <sys/types.h> - -#include <binder/Parcel.h> -#include <binder/IInterface.h> - -#include <ui/IOverlay.h> - -namespace android { - -enum { - DESTROY = IBinder::FIRST_CALL_TRANSACTION, // one-way transaction -}; - -class BpOverlay : public BpInterface<IOverlay> -{ -public: - BpOverlay(const sp<IBinder>& impl) - : BpInterface<IOverlay>(impl) - { - } - - virtual void destroy() - { - Parcel data, reply; - data.writeInterfaceToken(IOverlay::getInterfaceDescriptor()); - remote()->transact(DESTROY, data, &reply, IBinder::FLAG_ONEWAY); - } -}; - -IMPLEMENT_META_INTERFACE(Overlay, "android.ui.IOverlay"); - -// ---------------------------------------------------------------------- - -status_t BnOverlay::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case DESTROY: { - CHECK_INTERFACE(IOverlay, data, reply); - destroy(); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -}; // namespace android diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index 811edaf8a26f..e2e698ef7b3b 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -7,11 +7,127 @@ //#define LOG_NDEBUG 0 +#define DEBUG_PROBE 0 + +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> + #include <ui/Input.h> +#include <math.h> + +#ifdef HAVE_ANDROID_OS +#include <binder/Parcel.h> + +#include "SkPoint.h" +#include "SkMatrix.h" +#include "SkScalar.h" +#endif + namespace android { -// class InputEvent +static const char* CONFIGURATION_FILE_DIR[] = { + "idc/", + "keylayout/", + "keychars/", +}; + +static const char* CONFIGURATION_FILE_EXTENSION[] = { + ".idc", + ".kl", + ".kcm", +}; + +static bool isValidNameChar(char ch) { + return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); +} + +static void appendInputDeviceConfigurationFileRelativePath(String8& path, + const String8& name, InputDeviceConfigurationFileType type) { + path.append(CONFIGURATION_FILE_DIR[type]); + for (size_t i = 0; i < name.length(); i++) { + char ch = name[i]; + if (!isValidNameChar(ch)) { + ch = '_'; + } + path.append(&ch, 1); + } + path.append(CONFIGURATION_FILE_EXTENSION[type]); +} + +String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( + const InputDeviceIdentifier& deviceIdentifier, + InputDeviceConfigurationFileType type) { + if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { + if (deviceIdentifier.version != 0) { + // Try vendor product version. + String8 versionPath(getInputDeviceConfigurationFilePathByName( + String8::format("Vendor_%04x_Product_%04x_Version_%04x", + deviceIdentifier.vendor, deviceIdentifier.product, + deviceIdentifier.version), + type)); + if (!versionPath.isEmpty()) { + return versionPath; + } + } + + // Try vendor product. + String8 productPath(getInputDeviceConfigurationFilePathByName( + String8::format("Vendor_%04x_Product_%04x", + deviceIdentifier.vendor, deviceIdentifier.product), + type)); + if (!productPath.isEmpty()) { + return productPath; + } + } + + // Try device name. + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); +} + +String8 getInputDeviceConfigurationFilePathByName( + const String8& name, InputDeviceConfigurationFileType type) { + // Search system repository. + String8 path; + path.setTo(getenv("ANDROID_ROOT")); + path.append("/usr/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); +#if DEBUG_PROBE + LOGD("Probing for system provided input device configuration file: path='%s'", path.string()); +#endif + if (!access(path.string(), R_OK)) { +#if DEBUG_PROBE + LOGD("Found"); +#endif + return path; + } + + // Search user repository. + // TODO Should only look here if not in safe mode. + path.setTo(getenv("ANDROID_DATA")); + path.append("/system/devices/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); +#if DEBUG_PROBE + LOGD("Probing for system user input device configuration file: path='%s'", path.string()); +#endif + if (!access(path.string(), R_OK)) { +#if DEBUG_PROBE + LOGD("Found"); +#endif + return path; + } + + // Not found. +#if DEBUG_PROBE + LOGD("Probe failed to find input device configuration file: name='%s', type=%d", + name.string(), type); +#endif + return String8(); +} + + +// --- InputEvent --- void InputEvent::initialize(int32_t deviceId, int32_t source) { mDeviceId = deviceId; @@ -23,7 +139,7 @@ void InputEvent::initialize(const InputEvent& from) { mSource = from.mSource; } -// class KeyEvent +// --- KeyEvent --- bool KeyEvent::hasDefaultAction(int32_t keyCode) { switch (keyCode) { @@ -33,6 +149,7 @@ bool KeyEvent::hasDefaultAction(int32_t keyCode) { case AKEYCODE_ENDCALL: case AKEYCODE_VOLUME_UP: case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: case AKEYCODE_POWER: case AKEYCODE_CAMERA: case AKEYCODE_HEADSETHOOK: @@ -40,11 +157,14 @@ bool KeyEvent::hasDefaultAction(int32_t keyCode) { case AKEYCODE_NOTIFICATION: case AKEYCODE_FOCUS: case AKEYCODE_SEARCH: + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: case AKEYCODE_MEDIA_PLAY_PAUSE: case AKEYCODE_MEDIA_STOP: case AKEYCODE_MEDIA_NEXT: case AKEYCODE_MEDIA_PREVIOUS: case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: case AKEYCODE_MEDIA_FAST_FORWARD: case AKEYCODE_MUTE: return true; @@ -67,14 +187,18 @@ bool KeyEvent::isSystemKey(int32_t keyCode) { case AKEYCODE_ENDCALL: case AKEYCODE_VOLUME_UP: case AKEYCODE_VOLUME_DOWN: + case AKEYCODE_VOLUME_MUTE: case AKEYCODE_MUTE: case AKEYCODE_POWER: case AKEYCODE_HEADSETHOOK: + case AKEYCODE_MEDIA_PLAY: + case AKEYCODE_MEDIA_PAUSE: case AKEYCODE_MEDIA_PLAY_PAUSE: case AKEYCODE_MEDIA_STOP: case AKEYCODE_MEDIA_NEXT: case AKEYCODE_MEDIA_PREVIOUS: case AKEYCODE_MEDIA_REWIND: + case AKEYCODE_MEDIA_RECORD: case AKEYCODE_MEDIA_FAST_FORWARD: case AKEYCODE_CAMERA: case AKEYCODE_FOCUS: @@ -123,7 +247,90 @@ void KeyEvent::initialize(const KeyEvent& from) { mEventTime = from.mEventTime; } -// class MotionEvent + +// --- PointerCoords --- + +float PointerCoords::getAxisValue(int32_t axis) const { + if (axis < 0 || axis > 63) { + return 0; + } + + uint64_t axisBit = 1LL << axis; + if (!(bits & axisBit)) { + return 0; + } + uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL)); + return values[index]; +} + +status_t PointerCoords::setAxisValue(int32_t axis, float value) { + if (axis < 0 || axis > 63) { + return NAME_NOT_FOUND; + } + + uint64_t axisBit = 1LL << axis; + uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL)); + if (!(bits & axisBit)) { + uint32_t count = __builtin_popcountll(bits); + if (count >= MAX_AXES) { + tooManyAxes(axis); + return NO_MEMORY; + } + bits |= axisBit; + for (uint32_t i = count; i > index; i--) { + values[i] = values[i - 1]; + } + } + values[index] = value; + return OK; +} + +float* PointerCoords::editAxisValue(int32_t axis) { + if (axis < 0 || axis > 63) { + return NULL; + } + + uint64_t axisBit = 1LL << axis; + if (!(bits & axisBit)) { + return NULL; + } + uint32_t index = __builtin_popcountll(bits & (axisBit - 1LL)); + return &values[index]; +} + +#ifdef HAVE_ANDROID_OS +status_t PointerCoords::readFromParcel(Parcel* parcel) { + bits = parcel->readInt64(); + + uint32_t count = __builtin_popcountll(bits); + if (count > MAX_AXES) { + return BAD_VALUE; + } + + for (uint32_t i = 0; i < count; i++) { + values[i] = parcel->readInt32(); + } + return OK; +} + +status_t PointerCoords::writeToParcel(Parcel* parcel) const { + parcel->writeInt64(bits); + + uint32_t count = __builtin_popcountll(bits); + for (uint32_t i = 0; i < count; i++) { + parcel->writeInt32(values[i]); + } + return OK; +} +#endif + +void PointerCoords::tooManyAxes(int axis) { + LOGW("Could not set value for axis %d because the PointerCoords structure is full and " + "cannot contain more than %d axis values.", axis, int(MAX_AXES)); +} + + +// --- MotionEvent --- void MotionEvent::initialize( int32_t deviceId, @@ -158,6 +365,33 @@ void MotionEvent::initialize( addSample(eventTime, pointerCoords); } +void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { + InputEvent::initialize(other->mDeviceId, other->mSource); + mAction = other->mAction; + mFlags = other->mFlags; + mEdgeFlags = other->mEdgeFlags; + mMetaState = other->mMetaState; + mXOffset = other->mXOffset; + mYOffset = other->mYOffset; + mXPrecision = other->mXPrecision; + mYPrecision = other->mYPrecision; + mDownTime = other->mDownTime; + mPointerIds = other->mPointerIds; + + if (keepHistory) { + mSampleEventTimes = other->mSampleEventTimes; + mSamplePointerCoords = other->mSamplePointerCoords; + } else { + mSampleEventTimes.clear(); + mSampleEventTimes.push(other->getEventTime()); + mSamplePointerCoords.clear(); + size_t pointerCount = other->getPointerCount(); + size_t historySize = other->getHistorySize(); + mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array() + + (historySize * pointerCount), pointerCount); + } +} + void MotionEvent::addSample( int64_t eventTime, const PointerCoords* pointerCoords) { @@ -165,12 +399,242 @@ void MotionEvent::addSample( mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } +const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { + return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; +} + +float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { + return getRawPointerCoords(pointerIndex)->getAxisValue(axis); +} + +float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { + float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); + switch (axis) { + case AMOTION_EVENT_AXIS_X: + value += mXOffset; + break; + case AMOTION_EVENT_AXIS_Y: + value += mYOffset; + break; + } + return value; +} + +const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( + size_t pointerIndex, size_t historicalIndex) const { + return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex]; +} + +float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, + size_t historicalIndex) const { + return getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); +} + +float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, + size_t historicalIndex) const { + float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); + switch (axis) { + case AMOTION_EVENT_AXIS_X: + value += mXOffset; + break; + case AMOTION_EVENT_AXIS_Y: + value += mYOffset; + break; + } + return value; +} + void MotionEvent::offsetLocation(float xOffset, float yOffset) { mXOffset += xOffset; mYOffset += yOffset; } -// class InputDeviceInfo +static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) { + float* value = c.editAxisValue(axis); + if (value) { + *value *= scaleFactor; + } +} + +void MotionEvent::scale(float scaleFactor) { + mXOffset *= scaleFactor; + mYOffset *= scaleFactor; + mXPrecision *= scaleFactor; + mYPrecision *= scaleFactor; + + size_t numSamples = mSamplePointerCoords.size(); + for (size_t i = 0; i < numSamples; i++) { + PointerCoords& c = mSamplePointerCoords.editItemAt(i); + // No need to scale pressure or size since they are normalized. + // No need to scale orientation since it is meaningless to do so. + scaleAxisValue(c, AMOTION_EVENT_AXIS_X, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_Y, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); + scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); + } +} + +#ifdef HAVE_ANDROID_OS +static inline float transformAngle(const SkMatrix* matrix, float angleRadians) { + // Construct and transform a vector oriented at the specified clockwise angle from vertical. + // Coordinate system: down is increasing Y, right is increasing X. + SkPoint vector; + vector.fX = SkFloatToScalar(sinf(angleRadians)); + vector.fY = SkFloatToScalar(-cosf(angleRadians)); + matrix->mapVectors(& vector, 1); + + // Derive the transformed vector's clockwise angle from vertical. + float result = atan2f(SkScalarToFloat(vector.fX), SkScalarToFloat(-vector.fY)); + if (result < - M_PI_2) { + result += M_PI; + } else if (result > M_PI_2) { + result -= M_PI; + } + return result; +} + +void MotionEvent::transform(const SkMatrix* matrix) { + float oldXOffset = mXOffset; + float oldYOffset = mYOffset; + + // The tricky part of this implementation is to preserve the value of + // rawX and rawY. So we apply the transformation to the first point + // then derive an appropriate new X/Y offset that will preserve rawX and rawY. + SkPoint point; + float rawX = getRawX(0); + float rawY = getRawY(0); + matrix->mapXY(SkFloatToScalar(rawX + oldXOffset), SkFloatToScalar(rawY + oldYOffset), + & point); + float newX = SkScalarToFloat(point.fX); + float newY = SkScalarToFloat(point.fY); + float newXOffset = newX - rawX; + float newYOffset = newY - rawY; + + mXOffset = newXOffset; + mYOffset = newYOffset; + + // Apply the transformation to all samples. + size_t numSamples = mSamplePointerCoords.size(); + for (size_t i = 0; i < numSamples; i++) { + PointerCoords& c = mSamplePointerCoords.editItemAt(i); + float* xPtr = c.editAxisValue(AMOTION_EVENT_AXIS_X); + float* yPtr = c.editAxisValue(AMOTION_EVENT_AXIS_Y); + if (xPtr && yPtr) { + float x = *xPtr + oldXOffset; + float y = *yPtr + oldYOffset; + matrix->mapXY(SkFloatToScalar(x), SkFloatToScalar(y), & point); + *xPtr = SkScalarToFloat(point.fX) - newXOffset; + *yPtr = SkScalarToFloat(point.fY) - newYOffset; + } + + float* orientationPtr = c.editAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + if (orientationPtr) { + *orientationPtr = transformAngle(matrix, *orientationPtr); + } + } +} + +status_t MotionEvent::readFromParcel(Parcel* parcel) { + size_t pointerCount = parcel->readInt32(); + size_t sampleCount = parcel->readInt32(); + if (pointerCount == 0 || pointerCount > MAX_POINTERS || sampleCount == 0) { + return BAD_VALUE; + } + + mDeviceId = parcel->readInt32(); + mSource = parcel->readInt32(); + mAction = parcel->readInt32(); + mFlags = parcel->readInt32(); + mEdgeFlags = parcel->readInt32(); + mMetaState = parcel->readInt32(); + mXOffset = parcel->readFloat(); + mYOffset = parcel->readFloat(); + mXPrecision = parcel->readFloat(); + mYPrecision = parcel->readFloat(); + mDownTime = parcel->readInt64(); + + mPointerIds.clear(); + mPointerIds.setCapacity(pointerCount); + mSampleEventTimes.clear(); + mSampleEventTimes.setCapacity(sampleCount); + mSamplePointerCoords.clear(); + mSamplePointerCoords.setCapacity(sampleCount * pointerCount); + + for (size_t i = 0; i < pointerCount; i++) { + mPointerIds.push(parcel->readInt32()); + } + + while (sampleCount-- > 0) { + mSampleEventTimes.push(parcel->readInt64()); + for (size_t i = 0; i < pointerCount; i++) { + mSamplePointerCoords.push(); + status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel); + if (status) { + return status; + } + } + } + return OK; +} + +status_t MotionEvent::writeToParcel(Parcel* parcel) const { + size_t pointerCount = mPointerIds.size(); + size_t sampleCount = mSampleEventTimes.size(); + + parcel->writeInt32(pointerCount); + parcel->writeInt32(sampleCount); + + parcel->writeInt32(mDeviceId); + parcel->writeInt32(mSource); + parcel->writeInt32(mAction); + parcel->writeInt32(mFlags); + parcel->writeInt32(mEdgeFlags); + parcel->writeInt32(mMetaState); + parcel->writeFloat(mXOffset); + parcel->writeFloat(mYOffset); + parcel->writeFloat(mXPrecision); + parcel->writeFloat(mYPrecision); + parcel->writeInt64(mDownTime); + + for (size_t i = 0; i < pointerCount; i++) { + parcel->writeInt32(mPointerIds.itemAt(i)); + } + + const PointerCoords* pc = mSamplePointerCoords.array(); + for (size_t h = 0; h < sampleCount; h++) { + parcel->writeInt64(mSampleEventTimes.itemAt(h)); + for (size_t i = 0; i < pointerCount; i++) { + status_t status = (pc++)->writeToParcel(parcel); + if (status) { + return status; + } + } + } + return OK; +} +#endif + +bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { + if (source & AINPUT_SOURCE_CLASS_POINTER) { + // Specifically excludes HOVER_MOVE and SCROLL. + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_OUTSIDE: + return true; + } + } + return false; +} + + +// --- InputDeviceInfo --- InputDeviceInfo::InputDeviceInfo() { initialize(-1, String8("uninitialized device info")); @@ -193,23 +657,30 @@ void InputDeviceInfo::initialize(int32_t id, const String8& name) { mMotionRanges.clear(); } -const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(int32_t rangeType) const { - ssize_t index = mMotionRanges.indexOfKey(rangeType); - return index >= 0 ? & mMotionRanges.valueAt(index) : NULL; +const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( + int32_t axis, uint32_t source) const { + size_t numRanges = mMotionRanges.size(); + for (size_t i = 0; i < numRanges; i++) { + const MotionRange& range = mMotionRanges.itemAt(i); + if (range.axis == axis && range.source == source) { + return ⦥ + } + } + return NULL; } void InputDeviceInfo::addSource(uint32_t source) { mSources |= source; } -void InputDeviceInfo::addMotionRange(int32_t rangeType, float min, float max, +void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max, float flat, float fuzz) { - MotionRange range = { min, max, flat, fuzz }; - addMotionRange(rangeType, range); + MotionRange range = { axis, source, min, max, flat, fuzz }; + mMotionRanges.add(range); } -void InputDeviceInfo::addMotionRange(int32_t rangeType, const MotionRange& range) { - mMotionRanges.add(rangeType, range); +void InputDeviceInfo::addMotionRange(const MotionRange& range) { + mMotionRanges.add(range); } } // namespace android diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp deleted file mode 100644 index 46baf9d14fe2..000000000000 --- a/libs/ui/InputDispatcher.cpp +++ /dev/null @@ -1,3520 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// -// The input dispatcher. -// -#define LOG_TAG "InputDispatcher" - -//#define LOG_NDEBUG 0 - -// Log detailed debug messages about each inbound event notification to the dispatcher. -#define DEBUG_INBOUND_EVENT_DETAILS 0 - -// Log detailed debug messages about each outbound event processed by the dispatcher. -#define DEBUG_OUTBOUND_EVENT_DETAILS 0 - -// Log debug messages about batching. -#define DEBUG_BATCHING 0 - -// Log debug messages about the dispatch cycle. -#define DEBUG_DISPATCH_CYCLE 0 - -// Log debug messages about registrations. -#define DEBUG_REGISTRATION 0 - -// Log debug messages about performance statistics. -#define DEBUG_PERFORMANCE_STATISTICS 0 - -// Log debug messages about input event injection. -#define DEBUG_INJECTION 0 - -// Log debug messages about input event throttling. -#define DEBUG_THROTTLING 0 - -// Log debug messages about input focus tracking. -#define DEBUG_FOCUS 0 - -// Log debug messages about the app switch latency optimization. -#define DEBUG_APP_SWITCH 0 - -#include <cutils/log.h> -#include <ui/InputDispatcher.h> -#include <ui/PowerManager.h> - -#include <stddef.h> -#include <unistd.h> -#include <errno.h> -#include <limits.h> - -#define INDENT " " -#define INDENT2 " " - -namespace android { - -// Delay before reporting long touch events to the power manager. -const nsecs_t LONG_TOUCH_DELAY = 300 * 1000000LL; // 300 ms - -// Default input dispatching timeout if there is no focused application or paused window -// from which to determine an appropriate dispatching timeout. -const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec - -// Amount of time to allow for all pending events to be processed when an app switch -// key is on the way. This is used to preempt input dispatch and drop input events -// when an application takes too long to respond and the user has pressed an app switch key. -const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec - - -static inline nsecs_t now() { - return systemTime(SYSTEM_TIME_MONOTONIC); -} - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - -static inline int32_t getMotionEventActionPointerIndex(int32_t action) { - return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) - >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; -} - -static bool isValidKeyAction(int32_t action) { - switch (action) { - case AKEY_EVENT_ACTION_DOWN: - case AKEY_EVENT_ACTION_UP: - return true; - default: - return false; - } -} - -static bool validateKeyEvent(int32_t action) { - if (! isValidKeyAction(action)) { - LOGE("Key event has invalid action code 0x%x", action); - return false; - } - return true; -} - -static bool isValidMotionAction(int32_t action, size_t pointerCount) { - switch (action & AMOTION_EVENT_ACTION_MASK) { - case AMOTION_EVENT_ACTION_DOWN: - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: - case AMOTION_EVENT_ACTION_MOVE: - case AMOTION_EVENT_ACTION_OUTSIDE: - return true; - case AMOTION_EVENT_ACTION_POINTER_DOWN: - case AMOTION_EVENT_ACTION_POINTER_UP: { - int32_t index = getMotionEventActionPointerIndex(action); - return index >= 0 && size_t(index) < pointerCount; - } - default: - return false; - } -} - -static bool validateMotionEvent(int32_t action, size_t pointerCount, - const int32_t* pointerIds) { - if (! isValidMotionAction(action, pointerCount)) { - LOGE("Motion event has invalid action code 0x%x", action); - return false; - } - if (pointerCount < 1 || pointerCount > MAX_POINTERS) { - LOGE("Motion event has invalid pointer count %d; value must be between 1 and %d.", - pointerCount, MAX_POINTERS); - return false; - } - BitSet32 pointerIdBits; - for (size_t i = 0; i < pointerCount; i++) { - int32_t id = pointerIds[i]; - if (id < 0 || id > MAX_POINTER_ID) { - LOGE("Motion event has invalid pointer id %d; value must be between 0 and %d", - id, MAX_POINTER_ID); - return false; - } - if (pointerIdBits.hasBit(id)) { - LOGE("Motion event has duplicate pointer id %d", id); - return false; - } - pointerIdBits.markBit(id); - } - return true; -} - - -// --- InputWindow --- - -bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const { - return x >= touchableAreaLeft && x <= touchableAreaRight - && y >= touchableAreaTop && y <= touchableAreaBottom; -} - -bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const { - return x >= frameLeft && x <= frameRight - && y >= frameTop && y <= frameBottom; -} - -bool InputWindow::isTrustedOverlay() const { - return layoutParamsType == TYPE_INPUT_METHOD - || layoutParamsType == TYPE_INPUT_METHOD_DIALOG - || layoutParamsType == TYPE_SECURE_SYSTEM_OVERLAY; -} - - -// --- InputDispatcher --- - -InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : - mPolicy(policy), - mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX), - mDispatchEnabled(true), mDispatchFrozen(false), - mFocusedWindow(NULL), - mFocusedApplication(NULL), - mCurrentInputTargetsValid(false), - mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { - mLooper = new Looper(false); - - mInboundQueue.headSentinel.refCount = -1; - mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL; - mInboundQueue.headSentinel.eventTime = LONG_LONG_MIN; - - mInboundQueue.tailSentinel.refCount = -1; - mInboundQueue.tailSentinel.type = EventEntry::TYPE_SENTINEL; - mInboundQueue.tailSentinel.eventTime = LONG_LONG_MAX; - - mKeyRepeatState.lastKeyEntry = NULL; - - int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond(); - mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond; - mThrottleState.lastDeviceId = -1; - -#if DEBUG_THROTTLING - mThrottleState.originalSampleCount = 0; - LOGD("Throttling - Max events per second = %d", maxEventsPerSecond); -#endif -} - -InputDispatcher::~InputDispatcher() { - { // acquire lock - AutoMutex _l(mLock); - - resetKeyRepeatLocked(); - releasePendingEventLocked(); - drainInboundQueueLocked(); - } - - while (mConnectionsByReceiveFd.size() != 0) { - unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel); - } -} - -void InputDispatcher::dispatchOnce() { - nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout(); - nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay(); - - nsecs_t nextWakeupTime = LONG_LONG_MAX; - { // acquire lock - AutoMutex _l(mLock); - dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime); - - if (runCommandsLockedInterruptible()) { - nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately - } - } // release lock - - // Wait for callback or timeout or wake. (make sure we round up, not down) - nsecs_t currentTime = now(); - int32_t timeoutMillis; - if (nextWakeupTime > currentTime) { - uint64_t timeout = uint64_t(nextWakeupTime - currentTime); - timeout = (timeout + 999999LL) / 1000000LL; - timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout); - } else { - timeoutMillis = 0; - } - - mLooper->pollOnce(timeoutMillis); -} - -void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, - nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) { - nsecs_t currentTime = now(); - - // Reset the key repeat timer whenever we disallow key events, even if the next event - // is not a key. This is to ensure that we abort a key repeat if the device is just coming - // out of sleep. - if (keyRepeatTimeout < 0) { - resetKeyRepeatLocked(); - } - - // If dispatching is frozen, do not process timeouts or try to deliver any new events. - if (mDispatchFrozen) { -#if DEBUG_FOCUS - LOGD("Dispatch frozen. Waiting some more."); -#endif - return; - } - - // Optimize latency of app switches. - // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has - // been pressed. When it expires, we preempt dispatch and drop all other pending events. - bool isAppSwitchDue = mAppSwitchDueTime <= currentTime; - if (mAppSwitchDueTime < *nextWakeupTime) { - *nextWakeupTime = mAppSwitchDueTime; - } - - // Ready to start a new event. - // If we don't already have a pending event, go grab one. - if (! mPendingEvent) { - if (mInboundQueue.isEmpty()) { - if (isAppSwitchDue) { - // The inbound queue is empty so the app switch key we were waiting - // for will never arrive. Stop waiting for it. - resetPendingAppSwitchLocked(false); - isAppSwitchDue = false; - } - - // Synthesize a key repeat if appropriate. - if (mKeyRepeatState.lastKeyEntry) { - if (currentTime >= mKeyRepeatState.nextRepeatTime) { - mPendingEvent = synthesizeKeyRepeatLocked(currentTime, keyRepeatDelay); - } else { - if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) { - *nextWakeupTime = mKeyRepeatState.nextRepeatTime; - } - } - } - if (! mPendingEvent) { - return; - } - } else { - // Inbound queue has at least one entry. - EventEntry* entry = mInboundQueue.headSentinel.next; - - // Throttle the entry if it is a move event and there are no - // other events behind it in the queue. Due to movement batching, additional - // samples may be appended to this event by the time the throttling timeout - // expires. - // TODO Make this smarter and consider throttling per device independently. - if (entry->type == EventEntry::TYPE_MOTION - && !isAppSwitchDue - && mDispatchEnabled - && (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) - && !entry->isInjected()) { - MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); - int32_t deviceId = motionEntry->deviceId; - uint32_t source = motionEntry->source; - if (! isAppSwitchDue - && motionEntry->next == & mInboundQueue.tailSentinel // exactly one event - && motionEntry->action == AMOTION_EVENT_ACTION_MOVE - && deviceId == mThrottleState.lastDeviceId - && source == mThrottleState.lastSource) { - nsecs_t nextTime = mThrottleState.lastEventTime - + mThrottleState.minTimeBetweenEvents; - if (currentTime < nextTime) { - // Throttle it! -#if DEBUG_THROTTLING - LOGD("Throttling - Delaying motion event for " - "device 0x%x, source 0x%08x by up to %0.3fms.", - deviceId, source, (nextTime - currentTime) * 0.000001); -#endif - if (nextTime < *nextWakeupTime) { - *nextWakeupTime = nextTime; - } - if (mThrottleState.originalSampleCount == 0) { - mThrottleState.originalSampleCount = - motionEntry->countSamples(); - } - return; - } - } - -#if DEBUG_THROTTLING - if (mThrottleState.originalSampleCount != 0) { - uint32_t count = motionEntry->countSamples(); - LOGD("Throttling - Motion event sample count grew by %d from %d to %d.", - count - mThrottleState.originalSampleCount, - mThrottleState.originalSampleCount, count); - mThrottleState.originalSampleCount = 0; - } -#endif - - mThrottleState.lastEventTime = entry->eventTime < currentTime - ? entry->eventTime : currentTime; - mThrottleState.lastDeviceId = deviceId; - mThrottleState.lastSource = source; - } - - mInboundQueue.dequeue(entry); - mPendingEvent = entry; - } - - // Poke user activity for this event. - if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { - pokeUserActivityLocked(mPendingEvent); - } - } - - // Now we have an event to dispatch. - assert(mPendingEvent != NULL); - bool done = false; - DropReason dropReason = DROP_REASON_NOT_DROPPED; - if (!(mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER)) { - dropReason = DROP_REASON_POLICY; - } else if (!mDispatchEnabled) { - dropReason = DROP_REASON_DISABLED; - } - switch (mPendingEvent->type) { - case EventEntry::TYPE_CONFIGURATION_CHANGED: { - ConfigurationChangedEntry* typedEntry = - static_cast<ConfigurationChangedEntry*>(mPendingEvent); - done = dispatchConfigurationChangedLocked(currentTime, typedEntry); - dropReason = DROP_REASON_NOT_DROPPED; // configuration changes are never dropped - break; - } - - case EventEntry::TYPE_KEY: { - KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); - if (isAppSwitchDue) { - if (isAppSwitchKeyEventLocked(typedEntry)) { - resetPendingAppSwitchLocked(true); - isAppSwitchDue = false; - } else if (dropReason == DROP_REASON_NOT_DROPPED) { - dropReason = DROP_REASON_APP_SWITCH; - } - } - done = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout, - &dropReason, nextWakeupTime); - break; - } - - case EventEntry::TYPE_MOTION: { - MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); - if (dropReason == DROP_REASON_NOT_DROPPED && isAppSwitchDue) { - dropReason = DROP_REASON_APP_SWITCH; - } - done = dispatchMotionLocked(currentTime, typedEntry, - &dropReason, nextWakeupTime); - break; - } - - default: - assert(false); - break; - } - - if (done) { - if (dropReason != DROP_REASON_NOT_DROPPED) { - dropInboundEventLocked(mPendingEvent, dropReason); - } - - releasePendingEventLocked(); - *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately - } -} - -bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { - bool needWake = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(entry); - - switch (entry->type) { - case EventEntry::TYPE_KEY: { - KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); - if (isAppSwitchKeyEventLocked(keyEntry)) { - if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { - mAppSwitchSawKeyDown = true; - } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { - if (mAppSwitchSawKeyDown) { -#if DEBUG_APP_SWITCH - LOGD("App switch is pending!"); -#endif - mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT; - mAppSwitchSawKeyDown = false; - needWake = true; - } - } - } - break; - } - } - - return needWake; -} - -void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropReason) { - const char* reason; - switch (dropReason) { - case DROP_REASON_POLICY: -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("Dropped event because policy consumed it."); -#endif - reason = "inbound event was dropped because the policy consumed it"; - break; - case DROP_REASON_DISABLED: - LOGI("Dropped event because input dispatch is disabled."); - reason = "inbound event was dropped because input dispatch is disabled"; - break; - case DROP_REASON_APP_SWITCH: - LOGI("Dropped event because of pending overdue app switch."); - reason = "inbound event was dropped because of pending overdue app switch"; - break; - default: - assert(false); - return; - } - - switch (entry->type) { - case EventEntry::TYPE_KEY: - synthesizeCancelationEventsForAllConnectionsLocked( - InputState::CANCEL_NON_POINTER_EVENTS, reason); - break; - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); - if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { - synthesizeCancelationEventsForAllConnectionsLocked( - InputState::CANCEL_POINTER_EVENTS, reason); - } else { - synthesizeCancelationEventsForAllConnectionsLocked( - InputState::CANCEL_NON_POINTER_EVENTS, reason); - } - break; - } - } -} - -bool InputDispatcher::isAppSwitchKeyCode(int32_t keyCode) { - return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL; -} - -bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) { - return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) - && isAppSwitchKeyCode(keyEntry->keyCode) - && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) - && (keyEntry->policyFlags & POLICY_FLAG_PASS_TO_USER); -} - -bool InputDispatcher::isAppSwitchPendingLocked() { - return mAppSwitchDueTime != LONG_LONG_MAX; -} - -void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { - mAppSwitchDueTime = LONG_LONG_MAX; - -#if DEBUG_APP_SWITCH - if (handled) { - LOGD("App switch has arrived."); - } else { - LOGD("App switch was abandoned."); - } -#endif -} - -bool InputDispatcher::runCommandsLockedInterruptible() { - if (mCommandQueue.isEmpty()) { - return false; - } - - do { - CommandEntry* commandEntry = mCommandQueue.dequeueAtHead(); - - Command command = commandEntry->command; - (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible' - - commandEntry->connection.clear(); - mAllocator.releaseCommandEntry(commandEntry); - } while (! mCommandQueue.isEmpty()); - return true; -} - -InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command command) { - CommandEntry* commandEntry = mAllocator.obtainCommandEntry(command); - mCommandQueue.enqueueAtTail(commandEntry); - return commandEntry; -} - -void InputDispatcher::drainInboundQueueLocked() { - while (! mInboundQueue.isEmpty()) { - EventEntry* entry = mInboundQueue.dequeueAtHead(); - releaseInboundEventLocked(entry); - } -} - -void InputDispatcher::releasePendingEventLocked() { - if (mPendingEvent) { - releaseInboundEventLocked(mPendingEvent); - mPendingEvent = NULL; - } -} - -void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; - if (injectionState && injectionState->injectionResult == INPUT_EVENT_INJECTION_PENDING) { -#if DEBUG_DISPATCH_CYCLE - LOGD("Injected inbound event was dropped."); -#endif - setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); - } - mAllocator.releaseEventEntry(entry); -} - -void InputDispatcher::resetKeyRepeatLocked() { - if (mKeyRepeatState.lastKeyEntry) { - mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry); - mKeyRepeatState.lastKeyEntry = NULL; - } -} - -InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked( - nsecs_t currentTime, nsecs_t keyRepeatDelay) { - KeyEntry* entry = mKeyRepeatState.lastKeyEntry; - - // Reuse the repeated key entry if it is otherwise unreferenced. - uint32_t policyFlags = (entry->policyFlags & POLICY_FLAG_RAW_MASK) - | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED; - if (entry->refCount == 1) { - mAllocator.recycleKeyEntry(entry); - entry->eventTime = currentTime; - entry->policyFlags = policyFlags; - entry->repeatCount += 1; - } else { - KeyEntry* newEntry = mAllocator.obtainKeyEntry(currentTime, - entry->deviceId, entry->source, policyFlags, - entry->action, entry->flags, entry->keyCode, entry->scanCode, - entry->metaState, entry->repeatCount + 1, entry->downTime); - - mKeyRepeatState.lastKeyEntry = newEntry; - mAllocator.releaseKeyEntry(entry); - - entry = newEntry; - } - entry->syntheticRepeat = true; - - // Increment reference count since we keep a reference to the event in - // mKeyRepeatState.lastKeyEntry in addition to the one we return. - entry->refCount += 1; - - mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay; - return entry; -} - -bool InputDispatcher::dispatchConfigurationChangedLocked( - nsecs_t currentTime, ConfigurationChangedEntry* entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime); -#endif - - // Reset key repeating in case a keyboard device was added or removed or something. - resetKeyRepeatLocked(); - - // Enqueue a command to run outside the lock to tell the policy that the configuration changed. - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyConfigurationChangedInterruptible); - commandEntry->eventTime = entry->eventTime; - return true; -} - -bool InputDispatcher::dispatchKeyLocked( - nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout, - DropReason* dropReason, nsecs_t* nextWakeupTime) { - // Preprocessing. - if (! entry->dispatchInProgress) { - if (entry->repeatCount == 0 - && entry->action == AKEY_EVENT_ACTION_DOWN - && (entry->policyFlags & POLICY_FLAG_TRUSTED) - && !entry->isInjected()) { - if (mKeyRepeatState.lastKeyEntry - && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { - // We have seen two identical key downs in a row which indicates that the device - // driver is automatically generating key repeats itself. We take note of the - // repeat here, but we disable our own next key repeat timer since it is clear that - // we will not need to synthesize key repeats ourselves. - entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; - resetKeyRepeatLocked(); - mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves - } else { - // Not a repeat. Save key down state in case we do see a repeat later. - resetKeyRepeatLocked(); - mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout; - } - mKeyRepeatState.lastKeyEntry = entry; - entry->refCount += 1; - } else if (! entry->syntheticRepeat) { - resetKeyRepeatLocked(); - } - - if (entry->repeatCount == 1) { - entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS; - } else { - entry->flags &= ~AKEY_EVENT_FLAG_LONG_PRESS; - } - - entry->dispatchInProgress = true; - resetTargetsLocked(); - - logOutboundKeyDetailsLocked("dispatchKey - ", entry); - } - - // Give the policy a chance to intercept the key. - if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { - if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); - if (mFocusedWindow) { - commandEntry->inputChannel = mFocusedWindow->inputChannel; - } - commandEntry->keyEntry = entry; - entry->refCount += 1; - return false; // wait for the command to run - } else { - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; - } - } else if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { - if (*dropReason == DROP_REASON_NOT_DROPPED) { - *dropReason = DROP_REASON_POLICY; - } - } - - // Clean up if dropping the event. - if (*dropReason != DROP_REASON_NOT_DROPPED) { - resetTargetsLocked(); - setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY - ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); - return true; - } - - // Identify targets. - if (! mCurrentInputTargetsValid) { - int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, - entry, nextWakeupTime); - if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { - return false; - } - - setInjectionResultLocked(entry, injectionResult); - if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { - return true; - } - - addMonitoringTargetsLocked(); - commitTargetsLocked(); - } - - // Dispatch the key. - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); - return true; -} - -void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " - "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, " - "repeatCount=%d, downTime=%lld", - prefix, - entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, - entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, - entry->repeatCount, entry->downTime); -#endif -} - -bool InputDispatcher::dispatchMotionLocked( - nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime) { - // Preprocessing. - if (! entry->dispatchInProgress) { - entry->dispatchInProgress = true; - resetTargetsLocked(); - - logOutboundMotionDetailsLocked("dispatchMotion - ", entry); - } - - // Clean up if dropping the event. - if (*dropReason != DROP_REASON_NOT_DROPPED) { - resetTargetsLocked(); - setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY - ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); - return true; - } - - bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; - - // Identify targets. - if (! mCurrentInputTargetsValid) { - int32_t injectionResult; - if (isPointerEvent) { - // Pointer event. (eg. touchscreen) - injectionResult = findTouchedWindowTargetsLocked(currentTime, - entry, nextWakeupTime); - } else { - // Non touch event. (eg. trackball) - injectionResult = findFocusedWindowTargetsLocked(currentTime, - entry, nextWakeupTime); - } - if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { - return false; - } - - setInjectionResultLocked(entry, injectionResult); - if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { - return true; - } - - addMonitoringTargetsLocked(); - commitTargetsLocked(); - } - - // Dispatch the motion. - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); - return true; -} - - -void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " - "action=0x%x, flags=0x%x, " - "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld", - prefix, - entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, - entry->action, entry->flags, - entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision, - entry->downTime); - - // Print the most recent sample that we have available, this may change due to batching. - size_t sampleCount = 1; - const MotionSample* sample = & entry->firstSample; - for (; sample->next != NULL; sample = sample->next) { - sampleCount += 1; - } - for (uint32_t i = 0; i < entry->pointerCount; i++) { - LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, " - "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " - "orientation=%f", - i, entry->pointerIds[i], - sample->pointerCoords[i].x, sample->pointerCoords[i].y, - sample->pointerCoords[i].pressure, sample->pointerCoords[i].size, - sample->pointerCoords[i].touchMajor, sample->pointerCoords[i].touchMinor, - sample->pointerCoords[i].toolMajor, sample->pointerCoords[i].toolMinor, - sample->pointerCoords[i].orientation); - } - - // Keep in mind that due to batching, it is possible for the number of samples actually - // dispatched to change before the application finally consumed them. - if (entry->action == AMOTION_EVENT_ACTION_MOVE) { - LOGD(" ... Total movement samples currently batched %d ...", sampleCount); - } -#endif -} - -void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, - EventEntry* eventEntry, bool resumeWithAppendedMotionSample) { -#if DEBUG_DISPATCH_CYCLE - LOGD("dispatchEventToCurrentInputTargets - " - "resumeWithAppendedMotionSample=%s", - toString(resumeWithAppendedMotionSample)); -#endif - - assert(eventEntry->dispatchInProgress); // should already have been set to true - - pokeUserActivityLocked(eventEntry); - - for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { - const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); - - ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, - resumeWithAppendedMotionSample); - } else { -#if DEBUG_FOCUS - LOGD("Dropping event delivery to target with channel '%s' because it " - "is no longer registered with the input dispatcher.", - inputTarget.inputChannel->getName().string()); -#endif - } - } -} - -void InputDispatcher::resetTargetsLocked() { - mCurrentInputTargetsValid = false; - mCurrentInputTargets.clear(); - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; -} - -void InputDispatcher::commitTargetsLocked() { - mCurrentInputTargetsValid = true; -} - -int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, - const EventEntry* entry, const InputApplication* application, const InputWindow* window, - nsecs_t* nextWakeupTime) { - if (application == NULL && window == NULL) { - if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { -#if DEBUG_FOCUS - LOGD("Waiting for system to become ready for input."); -#endif - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; - mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = LONG_LONG_MAX; - mInputTargetWaitTimeoutExpired = false; - } - } else { - if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { -#if DEBUG_FOCUS - LOGD("Waiting for application to become ready for input: %s", - getApplicationWindowLabelLocked(application, window).string()); -#endif - nsecs_t timeout = window ? window->dispatchingTimeout : - application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT; - - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; - mInputTargetWaitStartTime = currentTime; - mInputTargetWaitTimeoutTime = currentTime + timeout; - mInputTargetWaitTimeoutExpired = false; - } - } - - if (mInputTargetWaitTimeoutExpired) { - return INPUT_EVENT_INJECTION_TIMED_OUT; - } - - if (currentTime >= mInputTargetWaitTimeoutTime) { - onANRLocked(currentTime, application, window, entry->eventTime, mInputTargetWaitStartTime); - - // Force poll loop to wake up immediately on next iteration once we get the - // ANR response back from the policy. - *nextWakeupTime = LONG_LONG_MIN; - return INPUT_EVENT_INJECTION_PENDING; - } else { - // Force poll loop to wake up when timeout is due. - if (mInputTargetWaitTimeoutTime < *nextWakeupTime) { - *nextWakeupTime = mInputTargetWaitTimeoutTime; - } - return INPUT_EVENT_INJECTION_PENDING; - } -} - -void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, - const sp<InputChannel>& inputChannel) { - if (newTimeout > 0) { - // Extend the timeout. - mInputTargetWaitTimeoutTime = now() + newTimeout; - } else { - // Give up. - mInputTargetWaitTimeoutExpired = true; - - // Release the touch targets. - mTouchState.reset(); - - // Input state will not be realistic. Mark it out of sync. - if (inputChannel.get()) { - ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - synthesizeCancelationEventsForConnectionLocked( - connection, InputState::CANCEL_ALL_EVENTS, - "application not responding"); - } - } - } -} - -nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked( - nsecs_t currentTime) { - if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { - return currentTime - mInputTargetWaitStartTime; - } - return 0; -} - -void InputDispatcher::resetANRTimeoutsLocked() { -#if DEBUG_FOCUS - LOGD("Resetting ANR timeouts."); -#endif - - // Reset input target wait timeout. - mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; -} - -int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, - const EventEntry* entry, nsecs_t* nextWakeupTime) { - mCurrentInputTargets.clear(); - - int32_t injectionResult; - - // If there is no currently focused window and no focused application - // then drop the event. - if (! mFocusedWindow) { - if (mFocusedApplication) { -#if DEBUG_FOCUS - LOGD("Waiting because there is no focused window but there is a " - "focused application that may eventually add a window: %s.", - getApplicationWindowLabelLocked(mFocusedApplication, NULL).string()); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplication, NULL, nextWakeupTime); - goto Unresponsive; - } - - LOGI("Dropping event because there is no focused window or focused application."); - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - - // Check permissions. - if (! checkInjectionPermission(mFocusedWindow, entry->injectionState)) { - injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; - goto Failed; - } - - // If the currently focused window is paused then keep waiting. - if (mFocusedWindow->paused) { -#if DEBUG_FOCUS - LOGD("Waiting because focused window is paused."); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplication, mFocusedWindow, nextWakeupTime); - goto Unresponsive; - } - - // If the currently focused window is still working on previous events then keep waiting. - if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindow)) { -#if DEBUG_FOCUS - LOGD("Waiting because focused window still processing previous input."); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplication, mFocusedWindow, nextWakeupTime); - goto Unresponsive; - } - - // Success! Output targets. - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_FOREGROUND, BitSet32(0)); - - // Done. -Failed: -Unresponsive: - nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); - updateDispatchStatisticsLocked(currentTime, entry, - injectionResult, timeSpentWaitingForApplication); -#if DEBUG_FOCUS - LOGD("findFocusedWindow finished: injectionResult=%d, " - "timeSpendWaitingForApplication=%0.1fms", - injectionResult, timeSpentWaitingForApplication / 1000000.0); -#endif - return injectionResult; -} - -int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, - const MotionEntry* entry, nsecs_t* nextWakeupTime) { - enum InjectionPermission { - INJECTION_PERMISSION_UNKNOWN, - INJECTION_PERMISSION_GRANTED, - INJECTION_PERMISSION_DENIED - }; - - mCurrentInputTargets.clear(); - - nsecs_t startTime = now(); - - // For security reasons, we defer updating the touch state until we are sure that - // event injection will be allowed. - // - // FIXME In the original code, screenWasOff could never be set to true. - // The reason is that the POLICY_FLAG_WOKE_HERE - // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw - // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was - // actually enqueued using the policyFlags that appeared in the final EV_SYN - // events upon which no preprocessing took place. So policyFlags was always 0. - // In the new native input dispatcher we're a bit more careful about event - // preprocessing so the touches we receive can actually have non-zero policyFlags. - // Unfortunately we obtain undesirable behavior. - // - // Here's what happens: - // - // When the device dims in anticipation of going to sleep, touches - // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause - // the device to brighten and reset the user activity timer. - // Touches on other windows (such as the launcher window) - // are dropped. Then after a moment, the device goes to sleep. Oops. - // - // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE - // instead of POLICY_FLAG_WOKE_HERE... - // - bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE; - - int32_t action = entry->action; - int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; - - // Update the touch state as needed based on the properties of the touch event. - int32_t injectionResult = INPUT_EVENT_INJECTION_PENDING; - InjectionPermission injectionPermission = INJECTION_PERMISSION_UNKNOWN; - if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - mTempTouchState.reset(); - mTempTouchState.down = true; - } else { - mTempTouchState.copyFrom(mTouchState); - } - - bool isSplit = mTempTouchState.split && mTempTouchState.down; - if (maskedAction == AMOTION_EVENT_ACTION_DOWN - || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { - /* Case 1: New splittable pointer going down. */ - - int32_t pointerIndex = getMotionEventActionPointerIndex(action); - int32_t x = int32_t(entry->firstSample.pointerCoords[pointerIndex].x); - int32_t y = int32_t(entry->firstSample.pointerCoords[pointerIndex].y); - const InputWindow* newTouchedWindow = NULL; - const InputWindow* topErrorWindow = NULL; - - // Traverse windows from front to back to find touched window and outside targets. - size_t numWindows = mWindows.size(); - for (size_t i = 0; i < numWindows; i++) { - const InputWindow* window = & mWindows.editItemAt(i); - int32_t flags = window->layoutParamsFlags; - - if (flags & InputWindow::FLAG_SYSTEM_ERROR) { - if (! topErrorWindow) { - topErrorWindow = window; - } - } - - if (window->visible) { - if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) { - bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE - | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0; - if (isTouchModal || window->touchableAreaContainsPoint(x, y)) { - if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) { - newTouchedWindow = window; - } - break; // found touched window, exit window loop - } - } - - if (maskedAction == AMOTION_EVENT_ACTION_DOWN - && (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH)) { - int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE; - if (isWindowObscuredAtPointLocked(window, x, y)) { - outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; - } - - mTempTouchState.addOrUpdateWindow(window, outsideTargetFlags, BitSet32(0)); - } - } - } - - // If there is an error window but it is not taking focus (typically because - // it is invisible) then wait for it. Any other focused window may in - // fact be in ANR state. - if (topErrorWindow && newTouchedWindow != topErrorWindow) { -#if DEBUG_FOCUS - LOGD("Waiting because system error window is pending."); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, NULL, nextWakeupTime); - injectionPermission = INJECTION_PERMISSION_UNKNOWN; - goto Unresponsive; - } - - // Figure out whether splitting will be allowed for this window. - if (newTouchedWindow - && (newTouchedWindow->layoutParamsFlags & InputWindow::FLAG_SPLIT_TOUCH)) { - // New window supports splitting. - isSplit = true; - } else if (isSplit) { - // New window does not support splitting but we have already split events. - // Assign the pointer to the first foreground window we find. - // (May be NULL which is why we put this code block before the next check.) - newTouchedWindow = mTempTouchState.getFirstForegroundWindow(); - } - - // If we did not find a touched window then fail. - if (! newTouchedWindow) { - if (mFocusedApplication) { -#if DEBUG_FOCUS - LOGD("Waiting because there is no touched window but there is a " - "focused application that may eventually add a new window: %s.", - getApplicationWindowLabelLocked(mFocusedApplication, NULL).string()); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplication, NULL, nextWakeupTime); - goto Unresponsive; - } - - LOGI("Dropping event because there is no touched window or focused application."); - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - - // Set target flags. - int32_t targetFlags = InputTarget::FLAG_FOREGROUND; - if (isSplit) { - targetFlags |= InputTarget::FLAG_SPLIT; - } - if (isWindowObscuredAtPointLocked(newTouchedWindow, x, y)) { - targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; - } - - // Update the temporary touch state. - BitSet32 pointerIds; - if (isSplit) { - uint32_t pointerId = entry->pointerIds[pointerIndex]; - pointerIds.markBit(pointerId); - } - mTempTouchState.addOrUpdateWindow(newTouchedWindow, targetFlags, pointerIds); - } else { - /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ - - // If the pointer is not currently down, then ignore the event. - if (! mTempTouchState.down) { - LOGI("Dropping event because the pointer is not down."); - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - } - - // Check permission to inject into all touched foreground windows and ensure there - // is at least one touched foreground window. - { - bool haveForegroundWindow = false; - for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { - const TouchedWindow& touchedWindow = mTempTouchState.windows[i]; - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { - haveForegroundWindow = true; - if (! checkInjectionPermission(touchedWindow.window, entry->injectionState)) { - injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; - injectionPermission = INJECTION_PERMISSION_DENIED; - goto Failed; - } - } - } - if (! haveForegroundWindow) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("Dropping event because there is no touched foreground window to receive it."); -#endif - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; - } - - // Permission granted to injection into all touched foreground windows. - injectionPermission = INJECTION_PERMISSION_GRANTED; - } - - // Ensure all touched foreground windows are ready for new input. - for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { - const TouchedWindow& touchedWindow = mTempTouchState.windows[i]; - if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { - // If the touched window is paused then keep waiting. - if (touchedWindow.window->paused) { -#if DEBUG_INPUT_DISPATCHER_POLICY - LOGD("Waiting because touched window is paused."); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, touchedWindow.window, nextWakeupTime); - goto Unresponsive; - } - - // If the touched window is still working on previous events then keep waiting. - if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.window)) { -#if DEBUG_FOCUS - LOGD("Waiting because touched window still processing previous input."); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, touchedWindow.window, nextWakeupTime); - goto Unresponsive; - } - } - } - - // If this is the first pointer going down and the touched window has a wallpaper - // then also add the touched wallpaper windows so they are locked in for the duration - // of the touch gesture. - if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow(); - if (foregroundWindow->hasWallpaper) { - for (size_t i = 0; i < mWindows.size(); i++) { - const InputWindow* window = & mWindows[i]; - if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) { - mTempTouchState.addOrUpdateWindow(window, - InputTarget::FLAG_WINDOW_IS_OBSCURED, BitSet32(0)); - } - } - } - } - - // Success! Output targets. - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - - for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { - const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i); - addWindowTargetLocked(touchedWindow.window, touchedWindow.targetFlags, - touchedWindow.pointerIds); - } - - // Drop the outside touch window since we will not care about them in the next iteration. - mTempTouchState.removeOutsideTouchWindows(); - -Failed: - // Check injection permission once and for all. - if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) { - if (checkInjectionPermission(NULL, entry->injectionState)) { - injectionPermission = INJECTION_PERMISSION_GRANTED; - } else { - injectionPermission = INJECTION_PERMISSION_DENIED; - } - } - - // Update final pieces of touch state if the injector had permission. - if (injectionPermission == INJECTION_PERMISSION_GRANTED) { - if (maskedAction == AMOTION_EVENT_ACTION_UP - || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { - // All pointers up or canceled. - mTempTouchState.reset(); - } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - // First pointer went down. - if (mTouchState.down) { -#if DEBUG_FOCUS - LOGD("Pointer down received while already down."); -#endif - } - } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { - // One pointer went up. - if (isSplit) { - int32_t pointerIndex = getMotionEventActionPointerIndex(action); - uint32_t pointerId = entry->pointerIds[pointerIndex]; - - for (size_t i = 0; i < mTempTouchState.windows.size(); ) { - TouchedWindow& touchedWindow = mTempTouchState.windows.editItemAt(i); - if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) { - touchedWindow.pointerIds.clearBit(pointerId); - if (touchedWindow.pointerIds.isEmpty()) { - mTempTouchState.windows.removeAt(i); - continue; - } - } - i += 1; - } - } - } - - // Save changes to touch state. - mTouchState.copyFrom(mTempTouchState); - } else { -#if DEBUG_FOCUS - LOGD("Not updating touch focus because injection was denied."); -#endif - } - -Unresponsive: - // Reset temporary touch state to ensure we release unnecessary references to input channels. - mTempTouchState.reset(); - - nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); - updateDispatchStatisticsLocked(currentTime, entry, - injectionResult, timeSpentWaitingForApplication); -#if DEBUG_FOCUS - LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); -#endif - return injectionResult; -} - -void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, - BitSet32 pointerIds) { - mCurrentInputTargets.push(); - - InputTarget& target = mCurrentInputTargets.editTop(); - target.inputChannel = window->inputChannel; - target.flags = targetFlags; - target.xOffset = - window->frameLeft; - target.yOffset = - window->frameTop; - target.pointerIds = pointerIds; -} - -void InputDispatcher::addMonitoringTargetsLocked() { - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - mCurrentInputTargets.push(); - - InputTarget& target = mCurrentInputTargets.editTop(); - target.inputChannel = mMonitoringChannels[i]; - target.flags = 0; - target.xOffset = 0; - target.yOffset = 0; - } -} - -bool InputDispatcher::checkInjectionPermission(const InputWindow* window, - const InjectionState* injectionState) { - if (injectionState - && (window == NULL || window->ownerUid != injectionState->injectorUid) - && !hasInjectionPermission(injectionState->injectorPid, injectionState->injectorUid)) { - if (window) { - LOGW("Permission denied: injecting event from pid %d uid %d to window " - "with input channel %s owned by uid %d", - injectionState->injectorPid, injectionState->injectorUid, - window->inputChannel->getName().string(), - window->ownerUid); - } else { - LOGW("Permission denied: injecting event from pid %d uid %d", - injectionState->injectorPid, injectionState->injectorUid); - } - return false; - } - return true; -} - -bool InputDispatcher::isWindowObscuredAtPointLocked( - const InputWindow* window, int32_t x, int32_t y) const { - size_t numWindows = mWindows.size(); - for (size_t i = 0; i < numWindows; i++) { - const InputWindow* other = & mWindows.itemAt(i); - if (other == window) { - break; - } - if (other->visible && ! other->isTrustedOverlay() && other->frameContainsPoint(x, y)) { - return true; - } - } - return false; -} - -bool InputDispatcher::isWindowFinishedWithPreviousInputLocked(const InputWindow* window) { - ssize_t connectionIndex = getConnectionIndexLocked(window->inputChannel); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - return connection->outboundQueue.isEmpty(); - } else { - return true; - } -} - -String8 InputDispatcher::getApplicationWindowLabelLocked(const InputApplication* application, - const InputWindow* window) { - if (application) { - if (window) { - String8 label(application->name); - label.append(" - "); - label.append(window->name); - return label; - } else { - return application->name; - } - } else if (window) { - return window->name; - } else { - return String8("<unknown application or window>"); - } -} - -void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { - int32_t eventType = POWER_MANAGER_BUTTON_EVENT; - switch (eventEntry->type) { - case EventEntry::TYPE_MOTION: { - const MotionEntry* motionEntry = static_cast<const MotionEntry*>(eventEntry); - if (motionEntry->action == AMOTION_EVENT_ACTION_CANCEL) { - return; - } - - if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { - switch (motionEntry->action) { - case AMOTION_EVENT_ACTION_DOWN: - eventType = POWER_MANAGER_TOUCH_EVENT; - break; - case AMOTION_EVENT_ACTION_UP: - eventType = POWER_MANAGER_TOUCH_UP_EVENT; - break; - default: - if (motionEntry->eventTime - motionEntry->downTime < LONG_TOUCH_DELAY) { - eventType = POWER_MANAGER_TOUCH_EVENT; - } else { - eventType = POWER_MANAGER_LONG_TOUCH_EVENT; - } - break; - } - } - break; - } - case EventEntry::TYPE_KEY: { - const KeyEntry* keyEntry = static_cast<const KeyEntry*>(eventEntry); - if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) { - return; - } - break; - } - } - - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doPokeUserActivityLockedInterruptible); - commandEntry->eventTime = eventEntry->eventTime; - commandEntry->userActivityEventType = eventType; -} - -void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, - bool resumeWithAppendedMotionSample) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, " - "xOffset=%f, yOffset=%f, " - "windowType=%d, pointerIds=0x%x, " - "resumeWithAppendedMotionSample=%s", - connection->getInputChannelName(), inputTarget->flags, - inputTarget->xOffset, inputTarget->yOffset, - inputTarget->windowType, inputTarget->pointerIds.value, - toString(resumeWithAppendedMotionSample)); -#endif - - // Make sure we are never called for streaming when splitting across multiple windows. - bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT; - assert(! (resumeWithAppendedMotionSample && isSplit)); - - // Skip this event if the connection status is not normal. - // We don't want to enqueue additional outbound events if the connection is broken. - if (connection->status != Connection::STATUS_NORMAL) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ Dropping event because the channel status is %s", - connection->getInputChannelName(), connection->getStatusLabel()); -#endif - return; - } - - // Split a motion event if needed. - if (isSplit) { - assert(eventEntry->type == EventEntry::TYPE_MOTION); - - MotionEntry* originalMotionEntry = static_cast<MotionEntry*>(eventEntry); - if (inputTarget->pointerIds.count() != originalMotionEntry->pointerCount) { - MotionEntry* splitMotionEntry = splitMotionEvent( - originalMotionEntry, inputTarget->pointerIds); -#if DEBUG_FOCUS - LOGD("channel '%s' ~ Split motion event.", - connection->getInputChannelName()); - logOutboundMotionDetailsLocked(" ", splitMotionEntry); -#endif - eventEntry = splitMotionEntry; - } - } - - // Resume the dispatch cycle with a freshly appended motion sample. - // First we check that the last dispatch entry in the outbound queue is for the same - // motion event to which we appended the motion sample. If we find such a dispatch - // entry, and if it is currently in progress then we try to stream the new sample. - bool wasEmpty = connection->outboundQueue.isEmpty(); - - if (! wasEmpty && resumeWithAppendedMotionSample) { - DispatchEntry* motionEventDispatchEntry = - connection->findQueuedDispatchEntryForEvent(eventEntry); - if (motionEventDispatchEntry) { - // If the dispatch entry is not in progress, then we must be busy dispatching an - // earlier event. Not a problem, the motion event is on the outbound queue and will - // be dispatched later. - if (! motionEventDispatchEntry->inProgress) { -#if DEBUG_BATCHING - LOGD("channel '%s' ~ Not streaming because the motion event has " - "not yet been dispatched. " - "(Waiting for earlier events to be consumed.)", - connection->getInputChannelName()); -#endif - return; - } - - // If the dispatch entry is in progress but it already has a tail of pending - // motion samples, then it must mean that the shared memory buffer filled up. - // Not a problem, when this dispatch cycle is finished, we will eventually start - // a new dispatch cycle to process the tail and that tail includes the newly - // appended motion sample. - if (motionEventDispatchEntry->tailMotionSample) { -#if DEBUG_BATCHING - LOGD("channel '%s' ~ Not streaming because no new samples can " - "be appended to the motion event in this dispatch cycle. " - "(Waiting for next dispatch cycle to start.)", - connection->getInputChannelName()); -#endif - return; - } - - // The dispatch entry is in progress and is still potentially open for streaming. - // Try to stream the new motion sample. This might fail if the consumer has already - // consumed the motion event (or if the channel is broken). - MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); - MotionSample* appendedMotionSample = motionEntry->lastSample; - status_t status = connection->inputPublisher.appendMotionSample( - appendedMotionSample->eventTime, appendedMotionSample->pointerCoords); - if (status == OK) { -#if DEBUG_BATCHING - LOGD("channel '%s' ~ Successfully streamed new motion sample.", - connection->getInputChannelName()); -#endif - return; - } - -#if DEBUG_BATCHING - if (status == NO_MEMORY) { - LOGD("channel '%s' ~ Could not append motion sample to currently " - "dispatched move event because the shared memory buffer is full. " - "(Waiting for next dispatch cycle to start.)", - connection->getInputChannelName()); - } else if (status == status_t(FAILED_TRANSACTION)) { - LOGD("channel '%s' ~ Could not append motion sample to currently " - "dispatched move event because the event has already been consumed. " - "(Waiting for next dispatch cycle to start.)", - connection->getInputChannelName()); - } else { - LOGD("channel '%s' ~ Could not append motion sample to currently " - "dispatched move event due to an error, status=%d. " - "(Waiting for next dispatch cycle to start.)", - connection->getInputChannelName(), status); - } -#endif - // Failed to stream. Start a new tail of pending motion samples to dispatch - // in the next cycle. - motionEventDispatchEntry->tailMotionSample = appendedMotionSample; - return; - } - } - - // This is a new event. - // Enqueue a new dispatch entry onto the outbound queue for this connection. - DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref - inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset); - if (dispatchEntry->hasForegroundTarget()) { - incrementPendingForegroundDispatchesLocked(eventEntry); - } - - // Handle the case where we could not stream a new motion sample because the consumer has - // already consumed the motion event (otherwise the corresponding dispatch entry would - // still be in the outbound queue for this connection). We set the head motion sample - // to the list starting with the newly appended motion sample. - if (resumeWithAppendedMotionSample) { -#if DEBUG_BATCHING - LOGD("channel '%s' ~ Preparing a new dispatch cycle for additional motion samples " - "that cannot be streamed because the motion event has already been consumed.", - connection->getInputChannelName()); -#endif - MotionSample* appendedMotionSample = static_cast<MotionEntry*>(eventEntry)->lastSample; - dispatchEntry->headMotionSample = appendedMotionSample; - } - - // Enqueue the dispatch entry. - connection->outboundQueue.enqueueAtTail(dispatchEntry); - - // If the outbound queue was previously empty, start the dispatch cycle going. - if (wasEmpty) { - activateConnectionLocked(connection.get()); - startDispatchCycleLocked(currentTime, connection); - } -} - -void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ startDispatchCycle", - connection->getInputChannelName()); -#endif - - assert(connection->status == Connection::STATUS_NORMAL); - assert(! connection->outboundQueue.isEmpty()); - - DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next; - assert(! dispatchEntry->inProgress); - - // Mark the dispatch entry as in progress. - dispatchEntry->inProgress = true; - - // Update the connection's input state. - EventEntry* eventEntry = dispatchEntry->eventEntry; - InputState::Consistency consistency = connection->inputState.trackEvent(eventEntry); - -#if FILTER_INPUT_EVENTS - // Filter out inconsistent sequences of input events. - // The input system may drop or inject events in a way that could violate implicit - // invariants on input state and potentially cause an application to crash - // or think that a key or pointer is stuck down. Technically we make no guarantees - // of consistency but it would be nice to improve on this where possible. - // XXX: This code is a proof of concept only. Not ready for prime time. - if (consistency == InputState::TOLERABLE) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ Sending an event that is inconsistent with the connection's " - "current input state but that is likely to be tolerated by the application.", - connection->getInputChannelName()); -#endif - } else if (consistency == InputState::BROKEN) { - LOGI("channel '%s' ~ Dropping an event that is inconsistent with the connection's " - "current input state and that is likely to cause the application to crash.", - connection->getInputChannelName()); - startNextDispatchCycleLocked(currentTime, connection); - return; - } -#endif - - // Publish the event. - status_t status; - switch (eventEntry->type) { - case EventEntry::TYPE_KEY: { - KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry); - - // Apply target flags. - int32_t action = keyEntry->action; - int32_t flags = keyEntry->flags; - - // Publish the key event. - status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source, - action, flags, keyEntry->keyCode, keyEntry->scanCode, - keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, - keyEntry->eventTime); - - if (status) { - LOGE("channel '%s' ~ Could not publish key event, " - "status=%d", connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection); - return; - } - break; - } - - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry); - - // Apply target flags. - int32_t action = motionEntry->action; - int32_t flags = motionEntry->flags; - if (dispatchEntry->targetFlags & InputTarget::FLAG_OUTSIDE) { - action = AMOTION_EVENT_ACTION_OUTSIDE; - } - if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) { - flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - } - - // If headMotionSample is non-NULL, then it points to the first new sample that we - // were unable to dispatch during the previous cycle so we resume dispatching from - // that point in the list of motion samples. - // Otherwise, we just start from the first sample of the motion event. - MotionSample* firstMotionSample = dispatchEntry->headMotionSample; - if (! firstMotionSample) { - firstMotionSample = & motionEntry->firstSample; - } - - // Set the X and Y offset depending on the input source. - float xOffset, yOffset; - if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { - xOffset = dispatchEntry->xOffset; - yOffset = dispatchEntry->yOffset; - } else { - xOffset = 0.0f; - yOffset = 0.0f; - } - - // Publish the motion event and the first motion sample. - status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId, - motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState, - xOffset, yOffset, - motionEntry->xPrecision, motionEntry->yPrecision, - motionEntry->downTime, firstMotionSample->eventTime, - motionEntry->pointerCount, motionEntry->pointerIds, - firstMotionSample->pointerCoords); - - if (status) { - LOGE("channel '%s' ~ Could not publish motion event, " - "status=%d", connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection); - return; - } - - // Append additional motion samples. - MotionSample* nextMotionSample = firstMotionSample->next; - for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) { - status = connection->inputPublisher.appendMotionSample( - nextMotionSample->eventTime, nextMotionSample->pointerCoords); - if (status == NO_MEMORY) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ Shared memory buffer full. Some motion samples will " - "be sent in the next dispatch cycle.", - connection->getInputChannelName()); -#endif - break; - } - if (status != OK) { - LOGE("channel '%s' ~ Could not append motion sample " - "for a reason other than out of memory, status=%d", - connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection); - return; - } - } - - // Remember the next motion sample that we could not dispatch, in case we ran out - // of space in the shared memory buffer. - dispatchEntry->tailMotionSample = nextMotionSample; - break; - } - - default: { - assert(false); - } - } - - // Send the dispatch signal. - status = connection->inputPublisher.sendDispatchSignal(); - if (status) { - LOGE("channel '%s' ~ Could not send dispatch signal, status=%d", - connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection); - return; - } - - // Record information about the newly started dispatch cycle. - connection->lastEventTime = eventEntry->eventTime; - connection->lastDispatchTime = currentTime; - - // Notify other system components. - onDispatchCycleStartedLocked(currentTime, connection); -} - -void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, " - "%01.1fms since dispatch", - connection->getInputChannelName(), - connection->getEventLatencyMillis(currentTime), - connection->getDispatchLatencyMillis(currentTime)); -#endif - - if (connection->status == Connection::STATUS_BROKEN - || connection->status == Connection::STATUS_ZOMBIE) { - return; - } - - // Notify other system components. - onDispatchCycleFinishedLocked(currentTime, connection); - - // Reset the publisher since the event has been consumed. - // We do this now so that the publisher can release some of its internal resources - // while waiting for the next dispatch cycle to begin. - status_t status = connection->inputPublisher.reset(); - if (status) { - LOGE("channel '%s' ~ Could not reset publisher, status=%d", - connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection); - return; - } - - startNextDispatchCycleLocked(currentTime, connection); -} - -void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection) { - // Start the next dispatch cycle for this connection. - while (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next; - if (dispatchEntry->inProgress) { - // Finish or resume current event in progress. - if (dispatchEntry->tailMotionSample) { - // We have a tail of undispatched motion samples. - // Reuse the same DispatchEntry and start a new cycle. - dispatchEntry->inProgress = false; - dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample; - dispatchEntry->tailMotionSample = NULL; - startDispatchCycleLocked(currentTime, connection); - return; - } - // Finished. - connection->outboundQueue.dequeueAtHead(); - if (dispatchEntry->hasForegroundTarget()) { - decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry); - } - mAllocator.releaseDispatchEntry(dispatchEntry); - } else { - // If the head is not in progress, then we must have already dequeued the in - // progress event, which means we actually aborted it. - // So just start the next event for this connection. - startDispatchCycleLocked(currentTime, connection); - return; - } - } - - // Outbound queue is empty, deactivate the connection. - deactivateConnectionLocked(connection.get()); -} - -void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ abortBrokenDispatchCycle - broken=%s", - connection->getInputChannelName(), toString(broken)); -#endif - - // Clear the outbound queue. - drainOutboundQueueLocked(connection.get()); - - // The connection appears to be unrecoverably broken. - // Ignore already broken or zombie connections. - if (connection->status == Connection::STATUS_NORMAL) { - connection->status = Connection::STATUS_BROKEN; - - // Notify other system components. - onDispatchCycleBrokenLocked(currentTime, connection); - } -} - -void InputDispatcher::drainOutboundQueueLocked(Connection* connection) { - while (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); - if (dispatchEntry->hasForegroundTarget()) { - decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry); - } - mAllocator.releaseDispatchEntry(dispatchEntry); - } - - deactivateConnectionLocked(connection); -} - -int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { - InputDispatcher* d = static_cast<InputDispatcher*>(data); - - { // acquire lock - AutoMutex _l(d->mLock); - - ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd); - if (connectionIndex < 0) { - LOGE("Received spurious receive callback for unknown input channel. " - "fd=%d, events=0x%x", receiveFd, events); - return 0; // remove the callback - } - - nsecs_t currentTime = now(); - - sp<Connection> connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex); - if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) { - LOGE("channel '%s' ~ Consumer closed input channel or an error occurred. " - "events=0x%x", connection->getInputChannelName(), events); - d->abortBrokenDispatchCycleLocked(currentTime, connection); - d->runCommandsLockedInterruptible(); - return 0; // remove the callback - } - - if (! (events & ALOOPER_EVENT_INPUT)) { - LOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " - "events=0x%x", connection->getInputChannelName(), events); - return 1; - } - - status_t status = connection->inputPublisher.receiveFinishedSignal(); - if (status) { - LOGE("channel '%s' ~ Failed to receive finished signal. status=%d", - connection->getInputChannelName(), status); - d->abortBrokenDispatchCycleLocked(currentTime, connection); - d->runCommandsLockedInterruptible(); - return 0; // remove the callback - } - - d->finishDispatchCycleLocked(currentTime, connection); - d->runCommandsLockedInterruptible(); - return 1; - } // release lock -} - -void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( - InputState::CancelationOptions options, const char* reason) { - for (size_t i = 0; i < mConnectionsByReceiveFd.size(); i++) { - synthesizeCancelationEventsForConnectionLocked( - mConnectionsByReceiveFd.valueAt(i), options, reason); - } -} - -void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( - const sp<InputChannel>& channel, InputState::CancelationOptions options, - const char* reason) { - ssize_t index = getConnectionIndexLocked(channel); - if (index >= 0) { - synthesizeCancelationEventsForConnectionLocked( - mConnectionsByReceiveFd.valueAt(index), options, reason); - } -} - -void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( - const sp<Connection>& connection, InputState::CancelationOptions options, - const char* reason) { - nsecs_t currentTime = now(); - - mTempCancelationEvents.clear(); - connection->inputState.synthesizeCancelationEvents(currentTime, & mAllocator, - mTempCancelationEvents, options); - - if (! mTempCancelationEvents.isEmpty() - && connection->status != Connection::STATUS_BROKEN) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("channel '%s' ~ Synthesized %d cancelation events to bring channel back in sync " - "with reality: %s, options=%d.", - connection->getInputChannelName(), mTempCancelationEvents.size(), reason, options); -#endif - for (size_t i = 0; i < mTempCancelationEvents.size(); i++) { - EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i); - switch (cancelationEventEntry->type) { - case EventEntry::TYPE_KEY: - logOutboundKeyDetailsLocked("cancel - ", - static_cast<KeyEntry*>(cancelationEventEntry)); - break; - case EventEntry::TYPE_MOTION: - logOutboundMotionDetailsLocked("cancel - ", - static_cast<MotionEntry*>(cancelationEventEntry)); - break; - } - - int32_t xOffset, yOffset; - const InputWindow* window = getWindowLocked(connection->inputChannel); - if (window) { - xOffset = -window->frameLeft; - yOffset = -window->frameTop; - } else { - xOffset = 0; - yOffset = 0; - } - - DispatchEntry* cancelationDispatchEntry = - mAllocator.obtainDispatchEntry(cancelationEventEntry, // increments ref - 0, xOffset, yOffset); - connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry); - - mAllocator.releaseEventEntry(cancelationEventEntry); - } - - if (!connection->outboundQueue.headSentinel.next->inProgress) { - startDispatchCycleLocked(currentTime, connection); - } - } -} - -InputDispatcher::MotionEntry* -InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds) { - assert(pointerIds.value != 0); - - uint32_t splitPointerIndexMap[MAX_POINTERS]; - int32_t splitPointerIds[MAX_POINTERS]; - PointerCoords splitPointerCoords[MAX_POINTERS]; - - uint32_t originalPointerCount = originalMotionEntry->pointerCount; - uint32_t splitPointerCount = 0; - - for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount; - originalPointerIndex++) { - int32_t pointerId = uint32_t(originalMotionEntry->pointerIds[originalPointerIndex]); - if (pointerIds.hasBit(pointerId)) { - splitPointerIndexMap[splitPointerCount] = originalPointerIndex; - splitPointerIds[splitPointerCount] = pointerId; - splitPointerCoords[splitPointerCount] = - originalMotionEntry->firstSample.pointerCoords[originalPointerIndex]; - splitPointerCount += 1; - } - } - assert(splitPointerCount == pointerIds.count()); - - int32_t action = originalMotionEntry->action; - int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; - if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN - || maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { - int32_t originalPointerIndex = getMotionEventActionPointerIndex(action); - int32_t pointerId = originalMotionEntry->pointerIds[originalPointerIndex]; - if (pointerIds.hasBit(pointerId)) { - if (pointerIds.count() == 1) { - // The first/last pointer went down/up. - action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN - ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else { - // A secondary pointer went down/up. - uint32_t splitPointerIndex = 0; - while (pointerId != splitPointerIds[splitPointerIndex]) { - splitPointerIndex += 1; - } - action = maskedAction | (splitPointerIndex - << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); - } - } else { - // An unrelated pointer changed. - action = AMOTION_EVENT_ACTION_MOVE; - } - } - - MotionEntry* splitMotionEntry = mAllocator.obtainMotionEntry( - originalMotionEntry->eventTime, - originalMotionEntry->deviceId, - originalMotionEntry->source, - originalMotionEntry->policyFlags, - action, - originalMotionEntry->flags, - originalMotionEntry->metaState, - originalMotionEntry->edgeFlags, - originalMotionEntry->xPrecision, - originalMotionEntry->yPrecision, - originalMotionEntry->downTime, - splitPointerCount, splitPointerIds, splitPointerCoords); - - for (MotionSample* originalMotionSample = originalMotionEntry->firstSample.next; - originalMotionSample != NULL; originalMotionSample = originalMotionSample->next) { - for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount; - splitPointerIndex++) { - uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex]; - splitPointerCoords[splitPointerIndex] = - originalMotionSample->pointerCoords[originalPointerIndex]; - } - - mAllocator.appendMotionSample(splitMotionEntry, originalMotionSample->eventTime, - splitPointerCoords); - } - - return splitMotionEntry; -} - -void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) { -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime); -#endif - - bool needWake; - { // acquire lock - AutoMutex _l(mLock); - - ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime); - needWake = enqueueInboundEventLocked(newEntry); - } // release lock - - if (needWake) { - mLooper->wake(); - } -} - -void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source, - uint32_t policyFlags, int32_t action, int32_t flags, - int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) { -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifyKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, " - "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", - eventTime, deviceId, source, policyFlags, action, flags, - keyCode, scanCode, metaState, downTime); -#endif - if (! validateKeyEvent(action)) { - return; - } - - policyFlags |= POLICY_FLAG_TRUSTED; - mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags, - keyCode, scanCode, /*byref*/ policyFlags); - - bool needWake; - { // acquire lock - AutoMutex _l(mLock); - - int32_t repeatCount = 0; - KeyEntry* newEntry = mAllocator.obtainKeyEntry(eventTime, - deviceId, source, policyFlags, action, flags, keyCode, scanCode, - metaState, repeatCount, downTime); - - needWake = enqueueInboundEventLocked(newEntry); - } // release lock - - if (needWake) { - mLooper->wake(); - } -} - -void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source, - uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t edgeFlags, - uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, - float xPrecision, float yPrecision, nsecs_t downTime) { -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifyMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " - "action=0x%x, flags=0x%x, metaState=0x%x, edgeFlags=0x%x, " - "xPrecision=%f, yPrecision=%f, downTime=%lld", - eventTime, deviceId, source, policyFlags, action, flags, metaState, edgeFlags, - xPrecision, yPrecision, downTime); - for (uint32_t i = 0; i < pointerCount; i++) { - LOGD(" Pointer %d: id=%d, x=%f, y=%f, pressure=%f, size=%f, " - "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " - "orientation=%f", - i, pointerIds[i], pointerCoords[i].x, pointerCoords[i].y, - pointerCoords[i].pressure, pointerCoords[i].size, - pointerCoords[i].touchMajor, pointerCoords[i].touchMinor, - pointerCoords[i].toolMajor, pointerCoords[i].toolMinor, - pointerCoords[i].orientation); - } -#endif - if (! validateMotionEvent(action, pointerCount, pointerIds)) { - return; - } - - policyFlags |= POLICY_FLAG_TRUSTED; - mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags); - - bool needWake; - { // acquire lock - AutoMutex _l(mLock); - - // Attempt batching and streaming of move events. - if (action == AMOTION_EVENT_ACTION_MOVE) { - // BATCHING CASE - // - // Try to append a move sample to the tail of the inbound queue for this device. - // Give up if we encounter a non-move motion event for this device since that - // means we cannot append any new samples until a new motion event has started. - for (EventEntry* entry = mInboundQueue.tailSentinel.prev; - entry != & mInboundQueue.headSentinel; entry = entry->prev) { - if (entry->type != EventEntry::TYPE_MOTION) { - // Keep looking for motion events. - continue; - } - - MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); - if (motionEntry->deviceId != deviceId) { - // Keep looking for this device. - continue; - } - - if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE - || motionEntry->pointerCount != pointerCount - || motionEntry->isInjected()) { - // Last motion event in the queue for this device is not compatible for - // appending new samples. Stop here. - goto NoBatchingOrStreaming; - } - - // The last motion event is a move and is compatible for appending. - // Do the batching magic. - mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); -#if DEBUG_BATCHING - LOGD("Appended motion sample onto batch for most recent " - "motion event for this device in the inbound queue."); -#endif - return; // done! - } - - // STREAMING CASE - // - // There is no pending motion event (of any kind) for this device in the inbound queue. - // Search the outbound queue for the current foreground targets to find a dispatched - // motion event that is still in progress. If found, then, appen the new sample to - // that event and push it out to all current targets. The logic in - // prepareDispatchCycleLocked takes care of the case where some targets may - // already have consumed the motion event by starting a new dispatch cycle if needed. - if (mCurrentInputTargetsValid) { - for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { - const InputTarget& inputTarget = mCurrentInputTargets[i]; - if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) { - // Skip non-foreground targets. We only want to stream if there is at - // least one foreground target whose dispatch is still in progress. - continue; - } - - ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); - if (connectionIndex < 0) { - // Connection must no longer be valid. - continue; - } - - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - if (connection->outboundQueue.isEmpty()) { - // This foreground target has an empty outbound queue. - continue; - } - - DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next; - if (! dispatchEntry->inProgress - || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION - || dispatchEntry->isSplit()) { - // No motion event is being dispatched, or it is being split across - // windows in which case we cannot stream. - continue; - } - - MotionEntry* motionEntry = static_cast<MotionEntry*>( - dispatchEntry->eventEntry); - if (motionEntry->action != AMOTION_EVENT_ACTION_MOVE - || motionEntry->deviceId != deviceId - || motionEntry->pointerCount != pointerCount - || motionEntry->isInjected()) { - // The motion event is not compatible with this move. - continue; - } - - // Hurray! This foreground target is currently dispatching a move event - // that we can stream onto. Append the motion sample and resume dispatch. - mAllocator.appendMotionSample(motionEntry, eventTime, pointerCoords); -#if DEBUG_BATCHING - LOGD("Appended motion sample onto batch for most recently dispatched " - "motion event for this device in the outbound queues. " - "Attempting to stream the motion sample."); -#endif - nsecs_t currentTime = now(); - dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry, - true /*resumeWithAppendedMotionSample*/); - - runCommandsLockedInterruptible(); - return; // done! - } - } - -NoBatchingOrStreaming:; - } - - // Just enqueue a new motion event. - MotionEntry* newEntry = mAllocator.obtainMotionEntry(eventTime, - deviceId, source, policyFlags, action, flags, metaState, edgeFlags, - xPrecision, yPrecision, downTime, - pointerCount, pointerIds, pointerCoords); - - needWake = enqueueInboundEventLocked(newEntry); - } // release lock - - if (needWake) { - mLooper->wake(); - } -} - -void InputDispatcher::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, - uint32_t policyFlags) { -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifySwitch - switchCode=%d, switchValue=%d, policyFlags=0x%x", - switchCode, switchValue, policyFlags); -#endif - - policyFlags |= POLICY_FLAG_TRUSTED; - mPolicy->notifySwitch(when, switchCode, switchValue, policyFlags); -} - -int32_t InputDispatcher::injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) { -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("injectInputEvent - eventType=%d, injectorPid=%d, injectorUid=%d, " - "syncMode=%d, timeoutMillis=%d", - event->getType(), injectorPid, injectorUid, syncMode, timeoutMillis); -#endif - - nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); - - uint32_t policyFlags = POLICY_FLAG_INJECTED; - if (hasInjectionPermission(injectorPid, injectorUid)) { - policyFlags |= POLICY_FLAG_TRUSTED; - } - - EventEntry* injectedEntry; - switch (event->getType()) { - case AINPUT_EVENT_TYPE_KEY: { - const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event); - int32_t action = keyEvent->getAction(); - if (! validateKeyEvent(action)) { - return INPUT_EVENT_INJECTION_FAILED; - } - - nsecs_t eventTime = keyEvent->getEventTime(); - int32_t deviceId = keyEvent->getDeviceId(); - int32_t flags = keyEvent->getFlags(); - int32_t keyCode = keyEvent->getKeyCode(); - int32_t scanCode = keyEvent->getScanCode(); - mPolicy->interceptKeyBeforeQueueing(eventTime, deviceId, action, /*byref*/ flags, - keyCode, scanCode, /*byref*/ policyFlags); - - mLock.lock(); - injectedEntry = mAllocator.obtainKeyEntry(eventTime, deviceId, keyEvent->getSource(), - policyFlags, action, flags, keyCode, scanCode, keyEvent->getMetaState(), - keyEvent->getRepeatCount(), keyEvent->getDownTime()); - break; - } - - case AINPUT_EVENT_TYPE_MOTION: { - const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event); - int32_t action = motionEvent->getAction(); - size_t pointerCount = motionEvent->getPointerCount(); - const int32_t* pointerIds = motionEvent->getPointerIds(); - if (! validateMotionEvent(action, pointerCount, pointerIds)) { - return INPUT_EVENT_INJECTION_FAILED; - } - - nsecs_t eventTime = motionEvent->getEventTime(); - mPolicy->interceptGenericBeforeQueueing(eventTime, /*byref*/ policyFlags); - - mLock.lock(); - const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); - const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); - MotionEntry* motionEntry = mAllocator.obtainMotionEntry(*sampleEventTimes, - motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags, - action, motionEvent->getFlags(), - motionEvent->getMetaState(), motionEvent->getEdgeFlags(), - motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), uint32_t(pointerCount), - pointerIds, samplePointerCoords); - for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { - sampleEventTimes += 1; - samplePointerCoords += pointerCount; - mAllocator.appendMotionSample(motionEntry, *sampleEventTimes, samplePointerCoords); - } - injectedEntry = motionEntry; - break; - } - - default: - LOGW("Cannot inject event of type %d", event->getType()); - return INPUT_EVENT_INJECTION_FAILED; - } - - InjectionState* injectionState = mAllocator.obtainInjectionState(injectorPid, injectorUid); - if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { - injectionState->injectionIsAsync = true; - } - - injectionState->refCount += 1; - injectedEntry->injectionState = injectionState; - - bool needWake = enqueueInboundEventLocked(injectedEntry); - mLock.unlock(); - - if (needWake) { - mLooper->wake(); - } - - int32_t injectionResult; - { // acquire lock - AutoMutex _l(mLock); - - if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) { - injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; - } else { - for (;;) { - injectionResult = injectionState->injectionResult; - if (injectionResult != INPUT_EVENT_INJECTION_PENDING) { - break; - } - - nsecs_t remainingTimeout = endTime - now(); - if (remainingTimeout <= 0) { -#if DEBUG_INJECTION - LOGD("injectInputEvent - Timed out waiting for injection result " - "to become available."); -#endif - injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; - break; - } - - mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout); - } - - if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED - && syncMode == INPUT_EVENT_INJECTION_SYNC_WAIT_FOR_FINISHED) { - while (injectionState->pendingForegroundDispatches != 0) { -#if DEBUG_INJECTION - LOGD("injectInputEvent - Waiting for %d pending foreground dispatches.", - injectionState->pendingForegroundDispatches); -#endif - nsecs_t remainingTimeout = endTime - now(); - if (remainingTimeout <= 0) { -#if DEBUG_INJECTION - LOGD("injectInputEvent - Timed out waiting for pending foreground " - "dispatches to finish."); -#endif - injectionResult = INPUT_EVENT_INJECTION_TIMED_OUT; - break; - } - - mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout); - } - } - } - - mAllocator.releaseInjectionState(injectionState); - } // release lock - -#if DEBUG_INJECTION - LOGD("injectInputEvent - Finished with result %d. " - "injectorPid=%d, injectorUid=%d", - injectionResult, injectorPid, injectorUid); -#endif - - return injectionResult; -} - -bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t injectorUid) { - return injectorUid == 0 - || mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); -} - -void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) { - InjectionState* injectionState = entry->injectionState; - if (injectionState) { -#if DEBUG_INJECTION - LOGD("Setting input event injection result to %d. " - "injectorPid=%d, injectorUid=%d", - injectionResult, injectionState->injectorPid, injectionState->injectorUid); -#endif - - if (injectionState->injectionIsAsync) { - // Log the outcome since the injector did not wait for the injection result. - switch (injectionResult) { - case INPUT_EVENT_INJECTION_SUCCEEDED: - LOGV("Asynchronous input event injection succeeded."); - break; - case INPUT_EVENT_INJECTION_FAILED: - LOGW("Asynchronous input event injection failed."); - break; - case INPUT_EVENT_INJECTION_PERMISSION_DENIED: - LOGW("Asynchronous input event injection permission denied."); - break; - case INPUT_EVENT_INJECTION_TIMED_OUT: - LOGW("Asynchronous input event injection timed out."); - break; - } - } - - injectionState->injectionResult = injectionResult; - mInjectionResultAvailableCondition.broadcast(); - } -} - -void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; - if (injectionState) { - injectionState->pendingForegroundDispatches += 1; - } -} - -void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) { - InjectionState* injectionState = entry->injectionState; - if (injectionState) { - injectionState->pendingForegroundDispatches -= 1; - - if (injectionState->pendingForegroundDispatches == 0) { - mInjectionSyncFinishedCondition.broadcast(); - } - } -} - -const InputWindow* InputDispatcher::getWindowLocked(const sp<InputChannel>& inputChannel) { - for (size_t i = 0; i < mWindows.size(); i++) { - const InputWindow* window = & mWindows[i]; - if (window->inputChannel == inputChannel) { - return window; - } - } - return NULL; -} - -void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { -#if DEBUG_FOCUS - LOGD("setInputWindows"); -#endif - { // acquire lock - AutoMutex _l(mLock); - - // Clear old window pointers. - sp<InputChannel> oldFocusedWindowChannel; - if (mFocusedWindow) { - oldFocusedWindowChannel = mFocusedWindow->inputChannel; - mFocusedWindow = NULL; - } - - mWindows.clear(); - - // Loop over new windows and rebuild the necessary window pointers for - // tracking focus and touch. - mWindows.appendVector(inputWindows); - - size_t numWindows = mWindows.size(); - for (size_t i = 0; i < numWindows; i++) { - const InputWindow* window = & mWindows.itemAt(i); - if (window->hasFocus) { - mFocusedWindow = window; - break; - } - } - - if (oldFocusedWindowChannel != NULL) { - if (!mFocusedWindow || oldFocusedWindowChannel != mFocusedWindow->inputChannel) { -#if DEBUG_FOCUS - LOGD("Focus left window: %s", - oldFocusedWindowChannel->getName().string()); -#endif - synthesizeCancelationEventsForInputChannelLocked(oldFocusedWindowChannel, - InputState::CANCEL_NON_POINTER_EVENTS, "focus left window"); - oldFocusedWindowChannel.clear(); - } - } - if (mFocusedWindow && oldFocusedWindowChannel == NULL) { -#if DEBUG_FOCUS - LOGD("Focus entered window: %s", - mFocusedWindow->inputChannel->getName().string()); -#endif - } - - for (size_t i = 0; i < mTouchState.windows.size(); ) { - TouchedWindow& touchedWindow = mTouchState.windows.editItemAt(i); - const InputWindow* window = getWindowLocked(touchedWindow.channel); - if (window) { - touchedWindow.window = window; - i += 1; - } else { -#if DEBUG_FOCUS - LOGD("Touched window was removed: %s", touchedWindow.channel->getName().string()); -#endif - synthesizeCancelationEventsForInputChannelLocked(touchedWindow.channel, - InputState::CANCEL_POINTER_EVENTS, "touched window was removed"); - mTouchState.windows.removeAt(i); - } - } - -#if DEBUG_FOCUS - //logDispatchStateLocked(); -#endif - } // release lock - - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); -} - -void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) { -#if DEBUG_FOCUS - LOGD("setFocusedApplication"); -#endif - { // acquire lock - AutoMutex _l(mLock); - - releaseFocusedApplicationLocked(); - - if (inputApplication) { - mFocusedApplicationStorage = *inputApplication; - mFocusedApplication = & mFocusedApplicationStorage; - } - -#if DEBUG_FOCUS - //logDispatchStateLocked(); -#endif - } // release lock - - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); -} - -void InputDispatcher::releaseFocusedApplicationLocked() { - if (mFocusedApplication) { - mFocusedApplication = NULL; - mFocusedApplicationStorage.handle.clear(); - } -} - -void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { -#if DEBUG_FOCUS - LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); -#endif - - bool changed; - { // acquire lock - AutoMutex _l(mLock); - - if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { - if (mDispatchFrozen && !frozen) { - resetANRTimeoutsLocked(); - } - - if (mDispatchEnabled && !enabled) { - resetAndDropEverythingLocked("dispatcher is being disabled"); - } - - mDispatchEnabled = enabled; - mDispatchFrozen = frozen; - changed = true; - } else { - changed = false; - } - -#if DEBUG_FOCUS - //logDispatchStateLocked(); -#endif - } // release lock - - if (changed) { - // Wake up poll loop since it may need to make new input dispatching choices. - mLooper->wake(); - } -} - -void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { -#if DEBUG_FOCUS - LOGD("Resetting and dropping all events (%s).", reason); -#endif - - synthesizeCancelationEventsForAllConnectionsLocked(InputState::CANCEL_ALL_EVENTS, reason); - - resetKeyRepeatLocked(); - releasePendingEventLocked(); - drainInboundQueueLocked(); - resetTargetsLocked(); - - mTouchState.reset(); -} - -void InputDispatcher::logDispatchStateLocked() { - String8 dump; - dumpDispatchStateLocked(dump); - - char* text = dump.lockBuffer(dump.size()); - char* start = text; - while (*start != '\0') { - char* end = strchr(start, '\n'); - if (*end == '\n') { - *(end++) = '\0'; - } - LOGD("%s", start); - start = end; - } -} - -void InputDispatcher::dumpDispatchStateLocked(String8& dump) { - dump.appendFormat(INDENT "DispatchEnabled: %d\n", mDispatchEnabled); - dump.appendFormat(INDENT "DispatchFrozen: %d\n", mDispatchFrozen); - - if (mFocusedApplication) { - dump.appendFormat(INDENT "FocusedApplication: name='%s', dispatchingTimeout=%0.3fms\n", - mFocusedApplication->name.string(), - mFocusedApplication->dispatchingTimeout / 1000000.0); - } else { - dump.append(INDENT "FocusedApplication: <null>\n"); - } - dump.appendFormat(INDENT "FocusedWindow: name='%s'\n", - mFocusedWindow != NULL ? mFocusedWindow->name.string() : "<null>"); - - dump.appendFormat(INDENT "TouchDown: %s\n", toString(mTouchState.down)); - dump.appendFormat(INDENT "TouchSplit: %s\n", toString(mTouchState.split)); - if (!mTouchState.windows.isEmpty()) { - dump.append(INDENT "TouchedWindows:\n"); - for (size_t i = 0; i < mTouchState.windows.size(); i++) { - const TouchedWindow& touchedWindow = mTouchState.windows[i]; - dump.appendFormat(INDENT2 "%d: name='%s', pointerIds=0x%0x, targetFlags=0x%x\n", - i, touchedWindow.window->name.string(), touchedWindow.pointerIds.value, - touchedWindow.targetFlags); - } - } else { - dump.append(INDENT "TouchedWindows: <none>\n"); - } - - if (!mWindows.isEmpty()) { - dump.append(INDENT "Windows:\n"); - for (size_t i = 0; i < mWindows.size(); i++) { - const InputWindow& window = mWindows[i]; - dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, " - "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, " - "frame=[%d,%d][%d,%d], " - "visibleFrame=[%d,%d][%d,%d], " - "touchableArea=[%d,%d][%d,%d], " - "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", - i, window.name.string(), - toString(window.paused), - toString(window.hasFocus), - toString(window.hasWallpaper), - toString(window.visible), - toString(window.canReceiveKeys), - window.layoutParamsFlags, window.layoutParamsType, - window.layer, - window.frameLeft, window.frameTop, - window.frameRight, window.frameBottom, - window.visibleFrameLeft, window.visibleFrameTop, - window.visibleFrameRight, window.visibleFrameBottom, - window.touchableAreaLeft, window.touchableAreaTop, - window.touchableAreaRight, window.touchableAreaBottom, - window.ownerPid, window.ownerUid, - window.dispatchingTimeout / 1000000.0); - } - } else { - dump.append(INDENT "Windows: <none>\n"); - } - - if (!mMonitoringChannels.isEmpty()) { - dump.append(INDENT "MonitoringChannels:\n"); - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - const sp<InputChannel>& channel = mMonitoringChannels[i]; - dump.appendFormat(INDENT2 "%d: '%s'\n", i, channel->getName().string()); - } - } else { - dump.append(INDENT "MonitoringChannels: <none>\n"); - } - - dump.appendFormat(INDENT "InboundQueue: length=%u\n", mInboundQueue.count()); - - if (!mActiveConnections.isEmpty()) { - dump.append(INDENT "ActiveConnections:\n"); - for (size_t i = 0; i < mActiveConnections.size(); i++) { - const Connection* connection = mActiveConnections[i]; - dump.appendFormat(INDENT2 "%d: '%s', status=%s, outboundQueueLength=%u" - "inputState.isNeutral=%s\n", - i, connection->getInputChannelName(), connection->getStatusLabel(), - connection->outboundQueue.count(), - toString(connection->inputState.isNeutral())); - } - } else { - dump.append(INDENT "ActiveConnections: <none>\n"); - } - - if (isAppSwitchPendingLocked()) { - dump.appendFormat(INDENT "AppSwitch: pending, due in %01.1fms\n", - (mAppSwitchDueTime - now()) / 1000000.0); - } else { - dump.append(INDENT "AppSwitch: not pending\n"); - } -} - -status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) { -#if DEBUG_REGISTRATION - LOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(), - toString(monitor)); -#endif - - { // acquire lock - AutoMutex _l(mLock); - - if (getConnectionIndexLocked(inputChannel) >= 0) { - LOGW("Attempted to register already registered input channel '%s'", - inputChannel->getName().string()); - return BAD_VALUE; - } - - sp<Connection> connection = new Connection(inputChannel); - status_t status = connection->initialize(); - if (status) { - LOGE("Failed to initialize input publisher for input channel '%s', status=%d", - inputChannel->getName().string(), status); - return status; - } - - int32_t receiveFd = inputChannel->getReceivePipeFd(); - mConnectionsByReceiveFd.add(receiveFd, connection); - - if (monitor) { - mMonitoringChannels.push(inputChannel); - } - - mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); - - runCommandsLockedInterruptible(); - } // release lock - return OK; -} - -status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) { -#if DEBUG_REGISTRATION - LOGD("channel '%s' ~ unregisterInputChannel", inputChannel->getName().string()); -#endif - - { // acquire lock - AutoMutex _l(mLock); - - ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); - if (connectionIndex < 0) { - LOGW("Attempted to unregister already unregistered input channel '%s'", - inputChannel->getName().string()); - return BAD_VALUE; - } - - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - mConnectionsByReceiveFd.removeItemsAt(connectionIndex); - - connection->status = Connection::STATUS_ZOMBIE; - - for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - if (mMonitoringChannels[i] == inputChannel) { - mMonitoringChannels.removeAt(i); - break; - } - } - - mLooper->removeFd(inputChannel->getReceivePipeFd()); - - nsecs_t currentTime = now(); - abortBrokenDispatchCycleLocked(currentTime, connection); - - runCommandsLockedInterruptible(); - } // release lock - - // Wake the poll loop because removing the connection may have changed the current - // synchronization state. - mLooper->wake(); - return OK; -} - -ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) { - ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd()); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - if (connection->inputChannel.get() == inputChannel.get()) { - return connectionIndex; - } - } - - return -1; -} - -void InputDispatcher::activateConnectionLocked(Connection* connection) { - for (size_t i = 0; i < mActiveConnections.size(); i++) { - if (mActiveConnections.itemAt(i) == connection) { - return; - } - } - mActiveConnections.add(connection); -} - -void InputDispatcher::deactivateConnectionLocked(Connection* connection) { - for (size_t i = 0; i < mActiveConnections.size(); i++) { - if (mActiveConnections.itemAt(i) == connection) { - mActiveConnections.removeAt(i); - return; - } - } -} - -void InputDispatcher::onDispatchCycleStartedLocked( - nsecs_t currentTime, const sp<Connection>& connection) { -} - -void InputDispatcher::onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp<Connection>& connection) { -} - -void InputDispatcher::onDispatchCycleBrokenLocked( - nsecs_t currentTime, const sp<Connection>& connection) { - LOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", - connection->getInputChannelName()); - - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); - commandEntry->connection = connection; -} - -void InputDispatcher::onANRLocked( - nsecs_t currentTime, const InputApplication* application, const InputWindow* window, - nsecs_t eventTime, nsecs_t waitStartTime) { - LOGI("Application is not responding: %s. " - "%01.1fms since event, %01.1fms since wait started", - getApplicationWindowLabelLocked(application, window).string(), - (currentTime - eventTime) / 1000000.0, - (currentTime - waitStartTime) / 1000000.0); - - CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyANRLockedInterruptible); - if (application) { - commandEntry->inputApplicationHandle = application->handle; - } - if (window) { - commandEntry->inputChannel = window->inputChannel; - } -} - -void InputDispatcher::doNotifyConfigurationChangedInterruptible( - CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->notifyConfigurationChanged(commandEntry->eventTime); - - mLock.lock(); -} - -void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( - CommandEntry* commandEntry) { - sp<Connection> connection = commandEntry->connection; - - if (connection->status != Connection::STATUS_ZOMBIE) { - mLock.unlock(); - - mPolicy->notifyInputChannelBroken(connection->inputChannel); - - mLock.lock(); - } -} - -void InputDispatcher::doNotifyANRLockedInterruptible( - CommandEntry* commandEntry) { - mLock.unlock(); - - nsecs_t newTimeout = mPolicy->notifyANR( - commandEntry->inputApplicationHandle, commandEntry->inputChannel); - - mLock.lock(); - - resumeAfterTargetsNotReadyTimeoutLocked(newTimeout, commandEntry->inputChannel); -} - -void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( - CommandEntry* commandEntry) { - KeyEntry* entry = commandEntry->keyEntry; - mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags, - entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, - entry->downTime, entry->eventTime); - - mLock.unlock(); - - bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel, - & mReusableKeyEvent, entry->policyFlags); - - mLock.lock(); - - entry->interceptKeyResult = consumed - ? KeyEntry::INTERCEPT_KEY_RESULT_SKIP - : KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; - mAllocator.releaseKeyEntry(entry); -} - -void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) { - mLock.unlock(); - - mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->userActivityEventType); - - mLock.lock(); -} - -void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry, - int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) { - // TODO Write some statistics about how long we spend waiting. -} - -void InputDispatcher::dump(String8& dump) { - dump.append("Input Dispatcher State:\n"); - dumpDispatchStateLocked(dump); -} - - -// --- InputDispatcher::Queue --- - -template <typename T> -uint32_t InputDispatcher::Queue<T>::count() const { - uint32_t result = 0; - for (const T* entry = headSentinel.next; entry != & tailSentinel; entry = entry->next) { - result += 1; - } - return result; -} - - -// --- InputDispatcher::Allocator --- - -InputDispatcher::Allocator::Allocator() { -} - -InputDispatcher::InjectionState* -InputDispatcher::Allocator::obtainInjectionState(int32_t injectorPid, int32_t injectorUid) { - InjectionState* injectionState = mInjectionStatePool.alloc(); - injectionState->refCount = 1; - injectionState->injectorPid = injectorPid; - injectionState->injectorUid = injectorUid; - injectionState->injectionIsAsync = false; - injectionState->injectionResult = INPUT_EVENT_INJECTION_PENDING; - injectionState->pendingForegroundDispatches = 0; - return injectionState; -} - -void InputDispatcher::Allocator::initializeEventEntry(EventEntry* entry, int32_t type, - nsecs_t eventTime, uint32_t policyFlags) { - entry->type = type; - entry->refCount = 1; - entry->dispatchInProgress = false; - entry->eventTime = eventTime; - entry->policyFlags = policyFlags; - entry->injectionState = NULL; -} - -void InputDispatcher::Allocator::releaseEventEntryInjectionState(EventEntry* entry) { - if (entry->injectionState) { - releaseInjectionState(entry->injectionState); - entry->injectionState = NULL; - } -} - -InputDispatcher::ConfigurationChangedEntry* -InputDispatcher::Allocator::obtainConfigurationChangedEntry(nsecs_t eventTime) { - ConfigurationChangedEntry* entry = mConfigurationChangeEntryPool.alloc(); - initializeEventEntry(entry, EventEntry::TYPE_CONFIGURATION_CHANGED, eventTime, 0); - return entry; -} - -InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t eventTime, - int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, - int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, - int32_t repeatCount, nsecs_t downTime) { - KeyEntry* entry = mKeyEntryPool.alloc(); - initializeEventEntry(entry, EventEntry::TYPE_KEY, eventTime, policyFlags); - - entry->deviceId = deviceId; - entry->source = source; - entry->action = action; - entry->flags = flags; - entry->keyCode = keyCode; - entry->scanCode = scanCode; - entry->metaState = metaState; - entry->repeatCount = repeatCount; - entry->downTime = downTime; - entry->syntheticRepeat = false; - entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; - return entry; -} - -InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsecs_t eventTime, - int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags, - int32_t metaState, int32_t edgeFlags, float xPrecision, float yPrecision, - nsecs_t downTime, uint32_t pointerCount, - const int32_t* pointerIds, const PointerCoords* pointerCoords) { - MotionEntry* entry = mMotionEntryPool.alloc(); - initializeEventEntry(entry, EventEntry::TYPE_MOTION, eventTime, policyFlags); - - entry->eventTime = eventTime; - entry->deviceId = deviceId; - entry->source = source; - entry->action = action; - entry->flags = flags; - entry->metaState = metaState; - entry->edgeFlags = edgeFlags; - entry->xPrecision = xPrecision; - entry->yPrecision = yPrecision; - entry->downTime = downTime; - entry->pointerCount = pointerCount; - entry->firstSample.eventTime = eventTime; - entry->firstSample.next = NULL; - entry->lastSample = & entry->firstSample; - for (uint32_t i = 0; i < pointerCount; i++) { - entry->pointerIds[i] = pointerIds[i]; - entry->firstSample.pointerCoords[i] = pointerCoords[i]; - } - return entry; -} - -InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry( - EventEntry* eventEntry, - int32_t targetFlags, float xOffset, float yOffset) { - DispatchEntry* entry = mDispatchEntryPool.alloc(); - entry->eventEntry = eventEntry; - eventEntry->refCount += 1; - entry->targetFlags = targetFlags; - entry->xOffset = xOffset; - entry->yOffset = yOffset; - entry->inProgress = false; - entry->headMotionSample = NULL; - entry->tailMotionSample = NULL; - return entry; -} - -InputDispatcher::CommandEntry* InputDispatcher::Allocator::obtainCommandEntry(Command command) { - CommandEntry* entry = mCommandEntryPool.alloc(); - entry->command = command; - return entry; -} - -void InputDispatcher::Allocator::releaseInjectionState(InjectionState* injectionState) { - injectionState->refCount -= 1; - if (injectionState->refCount == 0) { - mInjectionStatePool.free(injectionState); - } else { - assert(injectionState->refCount > 0); - } -} - -void InputDispatcher::Allocator::releaseEventEntry(EventEntry* entry) { - switch (entry->type) { - case EventEntry::TYPE_CONFIGURATION_CHANGED: - releaseConfigurationChangedEntry(static_cast<ConfigurationChangedEntry*>(entry)); - break; - case EventEntry::TYPE_KEY: - releaseKeyEntry(static_cast<KeyEntry*>(entry)); - break; - case EventEntry::TYPE_MOTION: - releaseMotionEntry(static_cast<MotionEntry*>(entry)); - break; - default: - assert(false); - break; - } -} - -void InputDispatcher::Allocator::releaseConfigurationChangedEntry( - ConfigurationChangedEntry* entry) { - entry->refCount -= 1; - if (entry->refCount == 0) { - releaseEventEntryInjectionState(entry); - mConfigurationChangeEntryPool.free(entry); - } else { - assert(entry->refCount > 0); - } -} - -void InputDispatcher::Allocator::releaseKeyEntry(KeyEntry* entry) { - entry->refCount -= 1; - if (entry->refCount == 0) { - releaseEventEntryInjectionState(entry); - mKeyEntryPool.free(entry); - } else { - assert(entry->refCount > 0); - } -} - -void InputDispatcher::Allocator::releaseMotionEntry(MotionEntry* entry) { - entry->refCount -= 1; - if (entry->refCount == 0) { - releaseEventEntryInjectionState(entry); - for (MotionSample* sample = entry->firstSample.next; sample != NULL; ) { - MotionSample* next = sample->next; - mMotionSamplePool.free(sample); - sample = next; - } - mMotionEntryPool.free(entry); - } else { - assert(entry->refCount > 0); - } -} - -void InputDispatcher::Allocator::releaseDispatchEntry(DispatchEntry* entry) { - releaseEventEntry(entry->eventEntry); - mDispatchEntryPool.free(entry); -} - -void InputDispatcher::Allocator::releaseCommandEntry(CommandEntry* entry) { - mCommandEntryPool.free(entry); -} - -void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, - nsecs_t eventTime, const PointerCoords* pointerCoords) { - MotionSample* sample = mMotionSamplePool.alloc(); - sample->eventTime = eventTime; - uint32_t pointerCount = motionEntry->pointerCount; - for (uint32_t i = 0; i < pointerCount; i++) { - sample->pointerCoords[i] = pointerCoords[i]; - } - - sample->next = NULL; - motionEntry->lastSample->next = sample; - motionEntry->lastSample = sample; -} - -void InputDispatcher::Allocator::recycleKeyEntry(KeyEntry* keyEntry) { - releaseEventEntryInjectionState(keyEntry); - - keyEntry->dispatchInProgress = false; - keyEntry->syntheticRepeat = false; - keyEntry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; -} - - -// --- InputDispatcher::MotionEntry --- - -uint32_t InputDispatcher::MotionEntry::countSamples() const { - uint32_t count = 1; - for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) { - count += 1; - } - return count; -} - - -// --- InputDispatcher::InputState --- - -InputDispatcher::InputState::InputState() { -} - -InputDispatcher::InputState::~InputState() { -} - -bool InputDispatcher::InputState::isNeutral() const { - return mKeyMementos.isEmpty() && mMotionMementos.isEmpty(); -} - -InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackEvent( - const EventEntry* entry) { - switch (entry->type) { - case EventEntry::TYPE_KEY: - return trackKey(static_cast<const KeyEntry*>(entry)); - - case EventEntry::TYPE_MOTION: - return trackMotion(static_cast<const MotionEntry*>(entry)); - - default: - return CONSISTENT; - } -} - -InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey( - const KeyEntry* entry) { - int32_t action = entry->action; - for (size_t i = 0; i < mKeyMementos.size(); i++) { - KeyMemento& memento = mKeyMementos.editItemAt(i); - if (memento.deviceId == entry->deviceId - && memento.source == entry->source - && memento.keyCode == entry->keyCode - && memento.scanCode == entry->scanCode) { - switch (action) { - case AKEY_EVENT_ACTION_UP: - mKeyMementos.removeAt(i); - return CONSISTENT; - - case AKEY_EVENT_ACTION_DOWN: - return TOLERABLE; - - default: - return BROKEN; - } - } - } - - switch (action) { - case AKEY_EVENT_ACTION_DOWN: { - mKeyMementos.push(); - KeyMemento& memento = mKeyMementos.editTop(); - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.keyCode = entry->keyCode; - memento.scanCode = entry->scanCode; - memento.downTime = entry->downTime; - return CONSISTENT; - } - - default: - return BROKEN; - } -} - -InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotion( - const MotionEntry* entry) { - int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK; - for (size_t i = 0; i < mMotionMementos.size(); i++) { - MotionMemento& memento = mMotionMementos.editItemAt(i); - if (memento.deviceId == entry->deviceId - && memento.source == entry->source) { - switch (action) { - case AMOTION_EVENT_ACTION_UP: - case AMOTION_EVENT_ACTION_CANCEL: - mMotionMementos.removeAt(i); - return CONSISTENT; - - case AMOTION_EVENT_ACTION_DOWN: - return TOLERABLE; - - case AMOTION_EVENT_ACTION_POINTER_DOWN: - if (entry->pointerCount == memento.pointerCount + 1) { - memento.setPointers(entry); - return CONSISTENT; - } - return BROKEN; - - case AMOTION_EVENT_ACTION_POINTER_UP: - if (entry->pointerCount == memento.pointerCount - 1) { - memento.setPointers(entry); - return CONSISTENT; - } - return BROKEN; - - case AMOTION_EVENT_ACTION_MOVE: - if (entry->pointerCount == memento.pointerCount) { - return CONSISTENT; - } - return BROKEN; - - default: - return BROKEN; - } - } - } - - switch (action) { - case AMOTION_EVENT_ACTION_DOWN: { - mMotionMementos.push(); - MotionMemento& memento = mMotionMementos.editTop(); - memento.deviceId = entry->deviceId; - memento.source = entry->source; - memento.xPrecision = entry->xPrecision; - memento.yPrecision = entry->yPrecision; - memento.downTime = entry->downTime; - memento.setPointers(entry); - return CONSISTENT; - } - - default: - return BROKEN; - } -} - -void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) { - pointerCount = entry->pointerCount; - for (uint32_t i = 0; i < entry->pointerCount; i++) { - pointerIds[i] = entry->pointerIds[i]; - pointerCoords[i] = entry->lastSample->pointerCoords[i]; - } -} - -void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime, - Allocator* allocator, Vector<EventEntry*>& outEvents, - CancelationOptions options) { - for (size_t i = 0; i < mKeyMementos.size(); ) { - const KeyMemento& memento = mKeyMementos.itemAt(i); - if (shouldCancelEvent(memento.source, options)) { - outEvents.push(allocator->obtainKeyEntry(currentTime, - memento.deviceId, memento.source, 0, - AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED, - memento.keyCode, memento.scanCode, 0, 0, memento.downTime)); - mKeyMementos.removeAt(i); - } else { - i += 1; - } - } - - for (size_t i = 0; i < mMotionMementos.size(); ) { - const MotionMemento& memento = mMotionMementos.itemAt(i); - if (shouldCancelEvent(memento.source, options)) { - outEvents.push(allocator->obtainMotionEntry(currentTime, - memento.deviceId, memento.source, 0, - AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0, - memento.xPrecision, memento.yPrecision, memento.downTime, - memento.pointerCount, memento.pointerIds, memento.pointerCoords)); - mMotionMementos.removeAt(i); - } else { - i += 1; - } - } -} - -void InputDispatcher::InputState::clear() { - mKeyMementos.clear(); - mMotionMementos.clear(); -} - -bool InputDispatcher::InputState::shouldCancelEvent(int32_t eventSource, - CancelationOptions options) { - switch (options) { - case CANCEL_POINTER_EVENTS: - return eventSource & AINPUT_SOURCE_CLASS_POINTER; - case CANCEL_NON_POINTER_EVENTS: - return !(eventSource & AINPUT_SOURCE_CLASS_POINTER); - default: - return true; - } -} - - -// --- InputDispatcher::Connection --- - -InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : - status(STATUS_NORMAL), inputChannel(inputChannel), inputPublisher(inputChannel), - lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) { -} - -InputDispatcher::Connection::~Connection() { -} - -status_t InputDispatcher::Connection::initialize() { - return inputPublisher.initialize(); -} - -const char* InputDispatcher::Connection::getStatusLabel() const { - switch (status) { - case STATUS_NORMAL: - return "NORMAL"; - - case STATUS_BROKEN: - return "BROKEN"; - - case STATUS_ZOMBIE: - return "ZOMBIE"; - - default: - return "UNKNOWN"; - } -} - -InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent( - const EventEntry* eventEntry) const { - for (DispatchEntry* dispatchEntry = outboundQueue.tailSentinel.prev; - dispatchEntry != & outboundQueue.headSentinel; dispatchEntry = dispatchEntry->prev) { - if (dispatchEntry->eventEntry == eventEntry) { - return dispatchEntry; - } - } - return NULL; -} - - -// --- InputDispatcher::CommandEntry --- - -InputDispatcher::CommandEntry::CommandEntry() : - keyEntry(NULL) { -} - -InputDispatcher::CommandEntry::~CommandEntry() { -} - - -// --- InputDispatcher::TouchState --- - -InputDispatcher::TouchState::TouchState() : - down(false), split(false) { -} - -InputDispatcher::TouchState::~TouchState() { -} - -void InputDispatcher::TouchState::reset() { - down = false; - split = false; - windows.clear(); -} - -void InputDispatcher::TouchState::copyFrom(const TouchState& other) { - down = other.down; - split = other.split; - windows.clear(); - windows.appendVector(other.windows); -} - -void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window, - int32_t targetFlags, BitSet32 pointerIds) { - if (targetFlags & InputTarget::FLAG_SPLIT) { - split = true; - } - - for (size_t i = 0; i < windows.size(); i++) { - TouchedWindow& touchedWindow = windows.editItemAt(i); - if (touchedWindow.window == window) { - touchedWindow.targetFlags |= targetFlags; - touchedWindow.pointerIds.value |= pointerIds.value; - return; - } - } - - windows.push(); - - TouchedWindow& touchedWindow = windows.editTop(); - touchedWindow.window = window; - touchedWindow.targetFlags = targetFlags; - touchedWindow.pointerIds = pointerIds; - touchedWindow.channel = window->inputChannel; -} - -void InputDispatcher::TouchState::removeOutsideTouchWindows() { - for (size_t i = 0 ; i < windows.size(); ) { - if (windows[i].targetFlags & InputTarget::FLAG_OUTSIDE) { - windows.removeAt(i); - } else { - i += 1; - } - } -} - -const InputWindow* InputDispatcher::TouchState::getFirstForegroundWindow() { - for (size_t i = 0; i < windows.size(); i++) { - if (windows[i].targetFlags & InputTarget::FLAG_FOREGROUND) { - return windows[i].window; - } - } - return NULL; -} - - -// --- InputDispatcherThread --- - -InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher) : - Thread(/*canCallJava*/ true), mDispatcher(dispatcher) { -} - -InputDispatcherThread::~InputDispatcherThread() { -} - -bool InputDispatcherThread::threadLoop() { - mDispatcher->dispatchOnce(); - return true; -} - -} // namespace android diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp deleted file mode 100644 index 09fce38d9c18..000000000000 --- a/libs/ui/InputManager.cpp +++ /dev/null @@ -1,83 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// -// The input manager. -// -#define LOG_TAG "InputManager" - -//#define LOG_NDEBUG 0 - -#include <cutils/log.h> -#include <ui/InputManager.h> -#include <ui/InputReader.h> -#include <ui/InputDispatcher.h> - -namespace android { - -InputManager::InputManager( - const sp<EventHubInterface>& eventHub, - const sp<InputReaderPolicyInterface>& readerPolicy, - const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) { - mDispatcher = new InputDispatcher(dispatcherPolicy); - mReader = new InputReader(eventHub, readerPolicy, mDispatcher); - initialize(); -} - -InputManager::InputManager( - const sp<InputReaderInterface>& reader, - const sp<InputDispatcherInterface>& dispatcher) : - mReader(reader), - mDispatcher(dispatcher) { - initialize(); -} - -InputManager::~InputManager() { - stop(); -} - -void InputManager::initialize() { - mReaderThread = new InputReaderThread(mReader); - mDispatcherThread = new InputDispatcherThread(mDispatcher); -} - -status_t InputManager::start() { - status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); - if (result) { - LOGE("Could not start InputDispatcher thread due to error %d.", result); - return result; - } - - result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); - if (result) { - LOGE("Could not start InputReader thread due to error %d.", result); - - mDispatcherThread->requestExit(); - return result; - } - - return OK; -} - -status_t InputManager::stop() { - status_t result = mReaderThread->requestExitAndWait(); - if (result) { - LOGW("Could not stop InputReader thread due to error %d.", result); - } - - result = mDispatcherThread->requestExitAndWait(); - if (result) { - LOGW("Could not stop InputDispatcher thread due to error %d.", result); - } - - return OK; -} - -sp<InputReaderInterface> InputManager::getReader() { - return mReader; -} - -sp<InputDispatcherInterface> InputManager::getDispatcher() { - return mDispatcher; -} - -} // namespace android diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp deleted file mode 100644 index 34e44e4c8281..000000000000 --- a/libs/ui/InputReader.cpp +++ /dev/null @@ -1,3528 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// -// The input reader. -// -#define LOG_TAG "InputReader" - -//#define LOG_NDEBUG 0 - -// Log debug messages for each raw event received from the EventHub. -#define DEBUG_RAW_EVENTS 0 - -// Log debug messages about touch screen filtering hacks. -#define DEBUG_HACKS 0 - -// Log debug messages about virtual key processing. -#define DEBUG_VIRTUAL_KEYS 0 - -// Log debug messages about pointers. -#define DEBUG_POINTERS 0 - -// Log debug messages about pointer assignment calculations. -#define DEBUG_POINTER_ASSIGNMENT 0 - -#include <cutils/log.h> -#include <ui/InputReader.h> - -#include <stddef.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <limits.h> -#include <math.h> - -#define INDENT " " -#define INDENT2 " " -#define INDENT3 " " -#define INDENT4 " " - -namespace android { - -// --- Static Functions --- - -template<typename T> -inline static T abs(const T& value) { - return value < 0 ? - value : value; -} - -template<typename T> -inline static T min(const T& a, const T& b) { - return a < b ? a : b; -} - -template<typename T> -inline static void swap(T& a, T& b) { - T temp = a; - a = b; - b = temp; -} - -inline static float avg(float x, float y) { - return (x + y) / 2; -} - -inline static float pythag(float x, float y) { - return sqrtf(x * x + y * y); -} - -static inline const char* toString(bool value) { - return value ? "true" : "false"; -} - - -int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { - int32_t mask; - switch (keyCode) { - case AKEYCODE_ALT_LEFT: - mask = AMETA_ALT_LEFT_ON; - break; - case AKEYCODE_ALT_RIGHT: - mask = AMETA_ALT_RIGHT_ON; - break; - case AKEYCODE_SHIFT_LEFT: - mask = AMETA_SHIFT_LEFT_ON; - break; - case AKEYCODE_SHIFT_RIGHT: - mask = AMETA_SHIFT_RIGHT_ON; - break; - case AKEYCODE_SYM: - mask = AMETA_SYM_ON; - break; - default: - return oldMetaState; - } - - int32_t newMetaState = down ? oldMetaState | mask : oldMetaState & ~ mask - & ~ (AMETA_ALT_ON | AMETA_SHIFT_ON); - - if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { - newMetaState |= AMETA_ALT_ON; - } - - if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { - newMetaState |= AMETA_SHIFT_ON; - } - - return newMetaState; -} - -static const int32_t keyCodeRotationMap[][4] = { - // key codes enumerated counter-clockwise with the original (unrotated) key first - // no rotation, 90 degree rotation, 180 degree rotation, 270 degree rotation - { AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT }, - { AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN }, - { AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT }, - { AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP }, -}; -static const int keyCodeRotationMapSize = - sizeof(keyCodeRotationMap) / sizeof(keyCodeRotationMap[0]); - -int32_t rotateKeyCode(int32_t keyCode, int32_t orientation) { - if (orientation != InputReaderPolicyInterface::ROTATION_0) { - for (int i = 0; i < keyCodeRotationMapSize; i++) { - if (keyCode == keyCodeRotationMap[i][0]) { - return keyCodeRotationMap[i][orientation]; - } - } - } - return keyCode; -} - -static inline bool sourcesMatchMask(uint32_t sources, uint32_t sourceMask) { - return (sources & sourceMask & ~ AINPUT_SOURCE_CLASS_MASK) != 0; -} - - -// --- InputDeviceCalibration --- - -InputDeviceCalibration::InputDeviceCalibration() { -} - -void InputDeviceCalibration::clear() { - mProperties.clear(); -} - -void InputDeviceCalibration::addProperty(const String8& key, const String8& value) { - mProperties.add(key, value); -} - -bool InputDeviceCalibration::tryGetProperty(const String8& key, String8& outValue) const { - ssize_t index = mProperties.indexOfKey(key); - if (index < 0) { - return false; - } - - outValue = mProperties.valueAt(index); - return true; -} - -bool InputDeviceCalibration::tryGetProperty(const String8& key, int32_t& outValue) const { - String8 stringValue; - if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { - return false; - } - - char* end; - int value = strtol(stringValue.string(), & end, 10); - if (*end != '\0') { - LOGW("Input device calibration key '%s' has invalid value '%s'. Expected an integer.", - key.string(), stringValue.string()); - return false; - } - outValue = value; - return true; -} - -bool InputDeviceCalibration::tryGetProperty(const String8& key, float& outValue) const { - String8 stringValue; - if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { - return false; - } - - char* end; - float value = strtof(stringValue.string(), & end); - if (*end != '\0') { - LOGW("Input device calibration key '%s' has invalid value '%s'. Expected a float.", - key.string(), stringValue.string()); - return false; - } - outValue = value; - return true; -} - - -// --- InputReader --- - -InputReader::InputReader(const sp<EventHubInterface>& eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputDispatcherInterface>& dispatcher) : - mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher), - mGlobalMetaState(0), mDisableVirtualKeysTimeout(-1) { - configureExcludedDevices(); - updateGlobalMetaState(); - updateInputConfiguration(); -} - -InputReader::~InputReader() { - for (size_t i = 0; i < mDevices.size(); i++) { - delete mDevices.valueAt(i); - } -} - -void InputReader::loopOnce() { - RawEvent rawEvent; - mEventHub->getEvent(& rawEvent); - -#if DEBUG_RAW_EVENTS - LOGD("Input event: device=0x%x type=0x%x scancode=%d keycode=%d value=%d", - rawEvent.deviceId, rawEvent.type, rawEvent.scanCode, rawEvent.keyCode, - rawEvent.value); -#endif - - process(& rawEvent); -} - -void InputReader::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EventHubInterface::DEVICE_ADDED: - addDevice(rawEvent->deviceId); - break; - - case EventHubInterface::DEVICE_REMOVED: - removeDevice(rawEvent->deviceId); - break; - - case EventHubInterface::FINISHED_DEVICE_SCAN: - handleConfigurationChanged(rawEvent->when); - break; - - default: - consumeEvent(rawEvent); - break; - } -} - -void InputReader::addDevice(int32_t deviceId) { - String8 name = mEventHub->getDeviceName(deviceId); - uint32_t classes = mEventHub->getDeviceClasses(deviceId); - - InputDevice* device = createDevice(deviceId, name, classes); - device->configure(); - - if (device->isIgnored()) { - LOGI("Device added: id=0x%x, name=%s (ignored non-input device)", deviceId, name.string()); - } else { - LOGI("Device added: id=0x%x, name=%s, sources=%08x", deviceId, name.string(), - device->getSources()); - } - - bool added = false; - { // acquire device registry writer lock - RWLock::AutoWLock _wl(mDeviceRegistryLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - mDevices.add(deviceId, device); - added = true; - } - } // release device registry writer lock - - if (! added) { - LOGW("Ignoring spurious device added event for deviceId %d.", deviceId); - delete device; - return; - } -} - -void InputReader::removeDevice(int32_t deviceId) { - bool removed = false; - InputDevice* device = NULL; - { // acquire device registry writer lock - RWLock::AutoWLock _wl(mDeviceRegistryLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - device = mDevices.valueAt(deviceIndex); - mDevices.removeItemsAt(deviceIndex, 1); - removed = true; - } - } // release device registry writer lock - - if (! removed) { - LOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); - return; - } - - if (device->isIgnored()) { - LOGI("Device removed: id=0x%x, name=%s (ignored non-input device)", - device->getId(), device->getName().string()); - } else { - LOGI("Device removed: id=0x%x, name=%s, sources=%08x", - device->getId(), device->getName().string(), device->getSources()); - } - - device->reset(); - - delete device; -} - -InputDevice* InputReader::createDevice(int32_t deviceId, const String8& name, uint32_t classes) { - InputDevice* device = new InputDevice(this, deviceId, name); - - const int32_t associatedDisplayId = 0; // FIXME: hardcoded for current single-display devices - - // Switch-like devices. - if (classes & INPUT_DEVICE_CLASS_SWITCH) { - device->addMapper(new SwitchInputMapper(device)); - } - - // Keyboard-like devices. - uint32_t keyboardSources = 0; - int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; - if (classes & INPUT_DEVICE_CLASS_KEYBOARD) { - keyboardSources |= AINPUT_SOURCE_KEYBOARD; - } - if (classes & INPUT_DEVICE_CLASS_ALPHAKEY) { - keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC; - } - if (classes & INPUT_DEVICE_CLASS_DPAD) { - keyboardSources |= AINPUT_SOURCE_DPAD; - } - - if (keyboardSources != 0) { - device->addMapper(new KeyboardInputMapper(device, - associatedDisplayId, keyboardSources, keyboardType)); - } - - // Trackball-like devices. - if (classes & INPUT_DEVICE_CLASS_TRACKBALL) { - device->addMapper(new TrackballInputMapper(device, associatedDisplayId)); - } - - // Touchscreen-like devices. - if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN_MT) { - device->addMapper(new MultiTouchInputMapper(device, associatedDisplayId)); - } else if (classes & INPUT_DEVICE_CLASS_TOUCHSCREEN) { - device->addMapper(new SingleTouchInputMapper(device, associatedDisplayId)); - } - - return device; -} - -void InputReader::consumeEvent(const RawEvent* rawEvent) { - int32_t deviceId = rawEvent->deviceId; - - { // acquire device registry reader lock - RWLock::AutoRLock _rl(mDeviceRegistryLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - LOGW("Discarding event for unknown deviceId %d.", deviceId); - return; - } - - InputDevice* device = mDevices.valueAt(deviceIndex); - if (device->isIgnored()) { - //LOGD("Discarding event for ignored deviceId %d.", deviceId); - return; - } - - device->process(rawEvent); - } // release device registry reader lock -} - -void InputReader::handleConfigurationChanged(nsecs_t when) { - // Reset global meta state because it depends on the list of all configured devices. - updateGlobalMetaState(); - - // Update input configuration. - updateInputConfiguration(); - - // Enqueue configuration changed. - mDispatcher->notifyConfigurationChanged(when); -} - -void InputReader::configureExcludedDevices() { - Vector<String8> excludedDeviceNames; - mPolicy->getExcludedDeviceNames(excludedDeviceNames); - - for (size_t i = 0; i < excludedDeviceNames.size(); i++) { - mEventHub->addExcludedDevice(excludedDeviceNames[i]); - } -} - -void InputReader::updateGlobalMetaState() { - { // acquire state lock - AutoMutex _l(mStateLock); - - mGlobalMetaState = 0; - - { // acquire device registry reader lock - RWLock::AutoRLock _rl(mDeviceRegistryLock); - - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - mGlobalMetaState |= device->getMetaState(); - } - } // release device registry reader lock - } // release state lock -} - -int32_t InputReader::getGlobalMetaState() { - { // acquire state lock - AutoMutex _l(mStateLock); - - return mGlobalMetaState; - } // release state lock -} - -void InputReader::updateInputConfiguration() { - { // acquire state lock - AutoMutex _l(mStateLock); - - int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH; - int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS; - int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV; - { // acquire device registry reader lock - RWLock::AutoRLock _rl(mDeviceRegistryLock); - - InputDeviceInfo deviceInfo; - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - device->getDeviceInfo(& deviceInfo); - uint32_t sources = deviceInfo.getSources(); - - if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) { - touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER; - } - if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) { - navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL; - } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) { - navigationConfig = InputConfiguration::NAVIGATION_DPAD; - } - if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) { - keyboardConfig = InputConfiguration::KEYBOARD_QWERTY; - } - } - } // release device registry reader lock - - mInputConfiguration.touchScreen = touchScreenConfig; - mInputConfiguration.keyboard = keyboardConfig; - mInputConfiguration.navigation = navigationConfig; - } // release state lock -} - -void InputReader::disableVirtualKeysUntil(nsecs_t time) { - mDisableVirtualKeysTimeout = time; -} - -bool InputReader::shouldDropVirtualKey(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode) { - if (now < mDisableVirtualKeysTimeout) { - LOGI("Dropping virtual key from device %s because virtual keys are " - "temporarily disabled for the next %0.3fms. keyCode=%d, scanCode=%d", - device->getName().string(), - (mDisableVirtualKeysTimeout - now) * 0.000001, - keyCode, scanCode); - return true; - } else { - return false; - } -} - -void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) { - { // acquire state lock - AutoMutex _l(mStateLock); - - *outConfiguration = mInputConfiguration; - } // release state lock -} - -status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { - { // acquire device registry reader lock - RWLock::AutoRLock _rl(mDeviceRegistryLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - return NAME_NOT_FOUND; - } - - InputDevice* device = mDevices.valueAt(deviceIndex); - if (device->isIgnored()) { - return NAME_NOT_FOUND; - } - - device->getDeviceInfo(outDeviceInfo); - return OK; - } // release device registy reader lock -} - -void InputReader::getInputDeviceIds(Vector<int32_t>& outDeviceIds) { - outDeviceIds.clear(); - - { // acquire device registry reader lock - RWLock::AutoRLock _rl(mDeviceRegistryLock); - - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); - if (! device->isIgnored()) { - outDeviceIds.add(device->getId()); - } - } - } // release device registy reader lock -} - -int32_t InputReader::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t keyCode) { - return getState(deviceId, sourceMask, keyCode, & InputDevice::getKeyCodeState); -} - -int32_t InputReader::getScanCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t scanCode) { - return getState(deviceId, sourceMask, scanCode, & InputDevice::getScanCodeState); -} - -int32_t InputReader::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t switchCode) { - return getState(deviceId, sourceMask, switchCode, & InputDevice::getSwitchState); -} - -int32_t InputReader::getState(int32_t deviceId, uint32_t sourceMask, int32_t code, - GetStateFunc getStateFunc) { - { // acquire device registry reader lock - RWLock::AutoRLock _rl(mDeviceRegistryLock); - - int32_t result = AKEY_STATE_UNKNOWN; - if (deviceId >= 0) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = (device->*getStateFunc)(sourceMask, code); - } - } - } else { - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = (device->*getStateFunc)(sourceMask, code); - if (result >= AKEY_STATE_DOWN) { - return result; - } - } - } - } - return result; - } // release device registy reader lock -} - -bool InputReader::hasKeys(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { - memset(outFlags, 0, numCodes); - return markSupportedKeyCodes(deviceId, sourceMask, numCodes, keyCodes, outFlags); -} - -bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - { // acquire device registry reader lock - RWLock::AutoRLock _rl(mDeviceRegistryLock); - bool result = false; - if (deviceId >= 0) { - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - InputDevice* device = mDevices.valueAt(deviceIndex); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = device->markSupportedKeyCodes(sourceMask, - numCodes, keyCodes, outFlags); - } - } - } else { - size_t numDevices = mDevices.size(); - for (size_t i = 0; i < numDevices; i++) { - InputDevice* device = mDevices.valueAt(i); - if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result |= device->markSupportedKeyCodes(sourceMask, - numCodes, keyCodes, outFlags); - } - } - } - return result; - } // release device registy reader lock -} - -void InputReader::dump(String8& dump) { - mEventHub->dump(dump); - dump.append("\n"); - - dump.append("Input Reader State:\n"); - - { // acquire device registry reader lock - RWLock::AutoRLock _rl(mDeviceRegistryLock); - - for (size_t i = 0; i < mDevices.size(); i++) { - mDevices.valueAt(i)->dump(dump); - } - } // release device registy reader lock -} - - -// --- InputReaderThread --- - -InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) : - Thread(/*canCallJava*/ true), mReader(reader) { -} - -InputReaderThread::~InputReaderThread() { -} - -bool InputReaderThread::threadLoop() { - mReader->loopOnce(); - return true; -} - - -// --- InputDevice --- - -InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) : - mContext(context), mId(id), mName(name), mSources(0) { -} - -InputDevice::~InputDevice() { - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - delete mMappers[i]; - } - mMappers.clear(); -} - -static void dumpMotionRange(String8& dump, const InputDeviceInfo& deviceInfo, - int32_t rangeType, const char* name) { - const InputDeviceInfo::MotionRange* range = deviceInfo.getMotionRange(rangeType); - if (range) { - dump.appendFormat(INDENT3 "%s: min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n", - name, range->min, range->max, range->flat, range->fuzz); - } -} - -void InputDevice::dump(String8& dump) { - InputDeviceInfo deviceInfo; - getDeviceInfo(& deviceInfo); - - dump.appendFormat(INDENT "Device 0x%x: %s\n", deviceInfo.getId(), - deviceInfo.getName().string()); - dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); - dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); - if (!deviceInfo.getMotionRanges().isEmpty()) { - dump.append(INDENT2 "Motion Ranges:\n"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_X, "X"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_Y, "Y"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_PRESSURE, "Pressure"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_SIZE, "Size"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MAJOR, "TouchMajor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOUCH_MINOR, "TouchMinor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MAJOR, "ToolMajor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_TOOL_MINOR, "ToolMinor"); - dumpMotionRange(dump, deviceInfo, AINPUT_MOTION_RANGE_ORIENTATION, "Orientation"); - } - - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->dump(dump); - } -} - -void InputDevice::addMapper(InputMapper* mapper) { - mMappers.add(mapper); -} - -void InputDevice::configure() { - if (! isIgnored()) { - mContext->getPolicy()->getInputDeviceCalibration(mName, mCalibration); - } - - mSources = 0; - - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->configure(); - mSources |= mapper->getSources(); - } -} - -void InputDevice::reset() { - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->reset(); - } -} - -void InputDevice::process(const RawEvent* rawEvent) { - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->process(rawEvent); - } -} - -void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { - outDeviceInfo->initialize(mId, mName); - - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->populateDeviceInfo(outDeviceInfo); - } -} - -int32_t InputDevice::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return getState(sourceMask, keyCode, & InputMapper::getKeyCodeState); -} - -int32_t InputDevice::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return getState(sourceMask, scanCode, & InputMapper::getScanCodeState); -} - -int32_t InputDevice::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return getState(sourceMask, switchCode, & InputMapper::getSwitchState); -} - -int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc) { - int32_t result = AKEY_STATE_UNKNOWN; - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - if (sourcesMatchMask(mapper->getSources(), sourceMask)) { - result = (mapper->*getStateFunc)(sourceMask, code); - if (result >= AKEY_STATE_DOWN) { - return result; - } - } - } - return result; -} - -bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - bool result = false; - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - if (sourcesMatchMask(mapper->getSources(), sourceMask)) { - result |= mapper->markSupportedKeyCodes(sourceMask, numCodes, keyCodes, outFlags); - } - } - return result; -} - -int32_t InputDevice::getMetaState() { - int32_t result = 0; - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - result |= mapper->getMetaState(); - } - return result; -} - - -// --- InputMapper --- - -InputMapper::InputMapper(InputDevice* device) : - mDevice(device), mContext(device->getContext()) { -} - -InputMapper::~InputMapper() { -} - -void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { - info->addSource(getSources()); -} - -void InputMapper::dump(String8& dump) { -} - -void InputMapper::configure() { -} - -void InputMapper::reset() { -} - -int32_t InputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return AKEY_STATE_UNKNOWN; -} - -int32_t InputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return AKEY_STATE_UNKNOWN; -} - -int32_t InputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return AKEY_STATE_UNKNOWN; -} - -bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return false; -} - -int32_t InputMapper::getMetaState() { - return 0; -} - - -// --- SwitchInputMapper --- - -SwitchInputMapper::SwitchInputMapper(InputDevice* device) : - InputMapper(device) { -} - -SwitchInputMapper::~SwitchInputMapper() { -} - -uint32_t SwitchInputMapper::getSources() { - return AINPUT_SOURCE_SWITCH; -} - -void SwitchInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_SW: - processSwitch(rawEvent->when, rawEvent->scanCode, rawEvent->value); - break; - } -} - -void SwitchInputMapper::processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) { - getDispatcher()->notifySwitch(when, switchCode, switchValue, 0); -} - -int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { - return getEventHub()->getSwitchState(getDeviceId(), switchCode); -} - - -// --- KeyboardInputMapper --- - -KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, int32_t associatedDisplayId, - uint32_t sources, int32_t keyboardType) : - InputMapper(device), mAssociatedDisplayId(associatedDisplayId), mSources(sources), - mKeyboardType(keyboardType) { - initializeLocked(); -} - -KeyboardInputMapper::~KeyboardInputMapper() { -} - -void KeyboardInputMapper::initializeLocked() { - mLocked.metaState = AMETA_NONE; - mLocked.downTime = 0; -} - -uint32_t KeyboardInputMapper::getSources() { - return mSources; -} - -void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - info->setKeyboardType(mKeyboardType); -} - -void KeyboardInputMapper::dump(String8& dump) { - { // acquire lock - AutoMutex _l(mLock); - dump.append(INDENT2 "Keyboard Input Mapper:\n"); - dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId); - dump.appendFormat(INDENT3 "KeyboardType: %d\n", mKeyboardType); - dump.appendFormat(INDENT3 "KeyDowns: %d keys currently down\n", mLocked.keyDowns.size()); - dump.appendFormat(INDENT3 "MetaState: 0x%0x\n", mLocked.metaState); - dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime); - } // release lock -} - -void KeyboardInputMapper::reset() { - for (;;) { - int32_t keyCode, scanCode; - { // acquire lock - AutoMutex _l(mLock); - - // Synthesize key up event on reset if keys are currently down. - if (mLocked.keyDowns.isEmpty()) { - initializeLocked(); - break; // done - } - - const KeyDown& keyDown = mLocked.keyDowns.top(); - keyCode = keyDown.keyCode; - scanCode = keyDown.scanCode; - } // release lock - - nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); - processKey(when, false, keyCode, scanCode, 0); - } - - InputMapper::reset(); - getContext()->updateGlobalMetaState(); -} - -void KeyboardInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_KEY: { - int32_t scanCode = rawEvent->scanCode; - if (isKeyboardOrGamepadKey(scanCode)) { - processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode, - rawEvent->flags); - } - break; - } - } -} - -bool KeyboardInputMapper::isKeyboardOrGamepadKey(int32_t scanCode) { - return scanCode < BTN_MOUSE - || scanCode >= KEY_OK - || (scanCode >= BTN_GAMEPAD && scanCode < BTN_DIGI); -} - -void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, - int32_t scanCode, uint32_t policyFlags) { - int32_t newMetaState; - nsecs_t downTime; - bool metaStateChanged = false; - - { // acquire lock - AutoMutex _l(mLock); - - if (down) { - // Rotate key codes according to orientation if needed. - // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. - if (mAssociatedDisplayId >= 0) { - int32_t orientation; - if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { - return; - } - - keyCode = rotateKeyCode(keyCode, orientation); - } - - // Add key down. - ssize_t keyDownIndex = findKeyDownLocked(scanCode); - if (keyDownIndex >= 0) { - // key repeat, be sure to use same keycode as before in case of rotation - keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode; - } else { - // key down - if ((policyFlags & POLICY_FLAG_VIRTUAL) - && mContext->shouldDropVirtualKey(when, getDevice(), keyCode, scanCode)) { - return; - } - - mLocked.keyDowns.push(); - KeyDown& keyDown = mLocked.keyDowns.editTop(); - keyDown.keyCode = keyCode; - keyDown.scanCode = scanCode; - } - - mLocked.downTime = when; - } else { - // Remove key down. - ssize_t keyDownIndex = findKeyDownLocked(scanCode); - if (keyDownIndex >= 0) { - // key up, be sure to use same keycode as before in case of rotation - keyCode = mLocked.keyDowns.itemAt(keyDownIndex).keyCode; - mLocked.keyDowns.removeAt(size_t(keyDownIndex)); - } else { - // key was not actually down - LOGI("Dropping key up from device %s because the key was not down. " - "keyCode=%d, scanCode=%d", - getDeviceName().string(), keyCode, scanCode); - return; - } - } - - int32_t oldMetaState = mLocked.metaState; - newMetaState = updateMetaState(keyCode, down, oldMetaState); - if (oldMetaState != newMetaState) { - mLocked.metaState = newMetaState; - metaStateChanged = true; - } - - downTime = mLocked.downTime; - } // release lock - - if (metaStateChanged) { - getContext()->updateGlobalMetaState(); - } - - getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, - down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, newMetaState, downTime); -} - -ssize_t KeyboardInputMapper::findKeyDownLocked(int32_t scanCode) { - size_t n = mLocked.keyDowns.size(); - for (size_t i = 0; i < n; i++) { - if (mLocked.keyDowns[i].scanCode == scanCode) { - return i; - } - } - return -1; -} - -int32_t KeyboardInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - return getEventHub()->getKeyCodeState(getDeviceId(), keyCode); -} - -int32_t KeyboardInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); -} - -bool KeyboardInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - return getEventHub()->markSupportedKeyCodes(getDeviceId(), numCodes, keyCodes, outFlags); -} - -int32_t KeyboardInputMapper::getMetaState() { - { // acquire lock - AutoMutex _l(mLock); - return mLocked.metaState; - } // release lock -} - - -// --- TrackballInputMapper --- - -TrackballInputMapper::TrackballInputMapper(InputDevice* device, int32_t associatedDisplayId) : - InputMapper(device), mAssociatedDisplayId(associatedDisplayId) { - mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - - initializeLocked(); -} - -TrackballInputMapper::~TrackballInputMapper() { -} - -uint32_t TrackballInputMapper::getSources() { - return AINPUT_SOURCE_TRACKBALL; -} - -void TrackballInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - info->addMotionRange(AINPUT_MOTION_RANGE_X, -1.0f, 1.0f, 0.0f, mXScale); - info->addMotionRange(AINPUT_MOTION_RANGE_Y, -1.0f, 1.0f, 0.0f, mYScale); -} - -void TrackballInputMapper::dump(String8& dump) { - { // acquire lock - AutoMutex _l(mLock); - dump.append(INDENT2 "Trackball Input Mapper:\n"); - dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId); - dump.appendFormat(INDENT3 "XPrecision: %0.3f\n", mXPrecision); - dump.appendFormat(INDENT3 "YPrecision: %0.3f\n", mYPrecision); - dump.appendFormat(INDENT3 "Down: %s\n", toString(mLocked.down)); - dump.appendFormat(INDENT3 "DownTime: %lld\n", mLocked.downTime); - } // release lock -} - -void TrackballInputMapper::initializeLocked() { - mAccumulator.clear(); - - mLocked.down = false; - mLocked.downTime = 0; -} - -void TrackballInputMapper::reset() { - for (;;) { - { // acquire lock - AutoMutex _l(mLock); - - if (! mLocked.down) { - initializeLocked(); - break; // done - } - } // release lock - - // Synthesize trackball button up event on reset. - nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); - mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE; - mAccumulator.btnMouse = false; - sync(when); - } - - InputMapper::reset(); -} - -void TrackballInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_KEY: - switch (rawEvent->scanCode) { - case BTN_MOUSE: - mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE; - mAccumulator.btnMouse = rawEvent->value != 0; - // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and - // we need to ensure that we report the up/down promptly. - sync(rawEvent->when); - break; - } - break; - - case EV_REL: - switch (rawEvent->scanCode) { - case REL_X: - mAccumulator.fields |= Accumulator::FIELD_REL_X; - mAccumulator.relX = rawEvent->value; - break; - case REL_Y: - mAccumulator.fields |= Accumulator::FIELD_REL_Y; - mAccumulator.relY = rawEvent->value; - break; - } - break; - - case EV_SYN: - switch (rawEvent->scanCode) { - case SYN_REPORT: - sync(rawEvent->when); - break; - } - break; - } -} - -void TrackballInputMapper::sync(nsecs_t when) { - uint32_t fields = mAccumulator.fields; - if (fields == 0) { - return; // no new state changes, so nothing to do - } - - int motionEventAction; - PointerCoords pointerCoords; - nsecs_t downTime; - { // acquire lock - AutoMutex _l(mLock); - - bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE; - - if (downChanged) { - if (mAccumulator.btnMouse) { - mLocked.down = true; - mLocked.downTime = when; - } else { - mLocked.down = false; - } - } - - downTime = mLocked.downTime; - float x = fields & Accumulator::FIELD_REL_X ? mAccumulator.relX * mXScale : 0.0f; - float y = fields & Accumulator::FIELD_REL_Y ? mAccumulator.relY * mYScale : 0.0f; - - if (downChanged) { - motionEventAction = mLocked.down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP; - } else { - motionEventAction = AMOTION_EVENT_ACTION_MOVE; - } - - pointerCoords.x = x; - pointerCoords.y = y; - pointerCoords.pressure = mLocked.down ? 1.0f : 0.0f; - pointerCoords.size = 0; - pointerCoords.touchMajor = 0; - pointerCoords.touchMinor = 0; - pointerCoords.toolMajor = 0; - pointerCoords.toolMinor = 0; - pointerCoords.orientation = 0; - - if (mAssociatedDisplayId >= 0 && (x != 0.0f || y != 0.0f)) { - // Rotate motion based on display orientation if needed. - // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. - int32_t orientation; - if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, NULL, NULL, & orientation)) { - return; - } - - float temp; - switch (orientation) { - case InputReaderPolicyInterface::ROTATION_90: - temp = pointerCoords.x; - pointerCoords.x = pointerCoords.y; - pointerCoords.y = - temp; - break; - - case InputReaderPolicyInterface::ROTATION_180: - pointerCoords.x = - pointerCoords.x; - pointerCoords.y = - pointerCoords.y; - break; - - case InputReaderPolicyInterface::ROTATION_270: - temp = pointerCoords.x; - pointerCoords.x = - pointerCoords.y; - pointerCoords.y = temp; - break; - } - } - } // release lock - - int32_t metaState = mContext->getGlobalMetaState(); - int32_t pointerId = 0; - getDispatcher()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_TRACKBALL, 0, - motionEventAction, 0, metaState, AMOTION_EVENT_EDGE_FLAG_NONE, - 1, &pointerId, &pointerCoords, mXPrecision, mYPrecision, downTime); - - mAccumulator.clear(); -} - -int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { - return getEventHub()->getScanCodeState(getDeviceId(), scanCode); - } else { - return AKEY_STATE_UNKNOWN; - } -} - - -// --- TouchInputMapper --- - -TouchInputMapper::TouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : - InputMapper(device), mAssociatedDisplayId(associatedDisplayId) { - mLocked.surfaceOrientation = -1; - mLocked.surfaceWidth = -1; - mLocked.surfaceHeight = -1; - - initializeLocked(); -} - -TouchInputMapper::~TouchInputMapper() { -} - -uint32_t TouchInputMapper::getSources() { - return mAssociatedDisplayId >= 0 ? AINPUT_SOURCE_TOUCHSCREEN : AINPUT_SOURCE_TOUCHPAD; -} - -void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { - InputMapper::populateDeviceInfo(info); - - { // acquire lock - AutoMutex _l(mLock); - - // Ensure surface information is up to date so that orientation changes are - // noticed immediately. - configureSurfaceLocked(); - - info->addMotionRange(AINPUT_MOTION_RANGE_X, mLocked.orientedRanges.x); - info->addMotionRange(AINPUT_MOTION_RANGE_Y, mLocked.orientedRanges.y); - - if (mLocked.orientedRanges.havePressure) { - info->addMotionRange(AINPUT_MOTION_RANGE_PRESSURE, - mLocked.orientedRanges.pressure); - } - - if (mLocked.orientedRanges.haveSize) { - info->addMotionRange(AINPUT_MOTION_RANGE_SIZE, - mLocked.orientedRanges.size); - } - - if (mLocked.orientedRanges.haveTouchSize) { - info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MAJOR, - mLocked.orientedRanges.touchMajor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOUCH_MINOR, - mLocked.orientedRanges.touchMinor); - } - - if (mLocked.orientedRanges.haveToolSize) { - info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MAJOR, - mLocked.orientedRanges.toolMajor); - info->addMotionRange(AINPUT_MOTION_RANGE_TOOL_MINOR, - mLocked.orientedRanges.toolMinor); - } - - if (mLocked.orientedRanges.haveOrientation) { - info->addMotionRange(AINPUT_MOTION_RANGE_ORIENTATION, - mLocked.orientedRanges.orientation); - } - } // release lock -} - -void TouchInputMapper::dump(String8& dump) { - { // acquire lock - AutoMutex _l(mLock); - dump.append(INDENT2 "Touch Input Mapper:\n"); - dump.appendFormat(INDENT3 "AssociatedDisplayId: %d\n", mAssociatedDisplayId); - dumpParameters(dump); - dumpVirtualKeysLocked(dump); - dumpRawAxes(dump); - dumpCalibration(dump); - dumpSurfaceLocked(dump); - dump.appendFormat(INDENT3 "Translation and Scaling Factors:"); - dump.appendFormat(INDENT4 "XOrigin: %d\n", mLocked.xOrigin); - dump.appendFormat(INDENT4 "YOrigin: %d\n", mLocked.yOrigin); - dump.appendFormat(INDENT4 "XScale: %0.3f\n", mLocked.xScale); - dump.appendFormat(INDENT4 "YScale: %0.3f\n", mLocked.yScale); - dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mLocked.xPrecision); - dump.appendFormat(INDENT4 "YPrecision: %0.3f\n", mLocked.yPrecision); - dump.appendFormat(INDENT4 "GeometricScale: %0.3f\n", mLocked.geometricScale); - dump.appendFormat(INDENT4 "ToolSizeLinearScale: %0.3f\n", mLocked.toolSizeLinearScale); - dump.appendFormat(INDENT4 "ToolSizeLinearBias: %0.3f\n", mLocked.toolSizeLinearBias); - dump.appendFormat(INDENT4 "ToolSizeAreaScale: %0.3f\n", mLocked.toolSizeAreaScale); - dump.appendFormat(INDENT4 "ToolSizeAreaBias: %0.3f\n", mLocked.toolSizeAreaBias); - dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mLocked.pressureScale); - dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mLocked.sizeScale); - dump.appendFormat(INDENT4 "OrientationSCale: %0.3f\n", mLocked.orientationScale); - } // release lock -} - -void TouchInputMapper::initializeLocked() { - mCurrentTouch.clear(); - mLastTouch.clear(); - mDownTime = 0; - - for (uint32_t i = 0; i < MAX_POINTERS; i++) { - mAveragingTouchFilter.historyStart[i] = 0; - mAveragingTouchFilter.historyEnd[i] = 0; - } - - mJumpyTouchFilter.jumpyPointsDropped = 0; - - mLocked.currentVirtualKey.down = false; - - mLocked.orientedRanges.havePressure = false; - mLocked.orientedRanges.haveSize = false; - mLocked.orientedRanges.haveTouchSize = false; - mLocked.orientedRanges.haveToolSize = false; - mLocked.orientedRanges.haveOrientation = false; -} - -void TouchInputMapper::configure() { - InputMapper::configure(); - - // Configure basic parameters. - configureParameters(); - - // Configure absolute axis information. - configureRawAxes(); - - // Prepare input device calibration. - parseCalibration(); - resolveCalibration(); - - { // acquire lock - AutoMutex _l(mLock); - - // Configure surface dimensions and orientation. - configureSurfaceLocked(); - } // release lock -} - -void TouchInputMapper::configureParameters() { - mParameters.useBadTouchFilter = getPolicy()->filterTouchEvents(); - mParameters.useAveragingTouchFilter = getPolicy()->filterTouchEvents(); - mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); - mParameters.virtualKeyQuietTime = getPolicy()->getVirtualKeyQuietTime(); -} - -void TouchInputMapper::dumpParameters(String8& dump) { - dump.appendFormat(INDENT3 "UseBadTouchFilter: %s\n", - toString(mParameters.useBadTouchFilter)); - dump.appendFormat(INDENT3 "UseAveragingTouchFilter: %s\n", - toString(mParameters.useAveragingTouchFilter)); - dump.appendFormat(INDENT3 "UseJumpyTouchFilter: %s\n", - toString(mParameters.useJumpyTouchFilter)); -} - -void TouchInputMapper::configureRawAxes() { - mRawAxes.x.clear(); - mRawAxes.y.clear(); - mRawAxes.pressure.clear(); - mRawAxes.touchMajor.clear(); - mRawAxes.touchMinor.clear(); - mRawAxes.toolMajor.clear(); - mRawAxes.toolMinor.clear(); - mRawAxes.orientation.clear(); -} - -static void dumpAxisInfo(String8& dump, RawAbsoluteAxisInfo axis, const char* name) { - if (axis.valid) { - dump.appendFormat(INDENT4 "%s: min=%d, max=%d, flat=%d, fuzz=%d\n", - name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); - } else { - dump.appendFormat(INDENT4 "%s: unknown range\n", name); - } -} - -void TouchInputMapper::dumpRawAxes(String8& dump) { - dump.append(INDENT3 "Raw Axes:\n"); - dumpAxisInfo(dump, mRawAxes.x, "X"); - dumpAxisInfo(dump, mRawAxes.y, "Y"); - dumpAxisInfo(dump, mRawAxes.pressure, "Pressure"); - dumpAxisInfo(dump, mRawAxes.touchMajor, "TouchMajor"); - dumpAxisInfo(dump, mRawAxes.touchMinor, "TouchMinor"); - dumpAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor"); - dumpAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor"); - dumpAxisInfo(dump, mRawAxes.orientation, "Orientation"); -} - -bool TouchInputMapper::configureSurfaceLocked() { - // Update orientation and dimensions if needed. - int32_t orientation; - int32_t width, height; - if (mAssociatedDisplayId >= 0) { - // Note: getDisplayInfo is non-reentrant so we can continue holding the lock. - if (! getPolicy()->getDisplayInfo(mAssociatedDisplayId, & width, & height, & orientation)) { - return false; - } - } else { - orientation = InputReaderPolicyInterface::ROTATION_0; - width = mRawAxes.x.getRange(); - height = mRawAxes.y.getRange(); - } - - bool orientationChanged = mLocked.surfaceOrientation != orientation; - if (orientationChanged) { - mLocked.surfaceOrientation = orientation; - } - - bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height; - if (sizeChanged) { - LOGI("Device reconfigured: id=0x%x, name=%s, display size is now %dx%d", - getDeviceId(), getDeviceName().string(), width, height); - - mLocked.surfaceWidth = width; - mLocked.surfaceHeight = height; - - // Configure X and Y factors. - if (mRawAxes.x.valid && mRawAxes.y.valid) { - mLocked.xOrigin = mRawAxes.x.minValue; - mLocked.yOrigin = mRawAxes.y.minValue; - mLocked.xScale = float(width) / mRawAxes.x.getRange(); - mLocked.yScale = float(height) / mRawAxes.y.getRange(); - mLocked.xPrecision = 1.0f / mLocked.xScale; - mLocked.yPrecision = 1.0f / mLocked.yScale; - - configureVirtualKeysLocked(); - } else { - LOGW(INDENT "Touch device did not report support for X or Y axis!"); - mLocked.xOrigin = 0; - mLocked.yOrigin = 0; - mLocked.xScale = 1.0f; - mLocked.yScale = 1.0f; - mLocked.xPrecision = 1.0f; - mLocked.yPrecision = 1.0f; - } - - // Scale factor for terms that are not oriented in a particular axis. - // If the pixels are square then xScale == yScale otherwise we fake it - // by choosing an average. - mLocked.geometricScale = avg(mLocked.xScale, mLocked.yScale); - - // Size of diagonal axis. - float diagonalSize = pythag(width, height); - - // TouchMajor and TouchMinor factors. - if (mCalibration.touchSizeCalibration != Calibration::TOUCH_SIZE_CALIBRATION_NONE) { - mLocked.orientedRanges.haveTouchSize = true; - mLocked.orientedRanges.touchMajor.min = 0; - mLocked.orientedRanges.touchMajor.max = diagonalSize; - mLocked.orientedRanges.touchMajor.flat = 0; - mLocked.orientedRanges.touchMajor.fuzz = 0; - mLocked.orientedRanges.touchMinor = mLocked.orientedRanges.touchMajor; - } - - // ToolMajor and ToolMinor factors. - mLocked.toolSizeLinearScale = 0; - mLocked.toolSizeLinearBias = 0; - mLocked.toolSizeAreaScale = 0; - mLocked.toolSizeAreaBias = 0; - if (mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) { - if (mCalibration.toolSizeCalibration == Calibration::TOOL_SIZE_CALIBRATION_LINEAR) { - if (mCalibration.haveToolSizeLinearScale) { - mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale; - } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) { - mLocked.toolSizeLinearScale = float(min(width, height)) - / mRawAxes.toolMajor.maxValue; - } - - if (mCalibration.haveToolSizeLinearBias) { - mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias; - } - } else if (mCalibration.toolSizeCalibration == - Calibration::TOOL_SIZE_CALIBRATION_AREA) { - if (mCalibration.haveToolSizeLinearScale) { - mLocked.toolSizeLinearScale = mCalibration.toolSizeLinearScale; - } else { - mLocked.toolSizeLinearScale = min(width, height); - } - - if (mCalibration.haveToolSizeLinearBias) { - mLocked.toolSizeLinearBias = mCalibration.toolSizeLinearBias; - } - - if (mCalibration.haveToolSizeAreaScale) { - mLocked.toolSizeAreaScale = mCalibration.toolSizeAreaScale; - } else if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) { - mLocked.toolSizeAreaScale = 1.0f / mRawAxes.toolMajor.maxValue; - } - - if (mCalibration.haveToolSizeAreaBias) { - mLocked.toolSizeAreaBias = mCalibration.toolSizeAreaBias; - } - } - - mLocked.orientedRanges.haveToolSize = true; - mLocked.orientedRanges.toolMajor.min = 0; - mLocked.orientedRanges.toolMajor.max = diagonalSize; - mLocked.orientedRanges.toolMajor.flat = 0; - mLocked.orientedRanges.toolMajor.fuzz = 0; - mLocked.orientedRanges.toolMinor = mLocked.orientedRanges.toolMajor; - } - - // Pressure factors. - mLocked.pressureScale = 0; - if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE) { - RawAbsoluteAxisInfo rawPressureAxis; - switch (mCalibration.pressureSource) { - case Calibration::PRESSURE_SOURCE_PRESSURE: - rawPressureAxis = mRawAxes.pressure; - break; - case Calibration::PRESSURE_SOURCE_TOUCH: - rawPressureAxis = mRawAxes.touchMajor; - break; - default: - rawPressureAxis.clear(); - } - - if (mCalibration.pressureCalibration == Calibration::PRESSURE_CALIBRATION_PHYSICAL - || mCalibration.pressureCalibration - == Calibration::PRESSURE_CALIBRATION_AMPLITUDE) { - if (mCalibration.havePressureScale) { - mLocked.pressureScale = mCalibration.pressureScale; - } else if (rawPressureAxis.valid && rawPressureAxis.maxValue != 0) { - mLocked.pressureScale = 1.0f / rawPressureAxis.maxValue; - } - } - - mLocked.orientedRanges.havePressure = true; - mLocked.orientedRanges.pressure.min = 0; - mLocked.orientedRanges.pressure.max = 1.0; - mLocked.orientedRanges.pressure.flat = 0; - mLocked.orientedRanges.pressure.fuzz = 0; - } - - // Size factors. - mLocked.sizeScale = 0; - if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { - if (mCalibration.sizeCalibration == Calibration::SIZE_CALIBRATION_NORMALIZED) { - if (mRawAxes.toolMajor.valid && mRawAxes.toolMajor.maxValue != 0) { - mLocked.sizeScale = 1.0f / mRawAxes.toolMajor.maxValue; - } - } - - mLocked.orientedRanges.haveSize = true; - mLocked.orientedRanges.size.min = 0; - mLocked.orientedRanges.size.max = 1.0; - mLocked.orientedRanges.size.flat = 0; - mLocked.orientedRanges.size.fuzz = 0; - } - - // Orientation - mLocked.orientationScale = 0; - if (mCalibration.orientationCalibration != Calibration::ORIENTATION_CALIBRATION_NONE) { - if (mCalibration.orientationCalibration - == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { - if (mRawAxes.orientation.valid && mRawAxes.orientation.maxValue != 0) { - mLocked.orientationScale = float(M_PI_2) / mRawAxes.orientation.maxValue; - } - } - - mLocked.orientedRanges.orientation.min = - M_PI_2; - mLocked.orientedRanges.orientation.max = M_PI_2; - mLocked.orientedRanges.orientation.flat = 0; - mLocked.orientedRanges.orientation.fuzz = 0; - } - } - - if (orientationChanged || sizeChanged) { - // Compute oriented surface dimensions, precision, and scales. - float orientedXScale, orientedYScale; - switch (mLocked.surfaceOrientation) { - case InputReaderPolicyInterface::ROTATION_90: - case InputReaderPolicyInterface::ROTATION_270: - mLocked.orientedSurfaceWidth = mLocked.surfaceHeight; - mLocked.orientedSurfaceHeight = mLocked.surfaceWidth; - mLocked.orientedXPrecision = mLocked.yPrecision; - mLocked.orientedYPrecision = mLocked.xPrecision; - orientedXScale = mLocked.yScale; - orientedYScale = mLocked.xScale; - break; - default: - mLocked.orientedSurfaceWidth = mLocked.surfaceWidth; - mLocked.orientedSurfaceHeight = mLocked.surfaceHeight; - mLocked.orientedXPrecision = mLocked.xPrecision; - mLocked.orientedYPrecision = mLocked.yPrecision; - orientedXScale = mLocked.xScale; - orientedYScale = mLocked.yScale; - break; - } - - // Configure position ranges. - mLocked.orientedRanges.x.min = 0; - mLocked.orientedRanges.x.max = mLocked.orientedSurfaceWidth; - mLocked.orientedRanges.x.flat = 0; - mLocked.orientedRanges.x.fuzz = orientedXScale; - - mLocked.orientedRanges.y.min = 0; - mLocked.orientedRanges.y.max = mLocked.orientedSurfaceHeight; - mLocked.orientedRanges.y.flat = 0; - mLocked.orientedRanges.y.fuzz = orientedYScale; - } - - return true; -} - -void TouchInputMapper::dumpSurfaceLocked(String8& dump) { - dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mLocked.surfaceWidth); - dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mLocked.surfaceHeight); - dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mLocked.surfaceOrientation); -} - -void TouchInputMapper::configureVirtualKeysLocked() { - assert(mRawAxes.x.valid && mRawAxes.y.valid); - - // Note: getVirtualKeyDefinitions is non-reentrant so we can continue holding the lock. - Vector<VirtualKeyDefinition> virtualKeyDefinitions; - getPolicy()->getVirtualKeyDefinitions(getDeviceName(), virtualKeyDefinitions); - - mLocked.virtualKeys.clear(); - - if (virtualKeyDefinitions.size() == 0) { - return; - } - - mLocked.virtualKeys.setCapacity(virtualKeyDefinitions.size()); - - int32_t touchScreenLeft = mRawAxes.x.minValue; - int32_t touchScreenTop = mRawAxes.y.minValue; - int32_t touchScreenWidth = mRawAxes.x.getRange(); - int32_t touchScreenHeight = mRawAxes.y.getRange(); - - for (size_t i = 0; i < virtualKeyDefinitions.size(); i++) { - const VirtualKeyDefinition& virtualKeyDefinition = - virtualKeyDefinitions[i]; - - mLocked.virtualKeys.add(); - VirtualKey& virtualKey = mLocked.virtualKeys.editTop(); - - virtualKey.scanCode = virtualKeyDefinition.scanCode; - int32_t keyCode; - uint32_t flags; - if (getEventHub()->scancodeToKeycode(getDeviceId(), virtualKey.scanCode, - & keyCode, & flags)) { - LOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", - virtualKey.scanCode); - mLocked.virtualKeys.pop(); // drop the key - continue; - } - - virtualKey.keyCode = keyCode; - virtualKey.flags = flags; - - // convert the key definition's display coordinates into touch coordinates for a hit box - int32_t halfWidth = virtualKeyDefinition.width / 2; - int32_t halfHeight = virtualKeyDefinition.height / 2; - - virtualKey.hitLeft = (virtualKeyDefinition.centerX - halfWidth) - * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft; - virtualKey.hitRight= (virtualKeyDefinition.centerX + halfWidth) - * touchScreenWidth / mLocked.surfaceWidth + touchScreenLeft; - virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) - * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop; - virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) - * touchScreenHeight / mLocked.surfaceHeight + touchScreenTop; - - } -} - -void TouchInputMapper::dumpVirtualKeysLocked(String8& dump) { - if (!mLocked.virtualKeys.isEmpty()) { - dump.append(INDENT3 "Virtual Keys:\n"); - - for (size_t i = 0; i < mLocked.virtualKeys.size(); i++) { - const VirtualKey& virtualKey = mLocked.virtualKeys.itemAt(i); - dump.appendFormat(INDENT4 "%d: scanCode=%d, keyCode=%d, " - "hitLeft=%d, hitRight=%d, hitTop=%d, hitBottom=%d\n", - i, virtualKey.scanCode, virtualKey.keyCode, - virtualKey.hitLeft, virtualKey.hitRight, - virtualKey.hitTop, virtualKey.hitBottom); - } - } -} - -void TouchInputMapper::parseCalibration() { - const InputDeviceCalibration& in = getDevice()->getCalibration(); - Calibration& out = mCalibration; - - // Touch Size - out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT; - String8 touchSizeCalibrationString; - if (in.tryGetProperty(String8("touch.touchSize.calibration"), touchSizeCalibrationString)) { - if (touchSizeCalibrationString == "none") { - out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_NONE; - } else if (touchSizeCalibrationString == "geometric") { - out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC; - } else if (touchSizeCalibrationString == "pressure") { - out.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE; - } else if (touchSizeCalibrationString != "default") { - LOGW("Invalid value for touch.touchSize.calibration: '%s'", - touchSizeCalibrationString.string()); - } - } - - // Tool Size - out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_DEFAULT; - String8 toolSizeCalibrationString; - if (in.tryGetProperty(String8("touch.toolSize.calibration"), toolSizeCalibrationString)) { - if (toolSizeCalibrationString == "none") { - out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE; - } else if (toolSizeCalibrationString == "geometric") { - out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC; - } else if (toolSizeCalibrationString == "linear") { - out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR; - } else if (toolSizeCalibrationString == "area") { - out.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_AREA; - } else if (toolSizeCalibrationString != "default") { - LOGW("Invalid value for touch.toolSize.calibration: '%s'", - toolSizeCalibrationString.string()); - } - } - - out.haveToolSizeLinearScale = in.tryGetProperty(String8("touch.toolSize.linearScale"), - out.toolSizeLinearScale); - out.haveToolSizeLinearBias = in.tryGetProperty(String8("touch.toolSize.linearBias"), - out.toolSizeLinearBias); - out.haveToolSizeAreaScale = in.tryGetProperty(String8("touch.toolSize.areaScale"), - out.toolSizeAreaScale); - out.haveToolSizeAreaBias = in.tryGetProperty(String8("touch.toolSize.areaBias"), - out.toolSizeAreaBias); - out.haveToolSizeIsSummed = in.tryGetProperty(String8("touch.toolSize.isSummed"), - out.toolSizeIsSummed); - - // Pressure - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_DEFAULT; - String8 pressureCalibrationString; - if (in.tryGetProperty(String8("touch.pressure.calibration"), pressureCalibrationString)) { - if (pressureCalibrationString == "none") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; - } else if (pressureCalibrationString == "physical") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_PHYSICAL; - } else if (pressureCalibrationString == "amplitude") { - out.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; - } else if (pressureCalibrationString != "default") { - LOGW("Invalid value for touch.pressure.calibration: '%s'", - pressureCalibrationString.string()); - } - } - - out.pressureSource = Calibration::PRESSURE_SOURCE_DEFAULT; - String8 pressureSourceString; - if (in.tryGetProperty(String8("touch.pressure.source"), pressureSourceString)) { - if (pressureSourceString == "pressure") { - out.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE; - } else if (pressureSourceString == "touch") { - out.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH; - } else if (pressureSourceString != "default") { - LOGW("Invalid value for touch.pressure.source: '%s'", - pressureSourceString.string()); - } - } - - out.havePressureScale = in.tryGetProperty(String8("touch.pressure.scale"), - out.pressureScale); - - // Size - out.sizeCalibration = Calibration::SIZE_CALIBRATION_DEFAULT; - String8 sizeCalibrationString; - if (in.tryGetProperty(String8("touch.size.calibration"), sizeCalibrationString)) { - if (sizeCalibrationString == "none") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; - } else if (sizeCalibrationString == "normalized") { - out.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED; - } else if (sizeCalibrationString != "default") { - LOGW("Invalid value for touch.size.calibration: '%s'", - sizeCalibrationString.string()); - } - } - - // Orientation - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_DEFAULT; - String8 orientationCalibrationString; - if (in.tryGetProperty(String8("touch.orientation.calibration"), orientationCalibrationString)) { - if (orientationCalibrationString == "none") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; - } else if (orientationCalibrationString == "interpolated") { - out.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; - } else if (orientationCalibrationString != "default") { - LOGW("Invalid value for touch.orientation.calibration: '%s'", - orientationCalibrationString.string()); - } - } -} - -void TouchInputMapper::resolveCalibration() { - // Pressure - switch (mCalibration.pressureSource) { - case Calibration::PRESSURE_SOURCE_DEFAULT: - if (mRawAxes.pressure.valid) { - mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_PRESSURE; - } else if (mRawAxes.touchMajor.valid) { - mCalibration.pressureSource = Calibration::PRESSURE_SOURCE_TOUCH; - } - break; - - case Calibration::PRESSURE_SOURCE_PRESSURE: - if (! mRawAxes.pressure.valid) { - LOGW("Calibration property touch.pressure.source is 'pressure' but " - "the pressure axis is not available."); - } - break; - - case Calibration::PRESSURE_SOURCE_TOUCH: - if (! mRawAxes.touchMajor.valid) { - LOGW("Calibration property touch.pressure.source is 'touch' but " - "the touchMajor axis is not available."); - } - break; - - default: - break; - } - - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_DEFAULT: - if (mCalibration.pressureSource != Calibration::PRESSURE_SOURCE_DEFAULT) { - mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_AMPLITUDE; - } else { - mCalibration.pressureCalibration = Calibration::PRESSURE_CALIBRATION_NONE; - } - break; - - default: - break; - } - - // Tool Size - switch (mCalibration.toolSizeCalibration) { - case Calibration::TOOL_SIZE_CALIBRATION_DEFAULT: - if (mRawAxes.toolMajor.valid) { - mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_LINEAR; - } else { - mCalibration.toolSizeCalibration = Calibration::TOOL_SIZE_CALIBRATION_NONE; - } - break; - - default: - break; - } - - // Touch Size - switch (mCalibration.touchSizeCalibration) { - case Calibration::TOUCH_SIZE_CALIBRATION_DEFAULT: - if (mCalibration.pressureCalibration != Calibration::PRESSURE_CALIBRATION_NONE - && mCalibration.toolSizeCalibration != Calibration::TOOL_SIZE_CALIBRATION_NONE) { - mCalibration.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE; - } else { - mCalibration.touchSizeCalibration = Calibration::TOUCH_SIZE_CALIBRATION_NONE; - } - break; - - default: - break; - } - - // Size - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_DEFAULT: - if (mRawAxes.toolMajor.valid) { - mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NORMALIZED; - } else { - mCalibration.sizeCalibration = Calibration::SIZE_CALIBRATION_NONE; - } - break; - - default: - break; - } - - // Orientation - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_DEFAULT: - if (mRawAxes.orientation.valid) { - mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_INTERPOLATED; - } else { - mCalibration.orientationCalibration = Calibration::ORIENTATION_CALIBRATION_NONE; - } - break; - - default: - break; - } -} - -void TouchInputMapper::dumpCalibration(String8& dump) { - dump.append(INDENT3 "Calibration:\n"); - - // Touch Size - switch (mCalibration.touchSizeCalibration) { - case Calibration::TOUCH_SIZE_CALIBRATION_NONE: - dump.append(INDENT4 "touch.touchSize.calibration: none\n"); - break; - case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC: - dump.append(INDENT4 "touch.touchSize.calibration: geometric\n"); - break; - case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE: - dump.append(INDENT4 "touch.touchSize.calibration: pressure\n"); - break; - default: - assert(false); - } - - // Tool Size - switch (mCalibration.toolSizeCalibration) { - case Calibration::TOOL_SIZE_CALIBRATION_NONE: - dump.append(INDENT4 "touch.toolSize.calibration: none\n"); - break; - case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC: - dump.append(INDENT4 "touch.toolSize.calibration: geometric\n"); - break; - case Calibration::TOOL_SIZE_CALIBRATION_LINEAR: - dump.append(INDENT4 "touch.toolSize.calibration: linear\n"); - break; - case Calibration::TOOL_SIZE_CALIBRATION_AREA: - dump.append(INDENT4 "touch.toolSize.calibration: area\n"); - break; - default: - assert(false); - } - - if (mCalibration.haveToolSizeLinearScale) { - dump.appendFormat(INDENT4 "touch.toolSize.linearScale: %0.3f\n", - mCalibration.toolSizeLinearScale); - } - - if (mCalibration.haveToolSizeLinearBias) { - dump.appendFormat(INDENT4 "touch.toolSize.linearBias: %0.3f\n", - mCalibration.toolSizeLinearBias); - } - - if (mCalibration.haveToolSizeAreaScale) { - dump.appendFormat(INDENT4 "touch.toolSize.areaScale: %0.3f\n", - mCalibration.toolSizeAreaScale); - } - - if (mCalibration.haveToolSizeAreaBias) { - dump.appendFormat(INDENT4 "touch.toolSize.areaBias: %0.3f\n", - mCalibration.toolSizeAreaBias); - } - - if (mCalibration.haveToolSizeIsSummed) { - dump.appendFormat(INDENT4 "touch.toolSize.isSummed: %d\n", - mCalibration.toolSizeIsSummed); - } - - // Pressure - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_NONE: - dump.append(INDENT4 "touch.pressure.calibration: none\n"); - break; - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - dump.append(INDENT4 "touch.pressure.calibration: physical\n"); - break; - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - dump.append(INDENT4 "touch.pressure.calibration: amplitude\n"); - break; - default: - assert(false); - } - - switch (mCalibration.pressureSource) { - case Calibration::PRESSURE_SOURCE_PRESSURE: - dump.append(INDENT4 "touch.pressure.source: pressure\n"); - break; - case Calibration::PRESSURE_SOURCE_TOUCH: - dump.append(INDENT4 "touch.pressure.source: touch\n"); - break; - case Calibration::PRESSURE_SOURCE_DEFAULT: - break; - default: - assert(false); - } - - if (mCalibration.havePressureScale) { - dump.appendFormat(INDENT4 "touch.pressure.scale: %0.3f\n", - mCalibration.pressureScale); - } - - // Size - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_NONE: - dump.append(INDENT4 "touch.size.calibration: none\n"); - break; - case Calibration::SIZE_CALIBRATION_NORMALIZED: - dump.append(INDENT4 "touch.size.calibration: normalized\n"); - break; - default: - assert(false); - } - - // Orientation - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_NONE: - dump.append(INDENT4 "touch.orientation.calibration: none\n"); - break; - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - dump.append(INDENT4 "touch.orientation.calibration: interpolated\n"); - break; - default: - assert(false); - } -} - -void TouchInputMapper::reset() { - // Synthesize touch up event if touch is currently down. - // This will also take care of finishing virtual key processing if needed. - if (mLastTouch.pointerCount != 0) { - nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); - mCurrentTouch.clear(); - syncTouch(when, true); - } - - { // acquire lock - AutoMutex _l(mLock); - initializeLocked(); - } // release lock - - InputMapper::reset(); -} - -void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { - uint32_t policyFlags = 0; - - // Preprocess pointer data. - - if (mParameters.useBadTouchFilter) { - if (applyBadTouchFilter()) { - havePointerIds = false; - } - } - - if (mParameters.useJumpyTouchFilter) { - if (applyJumpyTouchFilter()) { - havePointerIds = false; - } - } - - if (! havePointerIds) { - calculatePointerIds(); - } - - TouchData temp; - TouchData* savedTouch; - if (mParameters.useAveragingTouchFilter) { - temp.copyFrom(mCurrentTouch); - savedTouch = & temp; - - applyAveragingTouchFilter(); - } else { - savedTouch = & mCurrentTouch; - } - - // Process touches and virtual keys. - - TouchResult touchResult = consumeOffScreenTouches(when, policyFlags); - if (touchResult == DISPATCH_TOUCH) { - detectGestures(when); - dispatchTouches(when, policyFlags); - } - - // Copy current touch to last touch in preparation for the next cycle. - - if (touchResult == DROP_STROKE) { - mLastTouch.clear(); - } else { - mLastTouch.copyFrom(*savedTouch); - } -} - -TouchInputMapper::TouchResult TouchInputMapper::consumeOffScreenTouches( - nsecs_t when, uint32_t policyFlags) { - int32_t keyEventAction, keyEventFlags; - int32_t keyCode, scanCode, downTime; - TouchResult touchResult; - - { // acquire lock - AutoMutex _l(mLock); - - // Update surface size and orientation, including virtual key positions. - if (! configureSurfaceLocked()) { - return DROP_STROKE; - } - - // Check for virtual key press. - if (mLocked.currentVirtualKey.down) { - if (mCurrentTouch.pointerCount == 0) { - // Pointer went up while virtual key was down. - mLocked.currentVirtualKey.down = false; -#if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d", - mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.scanCode); -#endif - keyEventAction = AKEY_EVENT_ACTION_UP; - keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; - touchResult = SKIP_TOUCH; - goto DispatchVirtualKey; - } - - if (mCurrentTouch.pointerCount == 1) { - int32_t x = mCurrentTouch.pointers[0].x; - int32_t y = mCurrentTouch.pointers[0].y; - const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y); - if (virtualKey && virtualKey->keyCode == mLocked.currentVirtualKey.keyCode) { - // Pointer is still within the space of the virtual key. - return SKIP_TOUCH; - } - } - - // Pointer left virtual key area or another pointer also went down. - // Send key cancellation and drop the stroke so subsequent motions will be - // considered fresh downs. This is useful when the user swipes away from the - // virtual key area into the main display surface. - mLocked.currentVirtualKey.down = false; -#if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", - mLocked.currentVirtualKey.keyCode, mLocked.currentVirtualKey.scanCode); -#endif - keyEventAction = AKEY_EVENT_ACTION_UP; - keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY - | AKEY_EVENT_FLAG_CANCELED; - - // Check whether the pointer moved inside the display area where we should - // start a new stroke. - int32_t x = mCurrentTouch.pointers[0].x; - int32_t y = mCurrentTouch.pointers[0].y; - if (isPointInsideSurfaceLocked(x, y)) { - mLastTouch.clear(); - touchResult = DISPATCH_TOUCH; - } else { - touchResult = DROP_STROKE; - } - } else { - if (mCurrentTouch.pointerCount >= 1 && mLastTouch.pointerCount == 0) { - // Pointer just went down. Handle off-screen touches, if needed. - int32_t x = mCurrentTouch.pointers[0].x; - int32_t y = mCurrentTouch.pointers[0].y; - if (! isPointInsideSurfaceLocked(x, y)) { - // If exactly one pointer went down, check for virtual key hit. - // Otherwise we will drop the entire stroke. - if (mCurrentTouch.pointerCount == 1) { - const VirtualKey* virtualKey = findVirtualKeyHitLocked(x, y); - if (virtualKey) { - if (mContext->shouldDropVirtualKey(when, getDevice(), - virtualKey->keyCode, virtualKey->scanCode)) { - return DROP_STROKE; - } - - mLocked.currentVirtualKey.down = true; - mLocked.currentVirtualKey.downTime = when; - mLocked.currentVirtualKey.keyCode = virtualKey->keyCode; - mLocked.currentVirtualKey.scanCode = virtualKey->scanCode; -#if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d", - mLocked.currentVirtualKey.keyCode, - mLocked.currentVirtualKey.scanCode); -#endif - keyEventAction = AKEY_EVENT_ACTION_DOWN; - keyEventFlags = AKEY_EVENT_FLAG_FROM_SYSTEM - | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY; - touchResult = SKIP_TOUCH; - goto DispatchVirtualKey; - } - } - return DROP_STROKE; - } - } - return DISPATCH_TOUCH; - } - - DispatchVirtualKey: - // Collect remaining state needed to dispatch virtual key. - keyCode = mLocked.currentVirtualKey.keyCode; - scanCode = mLocked.currentVirtualKey.scanCode; - downTime = mLocked.currentVirtualKey.downTime; - } // release lock - - // Dispatch virtual key. - int32_t metaState = mContext->getGlobalMetaState(); - policyFlags |= POLICY_FLAG_VIRTUAL; - getDispatcher()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, policyFlags, - keyEventAction, keyEventFlags, keyCode, scanCode, metaState, downTime); - return touchResult; -} - -void TouchInputMapper::detectGestures(nsecs_t when) { - // Disable all virtual key touches that happen within a short time interval of the - // most recent touch. The idea is to filter out stray virtual key presses when - // interacting with the touch screen. - // - // Problems we're trying to solve: - // - // 1. While scrolling a list or dragging the window shade, the user swipes down into a - // virtual key area that is implemented by a separate touch panel and accidentally - // triggers a virtual key. - // - // 2. While typing in the on screen keyboard, the user taps slightly outside the screen - // area and accidentally triggers a virtual key. This often happens when virtual keys - // are layed out below the screen near to where the on screen keyboard's space bar - // is displayed. - if (mParameters.virtualKeyQuietTime > 0 && mCurrentTouch.pointerCount != 0) { - mContext->disableVirtualKeysUntil(when + mParameters.virtualKeyQuietTime); - } -} - -void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { - uint32_t currentPointerCount = mCurrentTouch.pointerCount; - uint32_t lastPointerCount = mLastTouch.pointerCount; - if (currentPointerCount == 0 && lastPointerCount == 0) { - return; // nothing to do! - } - - BitSet32 currentIdBits = mCurrentTouch.idBits; - BitSet32 lastIdBits = mLastTouch.idBits; - - if (currentIdBits == lastIdBits) { - // No pointer id changes so this is a move event. - // The dispatcher takes care of batching moves so we don't have to deal with that here. - int32_t motionEventAction = AMOTION_EVENT_ACTION_MOVE; - dispatchTouch(when, policyFlags, & mCurrentTouch, - currentIdBits, -1, currentPointerCount, motionEventAction); - } else { - // There may be pointers going up and pointers going down and pointers moving - // all at the same time. - BitSet32 upIdBits(lastIdBits.value & ~ currentIdBits.value); - BitSet32 downIdBits(currentIdBits.value & ~ lastIdBits.value); - BitSet32 activeIdBits(lastIdBits.value); - uint32_t pointerCount = lastPointerCount; - - // Produce an intermediate representation of the touch data that consists of the - // old location of pointers that have just gone up and the new location of pointers that - // have just moved but omits the location of pointers that have just gone down. - TouchData interimTouch; - interimTouch.copyFrom(mLastTouch); - - BitSet32 moveIdBits(lastIdBits.value & currentIdBits.value); - bool moveNeeded = false; - while (!moveIdBits.isEmpty()) { - uint32_t moveId = moveIdBits.firstMarkedBit(); - moveIdBits.clearBit(moveId); - - int32_t oldIndex = mLastTouch.idToIndex[moveId]; - int32_t newIndex = mCurrentTouch.idToIndex[moveId]; - if (mLastTouch.pointers[oldIndex] != mCurrentTouch.pointers[newIndex]) { - interimTouch.pointers[oldIndex] = mCurrentTouch.pointers[newIndex]; - moveNeeded = true; - } - } - - // Dispatch pointer up events using the interim pointer locations. - while (!upIdBits.isEmpty()) { - uint32_t upId = upIdBits.firstMarkedBit(); - upIdBits.clearBit(upId); - BitSet32 oldActiveIdBits = activeIdBits; - activeIdBits.clearBit(upId); - - int32_t motionEventAction; - if (activeIdBits.isEmpty()) { - motionEventAction = AMOTION_EVENT_ACTION_UP; - } else { - motionEventAction = AMOTION_EVENT_ACTION_POINTER_UP; - } - - dispatchTouch(when, policyFlags, &interimTouch, - oldActiveIdBits, upId, pointerCount, motionEventAction); - pointerCount -= 1; - } - - // Dispatch move events if any of the remaining pointers moved from their old locations. - // Although applications receive new locations as part of individual pointer up - // events, they do not generally handle them except when presented in a move event. - if (moveNeeded) { - dispatchTouch(when, policyFlags, &mCurrentTouch, - activeIdBits, -1, pointerCount, AMOTION_EVENT_ACTION_MOVE); - } - - // Dispatch pointer down events using the new pointer locations. - while (!downIdBits.isEmpty()) { - uint32_t downId = downIdBits.firstMarkedBit(); - downIdBits.clearBit(downId); - BitSet32 oldActiveIdBits = activeIdBits; - activeIdBits.markBit(downId); - - int32_t motionEventAction; - if (oldActiveIdBits.isEmpty()) { - motionEventAction = AMOTION_EVENT_ACTION_DOWN; - mDownTime = when; - } else { - motionEventAction = AMOTION_EVENT_ACTION_POINTER_DOWN; - } - - pointerCount += 1; - dispatchTouch(when, policyFlags, &mCurrentTouch, - activeIdBits, downId, pointerCount, motionEventAction); - } - } -} - -void TouchInputMapper::dispatchTouch(nsecs_t when, uint32_t policyFlags, - TouchData* touch, BitSet32 idBits, uint32_t changedId, uint32_t pointerCount, - int32_t motionEventAction) { - int32_t pointerIds[MAX_POINTERS]; - PointerCoords pointerCoords[MAX_POINTERS]; - int32_t motionEventEdgeFlags = 0; - float xPrecision, yPrecision; - - { // acquire lock - AutoMutex _l(mLock); - - // Walk through the the active pointers and map touch screen coordinates (TouchData) into - // display coordinates (PointerCoords) and adjust for display orientation. - for (uint32_t outIndex = 0; ! idBits.isEmpty(); outIndex++) { - uint32_t id = idBits.firstMarkedBit(); - idBits.clearBit(id); - uint32_t inIndex = touch->idToIndex[id]; - - const PointerData& in = touch->pointers[inIndex]; - - // X and Y - float x = float(in.x - mLocked.xOrigin) * mLocked.xScale; - float y = float(in.y - mLocked.yOrigin) * mLocked.yScale; - - // ToolMajor and ToolMinor - float toolMajor, toolMinor; - switch (mCalibration.toolSizeCalibration) { - case Calibration::TOOL_SIZE_CALIBRATION_GEOMETRIC: - toolMajor = in.toolMajor * mLocked.geometricScale; - if (mRawAxes.toolMinor.valid) { - toolMinor = in.toolMinor * mLocked.geometricScale; - } else { - toolMinor = toolMajor; - } - break; - case Calibration::TOOL_SIZE_CALIBRATION_LINEAR: - toolMajor = in.toolMajor != 0 - ? in.toolMajor * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias - : 0; - if (mRawAxes.toolMinor.valid) { - toolMinor = in.toolMinor != 0 - ? in.toolMinor * mLocked.toolSizeLinearScale - + mLocked.toolSizeLinearBias - : 0; - } else { - toolMinor = toolMajor; - } - break; - case Calibration::TOOL_SIZE_CALIBRATION_AREA: - if (in.toolMajor != 0) { - float diameter = sqrtf(in.toolMajor - * mLocked.toolSizeAreaScale + mLocked.toolSizeAreaBias); - toolMajor = diameter * mLocked.toolSizeLinearScale + mLocked.toolSizeLinearBias; - } else { - toolMajor = 0; - } - toolMinor = toolMajor; - break; - default: - toolMajor = 0; - toolMinor = 0; - break; - } - - if (mCalibration.haveToolSizeIsSummed && mCalibration.toolSizeIsSummed) { - toolMajor /= pointerCount; - toolMinor /= pointerCount; - } - - // Pressure - float rawPressure; - switch (mCalibration.pressureSource) { - case Calibration::PRESSURE_SOURCE_PRESSURE: - rawPressure = in.pressure; - break; - case Calibration::PRESSURE_SOURCE_TOUCH: - rawPressure = in.touchMajor; - break; - default: - rawPressure = 0; - } - - float pressure; - switch (mCalibration.pressureCalibration) { - case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - pressure = rawPressure * mLocked.pressureScale; - break; - default: - pressure = 1; - break; - } - - // TouchMajor and TouchMinor - float touchMajor, touchMinor; - switch (mCalibration.touchSizeCalibration) { - case Calibration::TOUCH_SIZE_CALIBRATION_GEOMETRIC: - touchMajor = in.touchMajor * mLocked.geometricScale; - if (mRawAxes.touchMinor.valid) { - touchMinor = in.touchMinor * mLocked.geometricScale; - } else { - touchMinor = touchMajor; - } - break; - case Calibration::TOUCH_SIZE_CALIBRATION_PRESSURE: - touchMajor = toolMajor * pressure; - touchMinor = toolMinor * pressure; - break; - default: - touchMajor = 0; - touchMinor = 0; - break; - } - - if (touchMajor > toolMajor) { - touchMajor = toolMajor; - } - if (touchMinor > toolMinor) { - touchMinor = toolMinor; - } - - // Size - float size; - switch (mCalibration.sizeCalibration) { - case Calibration::SIZE_CALIBRATION_NORMALIZED: { - float rawSize = mRawAxes.toolMinor.valid - ? avg(in.toolMajor, in.toolMinor) - : in.toolMajor; - size = rawSize * mLocked.sizeScale; - break; - } - default: - size = 0; - break; - } - - // Orientation - float orientation; - switch (mCalibration.orientationCalibration) { - case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - orientation = in.orientation * mLocked.orientationScale; - break; - default: - orientation = 0; - } - - // Adjust coords for orientation. - switch (mLocked.surfaceOrientation) { - case InputReaderPolicyInterface::ROTATION_90: { - float xTemp = x; - x = y; - y = mLocked.surfaceWidth - xTemp; - orientation -= M_PI_2; - if (orientation < - M_PI_2) { - orientation += M_PI; - } - break; - } - case InputReaderPolicyInterface::ROTATION_180: { - x = mLocked.surfaceWidth - x; - y = mLocked.surfaceHeight - y; - orientation = - orientation; - break; - } - case InputReaderPolicyInterface::ROTATION_270: { - float xTemp = x; - x = mLocked.surfaceHeight - y; - y = xTemp; - orientation += M_PI_2; - if (orientation > M_PI_2) { - orientation -= M_PI; - } - break; - } - } - - // Write output coords. - PointerCoords& out = pointerCoords[outIndex]; - out.x = x; - out.y = y; - out.pressure = pressure; - out.size = size; - out.touchMajor = touchMajor; - out.touchMinor = touchMinor; - out.toolMajor = toolMajor; - out.toolMinor = toolMinor; - out.orientation = orientation; - - pointerIds[outIndex] = int32_t(id); - - if (id == changedId) { - motionEventAction |= outIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - } - } - - // Check edge flags by looking only at the first pointer since the flags are - // global to the event. - if (motionEventAction == AMOTION_EVENT_ACTION_DOWN) { - if (pointerCoords[0].x <= 0) { - motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_LEFT; - } else if (pointerCoords[0].x >= mLocked.orientedSurfaceWidth) { - motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_RIGHT; - } - if (pointerCoords[0].y <= 0) { - motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_TOP; - } else if (pointerCoords[0].y >= mLocked.orientedSurfaceHeight) { - motionEventEdgeFlags |= AMOTION_EVENT_EDGE_FLAG_BOTTOM; - } - } - - xPrecision = mLocked.orientedXPrecision; - yPrecision = mLocked.orientedYPrecision; - } // release lock - - getDispatcher()->notifyMotion(when, getDeviceId(), getSources(), policyFlags, - motionEventAction, 0, getContext()->getGlobalMetaState(), motionEventEdgeFlags, - pointerCount, pointerIds, pointerCoords, - xPrecision, yPrecision, mDownTime); -} - -bool TouchInputMapper::isPointInsideSurfaceLocked(int32_t x, int32_t y) { - if (mRawAxes.x.valid && mRawAxes.y.valid) { - return x >= mRawAxes.x.minValue && x <= mRawAxes.x.maxValue - && y >= mRawAxes.y.minValue && y <= mRawAxes.y.maxValue; - } - return true; -} - -const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHitLocked( - int32_t x, int32_t y) { - size_t numVirtualKeys = mLocked.virtualKeys.size(); - for (size_t i = 0; i < numVirtualKeys; i++) { - const VirtualKey& virtualKey = mLocked.virtualKeys[i]; - -#if DEBUG_VIRTUAL_KEYS - LOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, " - "left=%d, top=%d, right=%d, bottom=%d", - x, y, - virtualKey.keyCode, virtualKey.scanCode, - virtualKey.hitLeft, virtualKey.hitTop, - virtualKey.hitRight, virtualKey.hitBottom); -#endif - - if (virtualKey.isHit(x, y)) { - return & virtualKey; - } - } - - return NULL; -} - -void TouchInputMapper::calculatePointerIds() { - uint32_t currentPointerCount = mCurrentTouch.pointerCount; - uint32_t lastPointerCount = mLastTouch.pointerCount; - - if (currentPointerCount == 0) { - // No pointers to assign. - mCurrentTouch.idBits.clear(); - } else if (lastPointerCount == 0) { - // All pointers are new. - mCurrentTouch.idBits.clear(); - for (uint32_t i = 0; i < currentPointerCount; i++) { - mCurrentTouch.pointers[i].id = i; - mCurrentTouch.idToIndex[i] = i; - mCurrentTouch.idBits.markBit(i); - } - } else if (currentPointerCount == 1 && lastPointerCount == 1) { - // Only one pointer and no change in count so it must have the same id as before. - uint32_t id = mLastTouch.pointers[0].id; - mCurrentTouch.pointers[0].id = id; - mCurrentTouch.idToIndex[id] = 0; - mCurrentTouch.idBits.value = BitSet32::valueForBit(id); - } else { - // General case. - // We build a heap of squared euclidean distances between current and last pointers - // associated with the current and last pointer indices. Then, we find the best - // match (by distance) for each current pointer. - PointerDistanceHeapElement heap[MAX_POINTERS * MAX_POINTERS]; - - uint32_t heapSize = 0; - for (uint32_t currentPointerIndex = 0; currentPointerIndex < currentPointerCount; - currentPointerIndex++) { - for (uint32_t lastPointerIndex = 0; lastPointerIndex < lastPointerCount; - lastPointerIndex++) { - int64_t deltaX = mCurrentTouch.pointers[currentPointerIndex].x - - mLastTouch.pointers[lastPointerIndex].x; - int64_t deltaY = mCurrentTouch.pointers[currentPointerIndex].y - - mLastTouch.pointers[lastPointerIndex].y; - - uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); - - // Insert new element into the heap (sift up). - heap[heapSize].currentPointerIndex = currentPointerIndex; - heap[heapSize].lastPointerIndex = lastPointerIndex; - heap[heapSize].distance = distance; - heapSize += 1; - } - } - - // Heapify - for (uint32_t startIndex = heapSize / 2; startIndex != 0; ) { - startIndex -= 1; - for (uint32_t parentIndex = startIndex; ;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize - && heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - } - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - initial distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", - i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, - heap[i].distance); - } -#endif - - // Pull matches out by increasing order of distance. - // To avoid reassigning pointers that have already been matched, the loop keeps track - // of which last and current pointers have been matched using the matchedXXXBits variables. - // It also tracks the used pointer id bits. - BitSet32 matchedLastBits(0); - BitSet32 matchedCurrentBits(0); - BitSet32 usedIdBits(0); - bool first = true; - for (uint32_t i = min(currentPointerCount, lastPointerCount); i > 0; i--) { - for (;;) { - if (first) { - // The first time through the loop, we just consume the root element of - // the heap (the one with smallest distance). - first = false; - } else { - // Previous iterations consumed the root element of the heap. - // Pop root element off of the heap (sift down). - heapSize -= 1; - assert(heapSize > 0); - - // Sift down. - heap[0] = heap[heapSize]; - for (uint32_t parentIndex = 0; ;) { - uint32_t childIndex = parentIndex * 2 + 1; - if (childIndex >= heapSize) { - break; - } - - if (childIndex + 1 < heapSize - && heap[childIndex + 1].distance < heap[childIndex].distance) { - childIndex += 1; - } - - if (heap[parentIndex].distance <= heap[childIndex].distance) { - break; - } - - swap(heap[parentIndex], heap[childIndex]); - parentIndex = childIndex; - } - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - reduced distance min-heap: size=%d", heapSize); - for (size_t i = 0; i < heapSize; i++) { - LOGD(" heap[%d]: cur=%d, last=%d, distance=%lld", - i, heap[i].currentPointerIndex, heap[i].lastPointerIndex, - heap[i].distance); - } -#endif - } - - uint32_t currentPointerIndex = heap[0].currentPointerIndex; - if (matchedCurrentBits.hasBit(currentPointerIndex)) continue; // already matched - - uint32_t lastPointerIndex = heap[0].lastPointerIndex; - if (matchedLastBits.hasBit(lastPointerIndex)) continue; // already matched - - matchedCurrentBits.markBit(currentPointerIndex); - matchedLastBits.markBit(lastPointerIndex); - - uint32_t id = mLastTouch.pointers[lastPointerIndex].id; - mCurrentTouch.pointers[currentPointerIndex].id = id; - mCurrentTouch.idToIndex[id] = currentPointerIndex; - usedIdBits.markBit(id); - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - matched: cur=%d, last=%d, id=%d, distance=%lld", - lastPointerIndex, currentPointerIndex, id, heap[0].distance); -#endif - break; - } - } - - // Assign fresh ids to new pointers. - if (currentPointerCount > lastPointerCount) { - for (uint32_t i = currentPointerCount - lastPointerCount; ;) { - uint32_t currentPointerIndex = matchedCurrentBits.firstUnmarkedBit(); - uint32_t id = usedIdBits.firstUnmarkedBit(); - - mCurrentTouch.pointers[currentPointerIndex].id = id; - mCurrentTouch.idToIndex[id] = currentPointerIndex; - usedIdBits.markBit(id); - -#if DEBUG_POINTER_ASSIGNMENT - LOGD("calculatePointerIds - assigned: cur=%d, id=%d", - currentPointerIndex, id); -#endif - - if (--i == 0) break; // done - matchedCurrentBits.markBit(currentPointerIndex); - } - } - - // Fix id bits. - mCurrentTouch.idBits = usedIdBits; - } -} - -/* Special hack for devices that have bad screen data: if one of the - * points has moved more than a screen height from the last position, - * then drop it. */ -bool TouchInputMapper::applyBadTouchFilter() { - // This hack requires valid axis parameters. - if (! mRawAxes.y.valid) { - return false; - } - - uint32_t pointerCount = mCurrentTouch.pointerCount; - - // Nothing to do if there are no points. - if (pointerCount == 0) { - return false; - } - - // Don't do anything if a finger is going down or up. We run - // here before assigning pointer IDs, so there isn't a good - // way to do per-finger matching. - if (pointerCount != mLastTouch.pointerCount) { - return false; - } - - // We consider a single movement across more than a 7/16 of - // the long size of the screen to be bad. This was a magic value - // determined by looking at the maximum distance it is feasible - // to actually move in one sample. - int32_t maxDeltaY = mRawAxes.y.getRange() * 7 / 16; - - // XXX The original code in InputDevice.java included commented out - // code for testing the X axis. Note that when we drop a point - // we don't actually restore the old X either. Strange. - // The old code also tries to track when bad points were previously - // detected but it turns out that due to the placement of a "break" - // at the end of the loop, we never set mDroppedBadPoint to true - // so it is effectively dead code. - // Need to figure out if the old code is busted or just overcomplicated - // but working as intended. - - // Look through all new points and see if any are farther than - // acceptable from all previous points. - for (uint32_t i = pointerCount; i-- > 0; ) { - int32_t y = mCurrentTouch.pointers[i].y; - int32_t closestY = INT_MAX; - int32_t closestDeltaY = 0; - -#if DEBUG_HACKS - LOGD("BadTouchFilter: Looking at next point #%d: y=%d", i, y); -#endif - - for (uint32_t j = pointerCount; j-- > 0; ) { - int32_t lastY = mLastTouch.pointers[j].y; - int32_t deltaY = abs(y - lastY); - -#if DEBUG_HACKS - LOGD("BadTouchFilter: Comparing with last point #%d: y=%d deltaY=%d", - j, lastY, deltaY); -#endif - - if (deltaY < maxDeltaY) { - goto SkipSufficientlyClosePoint; - } - if (deltaY < closestDeltaY) { - closestDeltaY = deltaY; - closestY = lastY; - } - } - - // Must not have found a close enough match. -#if DEBUG_HACKS - LOGD("BadTouchFilter: Dropping bad point #%d: newY=%d oldY=%d deltaY=%d maxDeltaY=%d", - i, y, closestY, closestDeltaY, maxDeltaY); -#endif - - mCurrentTouch.pointers[i].y = closestY; - return true; // XXX original code only corrects one point - - SkipSufficientlyClosePoint: ; - } - - // No change. - return false; -} - -/* Special hack for devices that have bad screen data: drop points where - * the coordinate value for one axis has jumped to the other pointer's location. - */ -bool TouchInputMapper::applyJumpyTouchFilter() { - // This hack requires valid axis parameters. - if (! mRawAxes.y.valid) { - return false; - } - - uint32_t pointerCount = mCurrentTouch.pointerCount; - if (mLastTouch.pointerCount != pointerCount) { -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Different pointer count %d -> %d", - mLastTouch.pointerCount, pointerCount); - for (uint32_t i = 0; i < pointerCount; i++) { - LOGD(" Pointer %d (%d, %d)", i, - mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y); - } -#endif - - if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_TRANSITION_DROPS) { - if (mLastTouch.pointerCount == 1 && pointerCount == 2) { - // Just drop the first few events going from 1 to 2 pointers. - // They're bad often enough that they're not worth considering. - mCurrentTouch.pointerCount = 1; - mJumpyTouchFilter.jumpyPointsDropped += 1; - -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Pointer 2 dropped"); -#endif - return true; - } else if (mLastTouch.pointerCount == 2 && pointerCount == 1) { - // The event when we go from 2 -> 1 tends to be messed up too - mCurrentTouch.pointerCount = 2; - mCurrentTouch.pointers[0] = mLastTouch.pointers[0]; - mCurrentTouch.pointers[1] = mLastTouch.pointers[1]; - mJumpyTouchFilter.jumpyPointsDropped += 1; - -#if DEBUG_HACKS - for (int32_t i = 0; i < 2; i++) { - LOGD("JumpyTouchFilter: Pointer %d replaced (%d, %d)", i, - mCurrentTouch.pointers[i].x, mCurrentTouch.pointers[i].y); - } -#endif - return true; - } - } - // Reset jumpy points dropped on other transitions or if limit exceeded. - mJumpyTouchFilter.jumpyPointsDropped = 0; - -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Transition - drop limit reset"); -#endif - return false; - } - - // We have the same number of pointers as last time. - // A 'jumpy' point is one where the coordinate value for one axis - // has jumped to the other pointer's location. No need to do anything - // else if we only have one pointer. - if (pointerCount < 2) { - return false; - } - - if (mJumpyTouchFilter.jumpyPointsDropped < JUMPY_DROP_LIMIT) { - int jumpyEpsilon = mRawAxes.y.getRange() / JUMPY_EPSILON_DIVISOR; - - // We only replace the single worst jumpy point as characterized by pointer distance - // in a single axis. - int32_t badPointerIndex = -1; - int32_t badPointerReplacementIndex = -1; - int32_t badPointerDistance = INT_MIN; // distance to be corrected - - for (uint32_t i = pointerCount; i-- > 0; ) { - int32_t x = mCurrentTouch.pointers[i].x; - int32_t y = mCurrentTouch.pointers[i].y; - -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Point %d (%d, %d)", i, x, y); -#endif - - // Check if a touch point is too close to another's coordinates - bool dropX = false, dropY = false; - for (uint32_t j = 0; j < pointerCount; j++) { - if (i == j) { - continue; - } - - if (abs(x - mCurrentTouch.pointers[j].x) <= jumpyEpsilon) { - dropX = true; - break; - } - - if (abs(y - mCurrentTouch.pointers[j].y) <= jumpyEpsilon) { - dropY = true; - break; - } - } - if (! dropX && ! dropY) { - continue; // not jumpy - } - - // Find a replacement candidate by comparing with older points on the - // complementary (non-jumpy) axis. - int32_t distance = INT_MIN; // distance to be corrected - int32_t replacementIndex = -1; - - if (dropX) { - // X looks too close. Find an older replacement point with a close Y. - int32_t smallestDeltaY = INT_MAX; - for (uint32_t j = 0; j < pointerCount; j++) { - int32_t deltaY = abs(y - mLastTouch.pointers[j].y); - if (deltaY < smallestDeltaY) { - smallestDeltaY = deltaY; - replacementIndex = j; - } - } - distance = abs(x - mLastTouch.pointers[replacementIndex].x); - } else { - // Y looks too close. Find an older replacement point with a close X. - int32_t smallestDeltaX = INT_MAX; - for (uint32_t j = 0; j < pointerCount; j++) { - int32_t deltaX = abs(x - mLastTouch.pointers[j].x); - if (deltaX < smallestDeltaX) { - smallestDeltaX = deltaX; - replacementIndex = j; - } - } - distance = abs(y - mLastTouch.pointers[replacementIndex].y); - } - - // If replacing this pointer would correct a worse error than the previous ones - // considered, then use this replacement instead. - if (distance > badPointerDistance) { - badPointerIndex = i; - badPointerReplacementIndex = replacementIndex; - badPointerDistance = distance; - } - } - - // Correct the jumpy pointer if one was found. - if (badPointerIndex >= 0) { -#if DEBUG_HACKS - LOGD("JumpyTouchFilter: Replacing bad pointer %d with (%d, %d)", - badPointerIndex, - mLastTouch.pointers[badPointerReplacementIndex].x, - mLastTouch.pointers[badPointerReplacementIndex].y); -#endif - - mCurrentTouch.pointers[badPointerIndex].x = - mLastTouch.pointers[badPointerReplacementIndex].x; - mCurrentTouch.pointers[badPointerIndex].y = - mLastTouch.pointers[badPointerReplacementIndex].y; - mJumpyTouchFilter.jumpyPointsDropped += 1; - return true; - } - } - - mJumpyTouchFilter.jumpyPointsDropped = 0; - return false; -} - -/* Special hack for devices that have bad screen data: aggregate and - * compute averages of the coordinate data, to reduce the amount of - * jitter seen by applications. */ -void TouchInputMapper::applyAveragingTouchFilter() { - for (uint32_t currentIndex = 0; currentIndex < mCurrentTouch.pointerCount; currentIndex++) { - uint32_t id = mCurrentTouch.pointers[currentIndex].id; - int32_t x = mCurrentTouch.pointers[currentIndex].x; - int32_t y = mCurrentTouch.pointers[currentIndex].y; - int32_t pressure; - switch (mCalibration.pressureSource) { - case Calibration::PRESSURE_SOURCE_PRESSURE: - pressure = mCurrentTouch.pointers[currentIndex].pressure; - break; - case Calibration::PRESSURE_SOURCE_TOUCH: - pressure = mCurrentTouch.pointers[currentIndex].touchMajor; - break; - default: - pressure = 1; - break; - } - - if (mLastTouch.idBits.hasBit(id)) { - // Pointer was down before and is still down now. - // Compute average over history trace. - uint32_t start = mAveragingTouchFilter.historyStart[id]; - uint32_t end = mAveragingTouchFilter.historyEnd[id]; - - int64_t deltaX = x - mAveragingTouchFilter.historyData[end].pointers[id].x; - int64_t deltaY = y - mAveragingTouchFilter.historyData[end].pointers[id].y; - uint64_t distance = uint64_t(deltaX * deltaX + deltaY * deltaY); - -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - Distance from last sample: %lld", - id, distance); -#endif - - if (distance < AVERAGING_DISTANCE_LIMIT) { - // Increment end index in preparation for recording new historical data. - end += 1; - if (end > AVERAGING_HISTORY_SIZE) { - end = 0; - } - - // If the end index has looped back to the start index then we have filled - // the historical trace up to the desired size so we drop the historical - // data at the start of the trace. - if (end == start) { - start += 1; - if (start > AVERAGING_HISTORY_SIZE) { - start = 0; - } - } - - // Add the raw data to the historical trace. - mAveragingTouchFilter.historyStart[id] = start; - mAveragingTouchFilter.historyEnd[id] = end; - mAveragingTouchFilter.historyData[end].pointers[id].x = x; - mAveragingTouchFilter.historyData[end].pointers[id].y = y; - mAveragingTouchFilter.historyData[end].pointers[id].pressure = pressure; - - // Average over all historical positions in the trace by total pressure. - int32_t averagedX = 0; - int32_t averagedY = 0; - int32_t totalPressure = 0; - for (;;) { - int32_t historicalX = mAveragingTouchFilter.historyData[start].pointers[id].x; - int32_t historicalY = mAveragingTouchFilter.historyData[start].pointers[id].y; - int32_t historicalPressure = mAveragingTouchFilter.historyData[start] - .pointers[id].pressure; - - averagedX += historicalX * historicalPressure; - averagedY += historicalY * historicalPressure; - totalPressure += historicalPressure; - - if (start == end) { - break; - } - - start += 1; - if (start > AVERAGING_HISTORY_SIZE) { - start = 0; - } - } - - if (totalPressure != 0) { - averagedX /= totalPressure; - averagedY /= totalPressure; - -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - " - "totalPressure=%d, averagedX=%d, averagedY=%d", id, totalPressure, - averagedX, averagedY); -#endif - - mCurrentTouch.pointers[currentIndex].x = averagedX; - mCurrentTouch.pointers[currentIndex].y = averagedY; - } - } else { -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - Exceeded max distance", id); -#endif - } - } else { -#if DEBUG_HACKS - LOGD("AveragingTouchFilter: Pointer id %d - Pointer went up", id); -#endif - } - - // Reset pointer history. - mAveragingTouchFilter.historyStart[id] = 0; - mAveragingTouchFilter.historyEnd[id] = 0; - mAveragingTouchFilter.historyData[0].pointers[id].x = x; - mAveragingTouchFilter.historyData[0].pointers[id].y = y; - mAveragingTouchFilter.historyData[0].pointers[id].pressure = pressure; - } -} - -int32_t TouchInputMapper::getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - { // acquire lock - AutoMutex _l(mLock); - - if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.keyCode == keyCode) { - return AKEY_STATE_VIRTUAL; - } - - size_t numVirtualKeys = mLocked.virtualKeys.size(); - for (size_t i = 0; i < numVirtualKeys; i++) { - const VirtualKey& virtualKey = mLocked.virtualKeys[i]; - if (virtualKey.keyCode == keyCode) { - return AKEY_STATE_UP; - } - } - } // release lock - - return AKEY_STATE_UNKNOWN; -} - -int32_t TouchInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - { // acquire lock - AutoMutex _l(mLock); - - if (mLocked.currentVirtualKey.down && mLocked.currentVirtualKey.scanCode == scanCode) { - return AKEY_STATE_VIRTUAL; - } - - size_t numVirtualKeys = mLocked.virtualKeys.size(); - for (size_t i = 0; i < numVirtualKeys; i++) { - const VirtualKey& virtualKey = mLocked.virtualKeys[i]; - if (virtualKey.scanCode == scanCode) { - return AKEY_STATE_UP; - } - } - } // release lock - - return AKEY_STATE_UNKNOWN; -} - -bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - { // acquire lock - AutoMutex _l(mLock); - - size_t numVirtualKeys = mLocked.virtualKeys.size(); - for (size_t i = 0; i < numVirtualKeys; i++) { - const VirtualKey& virtualKey = mLocked.virtualKeys[i]; - - for (size_t i = 0; i < numCodes; i++) { - if (virtualKey.keyCode == keyCodes[i]) { - outFlags[i] = 1; - } - } - } - } // release lock - - return true; -} - - -// --- SingleTouchInputMapper --- - -SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : - TouchInputMapper(device, associatedDisplayId) { - initialize(); -} - -SingleTouchInputMapper::~SingleTouchInputMapper() { -} - -void SingleTouchInputMapper::initialize() { - mAccumulator.clear(); - - mDown = false; - mX = 0; - mY = 0; - mPressure = 0; // default to 0 for devices that don't report pressure - mToolWidth = 0; // default to 0 for devices that don't report tool width -} - -void SingleTouchInputMapper::reset() { - TouchInputMapper::reset(); - - initialize(); - } - -void SingleTouchInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_KEY: - switch (rawEvent->scanCode) { - case BTN_TOUCH: - mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH; - mAccumulator.btnTouch = rawEvent->value != 0; - // Don't sync immediately. Wait until the next SYN_REPORT since we might - // not have received valid position information yet. This logic assumes that - // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet. - break; - } - break; - - case EV_ABS: - switch (rawEvent->scanCode) { - case ABS_X: - mAccumulator.fields |= Accumulator::FIELD_ABS_X; - mAccumulator.absX = rawEvent->value; - break; - case ABS_Y: - mAccumulator.fields |= Accumulator::FIELD_ABS_Y; - mAccumulator.absY = rawEvent->value; - break; - case ABS_PRESSURE: - mAccumulator.fields |= Accumulator::FIELD_ABS_PRESSURE; - mAccumulator.absPressure = rawEvent->value; - break; - case ABS_TOOL_WIDTH: - mAccumulator.fields |= Accumulator::FIELD_ABS_TOOL_WIDTH; - mAccumulator.absToolWidth = rawEvent->value; - break; - } - break; - - case EV_SYN: - switch (rawEvent->scanCode) { - case SYN_REPORT: - sync(rawEvent->when); - break; - } - break; - } -} - -void SingleTouchInputMapper::sync(nsecs_t when) { - uint32_t fields = mAccumulator.fields; - if (fields == 0) { - return; // no new state changes, so nothing to do - } - - if (fields & Accumulator::FIELD_BTN_TOUCH) { - mDown = mAccumulator.btnTouch; - } - - if (fields & Accumulator::FIELD_ABS_X) { - mX = mAccumulator.absX; - } - - if (fields & Accumulator::FIELD_ABS_Y) { - mY = mAccumulator.absY; - } - - if (fields & Accumulator::FIELD_ABS_PRESSURE) { - mPressure = mAccumulator.absPressure; - } - - if (fields & Accumulator::FIELD_ABS_TOOL_WIDTH) { - mToolWidth = mAccumulator.absToolWidth; - } - - mCurrentTouch.clear(); - - if (mDown) { - mCurrentTouch.pointerCount = 1; - mCurrentTouch.pointers[0].id = 0; - mCurrentTouch.pointers[0].x = mX; - mCurrentTouch.pointers[0].y = mY; - mCurrentTouch.pointers[0].pressure = mPressure; - mCurrentTouch.pointers[0].touchMajor = 0; - mCurrentTouch.pointers[0].touchMinor = 0; - mCurrentTouch.pointers[0].toolMajor = mToolWidth; - mCurrentTouch.pointers[0].toolMinor = mToolWidth; - mCurrentTouch.pointers[0].orientation = 0; - mCurrentTouch.idToIndex[0] = 0; - mCurrentTouch.idBits.markBit(0); - } - - syncTouch(when, true); - - mAccumulator.clear(); -} - -void SingleTouchInputMapper::configureRawAxes() { - TouchInputMapper::configureRawAxes(); - - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_X, & mRawAxes.x); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_Y, & mRawAxes.y); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mRawAxes.pressure); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mRawAxes.toolMajor); -} - - -// --- MultiTouchInputMapper --- - -MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device, int32_t associatedDisplayId) : - TouchInputMapper(device, associatedDisplayId) { - initialize(); -} - -MultiTouchInputMapper::~MultiTouchInputMapper() { -} - -void MultiTouchInputMapper::initialize() { - mAccumulator.clear(); -} - -void MultiTouchInputMapper::reset() { - TouchInputMapper::reset(); - - initialize(); -} - -void MultiTouchInputMapper::process(const RawEvent* rawEvent) { - switch (rawEvent->type) { - case EV_ABS: { - uint32_t pointerIndex = mAccumulator.pointerCount; - Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex]; - - switch (rawEvent->scanCode) { - case ABS_MT_POSITION_X: - pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_X; - pointer->absMTPositionX = rawEvent->value; - break; - case ABS_MT_POSITION_Y: - pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y; - pointer->absMTPositionY = rawEvent->value; - break; - case ABS_MT_TOUCH_MAJOR: - pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR; - pointer->absMTTouchMajor = rawEvent->value; - break; - case ABS_MT_TOUCH_MINOR: - pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR; - pointer->absMTTouchMinor = rawEvent->value; - break; - case ABS_MT_WIDTH_MAJOR: - pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR; - pointer->absMTWidthMajor = rawEvent->value; - break; - case ABS_MT_WIDTH_MINOR: - pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR; - pointer->absMTWidthMinor = rawEvent->value; - break; - case ABS_MT_ORIENTATION: - pointer->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION; - pointer->absMTOrientation = rawEvent->value; - break; - case ABS_MT_TRACKING_ID: - pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID; - pointer->absMTTrackingId = rawEvent->value; - break; - case ABS_MT_PRESSURE: - pointer->fields |= Accumulator::FIELD_ABS_MT_PRESSURE; - pointer->absMTPressure = rawEvent->value; - break; - } - break; - } - - case EV_SYN: - switch (rawEvent->scanCode) { - case SYN_MT_REPORT: { - // MultiTouch Sync: The driver has returned all data for *one* of the pointers. - uint32_t pointerIndex = mAccumulator.pointerCount; - - if (mAccumulator.pointers[pointerIndex].fields) { - if (pointerIndex == MAX_POINTERS) { - LOGW("MultiTouch device driver returned more than maximum of %d pointers.", - MAX_POINTERS); - } else { - pointerIndex += 1; - mAccumulator.pointerCount = pointerIndex; - } - } - - mAccumulator.pointers[pointerIndex].clear(); - break; - } - - case SYN_REPORT: - sync(rawEvent->when); - break; - } - break; - } -} - -void MultiTouchInputMapper::sync(nsecs_t when) { - static const uint32_t REQUIRED_FIELDS = - Accumulator::FIELD_ABS_MT_POSITION_X | Accumulator::FIELD_ABS_MT_POSITION_Y; - - uint32_t inCount = mAccumulator.pointerCount; - uint32_t outCount = 0; - bool havePointerIds = true; - - mCurrentTouch.clear(); - - for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) { - const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex]; - uint32_t fields = inPointer.fields; - - if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) { - // Some drivers send empty MT sync packets without X / Y to indicate a pointer up. - // Drop this finger. - continue; - } - - PointerData& outPointer = mCurrentTouch.pointers[outCount]; - outPointer.x = inPointer.absMTPositionX; - outPointer.y = inPointer.absMTPositionY; - - if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) { - if (inPointer.absMTPressure <= 0) { - // Some devices send sync packets with X / Y but with a 0 pressure to indicate - // a pointer going up. Drop this finger. - continue; - } - outPointer.pressure = inPointer.absMTPressure; - } else { - // Default pressure to 0 if absent. - outPointer.pressure = 0; - } - - if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) { - if (inPointer.absMTTouchMajor <= 0) { - // Some devices send sync packets with X / Y but with a 0 touch major to indicate - // a pointer going up. Drop this finger. - continue; - } - outPointer.touchMajor = inPointer.absMTTouchMajor; - } else { - // Default touch area to 0 if absent. - outPointer.touchMajor = 0; - } - - if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) { - outPointer.touchMinor = inPointer.absMTTouchMinor; - } else { - // Assume touch area is circular. - outPointer.touchMinor = outPointer.touchMajor; - } - - if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) { - outPointer.toolMajor = inPointer.absMTWidthMajor; - } else { - // Default tool area to 0 if absent. - outPointer.toolMajor = 0; - } - - if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) { - outPointer.toolMinor = inPointer.absMTWidthMinor; - } else { - // Assume tool area is circular. - outPointer.toolMinor = outPointer.toolMajor; - } - - if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) { - outPointer.orientation = inPointer.absMTOrientation; - } else { - // Default orientation to vertical if absent. - outPointer.orientation = 0; - } - - // Assign pointer id using tracking id if available. - if (havePointerIds) { - if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) { - uint32_t id = uint32_t(inPointer.absMTTrackingId); - - if (id > MAX_POINTER_ID) { -#if DEBUG_POINTERS - LOGD("Pointers: Ignoring driver provided pointer id %d because " - "it is larger than max supported id %d", - id, MAX_POINTER_ID); -#endif - havePointerIds = false; - } - else { - outPointer.id = id; - mCurrentTouch.idToIndex[id] = outCount; - mCurrentTouch.idBits.markBit(id); - } - } else { - havePointerIds = false; - } - } - - outCount += 1; - } - - mCurrentTouch.pointerCount = outCount; - - syncTouch(when, havePointerIds); - - mAccumulator.clear(); -} - -void MultiTouchInputMapper::configureRawAxes() { - TouchInputMapper::configureRawAxes(); - - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mRawAxes.x); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mRawAxes.y); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mRawAxes.touchMajor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mRawAxes.touchMinor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mRawAxes.toolMajor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mRawAxes.toolMinor); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mRawAxes.orientation); - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mRawAxes.pressure); -} - - -} // namespace android diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp index 2c6346ebeeb7..5c57a76f40f4 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/ui/InputTransport.cpp @@ -35,8 +35,12 @@ static const int DEFAULT_MESSAGE_BUFFER_SIZE = 16384; static const char INPUT_SIGNAL_DISPATCH = 'D'; // Signal sent by the consumer to the producer to inform it that it has finished -// consuming the most recent message. -static const char INPUT_SIGNAL_FINISHED = 'f'; +// consuming the most recent message and it handled it. +static const char INPUT_SIGNAL_FINISHED_HANDLED = 'f'; + +// Signal sent by the consumer to the producer to inform it that it has finished +// consuming the most recent message but it did not handle it. +static const char INPUT_SIGNAL_FINISHED_UNHANDLED = 'u'; // --- InputChannel --- @@ -408,7 +412,8 @@ status_t InputPublisher::publishMotionEvent( // Cache essential information about the motion event to ensure that a malicious consumer // cannot confuse the publisher by modifying the contents of the shared memory buffer while // it is being updated. - if (action == AMOTION_EVENT_ACTION_MOVE) { + if (action == AMOTION_EVENT_ACTION_MOVE + || action == AMOTION_EVENT_ACTION_HOVER_MOVE) { mMotionEventPointerCount = pointerCount; mMotionEventSampleDataStride = InputMessage::sampleDataStride(pointerCount); mMotionEventSampleDataTail = InputMessage::sampleDataPtrIncrement( @@ -497,7 +502,7 @@ status_t InputPublisher::sendDispatchSignal() { return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH); } -status_t InputPublisher::receiveFinishedSignal() { +status_t InputPublisher::receiveFinishedSignal(bool* outHandled) { #if DEBUG_TRANSPORT_ACTIONS LOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().string()); @@ -506,9 +511,14 @@ status_t InputPublisher::receiveFinishedSignal() { char signal; status_t result = mChannel->receiveSignal(& signal); if (result) { + *outHandled = false; return result; } - if (signal != INPUT_SIGNAL_FINISHED) { + if (signal == INPUT_SIGNAL_FINISHED_HANDLED) { + *outHandled = true; + } else if (signal == INPUT_SIGNAL_FINISHED_UNHANDLED) { + *outHandled = false; + } else { LOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer", mChannel->getName().string(), signal); return UNKNOWN_ERROR; @@ -626,13 +636,15 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent* return OK; } -status_t InputConsumer::sendFinishedSignal() { +status_t InputConsumer::sendFinishedSignal(bool handled) { #if DEBUG_TRANSPORT_ACTIONS - LOGD("channel '%s' consumer ~ sendFinishedSignal", - mChannel->getName().string()); + LOGD("channel '%s' consumer ~ sendFinishedSignal: handled=%d", + mChannel->getName().string(), handled); #endif - return mChannel->sendSignal(INPUT_SIGNAL_FINISHED); + return mChannel->sendSignal(handled + ? INPUT_SIGNAL_FINISHED_HANDLED + : INPUT_SIGNAL_FINISHED_UNHANDLED); } status_t InputConsumer::receiveDispatchSignal() { diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp index e891181c828a..2decfe93215a 100644 --- a/libs/ui/KeyCharacterMap.cpp +++ b/libs/ui/KeyCharacterMap.cpp @@ -1,263 +1,849 @@ -#define LOG_TAG "KeyCharacterMap" +/* + * 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. + */ -#include <ui/KeyCharacterMap.h> -#include <cutils/properties.h> +#define LOG_TAG "KeyCharacterMap" -#include <utils/Log.h> -#include <sys/types.h> -#include <unistd.h> #include <stdlib.h> -#include <fcntl.h> -#include <limits.h> #include <string.h> +#include <android/keycodes.h> +#include <ui/Keyboard.h> +#include <ui/KeyCharacterMap.h> +#include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + +// Enables debug output for mapping. +#define DEBUG_MAPPING 0 + + +namespace android { -struct Header -{ - char magic[8]; - unsigned int endian; - unsigned int version; - unsigned int keycount; - unsigned char kbdtype; - char padding[11]; +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r,:"; + +struct Modifier { + const char* label; + int32_t metaState; +}; +static const Modifier modifiers[] = { + { "shift", AMETA_SHIFT_ON }, + { "lshift", AMETA_SHIFT_LEFT_ON }, + { "rshift", AMETA_SHIFT_RIGHT_ON }, + { "alt", AMETA_ALT_ON }, + { "lalt", AMETA_ALT_LEFT_ON }, + { "ralt", AMETA_ALT_RIGHT_ON }, + { "ctrl", AMETA_CTRL_ON }, + { "lctrl", AMETA_CTRL_LEFT_ON }, + { "rctrl", AMETA_CTRL_RIGHT_ON }, + { "meta", AMETA_META_ON }, + { "lmeta", AMETA_META_LEFT_ON }, + { "rmeta", AMETA_META_RIGHT_ON }, + { "sym", AMETA_SYM_ON }, + { "fn", AMETA_FUNCTION_ON }, + { "capslock", AMETA_CAPS_LOCK_ON }, + { "numlock", AMETA_NUM_LOCK_ON }, + { "scrolllock", AMETA_SCROLL_LOCK_ON }, }; -KeyCharacterMap::KeyCharacterMap() -{ +#if DEBUG_MAPPING +static String8 toString(const char16_t* chars, size_t numChars) { + String8 result; + for (size_t i = 0; i < numChars; i++) { + result.appendFormat(i == 0 ? "%d" : ", %d", chars[i]); + } + return result; } +#endif -KeyCharacterMap::~KeyCharacterMap() -{ - free(m_keys); + +// --- KeyCharacterMap --- + +KeyCharacterMap::KeyCharacterMap() : + mType(KEYBOARD_TYPE_UNKNOWN) { } -unsigned short -KeyCharacterMap::get(int keycode, int meta) -{ - Key* k = find_key(keycode); - if (k != NULL) { - return k->data[meta & META_MASK]; +KeyCharacterMap::~KeyCharacterMap() { + for (size_t i = 0; i < mKeys.size(); i++) { + Key* key = mKeys.editValueAt(i); + delete key; } - return 0; } -unsigned short -KeyCharacterMap::getNumber(int keycode) -{ - Key* k = find_key(keycode); - if (k != NULL) { - return k->number; +status_t KeyCharacterMap::load(const String8& filename, KeyCharacterMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening key character map file %s.", status, filename.string()); + } else { + KeyCharacterMap* map = new KeyCharacterMap(); + if (!map) { + LOGE("Error allocating key character map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; } - return 0; + return status; } -unsigned short -KeyCharacterMap::getMatch(int keycode, const unsigned short* chars, - int charsize, uint32_t modifiers) -{ - Key* k = find_key(keycode); - modifiers &= 3; // ignore the SYM key because we don't have keymap entries for it - if (k != NULL) { - const uint16_t* data = k->data; - for (int j=0; j<charsize; j++) { - uint16_t c = chars[j]; - for (int i=0; i<(META_MASK + 1); i++) { - if ((modifiers == 0) || ((modifiers & i) != 0)) { - if (c == data[i]) { - return c; +status_t KeyCharacterMap::loadByDeviceId(int32_t deviceId, KeyCharacterMap** outMap) { + *outMap = NULL; + + String8 filename; + status_t result = getKeyCharacterMapFile(deviceId, filename); + if (!result) { + result = load(filename, outMap); + } + return result; +} + +int32_t KeyCharacterMap::getKeyboardType() const { + return mType; +} + +char16_t KeyCharacterMap::getDisplayLabel(int32_t keyCode) const { + char16_t result = 0; + const Key* key; + if (getKey(keyCode, &key)) { + result = key->label; + } +#if DEBUG_MAPPING + LOGD("getDisplayLabel: keyCode=%d ~ Result %d.", keyCode, result); +#endif + return result; +} + +char16_t KeyCharacterMap::getNumber(int32_t keyCode) const { + char16_t result = 0; + const Key* key; + if (getKey(keyCode, &key)) { + result = key->number; + } +#if DEBUG_MAPPING + LOGD("getNumber: keyCode=%d ~ Result %d.", keyCode, result); +#endif + return result; +} + +char16_t KeyCharacterMap::getCharacter(int32_t keyCode, int32_t metaState) const { + char16_t result = 0; + const Key* key; + const Behavior* behavior; + if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { + result = behavior->character; + } +#if DEBUG_MAPPING + LOGD("getCharacter: keyCode=%d, metaState=0x%08x ~ Result %d.", keyCode, metaState, result); +#endif + return result; +} + +bool KeyCharacterMap::getFallbackAction(int32_t keyCode, int32_t metaState, + FallbackAction* outFallbackAction) const { + outFallbackAction->keyCode = 0; + outFallbackAction->metaState = 0; + + bool result = false; + const Key* key; + const Behavior* behavior; + if (getKeyBehavior(keyCode, metaState, &key, &behavior)) { + if (behavior->fallbackKeyCode) { + outFallbackAction->keyCode = behavior->fallbackKeyCode; + outFallbackAction->metaState = metaState & ~behavior->metaState; + result = true; + } + } +#if DEBUG_MAPPING + LOGD("getFallbackKeyCode: keyCode=%d, metaState=0x%08x ~ Result %s, " + "fallback keyCode=%d, fallback metaState=0x%08x.", + keyCode, metaState, result ? "true" : "false", + outFallbackAction->keyCode, outFallbackAction->metaState); +#endif + return result; +} + +char16_t KeyCharacterMap::getMatch(int32_t keyCode, const char16_t* chars, size_t numChars, + int32_t metaState) const { + char16_t result = 0; + const Key* key; + if (getKey(keyCode, &key)) { + // Try to find the most general behavior that maps to this character. + // For example, the base key behavior will usually be last in the list. + // However, if we find a perfect meta state match for one behavior then use that one. + for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { + if (behavior->character) { + for (size_t i = 0; i < numChars; i++) { + if (behavior->character == chars[i]) { + result = behavior->character; + if ((behavior->metaState & metaState) == behavior->metaState) { + goto ExactMatch; + } + break; } } } } + ExactMatch: ; } - return 0; +#if DEBUG_MAPPING + LOGD("getMatch: keyCode=%d, chars=[%s], metaState=0x%08x ~ Result %d.", + keyCode, toString(chars, numChars).string(), metaState, result); +#endif + return result; } -unsigned short -KeyCharacterMap::getDisplayLabel(int keycode) -{ - Key* k = find_key(keycode); - if (k != NULL) { - return k->display_label; +bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, + Vector<KeyEvent>& outEvents) const { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + + for (size_t i = 0; i < numChars; i++) { + int32_t keyCode, metaState; + char16_t ch = chars[i]; + if (!findKey(ch, &keyCode, &metaState)) { +#if DEBUG_MAPPING + LOGD("getEvents: deviceId=%d, chars=[%s] ~ Failed to find mapping for character %d.", + deviceId, toString(chars, numChars).string(), ch); +#endif + return false; + } + + int32_t currentMetaState = 0; + addMetaKeys(outEvents, deviceId, metaState, true, now, ¤tMetaState); + addKey(outEvents, deviceId, keyCode, currentMetaState, true, now); + addKey(outEvents, deviceId, keyCode, currentMetaState, false, now); + addMetaKeys(outEvents, deviceId, metaState, false, now, ¤tMetaState); + } +#if DEBUG_MAPPING + LOGD("getEvents: deviceId=%d, chars=[%s] ~ Generated %d events.", + deviceId, toString(chars, numChars).string(), int32_t(outEvents.size())); + for (size_t i = 0; i < outEvents.size(); i++) { + LOGD(" Key: keyCode=%d, metaState=0x%08x, %s.", + outEvents[i].getKeyCode(), outEvents[i].getMetaState(), + outEvents[i].getAction() == AKEY_EVENT_ACTION_DOWN ? "down" : "up"); } - return 0; +#endif + return true; } -bool -KeyCharacterMap::getKeyData(int keycode, unsigned short *displayLabel, - unsigned short *number, unsigned short* results) -{ - Key* k = find_key(keycode); - if (k != NULL) { - memcpy(results, k->data, sizeof(short)*(META_MASK + 1)); - *number = k->number; - *displayLabel = k->display_label; +bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const { + ssize_t index = mKeys.indexOfKey(keyCode); + if (index >= 0) { + *outKey = mKeys.valueAt(index); return true; - } else { - return false; } + return false; } -bool -KeyCharacterMap::find_char(uint16_t c, uint32_t* key, uint32_t* mods) -{ - uint32_t N = m_keyCount; - for (int j=0; j<(META_MASK + 1); j++) { - Key const* keys = m_keys; - for (uint32_t i=0; i<N; i++) { - if (keys->data[j] == c) { - *key = keys->keycode; - *mods = j; +bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState, + const Key** outKey, const Behavior** outBehavior) const { + const Key* key; + if (getKey(keyCode, &key)) { + const Behavior* behavior = key->firstBehavior; + while (behavior) { + if ((behavior->metaState & metaState) == behavior->metaState) { + *outKey = key; + *outBehavior = behavior; return true; } - keys++; + behavior = behavior->next; } } return false; } -bool -KeyCharacterMap::getEvents(uint16_t* chars, size_t len, - Vector<int32_t>* keys, Vector<uint32_t>* modifiers) -{ - for (size_t i=0; i<len; i++) { - uint32_t k, mods; - if (find_char(chars[i], &k, &mods)) { - keys->add(k); - modifiers->add(mods); - } else { - return false; +bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const { + if (!ch) { + return false; + } + + for (size_t i = 0; i < mKeys.size(); i++) { + const Key* key = mKeys.valueAt(i); + + // Try to find the most general behavior that maps to this character. + // For example, the base key behavior will usually be last in the list. + const Behavior* found = NULL; + for (const Behavior* behavior = key->firstBehavior; behavior; behavior = behavior->next) { + if (behavior->character == ch) { + found = behavior; + } + } + if (found) { + *outKeyCode = mKeys.keyAt(i); + *outMetaState = found->metaState; + return true; } } - return true; + return false; } -KeyCharacterMap::Key* -KeyCharacterMap::find_key(int keycode) -{ - Key* keys = m_keys; - int low = 0; - int high = m_keyCount - 1; - int mid; - int n; - while (low <= high) { - mid = (low + high) / 2; - n = keys[mid].keycode; - if (keycode < n) { - high = mid - 1; - } else if (keycode > n) { - low = mid + 1; - } else { - return keys + mid; - } +void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) { + outEvents.push(); + KeyEvent& event = outEvents.editTop(); + event.initialize(deviceId, AINPUT_SOURCE_KEYBOARD, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + 0, keyCode, 0, metaState, 0, time, time); +} + +void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t* currentMetaState) { + // Add and remove meta keys symmetrically. + if (down) { + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); + + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, + AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, + AMETA_SHIFT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, + AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, + AMETA_ALT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, + AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, + AMETA_CTRL_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, + AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, + AMETA_META_ON, currentMetaState); + + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, true, time, + AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); + } else { + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_FUNCTION, AMETA_FUNCTION_ON, currentMetaState); + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_SYM, AMETA_SYM_ON, currentMetaState); + + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_META_LEFT, AMETA_META_LEFT_ON, + AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON, + AMETA_META_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON, + AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON, + AMETA_CTRL_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON, + AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON, + AMETA_ALT_ON, currentMetaState); + addDoubleEphemeralMetaKey(outEvents, deviceId, metaState, false, time, + AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON, + AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON, + AMETA_SHIFT_ON, currentMetaState); + + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON, currentMetaState); + addLockedMetaKey(outEvents, deviceId, metaState, time, + AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON, currentMetaState); + } +} + +bool KeyCharacterMap::addSingleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState) { + if ((metaState & keyMetaState) == keyMetaState) { + *currentMetaState = updateMetaState(keyCode, down, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, down, time); + return true; + } + return false; +} + +void KeyCharacterMap::addDoubleEphemeralMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, bool down, nsecs_t time, + int32_t leftKeyCode, int32_t leftKeyMetaState, + int32_t rightKeyCode, int32_t rightKeyMetaState, + int32_t eitherKeyMetaState, + int32_t* currentMetaState) { + bool specific = false; + specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + leftKeyCode, leftKeyMetaState, currentMetaState); + specific |= addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + rightKeyCode, rightKeyMetaState, currentMetaState); + + if (!specific) { + addSingleEphemeralMetaKey(outEvents, deviceId, metaState, down, time, + leftKeyCode, eitherKeyMetaState, currentMetaState); + } +} + +void KeyCharacterMap::addLockedMetaKey(Vector<KeyEvent>& outEvents, + int32_t deviceId, int32_t metaState, nsecs_t time, + int32_t keyCode, int32_t keyMetaState, + int32_t* currentMetaState) { + if ((metaState & keyMetaState) == keyMetaState) { + *currentMetaState = updateMetaState(keyCode, true, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, true, time); + *currentMetaState = updateMetaState(keyCode, false, *currentMetaState); + addKey(outEvents, deviceId, keyCode, *currentMetaState, false, time); + } +} + + +// --- KeyCharacterMap::Key --- + +KeyCharacterMap::Key::Key() : + label(0), number(0), firstBehavior(NULL) { +} + +KeyCharacterMap::Key::~Key() { + Behavior* behavior = firstBehavior; + while (behavior) { + Behavior* next = behavior->next; + delete behavior; + behavior = next; } - return NULL; } -KeyCharacterMap* -KeyCharacterMap::load(int id) -{ - KeyCharacterMap* rv = NULL; - char path[PATH_MAX]; - char propName[100]; - char dev[PROPERTY_VALUE_MAX]; - char tmpfn[PROPERTY_VALUE_MAX]; - int err; - const char* root = getenv("ANDROID_ROOT"); - - sprintf(propName, "hw.keyboards.%u.devname", id); - err = property_get(propName, dev, ""); - if (err > 0) { - // replace all the spaces with underscores - strcpy(tmpfn, dev); - for (char *p = strchr(tmpfn, ' '); p && *p; p = strchr(tmpfn, ' ')) - *p = '_'; - snprintf(path, sizeof(path), "%s/usr/keychars/%s.kcm.bin", root, tmpfn); - //LOGD("load: dev='%s' path='%s'\n", dev, path); - rv = try_file(path); - if (rv != NULL) { - return rv; + +// --- KeyCharacterMap::Behavior --- + +KeyCharacterMap::Behavior::Behavior() : + next(NULL), metaState(0), character(0), fallbackKeyCode(0) { +} + + +// --- KeyCharacterMap::Parser --- + +KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) { +} + +KeyCharacterMap::Parser::~Parser() { +} + +status_t KeyCharacterMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + switch (mState) { + case STATE_TOP: { + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "type") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseType(); + if (status) return status; + } else if (keywordToken == "key") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseKey(); + if (status) return status; + } else { + LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), + keywordToken.string()); + return BAD_VALUE; + } + break; + } + + case STATE_KEY: { + status_t status = parseKeyProperty(); + if (status) return status; + break; + } + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } } - LOGW("Error loading keycharmap file '%s'. %s='%s'", path, propName, dev); - } else { - LOGW("No keyboard for id %d", id); + + mTokenizer->nextLine(); + } + + if (mState != STATE_TOP) { + LOGE("%s: Unterminated key description at end of file.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - snprintf(path, sizeof(path), "%s/usr/keychars/qwerty.kcm.bin", root); - rv = try_file(path); - if (rv == NULL) { - LOGE("Can't find any keycharmaps (also tried %s)", path); - return NULL; + if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) { + LOGE("%s: Missing required keyboard 'type' declaration.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - LOGW("Using default keymap: %s", path); - return rv; + return NO_ERROR; } -KeyCharacterMap* -KeyCharacterMap::try_file(const char* filename) -{ - KeyCharacterMap* rv = NULL; - Key* keys; - int fd; - off_t filesize; - Header header; - int err; - - fd = open(filename, O_RDONLY); - if (fd == -1) { - LOGW("Can't open keycharmap file"); - return NULL; +status_t KeyCharacterMap::Parser::parseType() { + if (mMap->mType != KEYBOARD_TYPE_UNKNOWN) { + LOGE("%s: Duplicate keyboard 'type' declaration.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - filesize = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); + KeyboardType type; + String8 typeToken = mTokenizer->nextToken(WHITESPACE); + if (typeToken == "NUMERIC") { + type = KEYBOARD_TYPE_NUMERIC; + } else if (typeToken == "PREDICTIVE") { + type = KEYBOARD_TYPE_PREDICTIVE; + } else if (typeToken == "ALPHA") { + type = KEYBOARD_TYPE_ALPHA; + } else if (typeToken == "FULL") { + type = KEYBOARD_TYPE_FULL; + } else if (typeToken == "SPECIAL_FUNCTION") { + type = KEYBOARD_TYPE_SPECIAL_FUNCTION; + } else { + LOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), + typeToken.string()); + return BAD_VALUE; + } - // validate the header - if (filesize <= (off_t)sizeof(header)) { - LOGW("Bad keycharmap - filesize=%d\n", (int)filesize); - goto cleanup1; +#if DEBUG_PARSER + LOGD("Parsed type: type=%d.", type); +#endif + mMap->mType = type; + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseKey() { + String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + if (!keyCode) { + LOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; + } + if (mMap->mKeys.indexOfKey(keyCode) >= 0) { + LOGE("%s: Duplicate entry for key code '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; } - err = read(fd, &header, sizeof(header)); - if (err == -1) { - LOGW("Error reading keycharmap file"); - goto cleanup1; + mTokenizer->skipDelimiters(WHITESPACE); + String8 openBraceToken = mTokenizer->nextToken(WHITESPACE); + if (openBraceToken != "{") { + LOGE("%s: Expected '{' after key code label, got '%s'.", + mTokenizer->getLocation().string(), openBraceToken.string()); + return BAD_VALUE; } - if (0 != memcmp(header.magic, "keychar", 8)) { - LOGW("Bad keycharmap magic token"); - goto cleanup1; +#if DEBUG_PARSER + LOGD("Parsed beginning of key: keyCode=%d.", keyCode); +#endif + mKeyCode = keyCode; + mMap->mKeys.add(keyCode, new Key()); + mState = STATE_KEY; + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseKeyProperty() { + String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + if (token == "}") { + mState = STATE_TOP; + return NO_ERROR; } - if (header.endian != 0x12345678) { - LOGW("Bad keycharmap endians"); - goto cleanup1; + + Vector<Property> properties; + + // Parse all comma-delimited property names up to the first colon. + for (;;) { + if (token == "label") { + properties.add(Property(PROPERTY_LABEL)); + } else if (token == "number") { + properties.add(Property(PROPERTY_NUMBER)); + } else { + int32_t metaState; + status_t status = parseModifier(token, &metaState); + if (status) { + LOGE("%s: Expected a property name or modifier, got '%s'.", + mTokenizer->getLocation().string(), token.string()); + return status; + } + properties.add(Property(PROPERTY_META, metaState)); + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + char ch = mTokenizer->nextChar(); + if (ch == ':') { + break; + } else if (ch == ',') { + mTokenizer->skipDelimiters(WHITESPACE); + token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + continue; + } + } + + LOGE("%s: Expected ',' or ':' after property name.", + mTokenizer->getLocation().string()); + return BAD_VALUE; } - if ((header.version & 0xff) != 2) { - LOGW("Only support keycharmap version 2 (got 0x%08x)", header.version); - goto cleanup1; + + // Parse behavior after the colon. + mTokenizer->skipDelimiters(WHITESPACE); + + Behavior behavior; + bool haveCharacter = false; + bool haveFallback = false; + + do { + char ch = mTokenizer->peekChar(); + if (ch == '\'') { + char16_t character; + status_t status = parseCharacterLiteral(&character); + if (status || !character) { + LOGE("%s: Invalid character literal for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + if (haveCharacter) { + LOGE("%s: Cannot combine multiple character literals or 'none'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + behavior.character = character; + haveCharacter = true; + } else { + token = mTokenizer->nextToken(WHITESPACE); + if (token == "none") { + if (haveCharacter) { + LOGE("%s: Cannot combine multiple character literals or 'none'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + haveCharacter = true; + } else if (token == "fallback") { + mTokenizer->skipDelimiters(WHITESPACE); + token = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(token.string()); + if (!keyCode) { + LOGE("%s: Invalid key code label for fallback behavior, got '%s'.", + mTokenizer->getLocation().string(), + token.string()); + return BAD_VALUE; + } + if (haveFallback) { + LOGE("%s: Cannot combine multiple fallback key codes.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + behavior.fallbackKeyCode = keyCode; + haveFallback = true; + } else { + LOGE("%s: Expected a key behavior after ':'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } + + mTokenizer->skipDelimiters(WHITESPACE); + } while (!mTokenizer->isEol()); + + // Add the behavior. + Key* key = mMap->mKeys.valueFor(mKeyCode); + for (size_t i = 0; i < properties.size(); i++) { + const Property& property = properties.itemAt(i); + switch (property.property) { + case PROPERTY_LABEL: + if (key->label) { + LOGE("%s: Duplicate label for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + key->label = behavior.character; +#if DEBUG_PARSER + LOGD("Parsed key label: keyCode=%d, label=%d.", mKeyCode, key->label); +#endif + break; + case PROPERTY_NUMBER: + if (key->number) { + LOGE("%s: Duplicate number for key.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + key->number = behavior.character; +#if DEBUG_PARSER + LOGD("Parsed key number: keyCode=%d, number=%d.", mKeyCode, key->number); +#endif + break; + case PROPERTY_META: { + for (Behavior* b = key->firstBehavior; b; b = b->next) { + if (b->metaState == property.metaState) { + LOGE("%s: Duplicate key behavior for modifier.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } + Behavior* newBehavior = new Behavior(behavior); + newBehavior->metaState = property.metaState; + newBehavior->next = key->firstBehavior; + key->firstBehavior = newBehavior; +#if DEBUG_PARSER + LOGD("Parsed key meta: keyCode=%d, meta=0x%x, char=%d, fallback=%d.", mKeyCode, + newBehavior->metaState, newBehavior->character, newBehavior->fallbackKeyCode); +#endif + break; + } + } + } + return NO_ERROR; +} + +status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) { + if (token == "base") { + *outMetaState = 0; + return NO_ERROR; } - if (filesize < (off_t)(sizeof(Header)+(sizeof(Key)*header.keycount))) { - LOGW("Bad keycharmap file size\n"); - goto cleanup1; + + int32_t combinedMeta = 0; + + const char* str = token.string(); + const char* start = str; + for (const char* cur = str; ; cur++) { + char ch = *cur; + if (ch == '+' || ch == '\0') { + size_t len = cur - start; + int32_t metaState = 0; + for (size_t i = 0; i < sizeof(modifiers) / sizeof(Modifier); i++) { + if (strlen(modifiers[i].label) == len + && strncmp(modifiers[i].label, start, len) == 0) { + metaState = modifiers[i].metaState; + break; + } + } + if (!metaState) { + return BAD_VALUE; + } + if (combinedMeta & metaState) { + LOGE("%s: Duplicate modifier combination '%s'.", + mTokenizer->getLocation().string(), token.string()); + return BAD_VALUE; + } + + combinedMeta |= metaState; + start = cur + 1; + + if (ch == '\0') { + break; + } + } } + *outMetaState = combinedMeta; + return NO_ERROR; +} - // read the key data - keys = (Key*)malloc(sizeof(Key)*header.keycount); - err = read(fd, keys, sizeof(Key)*header.keycount); - if (err == -1) { - LOGW("Error reading keycharmap file"); - free(keys); - goto cleanup1; +status_t KeyCharacterMap::Parser::parseCharacterLiteral(char16_t* outCharacter) { + char ch = mTokenizer->nextChar(); + if (ch != '\'') { + goto Error; } - // return the object - rv = new KeyCharacterMap; - rv->m_keyCount = header.keycount; - rv->m_keys = keys; - rv->m_type = header.kbdtype; + ch = mTokenizer->nextChar(); + if (ch == '\\') { + // Escape sequence. + ch = mTokenizer->nextChar(); + if (ch == 'n') { + *outCharacter = '\n'; + } else if (ch == 't') { + *outCharacter = '\t'; + } else if (ch == '\\') { + *outCharacter = '\\'; + } else if (ch == '\'') { + *outCharacter = '\''; + } else if (ch == '"') { + *outCharacter = '"'; + } else if (ch == 'u') { + *outCharacter = 0; + for (int i = 0; i < 4; i++) { + ch = mTokenizer->nextChar(); + int digit; + if (ch >= '0' && ch <= '9') { + digit = ch - '0'; + } else if (ch >= 'A' && ch <= 'F') { + digit = ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'f') { + digit = ch - 'a' + 10; + } else { + goto Error; + } + *outCharacter = (*outCharacter << 4) | digit; + } + } else { + goto Error; + } + } else if (ch >= 32 && ch <= 126 && ch != '\'') { + // ASCII literal character. + *outCharacter = ch; + } else { + goto Error; + } -cleanup1: - close(fd); + ch = mTokenizer->nextChar(); + if (ch != '\'') { + goto Error; + } - return rv; + // Ensure that we consumed the entire token. + if (mTokenizer->nextToken(WHITESPACE).isEmpty()) { + return NO_ERROR; + } + +Error: + LOGE("%s: Malformed character literal.", mTokenizer->getLocation().string()); + return BAD_VALUE; } + +} // namespace android diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/ui/KeyLayoutMap.cpp index 15ae54c6f6b5..8626a03bf872 100644 --- a/libs/ui/KeyLayoutMap.cpp +++ b/libs/ui/KeyLayoutMap.cpp @@ -1,234 +1,340 @@ +/* + * 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. + */ + #define LOG_TAG "KeyLayoutMap" -#include "KeyLayoutMap.h" -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> -#include <utils/String8.h> #include <stdlib.h> -#include <ui/KeycodeLabels.h> +#include <android/keycodes.h> +#include <ui/Keyboard.h> +#include <ui/KeyLayoutMap.h> #include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + +// Enables debug output for mapping. +#define DEBUG_MAPPING 0 + namespace android { -KeyLayoutMap::KeyLayoutMap() - :m_status(NO_INIT), - m_keys() -{ +static const char* WHITESPACE = " \t\r"; + +// --- KeyLayoutMap --- + +KeyLayoutMap::KeyLayoutMap() { } -KeyLayoutMap::~KeyLayoutMap() -{ +KeyLayoutMap::~KeyLayoutMap() { } -static String8 -next_token(char const** p, int *line) -{ - bool begun = false; - const char* begin = *p; - const char* end = *p; - while (true) { - if (*end == '\n') { - (*line)++; - } - switch (*end) - { - case '#': - if (begun) { - *p = end; - return String8(begin, end-begin); - } else { - do { - begin++; - end++; - } while (*begin != '\0' && *begin != '\n'); - } - case '\0': - case ' ': - case '\n': - case '\r': - case '\t': - if (begun || (*end == '\0')) { - *p = end; - return String8(begin, end-begin); - } else { - begin++; - end++; - break; - } - default: - end++; - begun = true; +status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening key layout map file %s.", status, filename.string()); + } else { + KeyLayoutMap* map = new KeyLayoutMap(); + if (!map) { + LOGE("Error allocating key layout map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } } + delete tokenizer; } + return status; +} + +status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const { + ssize_t index = mKeys.indexOfKey(scanCode); + if (index < 0) { +#if DEBUG_MAPPING + LOGD("mapKey: scanCode=%d ~ Failed.", scanCode); +#endif + *keyCode = AKEYCODE_UNKNOWN; + *flags = 0; + return NAME_NOT_FOUND; + } + + const Key& k = mKeys.valueAt(index); + *keyCode = k.keyCode; + *flags = k.flags; + +#if DEBUG_MAPPING + LOGD("mapKey: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags); +#endif + return NO_ERROR; } -static int32_t -token_to_value(const char *literal, const KeycodeLabel *list) -{ - while (list->literal) { - if (0 == strcmp(literal, list->literal)) { - return list->value; +status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const { + const size_t N = mKeys.size(); + for (size_t i=0; i<N; i++) { + if (mKeys.valueAt(i).keyCode == keyCode) { + outScanCodes->add(mKeys.keyAt(i)); } - list++; } - return list->value; + return NO_ERROR; } -status_t -KeyLayoutMap::load(const char* filename) -{ - int fd = open(filename, O_RDONLY); - if (fd < 0) { - LOGE("error opening file=%s err=%s\n", filename, strerror(errno)); - m_status = errno; - return errno; +status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { + ssize_t index = mAxes.indexOfKey(scanCode); + if (index < 0) { +#if DEBUG_MAPPING + LOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); +#endif + return NAME_NOT_FOUND; } - off_t len = lseek(fd, 0, SEEK_END); - off_t errlen = lseek(fd, 0, SEEK_SET); - if (len < 0 || errlen < 0) { - close(fd); - LOGE("error seeking file=%s err=%s\n", filename, strerror(errno)); - m_status = errno; - return errno; + *outAxisInfo = mAxes.valueAt(index); + +#if DEBUG_MAPPING + LOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, + outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, + outAxisInfo->splitValue, outAxisInfo->flatOverride); +#endif + return NO_ERROR; +} + + +// --- KeyLayoutMap::Parser --- + +KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +KeyLayoutMap::Parser::~Parser() { +} + +status_t KeyLayoutMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "key") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseKey(); + if (status) return status; + } else if (keywordToken == "axis") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseAxis(); + if (status) return status; + } else { + LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), + keywordToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + } + + mTokenizer->nextLine(); + } + return NO_ERROR; +} + +status_t KeyLayoutMap::Parser::parseKey() { + String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); + char* end; + int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); + if (*end) { + LOGE("%s: Expected key scan code number, got '%s'.", mTokenizer->getLocation().string(), + scanCodeToken.string()); + return BAD_VALUE; + } + if (mMap->mKeys.indexOfKey(scanCode) >= 0) { + LOGE("%s: Duplicate entry for key scan code '%s'.", mTokenizer->getLocation().string(), + scanCodeToken.string()); + return BAD_VALUE; } - char* buf = (char*)malloc(len+1); - if (read(fd, buf, len) != len) { - LOGE("error reading file=%s err=%s\n", filename, strerror(errno)); - m_status = errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); - return errno != 0 ? errno : ((int)NOT_ENOUGH_DATA); + mTokenizer->skipDelimiters(WHITESPACE); + String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + if (!keyCode) { + LOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; } - errno = 0; - buf[len] = '\0'; - int32_t scancode = -1; - int32_t keycode = -1; uint32_t flags = 0; - uint32_t tmp; - char* end; - status_t err = NO_ERROR; - int line = 1; - char const* p = buf; - enum { BEGIN, SCANCODE, KEYCODE, FLAG } state = BEGIN; - while (true) { - String8 token = next_token(&p, &line); - if (*p == '\0') { - break; + for (;;) { + mTokenizer->skipDelimiters(WHITESPACE); + if (mTokenizer->isEol()) break; + + String8 flagToken = mTokenizer->nextToken(WHITESPACE); + uint32_t flag = getKeyFlagByLabel(flagToken.string()); + if (!flag) { + LOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().string(), + flagToken.string()); + return BAD_VALUE; } - switch (state) - { - case BEGIN: - if (token == "key") { - state = SCANCODE; - } else { - LOGE("%s:%d: expected key, got '%s'\n", filename, line, - token.string()); - err = BAD_VALUE; - goto done; - } - break; - case SCANCODE: - scancode = strtol(token.string(), &end, 0); - if (*end != '\0') { - LOGE("%s:%d: expected scancode (a number), got '%s'\n", - filename, line, token.string()); - goto done; - } - //LOGI("%s:%d: got scancode %d\n", filename, line, scancode ); - state = KEYCODE; - break; - case KEYCODE: - keycode = token_to_value(token.string(), KEYCODES); - //LOGI("%s:%d: got keycode %d for %s\n", filename, line, keycode, token.string() ); - if (keycode == 0) { - LOGE("%s:%d: expected keycode, got '%s'\n", - filename, line, token.string()); - goto done; - } - state = FLAG; - break; - case FLAG: - if (token == "key") { - if (scancode != -1) { - //LOGI("got key decl scancode=%d keycode=%d" - // " flags=0x%08x\n", scancode, keycode, flags); - Key k = { keycode, flags }; - m_keys.add(scancode, k); - state = SCANCODE; - scancode = -1; - keycode = -1; - flags = 0; - break; - } - } - tmp = token_to_value(token.string(), FLAGS); - //LOGI("%s:%d: got flags %x for %s\n", filename, line, tmp, token.string() ); - if (tmp == 0) { - LOGE("%s:%d: expected flag, got '%s'\n", - filename, line, token.string()); - goto done; - } - flags |= tmp; - break; + if (flags & flag) { + LOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().string(), + flagToken.string()); + return BAD_VALUE; } - } - if (state == FLAG && scancode != -1 ) { - //LOGI("got key decl scancode=%d keycode=%d" - // " flags=0x%08x\n", scancode, keycode, flags); - Key k = { keycode, flags }; - m_keys.add(scancode, k); + flags |= flag; } -done: - free(buf); - close(fd); - - m_status = err; - return err; +#if DEBUG_PARSER + LOGD("Parsed key: scanCode=%d, keyCode=%d, flags=0x%08x.", scanCode, keyCode, flags); +#endif + Key key; + key.keyCode = keyCode; + key.flags = flags; + mMap->mKeys.add(scanCode, key); + return NO_ERROR; } -status_t -KeyLayoutMap::map(int32_t scancode, int32_t *keycode, uint32_t *flags) const -{ - if (m_status != NO_ERROR) { - return m_status; +status_t KeyLayoutMap::Parser::parseAxis() { + String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); + char* end; + int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); + if (*end) { + LOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().string(), + scanCodeToken.string()); + return BAD_VALUE; } - - ssize_t index = m_keys.indexOfKey(scancode); - if (index < 0) { - //LOGW("couldn't map scancode=%d\n", scancode); - return NAME_NOT_FOUND; + if (mMap->mAxes.indexOfKey(scanCode) >= 0) { + LOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), + scanCodeToken.string()); + return BAD_VALUE; } - const Key& k = m_keys.valueAt(index); + AxisInfo axisInfo; - *keycode = k.keycode; - *flags = k.flags; + mTokenizer->skipDelimiters(WHITESPACE); + String8 token = mTokenizer->nextToken(WHITESPACE); + if (token == "invert") { + axisInfo.mode = AxisInfo::MODE_INVERT; + + mTokenizer->skipDelimiters(WHITESPACE); + String8 axisToken = mTokenizer->nextToken(WHITESPACE); + axisInfo.axis = getAxisByLabel(axisToken.string()); + if (axisInfo.axis < 0) { + LOGE("%s: Expected inverted axis label, got '%s'.", + mTokenizer->getLocation().string(), axisToken.string()); + return BAD_VALUE; + } + } else if (token == "split") { + axisInfo.mode = AxisInfo::MODE_SPLIT; - //LOGD("mapped scancode=%d to keycode=%d flags=0x%08x\n", scancode, - // keycode, flags); + mTokenizer->skipDelimiters(WHITESPACE); + String8 splitToken = mTokenizer->nextToken(WHITESPACE); + axisInfo.splitValue = int32_t(strtol(splitToken.string(), &end, 0)); + if (*end) { + LOGE("%s: Expected split value, got '%s'.", + mTokenizer->getLocation().string(), splitToken.string()); + return BAD_VALUE; + } - return NO_ERROR; -} + mTokenizer->skipDelimiters(WHITESPACE); + String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); + axisInfo.axis = getAxisByLabel(lowAxisToken.string()); + if (axisInfo.axis < 0) { + LOGE("%s: Expected low axis label, got '%s'.", + mTokenizer->getLocation().string(), lowAxisToken.string()); + return BAD_VALUE; + } -status_t -KeyLayoutMap::findScancodes(int32_t keycode, Vector<int32_t>* outScancodes) const -{ - if (m_status != NO_ERROR) { - return m_status; + mTokenizer->skipDelimiters(WHITESPACE); + String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); + axisInfo.highAxis = getAxisByLabel(highAxisToken.string()); + if (axisInfo.highAxis < 0) { + LOGE("%s: Expected high axis label, got '%s'.", + mTokenizer->getLocation().string(), highAxisToken.string()); + return BAD_VALUE; + } + } else { + axisInfo.axis = getAxisByLabel(token.string()); + if (axisInfo.axis < 0) { + LOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", + mTokenizer->getLocation().string(), token.string()); + return BAD_VALUE; + } } - - const size_t N = m_keys.size(); - for (size_t i=0; i<N; i++) { - if (m_keys.valueAt(i).keycode == keycode) { - outScancodes->add(m_keys.keyAt(i)); + + for (;;) { + mTokenizer->skipDelimiters(WHITESPACE); + if (mTokenizer->isEol()) { + break; + } + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "flat") { + mTokenizer->skipDelimiters(WHITESPACE); + String8 flatToken = mTokenizer->nextToken(WHITESPACE); + axisInfo.flatOverride = int32_t(strtol(flatToken.string(), &end, 0)); + if (*end) { + LOGE("%s: Expected flat value, got '%s'.", + mTokenizer->getLocation().string(), flatToken.string()); + return BAD_VALUE; + } + } else { + LOGE("%s: Expected keyword 'flat', got '%s'.", + mTokenizer->getLocation().string(), keywordToken.string()); + return BAD_VALUE; } } - + +#if DEBUG_PARSER + LOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, + axisInfo.mode, axisInfo.axis, axisInfo.highAxis, + axisInfo.splitValue, axisInfo.flatOverride); +#endif + mMap->mAxes.add(scanCode, axisInfo); return NO_ERROR; } diff --git a/libs/ui/KeyLayoutMap.h b/libs/ui/KeyLayoutMap.h deleted file mode 100644 index 43f84ce4964d..000000000000 --- a/libs/ui/KeyLayoutMap.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef KEYLAYOUTMAP_H -#define KEYLAYOUTMAP_H - -#include <utils/KeyedVector.h> - -namespace android { - -class KeyLayoutMap -{ -public: - KeyLayoutMap(); - ~KeyLayoutMap(); - - status_t load(const char* filename); - - status_t map(int32_t scancode, int32_t *keycode, uint32_t *flags) const; - status_t findScancodes(int32_t keycode, Vector<int32_t>* outScancodes) const; - -private: - struct Key { - int32_t keycode; - uint32_t flags; - }; - - status_t m_status; - KeyedVector<int32_t,Key> m_keys; -}; - -}; - -#endif // KEYLAYOUTMAP_H diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp new file mode 100644 index 000000000000..600a951d5f90 --- /dev/null +++ b/libs/ui/Keyboard.cpp @@ -0,0 +1,347 @@ +/* + * 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. + */ + +#define LOG_TAG "Keyboard" + +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> + +#include <ui/Keyboard.h> +#include <ui/KeycodeLabels.h> +#include <ui/KeyLayoutMap.h> +#include <ui/KeyCharacterMap.h> +#include <utils/Errors.h> +#include <utils/Log.h> +#include <cutils/properties.h> + +namespace android { + +// --- KeyMap --- + +KeyMap::KeyMap() : + keyLayoutMap(NULL), keyCharacterMap(NULL) { +} + +KeyMap::~KeyMap() { + delete keyLayoutMap; + delete keyCharacterMap; +} + +status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, + const PropertyMap* deviceConfiguration) { + // Use the configured key layout if available. + if (deviceConfiguration) { + String8 keyLayoutName; + if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), + keyLayoutName)) { + status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName); + if (status == NAME_NOT_FOUND) { + LOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " + "it was not found.", + deviceIdenfifier.name.string(), keyLayoutName.string()); + } + } + + String8 keyCharacterMapName; + if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), + keyCharacterMapName)) { + status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName); + if (status == NAME_NOT_FOUND) { + LOGE("Configuration for keyboard device '%s' requested keyboard character " + "map '%s' but it was not found.", + deviceIdenfifier.name.string(), keyLayoutName.string()); + } + } + + if (isComplete()) { + return OK; + } + } + + // Try searching by device identifier. + if (probeKeyMap(deviceIdenfifier, String8::empty())) { + return OK; + } + + // Fall back on the Generic key map. + // TODO Apply some additional heuristics here to figure out what kind of + // generic key map to use (US English, etc.) for typical external keyboards. + if (probeKeyMap(deviceIdenfifier, String8("Generic"))) { + return OK; + } + + // Try the Virtual key map as a last resort. + if (probeKeyMap(deviceIdenfifier, String8("Virtual"))) { + return OK; + } + + // Give up! + LOGE("Could not determine key map for device '%s' and no default key maps were found!", + deviceIdenfifier.name.string()); + return NAME_NOT_FOUND; +} + +bool KeyMap::probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, + const String8& keyMapName) { + if (!haveKeyLayout()) { + loadKeyLayout(deviceIdentifier, keyMapName); + } + if (!haveKeyCharacterMap()) { + loadKeyCharacterMap(deviceIdentifier, keyMapName); + } + return isComplete(); +} + +status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, + const String8& name) { + String8 path(getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT)); + if (path.isEmpty()) { + return NAME_NOT_FOUND; + } + + KeyLayoutMap* map; + status_t status = KeyLayoutMap::load(path, &map); + if (status) { + return status; + } + + keyLayoutFile.setTo(path); + keyLayoutMap = map; + return OK; +} + +status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, + const String8& name) { + String8 path(getPath(deviceIdentifier, name, + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); + if (path.isEmpty()) { + return NAME_NOT_FOUND; + } + + KeyCharacterMap* map; + status_t status = KeyCharacterMap::load(path, &map); + if (status) { + return status; + } + + keyCharacterMapFile.setTo(path); + keyCharacterMap = map; + return OK; +} + +String8 KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, + const String8& name, InputDeviceConfigurationFileType type) { + return name.isEmpty() + ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) + : getInputDeviceConfigurationFilePathByName(name, type); +} + + +// --- Global functions --- + +bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, + const PropertyMap* deviceConfiguration, const KeyMap* keyMap) { + if (!keyMap->haveKeyCharacterMap() + || keyMap->keyCharacterMap->getKeyboardType() + == KeyCharacterMap::KEYBOARD_TYPE_SPECIAL_FUNCTION) { + return false; + } + + if (deviceConfiguration) { + bool builtIn = false; + if (deviceConfiguration->tryGetProperty(String8("keyboard.builtIn"), builtIn) + && builtIn) { + return true; + } + } + + return strstr(deviceIdentifier.name.string(), "-keypad"); +} + +void setKeyboardProperties(int32_t deviceId, + const InputDeviceIdentifier& deviceIdentifier, + const String8& keyLayoutFile, const String8& keyCharacterMapFile) { + char propName[PROPERTY_KEY_MAX]; + snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId); + property_set(propName, deviceIdentifier.name.string()); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId); + property_set(propName, keyLayoutFile.string()); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); + property_set(propName, keyCharacterMapFile.string()); +} + +void clearKeyboardProperties(int32_t deviceId) { + char propName[PROPERTY_KEY_MAX]; + snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId); + property_set(propName, ""); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId); + property_set(propName, ""); + snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); + property_set(propName, ""); +} + +status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) { + if (deviceId != DEVICE_ID_VIRTUAL_KEYBOARD) { + char propName[PROPERTY_KEY_MAX]; + char fn[PROPERTY_VALUE_MAX]; + snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); + if (property_get(propName, fn, "") > 0) { + outKeyCharacterMapFile.setTo(fn); + return OK; + } + } + + // Default to Virtual since the keyboard does not appear to be installed. + outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePathByName(String8("Virtual"), + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); + if (!outKeyCharacterMapFile.isEmpty()) { + return OK; + } + + LOGE("Can't find any key character map files including the Virtual key map!"); + return NAME_NOT_FOUND; +} + +static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) { + while (list->literal) { + if (strcmp(literal, list->literal) == 0) { + return list->value; + } + list++; + } + return list->value; +} + +static const char* lookupLabelByValue(int value, const KeycodeLabel *list) { + while (list->literal) { + if (list->value == value) { + return list->literal; + } + list++; + } + return NULL; +} + +int32_t getKeyCodeByLabel(const char* label) { + return int32_t(lookupValueByLabel(label, KEYCODES)); +} + +uint32_t getKeyFlagByLabel(const char* label) { + return uint32_t(lookupValueByLabel(label, FLAGS)); +} + +int32_t getAxisByLabel(const char* label) { + return int32_t(lookupValueByLabel(label, AXES)); +} + +const char* getAxisLabel(int32_t axisId) { + return lookupLabelByValue(axisId, AXES); +} + +static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) { + int32_t newMetaState; + if (down) { + newMetaState = oldMetaState | mask; + } else { + newMetaState = oldMetaState & + ~(mask | AMETA_ALT_ON | AMETA_SHIFT_ON | AMETA_CTRL_ON | AMETA_META_ON); + } + + if (newMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { + newMetaState |= AMETA_ALT_ON; + } + + if (newMetaState & (AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { + newMetaState |= AMETA_SHIFT_ON; + } + + if (newMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { + newMetaState |= AMETA_CTRL_ON; + } + + if (newMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { + newMetaState |= AMETA_META_ON; + } + return newMetaState; +} + +static int32_t toggleLockedMetaState(int32_t mask, bool down, int32_t oldMetaState) { + if (down) { + return oldMetaState; + } else { + return oldMetaState ^ mask; + } +} + +int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState) { + int32_t mask; + switch (keyCode) { + case AKEYCODE_ALT_LEFT: + return setEphemeralMetaState(AMETA_ALT_LEFT_ON, down, oldMetaState); + case AKEYCODE_ALT_RIGHT: + return setEphemeralMetaState(AMETA_ALT_RIGHT_ON, down, oldMetaState); + case AKEYCODE_SHIFT_LEFT: + return setEphemeralMetaState(AMETA_SHIFT_LEFT_ON, down, oldMetaState); + case AKEYCODE_SHIFT_RIGHT: + return setEphemeralMetaState(AMETA_SHIFT_RIGHT_ON, down, oldMetaState); + case AKEYCODE_SYM: + return setEphemeralMetaState(AMETA_SYM_ON, down, oldMetaState); + case AKEYCODE_FUNCTION: + return setEphemeralMetaState(AMETA_FUNCTION_ON, down, oldMetaState); + case AKEYCODE_CTRL_LEFT: + return setEphemeralMetaState(AMETA_CTRL_LEFT_ON, down, oldMetaState); + case AKEYCODE_CTRL_RIGHT: + return setEphemeralMetaState(AMETA_CTRL_RIGHT_ON, down, oldMetaState); + case AKEYCODE_META_LEFT: + return setEphemeralMetaState(AMETA_META_LEFT_ON, down, oldMetaState); + case AKEYCODE_META_RIGHT: + return setEphemeralMetaState(AMETA_META_RIGHT_ON, down, oldMetaState); + case AKEYCODE_CAPS_LOCK: + return toggleLockedMetaState(AMETA_CAPS_LOCK_ON, down, oldMetaState); + case AKEYCODE_NUM_LOCK: + return toggleLockedMetaState(AMETA_NUM_LOCK_ON, down, oldMetaState); + case AKEYCODE_SCROLL_LOCK: + return toggleLockedMetaState(AMETA_SCROLL_LOCK_ON, down, oldMetaState); + default: + return oldMetaState; + } +} + +bool isMetaKey(int32_t keyCode) { + switch (keyCode) { + case AKEYCODE_ALT_LEFT: + case AKEYCODE_ALT_RIGHT: + case AKEYCODE_SHIFT_LEFT: + case AKEYCODE_SHIFT_RIGHT: + case AKEYCODE_SYM: + case AKEYCODE_FUNCTION: + case AKEYCODE_CTRL_LEFT: + case AKEYCODE_CTRL_RIGHT: + case AKEYCODE_META_LEFT: + case AKEYCODE_META_RIGHT: + case AKEYCODE_CAPS_LOCK: + case AKEYCODE_NUM_LOCK: + case AKEYCODE_SCROLL_LOCK: + return true; + default: + return false; + } +} + + +} // namespace android diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp deleted file mode 100644 index b082c534a0fb..000000000000 --- a/libs/ui/Overlay.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* - * 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 <binder/IMemory.h> -#include <binder/Parcel.h> -#include <utils/Errors.h> -#include <binder/MemoryHeapBase.h> - -#include <ui/IOverlay.h> -#include <ui/Overlay.h> - -#include <hardware/overlay.h> - -namespace android { - -Overlay::Overlay(const sp<OverlayRef>& overlayRef) - : mOverlayRef(overlayRef), mOverlayData(0), mStatus(NO_INIT) -{ - mOverlayData = NULL; - hw_module_t const* module; - if (overlayRef != 0) { - if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { - if (overlay_data_open(module, &mOverlayData) == NO_ERROR) { - mStatus = mOverlayData->initialize(mOverlayData, - overlayRef->mOverlayHandle); - } - } - } -} - -Overlay::~Overlay() { - if (mOverlayData) { - overlay_data_close(mOverlayData); - } -} - -status_t Overlay::dequeueBuffer(overlay_buffer_t* buffer) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->dequeueBuffer(mOverlayData, buffer); -} - -status_t Overlay::queueBuffer(overlay_buffer_t buffer) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->queueBuffer(mOverlayData, buffer); -} - -status_t Overlay::resizeInput(uint32_t width, uint32_t height) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->resizeInput(mOverlayData, width, height); -} - -status_t Overlay::setParameter(int param, int value) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->setParameter(mOverlayData, param, value); -} - -status_t Overlay::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->setCrop(mOverlayData, x, y, w, h); -} - -status_t Overlay::getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->getCrop(mOverlayData, x, y, w, h); -} - -int32_t Overlay::getBufferCount() const -{ - if (mStatus != NO_ERROR) return mStatus; - return mOverlayData->getBufferCount(mOverlayData); -} - -void* Overlay::getBufferAddress(overlay_buffer_t buffer) -{ - if (mStatus != NO_ERROR) return NULL; - return mOverlayData->getBufferAddress(mOverlayData, buffer); -} - -void Overlay::destroy() { - - // Must delete the objects in reverse creation order, thus the - // data side must be closed first and then the destroy send to - // the control side. - if (mOverlayData) { - overlay_data_close(mOverlayData); - mOverlayData = NULL; - } else { - LOGD("Overlay::destroy mOverlayData is NULL"); - } - - if (mOverlayRef != 0) { - mOverlayRef->mOverlayChannel->destroy(); - } else { - LOGD("Overlay::destroy mOverlayRef is NULL"); - } -} - -status_t Overlay::getStatus() const { - return mStatus; -} - -overlay_handle_t Overlay::getHandleRef() const { - if (mStatus != NO_ERROR) return NULL; - return mOverlayRef->mOverlayHandle; -} - -uint32_t Overlay::getWidth() const { - if (mStatus != NO_ERROR) return 0; - return mOverlayRef->mWidth; -} - -uint32_t Overlay::getHeight() const { - if (mStatus != NO_ERROR) return 0; - return mOverlayRef->mHeight; -} - -int32_t Overlay::getFormat() const { - if (mStatus != NO_ERROR) return -1; - return mOverlayRef->mFormat; -} - -int32_t Overlay::getWidthStride() const { - if (mStatus != NO_ERROR) return 0; - return mOverlayRef->mWidthStride; -} - -int32_t Overlay::getHeightStride() const { - if (mStatus != NO_ERROR) return 0; - return mOverlayRef->mHeightStride; -} -// ---------------------------------------------------------------------------- - -OverlayRef::OverlayRef() - : mOverlayHandle(0), - mWidth(0), mHeight(0), mFormat(0), mWidthStride(0), mHeightStride(0), - mOwnHandle(true) -{ -} - -OverlayRef::OverlayRef(overlay_handle_t handle, const sp<IOverlay>& channel, - uint32_t w, uint32_t h, int32_t f, uint32_t ws, uint32_t hs) - : mOverlayHandle(handle), mOverlayChannel(channel), - mWidth(w), mHeight(h), mFormat(f), mWidthStride(ws), mHeightStride(hs), - mOwnHandle(false) -{ -} - -OverlayRef::~OverlayRef() -{ - if (mOwnHandle) { - native_handle_close(mOverlayHandle); - native_handle_delete(const_cast<native_handle*>(mOverlayHandle)); - } -} - -sp<OverlayRef> OverlayRef::readFromParcel(const Parcel& data) { - sp<OverlayRef> result; - sp<IOverlay> overlay = IOverlay::asInterface(data.readStrongBinder()); - if (overlay != NULL) { - uint32_t w = data.readInt32(); - uint32_t h = data.readInt32(); - uint32_t f = data.readInt32(); - uint32_t ws = data.readInt32(); - uint32_t hs = data.readInt32(); - native_handle* handle = data.readNativeHandle(); - - result = new OverlayRef(); - result->mOverlayHandle = handle; - result->mOverlayChannel = overlay; - result->mWidth = w; - result->mHeight = h; - result->mFormat = f; - result->mWidthStride = ws; - result->mHeightStride = hs; - } - return result; -} - -status_t OverlayRef::writeToParcel(Parcel* reply, const sp<OverlayRef>& o) { - if (o != NULL) { - reply->writeStrongBinder(o->mOverlayChannel->asBinder()); - reply->writeInt32(o->mWidth); - reply->writeInt32(o->mHeight); - reply->writeInt32(o->mFormat); - reply->writeInt32(o->mWidthStride); - reply->writeInt32(o->mHeightStride); - reply->writeNativeHandle(o->mOverlayHandle); - } else { - reply->writeStrongBinder(NULL); - } - return NO_ERROR; -} - -// ---------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index 1994f6a4325f..a060a5f39db5 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -56,6 +56,9 @@ Region::Region() Region::Region(const Region& rhs) : mBounds(rhs.mBounds), mStorage(rhs.mStorage) { +#if VALIDATE_REGIONS + validate(rhs, "rhs copy-ctor"); +#endif } Region::Region(const Rect& rhs) @@ -76,7 +79,8 @@ Region::~Region() Region& Region::operator = (const Region& rhs) { #if VALIDATE_REGIONS - validate(rhs, "operator="); + validate(*this, "this->operator="); + validate(rhs, "rhs.operator="); #endif mBounds = rhs.mBounds; mStorage = rhs.mStorage; @@ -366,6 +370,12 @@ void Region::boolean_operation(int op, Region& dst, const Region& lhs, const Region& rhs, int dx, int dy) { +#if VALIDATE_REGIONS + validate(lhs, "boolean_operation (before): lhs"); + validate(rhs, "boolean_operation (before): rhs"); + validate(dst, "boolean_operation (before): dst"); +#endif + size_t lhs_count; Rect const * const lhs_rects = lhs.getArray(&lhs_count); diff --git a/libs/ui/VirtualKeyMap.cpp b/libs/ui/VirtualKeyMap.cpp new file mode 100644 index 000000000000..e756cdd782b1 --- /dev/null +++ b/libs/ui/VirtualKeyMap.cpp @@ -0,0 +1,171 @@ +/* + * 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. + */ + +#define LOG_TAG "VirtualKeyMap" + +#include <stdlib.h> +#include <string.h> +#include <ui/VirtualKeyMap.h> +#include <utils/Log.h> +#include <utils/Errors.h> +#include <utils/Tokenizer.h> +#include <utils/Timers.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_FIELD_DELIMITER = " \t\r:"; + + +// --- VirtualKeyMap --- + +VirtualKeyMap::VirtualKeyMap() { +} + +VirtualKeyMap::~VirtualKeyMap() { +} + +status_t VirtualKeyMap::load(const String8& filename, VirtualKeyMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening virtual key map file %s.", status, filename.string()); + } else { + VirtualKeyMap* map = new VirtualKeyMap(); + if (!map) { + LOGE("Error allocating virtual key map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + + +// --- VirtualKeyMap::Parser --- + +VirtualKeyMap::Parser::Parser(VirtualKeyMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +VirtualKeyMap::Parser::~Parser() { +} + +status_t VirtualKeyMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + // Multiple keys can appear on one line or they can be broken up across multiple lines. + do { + String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); + if (token != "0x01") { + LOGE("%s: Unknown virtual key type, expected 0x01.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + VirtualKeyDefinition defn; + bool success = parseNextIntField(&defn.scanCode) + && parseNextIntField(&defn.centerX) + && parseNextIntField(&defn.centerY) + && parseNextIntField(&defn.width) + && parseNextIntField(&defn.height); + if (!success) { + LOGE("%s: Expected 5 colon-delimited integers in virtual key definition.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + +#if DEBUG_PARSER + LOGD("Parsed virtual key: scanCode=%d, centerX=%d, centerY=%d, " + "width=%d, height=%d", + defn.scanCode, defn.centerX, defn.centerY, defn.width, defn.height); +#endif + mMap->mVirtualKeys.push(defn); + } while (consumeFieldDelimiterAndSkipWhitespace()); + + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + } + + mTokenizer->nextLine(); + } + + return NO_ERROR; +} + +bool VirtualKeyMap::Parser::consumeFieldDelimiterAndSkipWhitespace() { + mTokenizer->skipDelimiters(WHITESPACE); + if (mTokenizer->peekChar() == ':') { + mTokenizer->nextChar(); + mTokenizer->skipDelimiters(WHITESPACE); + return true; + } + return false; +} + +bool VirtualKeyMap::Parser::parseNextIntField(int32_t* outValue) { + if (!consumeFieldDelimiterAndSkipWhitespace()) { + return false; + } + + String8 token = mTokenizer->nextToken(WHITESPACE_OR_FIELD_DELIMITER); + char* end; + *outValue = strtol(token.string(), &end, 0); + if (token.isEmpty() || *end != '\0') { + LOGE("Expected an integer, got '%s'.", token.string()); + return false; + } + return true; +} + +} // namespace android diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk index aa017b978825..e23197185c85 100644 --- a/libs/ui/tests/Android.mk +++ b/libs/ui/tests/Android.mk @@ -7,8 +7,7 @@ ifneq ($(TARGET_SIMULATOR),true) # Build the unit tests. test_src_files := \ InputChannel_test.cpp \ - InputReader_test.cpp \ - InputDispatcher_test.cpp \ + InputEvent_test.cpp \ InputPublisherAndConsumer_test.cpp shared_libraries := \ @@ -20,7 +19,8 @@ shared_libraries := \ libhardware \ libhardware_legacy \ libui \ - libstlport + libstlport \ + libskia static_libraries := \ libgtest \ @@ -30,7 +30,8 @@ c_includes := \ bionic \ bionic/libstdc++/include \ external/gtest/include \ - external/stlport/stlport + external/stlport/stlport \ + external/skia/include/core module_tags := eng tests diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/ui/tests/InputChannel_test.cpp index 6cec1c02ea95..eff22ee5f0eb 100644 --- a/libs/ui/tests/InputChannel_test.cpp +++ b/libs/ui/tests/InputChannel_test.cpp @@ -1,6 +1,18 @@ -// -// Copyright 2010 The Android Open Source Project -// +/* + * 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 <ui/InputTransport.h> #include <utils/Timers.h> diff --git a/libs/ui/tests/InputDispatcher_test.cpp b/libs/ui/tests/InputDispatcher_test.cpp deleted file mode 100644 index 8874dfe3c9f8..000000000000 --- a/libs/ui/tests/InputDispatcher_test.cpp +++ /dev/null @@ -1,226 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// - -#include <ui/InputDispatcher.h> -#include <gtest/gtest.h> -#include <linux/input.h> - -namespace android { - -// An arbitrary time value. -static const nsecs_t ARBITRARY_TIME = 1234; - -// An arbitrary device id. -static const int32_t DEVICE_ID = 1; - -// An arbitrary injector pid / uid pair that has permission to inject events. -static const int32_t INJECTOR_PID = 999; -static const int32_t INJECTOR_UID = 1001; - - -// --- FakeInputDispatcherPolicy --- - -class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { -protected: - virtual ~FakeInputDispatcherPolicy() { - } - -public: - FakeInputDispatcherPolicy() { - } - -private: - virtual void notifyConfigurationChanged(nsecs_t when) { - } - - virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, - const sp<InputChannel>& inputChannel) { - return 0; - } - - virtual void notifyInputChannelBroken(const sp<InputChannel>& inputChannel) { - } - - virtual nsecs_t getKeyRepeatTimeout() { - return 500 * 1000000LL; - } - - virtual nsecs_t getKeyRepeatDelay() { - return 50 * 1000000LL; - } - - virtual int32_t getMaxEventsPerSecond() { - return 60; - } - - virtual void interceptKeyBeforeQueueing(nsecs_t when, int32_t deviceId, - int32_t action, int32_t& flags, int32_t keyCode, int32_t scanCode, - uint32_t& policyFlags) { - } - - virtual void interceptGenericBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { - } - - virtual bool interceptKeyBeforeDispatching(const sp<InputChannel>& inputChannel, - const KeyEvent* keyEvent, uint32_t policyFlags) { - return false; - } - - virtual void notifySwitch(nsecs_t when, - int32_t switchCode, int32_t switchValue, uint32_t policyFlags) { - } - - virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) { - } - - virtual bool checkInjectEventsPermissionNonReentrant( - int32_t injectorPid, int32_t injectorUid) { - return false; - } -}; - - -// --- InputDispatcherTest --- - -class InputDispatcherTest : public testing::Test { -protected: - sp<FakeInputDispatcherPolicy> mFakePolicy; - sp<InputDispatcher> mDispatcher; - - virtual void SetUp() { - mFakePolicy = new FakeInputDispatcherPolicy(); - mDispatcher = new InputDispatcher(mFakePolicy); - } - - virtual void TearDown() { - mFakePolicy.clear(); - mDispatcher.clear(); - } -}; - - -TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesKeyEvents) { - KeyEvent event; - - // Rejects undefined key actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - /*action*/ -1, 0, - AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject key events with undefined action."; - - // Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API. - event.initialize(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, - AKEY_EVENT_ACTION_MULTIPLE, 0, - AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject key events with ACTION_MULTIPLE."; -} - -TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { - MotionEvent event; - int32_t pointerIds[MAX_POINTERS + 1]; - PointerCoords pointerCoords[MAX_POINTERS + 1]; - for (int i = 0; i <= MAX_POINTERS; i++) { - pointerIds[i] = i; - } - - // Rejects undefined motion actions. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - /*action*/ -1, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with undefined action."; - - // Rejects pointer down with invalid index. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer down index too large."; - - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_POINTER_DOWN | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer down index too small."; - - // Rejects pointer up with invalid index. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer up index too large."; - - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_POINTER_UP | (-1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer up index too small."; - - // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 0, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with 0 pointers."; - - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ MAX_POINTERS + 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with more than MAX_POINTERS pointers."; - - // Rejects motion events with invalid pointer ids. - pointerIds[0] = -1; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer ids less than 0."; - - pointerIds[0] = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 1, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with pointer ids greater than MAX_POINTER_ID."; - - // Rejects motion events with duplicate pointer ids. - pointerIds[0] = 1; - pointerIds[1] = 1; - event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMETA_NONE, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ 2, pointerIds, pointerCoords); - ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent(&event, - INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0)) - << "Should reject motion events with duplicate pointer ids."; -} - -} // namespace android diff --git a/libs/ui/tests/InputEvent_test.cpp b/libs/ui/tests/InputEvent_test.cpp new file mode 100644 index 000000000000..b77489e1586f --- /dev/null +++ b/libs/ui/tests/InputEvent_test.cpp @@ -0,0 +1,582 @@ +/* + * 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 <ui/Input.h> +#include <gtest/gtest.h> +#include <binder/Parcel.h> + +#include <math.h> +#include <SkMatrix.h> + +namespace android { + +class BaseTest : public testing::Test { +protected: + virtual void SetUp() { } + virtual void TearDown() { } +}; + +// --- PointerCoordsTest --- + +class PointerCoordsTest : public BaseTest { +}; + +TEST_F(PointerCoordsTest, ClearSetsBitsToZero) { + PointerCoords coords; + coords.clear(); + + ASSERT_EQ(0ULL, coords.bits); +} + +TEST_F(PointerCoordsTest, AxisValues) { + float* valuePtr; + PointerCoords coords; + coords.clear(); + + // Check invariants when no axes are present. + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(0, coords.getAxisValue(1)) + << "getAxisValue should return zero because axis is not present"; + + ASSERT_EQ(NULL, coords.editAxisValue(0)) + << "editAxisValue should return null because axis is not present"; + + // Set first axis. + ASSERT_EQ(OK, coords.setAxisValue(1, 5)); + ASSERT_EQ(0x00000002ULL, coords.bits); + ASSERT_EQ(5, coords.values[0]); + + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + + // Set an axis with a higher id than all others. (appending value at the end) + ASSERT_EQ(OK, coords.setAxisValue(3, 2)); + ASSERT_EQ(0x0000000aULL, coords.bits); + ASSERT_EQ(5, coords.values[0]); + ASSERT_EQ(2, coords.values[1]); + + ASSERT_EQ(0, coords.getAxisValue(0)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(0, coords.getAxisValue(2)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set an axis with an id lower than all others. (prepending value at beginning) + ASSERT_EQ(OK, coords.setAxisValue(0, 4)); + ASSERT_EQ(0x0000000bULL, coords.bits); + ASSERT_EQ(4, coords.values[0]); + ASSERT_EQ(5, coords.values[1]); + ASSERT_EQ(2, coords.values[2]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(5, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(0, coords.getAxisValue(2)) + << "getAxisValue should return zero because axis is not present"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Edit an existing axis value in place. + valuePtr = coords.editAxisValue(1); + ASSERT_EQ(5, *valuePtr) + << "editAxisValue should return pointer to axis value"; + + *valuePtr = 7; + ASSERT_EQ(7, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + + // Set an axis with an id between the others. (inserting value in the middle) + ASSERT_EQ(OK, coords.setAxisValue(2, 1)); + ASSERT_EQ(0x0000000fULL, coords.bits); + ASSERT_EQ(4, coords.values[0]); + ASSERT_EQ(7, coords.values[1]); + ASSERT_EQ(1, coords.values[2]); + ASSERT_EQ(2, coords.values[3]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(7, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(1, coords.getAxisValue(2)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set an existing axis value in place. + ASSERT_EQ(OK, coords.setAxisValue(1, 6)); + ASSERT_EQ(0x0000000fULL, coords.bits); + ASSERT_EQ(4, coords.values[0]); + ASSERT_EQ(6, coords.values[1]); + ASSERT_EQ(1, coords.values[2]); + ASSERT_EQ(2, coords.values[3]); + + ASSERT_EQ(4, coords.getAxisValue(0)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(6, coords.getAxisValue(1)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(1, coords.getAxisValue(2)) + << "getAxisValue should return value of axis"; + ASSERT_EQ(2, coords.getAxisValue(3)) + << "getAxisValue should return value of axis"; + + // Set maximum number of axes. + for (size_t axis = 4; axis < PointerCoords::MAX_AXES; axis++) { + ASSERT_EQ(OK, coords.setAxisValue(axis, axis)); + } + ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits)); + + // Try to set one more axis beyond maximum number. + // Ensure bits are unchanged. + ASSERT_EQ(NO_MEMORY, coords.setAxisValue(PointerCoords::MAX_AXES, 100)); + ASSERT_EQ(PointerCoords::MAX_AXES, __builtin_popcountll(coords.bits)); +} + +TEST_F(PointerCoordsTest, Parcel) { + Parcel parcel; + + PointerCoords inCoords; + inCoords.clear(); + PointerCoords outCoords; + + // Round trip with empty coords. + inCoords.writeToParcel(&parcel); + parcel.setDataPosition(0); + outCoords.readFromParcel(&parcel); + + ASSERT_EQ(0ULL, outCoords.bits); + + // Round trip with some values. + parcel.freeData(); + inCoords.setAxisValue(2, 5); + inCoords.setAxisValue(5, 8); + + inCoords.writeToParcel(&parcel); + parcel.setDataPosition(0); + outCoords.readFromParcel(&parcel); + + ASSERT_EQ(outCoords.bits, inCoords.bits); + ASSERT_EQ(outCoords.values[0], inCoords.values[0]); + ASSERT_EQ(outCoords.values[1], inCoords.values[1]); +} + + +// --- KeyEventTest --- + +class KeyEventTest : public BaseTest { +}; + +TEST_F(KeyEventTest, Properties) { + KeyEvent event; + + // Initialize and get properties. + const nsecs_t ARBITRARY_DOWN_TIME = 1; + const nsecs_t ARBITRARY_EVENT_TIME = 2; + event.initialize(2, AINPUT_SOURCE_GAMEPAD, AKEY_EVENT_ACTION_DOWN, + AKEY_EVENT_FLAG_FROM_SYSTEM, AKEYCODE_BUTTON_X, 121, + AMETA_ALT_ON, 1, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME); + + ASSERT_EQ(AINPUT_EVENT_TYPE_KEY, event.getType()); + ASSERT_EQ(2, event.getDeviceId()); + ASSERT_EQ(AINPUT_SOURCE_GAMEPAD, event.getSource()); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, event.getAction()); + ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, event.getFlags()); + ASSERT_EQ(AKEYCODE_BUTTON_X, event.getKeyCode()); + ASSERT_EQ(121, event.getScanCode()); + ASSERT_EQ(AMETA_ALT_ON, event.getMetaState()); + ASSERT_EQ(1, event.getRepeatCount()); + ASSERT_EQ(ARBITRARY_DOWN_TIME, event.getDownTime()); + ASSERT_EQ(ARBITRARY_EVENT_TIME, event.getEventTime()); + + // Set source. + event.setSource(AINPUT_SOURCE_JOYSTICK); + ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); +} + + +// --- MotionEventTest --- + +class MotionEventTest : public BaseTest { +protected: + static const nsecs_t ARBITRARY_DOWN_TIME; + static const nsecs_t ARBITRARY_EVENT_TIME; + static const float X_OFFSET; + static const float Y_OFFSET; + + void initializeEventWithHistory(MotionEvent* event); + void assertEqualsEventWithHistory(const MotionEvent* event); +}; + +const nsecs_t MotionEventTest::ARBITRARY_DOWN_TIME = 1; +const nsecs_t MotionEventTest::ARBITRARY_EVENT_TIME = 2; +const float MotionEventTest::X_OFFSET = 1.0f; +const float MotionEventTest::Y_OFFSET = 1.1f; + +void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { + int32_t pointerIds[] = { 1, 2 }; + PointerCoords pointerCoords[2]; + pointerCoords[0].clear(); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); + pointerCoords[1].clear(); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); + event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, + AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, + X_OFFSET, Y_OFFSET, 2.0f, 2.1f, + ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, + 2, pointerIds, pointerCoords); + + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128); + event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords); + + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); + pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227); + pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228); + event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords); +} + +void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { + // Check properties. + ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType()); + ASSERT_EQ(2, event->getDeviceId()); + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, event->getSource()); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); + ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); + ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); + ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); + ASSERT_EQ(X_OFFSET, event->getXOffset()); + ASSERT_EQ(Y_OFFSET, event->getYOffset()); + ASSERT_EQ(2.0f, event->getXPrecision()); + ASSERT_EQ(2.1f, event->getYPrecision()); + ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime()); + + ASSERT_EQ(2U, event->getPointerCount()); + ASSERT_EQ(1, event->getPointerId(0)); + ASSERT_EQ(2, event->getPointerId(1)); + + ASSERT_EQ(2U, event->getHistorySize()); + + // Check data. + ASSERT_EQ(ARBITRARY_EVENT_TIME, event->getHistoricalEventTime(0)); + ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1)); + ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime()); + + ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(211, event->getRawPointerCoords(0)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(221, event->getRawPointerCoords(1)-> + getAxisValue(AMOTION_EVENT_AXIS_Y)); + + ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); + ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); + ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); + ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); + ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); + ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); + + ASSERT_EQ(10, event->getHistoricalRawX(0, 0)); + ASSERT_EQ(20, event->getHistoricalRawX(1, 0)); + ASSERT_EQ(110, event->getHistoricalRawX(0, 1)); + ASSERT_EQ(120, event->getHistoricalRawX(1, 1)); + ASSERT_EQ(210, event->getRawX(0)); + ASSERT_EQ(220, event->getRawX(1)); + + ASSERT_EQ(11, event->getHistoricalRawY(0, 0)); + ASSERT_EQ(21, event->getHistoricalRawY(1, 0)); + ASSERT_EQ(111, event->getHistoricalRawY(0, 1)); + ASSERT_EQ(121, event->getHistoricalRawY(1, 1)); + ASSERT_EQ(211, event->getRawY(0)); + ASSERT_EQ(221, event->getRawY(1)); + + ASSERT_EQ(X_OFFSET + 10, event->getHistoricalX(0, 0)); + ASSERT_EQ(X_OFFSET + 20, event->getHistoricalX(1, 0)); + ASSERT_EQ(X_OFFSET + 110, event->getHistoricalX(0, 1)); + ASSERT_EQ(X_OFFSET + 120, event->getHistoricalX(1, 1)); + ASSERT_EQ(X_OFFSET + 210, event->getX(0)); + ASSERT_EQ(X_OFFSET + 220, event->getX(1)); + + ASSERT_EQ(Y_OFFSET + 11, event->getHistoricalY(0, 0)); + ASSERT_EQ(Y_OFFSET + 21, event->getHistoricalY(1, 0)); + ASSERT_EQ(Y_OFFSET + 111, event->getHistoricalY(0, 1)); + ASSERT_EQ(Y_OFFSET + 121, event->getHistoricalY(1, 1)); + ASSERT_EQ(Y_OFFSET + 211, event->getY(0)); + ASSERT_EQ(Y_OFFSET + 221, event->getY(1)); + + ASSERT_EQ(12, event->getHistoricalPressure(0, 0)); + ASSERT_EQ(22, event->getHistoricalPressure(1, 0)); + ASSERT_EQ(112, event->getHistoricalPressure(0, 1)); + ASSERT_EQ(122, event->getHistoricalPressure(1, 1)); + ASSERT_EQ(212, event->getPressure(0)); + ASSERT_EQ(222, event->getPressure(1)); + + ASSERT_EQ(13, event->getHistoricalSize(0, 0)); + ASSERT_EQ(23, event->getHistoricalSize(1, 0)); + ASSERT_EQ(113, event->getHistoricalSize(0, 1)); + ASSERT_EQ(123, event->getHistoricalSize(1, 1)); + ASSERT_EQ(213, event->getSize(0)); + ASSERT_EQ(223, event->getSize(1)); + + ASSERT_EQ(14, event->getHistoricalTouchMajor(0, 0)); + ASSERT_EQ(24, event->getHistoricalTouchMajor(1, 0)); + ASSERT_EQ(114, event->getHistoricalTouchMajor(0, 1)); + ASSERT_EQ(124, event->getHistoricalTouchMajor(1, 1)); + ASSERT_EQ(214, event->getTouchMajor(0)); + ASSERT_EQ(224, event->getTouchMajor(1)); + + ASSERT_EQ(15, event->getHistoricalTouchMinor(0, 0)); + ASSERT_EQ(25, event->getHistoricalTouchMinor(1, 0)); + ASSERT_EQ(115, event->getHistoricalTouchMinor(0, 1)); + ASSERT_EQ(125, event->getHistoricalTouchMinor(1, 1)); + ASSERT_EQ(215, event->getTouchMinor(0)); + ASSERT_EQ(225, event->getTouchMinor(1)); + + ASSERT_EQ(16, event->getHistoricalToolMajor(0, 0)); + ASSERT_EQ(26, event->getHistoricalToolMajor(1, 0)); + ASSERT_EQ(116, event->getHistoricalToolMajor(0, 1)); + ASSERT_EQ(126, event->getHistoricalToolMajor(1, 1)); + ASSERT_EQ(216, event->getToolMajor(0)); + ASSERT_EQ(226, event->getToolMajor(1)); + + ASSERT_EQ(17, event->getHistoricalToolMinor(0, 0)); + ASSERT_EQ(27, event->getHistoricalToolMinor(1, 0)); + ASSERT_EQ(117, event->getHistoricalToolMinor(0, 1)); + ASSERT_EQ(127, event->getHistoricalToolMinor(1, 1)); + ASSERT_EQ(217, event->getToolMinor(0)); + ASSERT_EQ(227, event->getToolMinor(1)); + + ASSERT_EQ(18, event->getHistoricalOrientation(0, 0)); + ASSERT_EQ(28, event->getHistoricalOrientation(1, 0)); + ASSERT_EQ(118, event->getHistoricalOrientation(0, 1)); + ASSERT_EQ(128, event->getHistoricalOrientation(1, 1)); + ASSERT_EQ(218, event->getOrientation(0)); + ASSERT_EQ(228, event->getOrientation(1)); +} + +TEST_F(MotionEventTest, Properties) { + MotionEvent event; + + // Initialize, add samples and check properties. + initializeEventWithHistory(&event); + ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event)); + + // Set source. + event.setSource(AINPUT_SOURCE_JOYSTICK); + ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource()); + + // Set action. + event.setAction(AMOTION_EVENT_ACTION_CANCEL); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, event.getAction()); + + // Set meta state. + event.setMetaState(AMETA_CTRL_ON); + ASSERT_EQ(AMETA_CTRL_ON, event.getMetaState()); +} + +TEST_F(MotionEventTest, CopyFrom_KeepHistory) { + MotionEvent event; + initializeEventWithHistory(&event); + + MotionEvent copy; + copy.copyFrom(&event, true /*keepHistory*/); + + ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&event)); +} + +TEST_F(MotionEventTest, CopyFrom_DoNotKeepHistory) { + MotionEvent event; + initializeEventWithHistory(&event); + + MotionEvent copy; + copy.copyFrom(&event, false /*keepHistory*/); + + ASSERT_EQ(event.getPointerCount(), copy.getPointerCount()); + ASSERT_EQ(0U, copy.getHistorySize()); + + ASSERT_EQ(event.getPointerId(0), copy.getPointerId(0)); + ASSERT_EQ(event.getPointerId(1), copy.getPointerId(1)); + + ASSERT_EQ(event.getEventTime(), copy.getEventTime()); + + ASSERT_EQ(event.getX(0), copy.getX(0)); +} + +TEST_F(MotionEventTest, OffsetLocation) { + MotionEvent event; + initializeEventWithHistory(&event); + + event.offsetLocation(5.0f, -2.0f); + + ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset()); + ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset()); +} + +TEST_F(MotionEventTest, Scale) { + MotionEvent event; + initializeEventWithHistory(&event); + + event.scale(2.0f); + + ASSERT_EQ(X_OFFSET * 2, event.getXOffset()); + ASSERT_EQ(Y_OFFSET * 2, event.getYOffset()); + + ASSERT_EQ(210 * 2, event.getRawX(0)); + ASSERT_EQ(211 * 2, event.getRawY(0)); + ASSERT_EQ((X_OFFSET + 210) * 2, event.getX(0)); + ASSERT_EQ((Y_OFFSET + 211) * 2, event.getY(0)); + ASSERT_EQ(212, event.getPressure(0)); + ASSERT_EQ(213, event.getSize(0)); + ASSERT_EQ(214 * 2, event.getTouchMajor(0)); + ASSERT_EQ(215 * 2, event.getTouchMinor(0)); + ASSERT_EQ(216 * 2, event.getToolMajor(0)); + ASSERT_EQ(217 * 2, event.getToolMinor(0)); + ASSERT_EQ(218, event.getOrientation(0)); +} + +TEST_F(MotionEventTest, Parcel) { + Parcel parcel; + + MotionEvent inEvent; + initializeEventWithHistory(&inEvent); + MotionEvent outEvent; + + // Round trip. + inEvent.writeToParcel(&parcel); + parcel.setDataPosition(0); + outEvent.readFromParcel(&parcel); + + ASSERT_NO_FATAL_FAILURE(assertEqualsEventWithHistory(&outEvent)); +} + +TEST_F(MotionEventTest, Transform) { + // Generate some points on a circle. + // Each point 'i' is a point on a circle of radius ROTATION centered at (3,2) at an angle + // of ARC * i degrees clockwise relative to the Y axis. + // The geometrical representation is irrelevant to the test, it's just easy to generate + // and check rotation. We set the orientation to the same angle. + // Coordinate system: down is increasing Y, right is increasing X. + const float PI_180 = float(M_PI / 180); + const float RADIUS = 10; + const float ARC = 36; + const float ROTATION = ARC * 2; + + const size_t pointerCount = 11; + int pointerIds[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + float angle = float(i * ARC * PI_180); + pointerIds[i] = i; + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, sinf(angle) * RADIUS + 3); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, -cosf(angle) * RADIUS + 2); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, angle); + } + MotionEvent event; + event.initialize(0, 0, AMOTION_EVENT_ACTION_MOVE, 0, 0, 0, + 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); + float originalRawX = 0 + 3; + float originalRawY = -RADIUS + 2; + + // Check original raw X and Y assumption. + ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); + ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); + + // Now translate the motion event so the circle's origin is at (0,0). + event.offsetLocation(-3, -2); + + // Offsetting the location should preserve the raw X and Y of the first point. + ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); + ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); + + // Apply a rotation about the origin by ROTATION degrees clockwise. + SkMatrix matrix; + matrix.setRotate(ROTATION); + event.transform(&matrix); + + // Check the points. + for (size_t i = 0; i < pointerCount; i++) { + float angle = float((i * ARC + ROTATION) * PI_180); + ASSERT_NEAR(sinf(angle) * RADIUS, event.getX(i), 0.001); + ASSERT_NEAR(-cosf(angle) * RADIUS, event.getY(i), 0.001); + ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1); + } + + // Applying the transformation should preserve the raw X and Y of the first point. + ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); + ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); +} + +} // namespace android diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/ui/tests/InputPublisherAndConsumer_test.cpp index 952b9747691e..6e18a4f1abb6 100644 --- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/ui/tests/InputPublisherAndConsumer_test.cpp @@ -1,6 +1,18 @@ -// -// Copyright 2010 The Android Open Source Project -// +/* + * 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 <ui/InputTransport.h> #include <utils/Timers.h> @@ -118,13 +130,16 @@ void InputPublisherAndConsumerTest::PublishAndConsumeKeyEvent() { EXPECT_EQ(downTime, keyEvent->getDownTime()); EXPECT_EQ(eventTime, keyEvent->getEventTime()); - status = mConsumer->sendFinishedSignal(); + status = mConsumer->sendFinishedSignal(true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; - status = mPublisher->receiveFinishedSignal(); + bool handled = false; + status = mPublisher->receiveFinishedSignal(&handled); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_TRUE(handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; status = mPublisher->reset(); ASSERT_EQ(OK, status) @@ -156,15 +171,17 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( sampleEventTimes.push(i + 10); for (size_t j = 0; j < pointerCount; j++) { samplePointerCoords.push(); - samplePointerCoords.editTop().x = 100 * i + j; - samplePointerCoords.editTop().y = 200 * i + j; - samplePointerCoords.editTop().pressure = 0.5 * i + j; - samplePointerCoords.editTop().size = 0.7 * i + j; - samplePointerCoords.editTop().touchMajor = 1.5 * i + j; - samplePointerCoords.editTop().touchMinor = 1.7 * i + j; - samplePointerCoords.editTop().toolMajor = 2.5 * i + j; - samplePointerCoords.editTop().toolMinor = 2.7 * i + j; - samplePointerCoords.editTop().orientation = 3.5 * i + j; + PointerCoords& pc = samplePointerCoords.editTop(); + pc.clear(); + pc.setAxisValue(AMOTION_EVENT_AXIS_X, 100 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_Y, 200 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i + j); + pc.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i + j); } } @@ -236,27 +253,27 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); size_t offset = sampleIndex * pointerCount + i; - EXPECT_EQ(samplePointerCoords[offset].x, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X), motionEvent->getHistoricalRawX(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].y, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y), motionEvent->getHistoricalRawY(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].x + xOffset, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, motionEvent->getHistoricalX(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].y + yOffset, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, motionEvent->getHistoricalY(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].pressure, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getHistoricalPressure(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].size, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getHistoricalSize(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].touchMajor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getHistoricalTouchMajor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].touchMinor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getHistoricalTouchMinor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].toolMajor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getHistoricalToolMajor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].toolMinor, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getHistoricalToolMinor(i, sampleIndex)); - EXPECT_EQ(samplePointerCoords[offset].orientation, + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getHistoricalOrientation(i, sampleIndex)); } } @@ -266,26 +283,40 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent( for (size_t i = 0; i < pointerCount; i++) { SCOPED_TRACE(i); size_t offset = lastSampleIndex * pointerCount + i; - EXPECT_EQ(samplePointerCoords[offset].x, motionEvent->getRawX(i)); - EXPECT_EQ(samplePointerCoords[offset].y, motionEvent->getRawY(i)); - EXPECT_EQ(samplePointerCoords[offset].x + xOffset, motionEvent->getX(i)); - EXPECT_EQ(samplePointerCoords[offset].y + yOffset, motionEvent->getY(i)); - EXPECT_EQ(samplePointerCoords[offset].pressure, motionEvent->getPressure(i)); - EXPECT_EQ(samplePointerCoords[offset].size, motionEvent->getSize(i)); - EXPECT_EQ(samplePointerCoords[offset].touchMajor, motionEvent->getTouchMajor(i)); - EXPECT_EQ(samplePointerCoords[offset].touchMinor, motionEvent->getTouchMinor(i)); - EXPECT_EQ(samplePointerCoords[offset].toolMajor, motionEvent->getToolMajor(i)); - EXPECT_EQ(samplePointerCoords[offset].toolMinor, motionEvent->getToolMinor(i)); - EXPECT_EQ(samplePointerCoords[offset].orientation, motionEvent->getOrientation(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X), + motionEvent->getRawX(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y), + motionEvent->getRawY(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_X) + xOffset, + motionEvent->getX(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_Y) + yOffset, + motionEvent->getY(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + motionEvent->getPressure(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + motionEvent->getSize(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + motionEvent->getTouchMajor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + motionEvent->getTouchMinor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + motionEvent->getToolMajor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + motionEvent->getToolMinor(i)); + EXPECT_EQ(samplePointerCoords[offset].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), + motionEvent->getOrientation(i)); } - status = mConsumer->sendFinishedSignal(); + status = mConsumer->sendFinishedSignal(false); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; - status = mPublisher->receiveFinishedSignal(); + bool handled = true; + status = mPublisher->receiveFinishedSignal(&handled); ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK"; + ASSERT_FALSE(handled) + << "publisher receiveFinishedSignal should have set handled to consumer's reply"; status = mPublisher->reset(); ASSERT_EQ(OK, status) @@ -322,7 +353,8 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenNotReset_ReturnsErr const size_t pointerCount = 1; int32_t pointerIds[pointerCount] = { 0 }; - PointerCoords pointerCoords[pointerCount] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; + PointerCoords pointerCoords[pointerCount]; + pointerCoords[0].clear(); status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, pointerCount, pointerIds, pointerCoords); diff --git a/libs/ui/tests/InputReader_test.cpp b/libs/ui/tests/InputReader_test.cpp deleted file mode 100644 index 09d1680e355d..000000000000 --- a/libs/ui/tests/InputReader_test.cpp +++ /dev/null @@ -1,3380 +0,0 @@ -// -// Copyright 2010 The Android Open Source Project -// - -#include <ui/InputReader.h> -#include <utils/List.h> -#include <gtest/gtest.h> -#include <math.h> - -namespace android { - -// An arbitrary time value. -static const nsecs_t ARBITRARY_TIME = 1234; - -// Arbitrary display properties. -static const int32_t DISPLAY_ID = 0; -static const int32_t DISPLAY_WIDTH = 480; -static const int32_t DISPLAY_HEIGHT = 800; - -// Error tolerance for floating point assertions. -static const float EPSILON = 0.001f; - -template<typename T> -static inline T min(T a, T b) { - return a < b ? a : b; -} - -static inline float avg(float x, float y) { - return (x + y) / 2; -} - - -// --- FakeInputReaderPolicy --- - -class FakeInputReaderPolicy : public InputReaderPolicyInterface { - struct DisplayInfo { - int32_t width; - int32_t height; - int32_t orientation; - }; - - KeyedVector<int32_t, DisplayInfo> mDisplayInfos; - bool mFilterTouchEvents; - bool mFilterJumpyTouchEvents; - KeyedVector<String8, Vector<VirtualKeyDefinition> > mVirtualKeyDefinitions; - KeyedVector<String8, InputDeviceCalibration> mInputDeviceCalibrations; - Vector<String8> mExcludedDeviceNames; - -protected: - virtual ~FakeInputReaderPolicy() { } - -public: - FakeInputReaderPolicy() : - mFilterTouchEvents(false), mFilterJumpyTouchEvents(false) { - } - - void removeDisplayInfo(int32_t displayId) { - mDisplayInfos.removeItem(displayId); - } - - void setDisplayInfo(int32_t displayId, int32_t width, int32_t height, int32_t orientation) { - removeDisplayInfo(displayId); - - DisplayInfo info; - info.width = width; - info.height = height; - info.orientation = orientation; - mDisplayInfos.add(displayId, info); - } - - void setFilterTouchEvents(bool enabled) { - mFilterTouchEvents = enabled; - } - - void setFilterJumpyTouchEvents(bool enabled) { - mFilterJumpyTouchEvents = enabled; - } - - void addInputDeviceCalibration(const String8& deviceName, - const InputDeviceCalibration& calibration) { - mInputDeviceCalibrations.add(deviceName, calibration); - } - - void addInputDeviceCalibrationProperty(const String8& deviceName, - const String8& key, const String8& value) { - ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName); - if (index < 0) { - index = mInputDeviceCalibrations.add(deviceName, InputDeviceCalibration()); - } - mInputDeviceCalibrations.editValueAt(index).addProperty(key, value); - } - - void addVirtualKeyDefinition(const String8& deviceName, - const VirtualKeyDefinition& definition) { - if (mVirtualKeyDefinitions.indexOfKey(deviceName) < 0) { - mVirtualKeyDefinitions.add(deviceName, Vector<VirtualKeyDefinition>()); - } - - mVirtualKeyDefinitions.editValueFor(deviceName).push(definition); - } - - void addExcludedDeviceName(const String8& deviceName) { - mExcludedDeviceNames.push(deviceName); - } - -private: - virtual bool getDisplayInfo(int32_t displayId, - int32_t* width, int32_t* height, int32_t* orientation) { - ssize_t index = mDisplayInfos.indexOfKey(displayId); - if (index >= 0) { - const DisplayInfo& info = mDisplayInfos.valueAt(index); - if (width) { - *width = info.width; - } - if (height) { - *height = info.height; - } - if (orientation) { - *orientation = info.orientation; - } - return true; - } - return false; - } - - virtual bool filterTouchEvents() { - return mFilterTouchEvents; - } - - virtual bool filterJumpyTouchEvents() { - return mFilterJumpyTouchEvents; - } - - virtual nsecs_t getVirtualKeyQuietTime() { - return 0; - } - - virtual void getVirtualKeyDefinitions(const String8& deviceName, - Vector<VirtualKeyDefinition>& outVirtualKeyDefinitions) { - ssize_t index = mVirtualKeyDefinitions.indexOfKey(deviceName); - if (index >= 0) { - outVirtualKeyDefinitions.appendVector(mVirtualKeyDefinitions.valueAt(index)); - } - } - - virtual void getInputDeviceCalibration(const String8& deviceName, - InputDeviceCalibration& outCalibration) { - ssize_t index = mInputDeviceCalibrations.indexOfKey(deviceName); - if (index >= 0) { - outCalibration = mInputDeviceCalibrations.valueAt(index); - } - } - - virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) { - outExcludedDeviceNames.appendVector(mExcludedDeviceNames); - } -}; - - -// --- FakeInputDispatcher --- - -class FakeInputDispatcher : public InputDispatcherInterface { -public: - struct NotifyConfigurationChangedArgs { - nsecs_t eventTime; - }; - - struct NotifyKeyArgs { - nsecs_t eventTime; - int32_t deviceId; - int32_t source; - uint32_t policyFlags; - int32_t action; - int32_t flags; - int32_t keyCode; - int32_t scanCode; - int32_t metaState; - nsecs_t downTime; - }; - - struct NotifyMotionArgs { - nsecs_t eventTime; - int32_t deviceId; - int32_t source; - uint32_t policyFlags; - int32_t action; - int32_t flags; - int32_t metaState; - int32_t edgeFlags; - uint32_t pointerCount; - Vector<int32_t> pointerIds; - Vector<PointerCoords> pointerCoords; - float xPrecision; - float yPrecision; - nsecs_t downTime; - }; - - struct NotifySwitchArgs { - nsecs_t when; - int32_t switchCode; - int32_t switchValue; - uint32_t policyFlags; - }; - -private: - List<NotifyConfigurationChangedArgs> mNotifyConfigurationChangedArgs; - List<NotifyKeyArgs> mNotifyKeyArgs; - List<NotifyMotionArgs> mNotifyMotionArgs; - List<NotifySwitchArgs> mNotifySwitchArgs; - -protected: - virtual ~FakeInputDispatcher() { } - -public: - FakeInputDispatcher() { - } - - void assertNotifyConfigurationChangedWasCalled(NotifyConfigurationChangedArgs* outArgs = NULL) { - ASSERT_FALSE(mNotifyConfigurationChangedArgs.empty()) - << "Expected notifyConfigurationChanged() to have been called."; - if (outArgs) { - *outArgs = *mNotifyConfigurationChangedArgs.begin(); - } - mNotifyConfigurationChangedArgs.erase(mNotifyConfigurationChangedArgs.begin()); - } - - void assertNotifyKeyWasCalled(NotifyKeyArgs* outArgs = NULL) { - ASSERT_FALSE(mNotifyKeyArgs.empty()) - << "Expected notifyKey() to have been called."; - if (outArgs) { - *outArgs = *mNotifyKeyArgs.begin(); - } - mNotifyKeyArgs.erase(mNotifyKeyArgs.begin()); - } - - void assertNotifyKeyWasNotCalled() { - ASSERT_TRUE(mNotifyKeyArgs.empty()) - << "Expected notifyKey() to not have been called."; - } - - void assertNotifyMotionWasCalled(NotifyMotionArgs* outArgs = NULL) { - ASSERT_FALSE(mNotifyMotionArgs.empty()) - << "Expected notifyMotion() to have been called."; - if (outArgs) { - *outArgs = *mNotifyMotionArgs.begin(); - } - mNotifyMotionArgs.erase(mNotifyMotionArgs.begin()); - } - - void assertNotifyMotionWasNotCalled() { - ASSERT_TRUE(mNotifyMotionArgs.empty()) - << "Expected notifyMotion() to not have been called."; - } - - void assertNotifySwitchWasCalled(NotifySwitchArgs* outArgs = NULL) { - ASSERT_FALSE(mNotifySwitchArgs.empty()) - << "Expected notifySwitch() to have been called."; - if (outArgs) { - *outArgs = *mNotifySwitchArgs.begin(); - } - mNotifySwitchArgs.erase(mNotifySwitchArgs.begin()); - } - -private: - virtual void notifyConfigurationChanged(nsecs_t eventTime) { - NotifyConfigurationChangedArgs args; - args.eventTime = eventTime; - mNotifyConfigurationChangedArgs.push_back(args); - } - - virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source, - uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, - int32_t scanCode, int32_t metaState, nsecs_t downTime) { - NotifyKeyArgs args; - args.eventTime = eventTime; - args.deviceId = deviceId; - args.source = source; - args.policyFlags = policyFlags; - args.action = action; - args.flags = flags; - args.keyCode = keyCode; - args.scanCode = scanCode; - args.metaState = metaState; - args.downTime = downTime; - mNotifyKeyArgs.push_back(args); - } - - virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t source, - uint32_t policyFlags, int32_t action, int32_t flags, - int32_t metaState, int32_t edgeFlags, - uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords, - float xPrecision, float yPrecision, nsecs_t downTime) { - NotifyMotionArgs args; - args.eventTime = eventTime; - args.deviceId = deviceId; - args.source = source; - args.policyFlags = policyFlags; - args.action = action; - args.flags = flags; - args.metaState = metaState; - args.edgeFlags = edgeFlags; - args.pointerCount = pointerCount; - args.pointerIds.clear(); - args.pointerIds.appendArray(pointerIds, pointerCount); - args.pointerCoords.clear(); - args.pointerCoords.appendArray(pointerCoords, pointerCount); - args.xPrecision = xPrecision; - args.yPrecision = yPrecision; - args.downTime = downTime; - mNotifyMotionArgs.push_back(args); - } - - virtual void notifySwitch(nsecs_t when, - int32_t switchCode, int32_t switchValue, uint32_t policyFlags) { - NotifySwitchArgs args; - args.when = when; - args.switchCode = switchCode; - args.switchValue = switchValue; - args.policyFlags = policyFlags; - mNotifySwitchArgs.push_back(args); - } - - virtual void dump(String8& dump) { - ADD_FAILURE() << "Should never be called by input reader."; - } - - virtual void dispatchOnce() { - ADD_FAILURE() << "Should never be called by input reader."; - } - - virtual int32_t injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) { - ADD_FAILURE() << "Should never be called by input reader."; - return INPUT_EVENT_INJECTION_FAILED; - } - - virtual void setInputWindows(const Vector<InputWindow>& inputWindows) { - ADD_FAILURE() << "Should never be called by input reader."; - } - - virtual void setFocusedApplication(const InputApplication* inputApplication) { - ADD_FAILURE() << "Should never be called by input reader."; - } - - virtual void setInputDispatchMode(bool enabled, bool frozen) { - ADD_FAILURE() << "Should never be called by input reader."; - } - - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) { - ADD_FAILURE() << "Should never be called by input reader."; - return 0; - } - - virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) { - ADD_FAILURE() << "Should never be called by input reader."; - return 0; - } -}; - - -// --- FakeEventHub --- - -class FakeEventHub : public EventHubInterface { - struct KeyInfo { - int32_t keyCode; - uint32_t flags; - }; - - struct Device { - String8 name; - uint32_t classes; - KeyedVector<int, RawAbsoluteAxisInfo> axes; - KeyedVector<int32_t, int32_t> keyCodeStates; - KeyedVector<int32_t, int32_t> scanCodeStates; - KeyedVector<int32_t, int32_t> switchStates; - KeyedVector<int32_t, KeyInfo> keys; - - Device(const String8& name, uint32_t classes) : - name(name), classes(classes) { - } - }; - - KeyedVector<int32_t, Device*> mDevices; - Vector<String8> mExcludedDevices; - List<RawEvent> mEvents; - -protected: - virtual ~FakeEventHub() { - for (size_t i = 0; i < mDevices.size(); i++) { - delete mDevices.valueAt(i); - } - } - -public: - FakeEventHub() { } - - void addDevice(int32_t deviceId, const String8& name, uint32_t classes) { - Device* device = new Device(name, classes); - mDevices.add(deviceId, device); - - enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_ADDED, 0, 0, 0, 0); - } - - void removeDevice(int32_t deviceId) { - delete mDevices.valueFor(deviceId); - mDevices.removeItem(deviceId); - - enqueueEvent(ARBITRARY_TIME, deviceId, EventHubInterface::DEVICE_REMOVED, 0, 0, 0, 0); - } - - void finishDeviceScan() { - enqueueEvent(ARBITRARY_TIME, 0, EventHubInterface::FINISHED_DEVICE_SCAN, 0, 0, 0, 0); - } - - void addAxis(int32_t deviceId, int axis, - int32_t minValue, int32_t maxValue, int flat, int fuzz) { - Device* device = getDevice(deviceId); - - RawAbsoluteAxisInfo info; - info.valid = true; - info.minValue = minValue; - info.maxValue = maxValue; - info.flat = flat; - info.fuzz = fuzz; - device->axes.add(axis, info); - } - - void setKeyCodeState(int32_t deviceId, int32_t keyCode, int32_t state) { - Device* device = getDevice(deviceId); - device->keyCodeStates.replaceValueFor(keyCode, state); - } - - void setScanCodeState(int32_t deviceId, int32_t scanCode, int32_t state) { - Device* device = getDevice(deviceId); - device->scanCodeStates.replaceValueFor(scanCode, state); - } - - void setSwitchState(int32_t deviceId, int32_t switchCode, int32_t state) { - Device* device = getDevice(deviceId); - device->switchStates.replaceValueFor(switchCode, state); - } - - void addKey(int32_t deviceId, int32_t scanCode, int32_t keyCode, uint32_t flags) { - Device* device = getDevice(deviceId); - KeyInfo info; - info.keyCode = keyCode; - info.flags = flags; - device->keys.add(scanCode, info); - } - - Vector<String8>& getExcludedDevices() { - return mExcludedDevices; - } - - void enqueueEvent(nsecs_t when, int32_t deviceId, int32_t type, - int32_t scanCode, int32_t keyCode, int32_t value, uint32_t flags) { - RawEvent event; - event.when = when; - event.deviceId = deviceId; - event.type = type; - event.scanCode = scanCode; - event.keyCode = keyCode; - event.value = value; - event.flags = flags; - mEvents.push_back(event); - } - - void assertQueueIsEmpty() { - ASSERT_EQ(size_t(0), mEvents.size()) - << "Expected the event queue to be empty (fully consumed)."; - } - -private: - Device* getDevice(int32_t deviceId) const { - ssize_t index = mDevices.indexOfKey(deviceId); - return index >= 0 ? mDevices.valueAt(index) : NULL; - } - - virtual uint32_t getDeviceClasses(int32_t deviceId) const { - Device* device = getDevice(deviceId); - return device ? device->classes : 0; - } - - virtual String8 getDeviceName(int32_t deviceId) const { - Device* device = getDevice(deviceId); - return device ? device->name : String8("unknown"); - } - - virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->axes.indexOfKey(axis); - if (index >= 0) { - *outAxisInfo = device->axes.valueAt(index); - return OK; - } - } - return -1; - } - - virtual status_t scancodeToKeycode(int32_t deviceId, int scancode, - int32_t* outKeycode, uint32_t* outFlags) const { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->keys.indexOfKey(scancode); - if (index >= 0) { - if (outKeycode) { - *outKeycode = device->keys.valueAt(index).keyCode; - } - if (outFlags) { - *outFlags = device->keys.valueAt(index).flags; - } - return OK; - } - } - return NAME_NOT_FOUND; - } - - virtual void addExcludedDevice(const char* deviceName) { - mExcludedDevices.add(String8(deviceName)); - } - - virtual bool getEvent(RawEvent* outEvent) { - if (mEvents.empty()) { - return false; - } - - *outEvent = *mEvents.begin(); - mEvents.erase(mEvents.begin()); - return true; - } - - virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->scanCodeStates.indexOfKey(scanCode); - if (index >= 0) { - return device->scanCodeStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->keyCodeStates.indexOfKey(keyCode); - if (index >= 0) { - return device->keyCodeStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const { - Device* device = getDevice(deviceId); - if (device) { - ssize_t index = device->switchStates.indexOfKey(sw); - if (index >= 0) { - return device->switchStates.valueAt(index); - } - } - return AKEY_STATE_UNKNOWN; - } - - virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) const { - bool result = false; - Device* device = getDevice(deviceId); - if (device) { - for (size_t i = 0; i < numCodes; i++) { - for (size_t j = 0; j < device->keys.size(); j++) { - if (keyCodes[i] == device->keys.valueAt(j).keyCode) { - outFlags[i] = 1; - result = true; - } - } - } - } - return result; - } - - virtual void dump(String8& dump) { - } -}; - - -// --- FakeInputReaderContext --- - -class FakeInputReaderContext : public InputReaderContext { - sp<EventHubInterface> mEventHub; - sp<InputReaderPolicyInterface> mPolicy; - sp<InputDispatcherInterface> mDispatcher; - int32_t mGlobalMetaState; - bool mUpdateGlobalMetaStateWasCalled; - -public: - FakeInputReaderContext(const sp<EventHubInterface>& eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputDispatcherInterface>& dispatcher) : - mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher), - mGlobalMetaState(0) { - } - - virtual ~FakeInputReaderContext() { } - - void assertUpdateGlobalMetaStateWasCalled() { - ASSERT_TRUE(mUpdateGlobalMetaStateWasCalled) - << "Expected updateGlobalMetaState() to have been called."; - mUpdateGlobalMetaStateWasCalled = false; - } - - void setGlobalMetaState(int32_t state) { - mGlobalMetaState = state; - } - -private: - virtual void updateGlobalMetaState() { - mUpdateGlobalMetaStateWasCalled = true; - } - - virtual int32_t getGlobalMetaState() { - return mGlobalMetaState; - } - - virtual EventHubInterface* getEventHub() { - return mEventHub.get(); - } - - virtual InputReaderPolicyInterface* getPolicy() { - return mPolicy.get(); - } - - virtual InputDispatcherInterface* getDispatcher() { - return mDispatcher.get(); - } - - virtual void disableVirtualKeysUntil(nsecs_t time) { - } - - virtual bool shouldDropVirtualKey(nsecs_t now, - InputDevice* device, int32_t keyCode, int32_t scanCode) { - return false; - } -}; - - -// --- FakeInputMapper --- - -class FakeInputMapper : public InputMapper { - uint32_t mSources; - int32_t mKeyboardType; - int32_t mMetaState; - KeyedVector<int32_t, int32_t> mKeyCodeStates; - KeyedVector<int32_t, int32_t> mScanCodeStates; - KeyedVector<int32_t, int32_t> mSwitchStates; - Vector<int32_t> mSupportedKeyCodes; - RawEvent mLastEvent; - - bool mConfigureWasCalled; - bool mResetWasCalled; - bool mProcessWasCalled; - -public: - FakeInputMapper(InputDevice* device, uint32_t sources) : - InputMapper(device), - mSources(sources), mKeyboardType(AINPUT_KEYBOARD_TYPE_NONE), - mMetaState(0), - mConfigureWasCalled(false), mResetWasCalled(false), mProcessWasCalled(false) { - } - - virtual ~FakeInputMapper() { } - - void setKeyboardType(int32_t keyboardType) { - mKeyboardType = keyboardType; - } - - void setMetaState(int32_t metaState) { - mMetaState = metaState; - } - - void assertConfigureWasCalled() { - ASSERT_TRUE(mConfigureWasCalled) - << "Expected configure() to have been called."; - mConfigureWasCalled = false; - } - - void assertResetWasCalled() { - ASSERT_TRUE(mResetWasCalled) - << "Expected reset() to have been called."; - mResetWasCalled = false; - } - - void assertProcessWasCalled(RawEvent* outLastEvent = NULL) { - ASSERT_TRUE(mProcessWasCalled) - << "Expected process() to have been called."; - if (outLastEvent) { - *outLastEvent = mLastEvent; - } - mProcessWasCalled = false; - } - - void setKeyCodeState(int32_t keyCode, int32_t state) { - mKeyCodeStates.replaceValueFor(keyCode, state); - } - - void setScanCodeState(int32_t scanCode, int32_t state) { - mScanCodeStates.replaceValueFor(scanCode, state); - } - - void setSwitchState(int32_t switchCode, int32_t state) { - mSwitchStates.replaceValueFor(switchCode, state); - } - - void addSupportedKeyCode(int32_t keyCode) { - mSupportedKeyCodes.add(keyCode); - } - -private: - virtual uint32_t getSources() { - return mSources; - } - - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) { - InputMapper::populateDeviceInfo(deviceInfo); - - if (mKeyboardType != AINPUT_KEYBOARD_TYPE_NONE) { - deviceInfo->setKeyboardType(mKeyboardType); - } - } - - virtual void configure() { - mConfigureWasCalled = true; - } - - virtual void reset() { - mResetWasCalled = true; - } - - virtual void process(const RawEvent* rawEvent) { - mLastEvent = *rawEvent; - mProcessWasCalled = true; - } - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) { - ssize_t index = mKeyCodeStates.indexOfKey(keyCode); - return index >= 0 ? mKeyCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN; - } - - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) { - ssize_t index = mScanCodeStates.indexOfKey(scanCode); - return index >= 0 ? mScanCodeStates.valueAt(index) : AKEY_STATE_UNKNOWN; - } - - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) { - ssize_t index = mSwitchStates.indexOfKey(switchCode); - return index >= 0 ? mSwitchStates.valueAt(index) : AKEY_STATE_UNKNOWN; - } - - virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags) { - bool result = false; - for (size_t i = 0; i < numCodes; i++) { - for (size_t j = 0; j < mSupportedKeyCodes.size(); j++) { - if (keyCodes[i] == mSupportedKeyCodes[j]) { - outFlags[i] = 1; - result = true; - } - } - } - return result; - } - - virtual int32_t getMetaState() { - return mMetaState; - } -}; - - -// --- InstrumentedInputReader --- - -class InstrumentedInputReader : public InputReader { - InputDevice* mNextDevice; - -public: - InstrumentedInputReader(const sp<EventHubInterface>& eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputDispatcherInterface>& dispatcher) : - InputReader(eventHub, policy, dispatcher) { - } - - virtual ~InstrumentedInputReader() { - if (mNextDevice) { - delete mNextDevice; - } - } - - void setNextDevice(InputDevice* device) { - mNextDevice = device; - } - -protected: - virtual InputDevice* createDevice(int32_t deviceId, const String8& name, uint32_t classes) { - if (mNextDevice) { - InputDevice* device = mNextDevice; - mNextDevice = NULL; - return device; - } - return InputReader::createDevice(deviceId, name, classes); - } - - friend class InputReaderTest; -}; - - -// --- InputReaderTest --- - -class InputReaderTest : public testing::Test { -protected: - sp<FakeInputDispatcher> mFakeDispatcher; - sp<FakeInputReaderPolicy> mFakePolicy; - sp<FakeEventHub> mFakeEventHub; - sp<InstrumentedInputReader> mReader; - - virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); - mFakePolicy = new FakeInputReaderPolicy(); - mFakeDispatcher = new FakeInputDispatcher(); - - mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeDispatcher); - } - - virtual void TearDown() { - mReader.clear(); - - mFakeDispatcher.clear(); - mFakePolicy.clear(); - mFakeEventHub.clear(); - } - - void addDevice(int32_t deviceId, const String8& name, uint32_t classes) { - mFakeEventHub->addDevice(deviceId, name, classes); - mFakeEventHub->finishDeviceScan(); - mReader->loopOnce(); - mReader->loopOnce(); - mFakeEventHub->assertQueueIsEmpty(); - } - - FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, - const String8& name, uint32_t classes, uint32_t sources) { - InputDevice* device = new InputDevice(mReader.get(), deviceId, name); - FakeInputMapper* mapper = new FakeInputMapper(device, sources); - device->addMapper(mapper); - mReader->setNextDevice(device); - addDevice(deviceId, name, classes); - return mapper; - } -}; - -TEST_F(InputReaderTest, GetInputConfiguration_WhenNoDevices_ReturnsDefaults) { - InputConfiguration config; - mReader->getInputConfiguration(&config); - - ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard); - ASSERT_EQ(InputConfiguration::NAVIGATION_NONAV, config.navigation); - ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen); -} - -TEST_F(InputReaderTest, GetInputConfiguration_WhenAlphabeticKeyboardPresent_ReturnsQwertyKeyboard) { - ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("keyboard"), - INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY)); - - InputConfiguration config; - mReader->getInputConfiguration(&config); - - ASSERT_EQ(InputConfiguration::KEYBOARD_QWERTY, config.keyboard); - ASSERT_EQ(InputConfiguration::NAVIGATION_NONAV, config.navigation); - ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen); -} - -TEST_F(InputReaderTest, GetInputConfiguration_WhenTouchScreenPresent_ReturnsFingerTouchScreen) { - ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("touchscreen"), - INPUT_DEVICE_CLASS_TOUCHSCREEN)); - - InputConfiguration config; - mReader->getInputConfiguration(&config); - - ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard); - ASSERT_EQ(InputConfiguration::NAVIGATION_NONAV, config.navigation); - ASSERT_EQ(InputConfiguration::TOUCHSCREEN_FINGER, config.touchScreen); -} - -TEST_F(InputReaderTest, GetInputConfiguration_WhenTrackballPresent_ReturnsTrackballNavigation) { - ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("trackball"), - INPUT_DEVICE_CLASS_TRACKBALL)); - - InputConfiguration config; - mReader->getInputConfiguration(&config); - - ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard); - ASSERT_EQ(InputConfiguration::NAVIGATION_TRACKBALL, config.navigation); - ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen); -} - -TEST_F(InputReaderTest, GetInputConfiguration_WhenDPadPresent_ReturnsDPadNavigation) { - ASSERT_NO_FATAL_FAILURE(addDevice(0, String8("dpad"), - INPUT_DEVICE_CLASS_DPAD)); - - InputConfiguration config; - mReader->getInputConfiguration(&config); - - ASSERT_EQ(InputConfiguration::KEYBOARD_NOKEYS, config.keyboard); - ASSERT_EQ(InputConfiguration::NAVIGATION_DPAD, config.navigation); - ASSERT_EQ(InputConfiguration::TOUCHSCREEN_NOTOUCH, config.touchScreen); -} - -TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsValid) { - ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"), - INPUT_DEVICE_CLASS_KEYBOARD)); - - InputDeviceInfo info; - status_t result = mReader->getInputDeviceInfo(1, &info); - - ASSERT_EQ(OK, result); - ASSERT_EQ(1, info.getId()); - ASSERT_STREQ("keyboard", info.getName().string()); - ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC, info.getKeyboardType()); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, info.getSources()); - ASSERT_EQ(size_t(0), info.getMotionRanges().size()); -} - -TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsInvalid) { - InputDeviceInfo info; - status_t result = mReader->getInputDeviceInfo(-1, &info); - - ASSERT_EQ(NAME_NOT_FOUND, result); -} - -TEST_F(InputReaderTest, GetInputDeviceInfo_WhenDeviceIdIsIgnored) { - addDevice(1, String8("ignored"), 0); // no classes so device will be ignored - - InputDeviceInfo info; - status_t result = mReader->getInputDeviceInfo(1, &info); - - ASSERT_EQ(NAME_NOT_FOUND, result); -} - -TEST_F(InputReaderTest, GetInputDeviceIds) { - ASSERT_NO_FATAL_FAILURE(addDevice(1, String8("keyboard"), - INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_ALPHAKEY)); - ASSERT_NO_FATAL_FAILURE(addDevice(2, String8("trackball"), - INPUT_DEVICE_CLASS_TRACKBALL)); - - Vector<int32_t> ids; - mReader->getInputDeviceIds(ids); - - ASSERT_EQ(size_t(2), ids.size()); - ASSERT_EQ(1, ids[0]); - ASSERT_EQ(2, ids[1]); -} - -TEST_F(InputReaderTest, GetKeyCodeState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD)); - mapper->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(0, - AINPUT_SOURCE_ANY, AKEYCODE_A)) - << "Should return unknown when the device id is >= 0 but unknown."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(1, - AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getKeyCodeState(-1, - AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown when the device id is < 0 but the sources are not supported by any device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getKeyCodeState(-1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources."; -} - -TEST_F(InputReaderTest, GetScanCodeState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD)); - mapper->setScanCodeState(KEY_A, AKEY_STATE_DOWN); - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(0, - AINPUT_SOURCE_ANY, KEY_A)) - << "Should return unknown when the device id is >= 0 but unknown."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(1, - AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getScanCodeState(-1, - AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return unknown when the device id is < 0 but the sources are not supported by any device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getScanCodeState(-1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, KEY_A)) - << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources."; -} - -TEST_F(InputReaderTest, GetSwitchState_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD)); - mapper->setSwitchState(SW_LID, AKEY_STATE_DOWN); - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(0, - AINPUT_SOURCE_ANY, SW_LID)) - << "Should return unknown when the device id is >= 0 but unknown."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(1, - AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return unknown when the device id is valid but the sources are not supported by the device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mReader->getSwitchState(-1, - AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return unknown when the device id is < 0 but the sources are not supported by any device."; - - ASSERT_EQ(AKEY_STATE_DOWN, mReader->getSwitchState(-1, - AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, SW_LID)) - << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources."; -} - -TEST_F(InputReaderTest, MarkSupportedKeyCodes_ForwardsRequestsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD)); - mapper->addSupportedKeyCode(AKEYCODE_A); - mapper->addSupportedKeyCode(AKEYCODE_B); - - const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 }; - uint8_t flags[4] = { 0, 0, 0, 1 }; - - ASSERT_FALSE(mReader->hasKeys(0, AINPUT_SOURCE_ANY, 4, keyCodes, flags)) - << "Should return false when device id is >= 0 but unknown."; - ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); - - flags[3] = 1; - ASSERT_FALSE(mReader->hasKeys(1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return false when device id is valid but the sources are not supported by the device."; - ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); - - flags[3] = 1; - ASSERT_TRUE(mReader->hasKeys(1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return value provided by mapper when device id is valid and the device supports some of the sources."; - ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); - - flags[3] = 1; - ASSERT_FALSE(mReader->hasKeys(-1, AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return false when the device id is < 0 but the sources are not supported by any device."; - ASSERT_TRUE(!flags[0] && !flags[1] && !flags[2] && !flags[3]); - - flags[3] = 1; - ASSERT_TRUE(mReader->hasKeys(-1, AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should return value provided by mapper when device id is < 0 and one of the devices supports some of the sources."; - ASSERT_TRUE(flags[0] && flags[1] && !flags[2] && !flags[3]); -} - -TEST_F(InputReaderTest, LoopOnce_WhenDeviceScanFinished_SendsConfigurationChanged) { - addDevice(1, String8("ignored"), INPUT_DEVICE_CLASS_KEYBOARD); - - FakeInputDispatcher::NotifyConfigurationChangedArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyConfigurationChangedWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); -} - -TEST_F(InputReaderTest, LoopOnce_ForwardsRawEventsToMappers) { - FakeInputMapper* mapper = NULL; - ASSERT_NO_FATAL_FAILURE(mapper = addDeviceWithFakeInputMapper(1, String8("fake"), - INPUT_DEVICE_CLASS_KEYBOARD, AINPUT_SOURCE_KEYBOARD)); - - mFakeEventHub->enqueueEvent(0, 1, EV_KEY, KEY_A, AKEYCODE_A, 1, POLICY_FLAG_WAKE); - mReader->loopOnce(); - ASSERT_NO_FATAL_FAILURE(mFakeEventHub->assertQueueIsEmpty()); - - RawEvent event; - ASSERT_NO_FATAL_FAILURE(mapper->assertProcessWasCalled(&event)); - ASSERT_EQ(0, event.when); - ASSERT_EQ(1, event.deviceId); - ASSERT_EQ(EV_KEY, event.type); - ASSERT_EQ(KEY_A, event.scanCode); - ASSERT_EQ(AKEYCODE_A, event.keyCode); - ASSERT_EQ(1, event.value); - ASSERT_EQ(POLICY_FLAG_WAKE, event.flags); -} - - -// --- InputDeviceTest --- - -class InputDeviceTest : public testing::Test { -protected: - static const char* DEVICE_NAME; - static const int32_t DEVICE_ID; - - sp<FakeEventHub> mFakeEventHub; - sp<FakeInputReaderPolicy> mFakePolicy; - sp<FakeInputDispatcher> mFakeDispatcher; - FakeInputReaderContext* mFakeContext; - - InputDevice* mDevice; - - virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); - mFakePolicy = new FakeInputReaderPolicy(); - mFakeDispatcher = new FakeInputDispatcher(); - mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeDispatcher); - - mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME)); - } - - virtual void TearDown() { - delete mDevice; - - delete mFakeContext; - mFakeDispatcher.clear(); - mFakePolicy.clear(); - mFakeEventHub.clear(); - } -}; - -const char* InputDeviceTest::DEVICE_NAME = "device"; -const int32_t InputDeviceTest::DEVICE_ID = 1; - -TEST_F(InputDeviceTest, ImmutableProperties) { - ASSERT_EQ(DEVICE_ID, mDevice->getId()); - ASSERT_STREQ(DEVICE_NAME, mDevice->getName()); -} - -TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { - // Configuration. - mDevice->configure(); - - // Metadata. - ASSERT_TRUE(mDevice->isIgnored()); - ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, mDevice->getSources()); - - InputDeviceInfo info; - mDevice->getDeviceInfo(&info); - ASSERT_EQ(DEVICE_ID, info.getId()); - ASSERT_STREQ(DEVICE_NAME, info.getName().string()); - ASSERT_EQ(AINPUT_KEYBOARD_TYPE_NONE, info.getKeyboardType()); - ASSERT_EQ(AINPUT_SOURCE_UNKNOWN, info.getSources()); - - // State queries. - ASSERT_EQ(0, mDevice->getMetaState()); - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_KEYBOARD, 0)) - << "Ignored device should return unknown key code state."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getScanCodeState(AINPUT_SOURCE_KEYBOARD, 0)) - << "Ignored device should return unknown scan code state."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getSwitchState(AINPUT_SOURCE_KEYBOARD, 0)) - << "Ignored device should return unknown switch state."; - - const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B }; - uint8_t flags[2] = { 0, 1 }; - ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, 2, keyCodes, flags)) - << "Ignored device should never mark any key codes."; - ASSERT_EQ(0, flags[0]) << "Flag for unsupported key should be unchanged."; - ASSERT_EQ(1, flags[1]) << "Flag for unsupported key should be unchanged."; - - // Reset. - mDevice->reset(); -} - -TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRequestsToMappers) { - // Configuration. - InputDeviceCalibration calibration; - calibration.addProperty(String8("key"), String8("value")); - mFakePolicy->addInputDeviceCalibration(String8(DEVICE_NAME), calibration); - - FakeInputMapper* mapper1 = new FakeInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD); - mapper1->setKeyboardType(AINPUT_KEYBOARD_TYPE_ALPHABETIC); - mapper1->setMetaState(AMETA_ALT_ON); - mapper1->addSupportedKeyCode(AKEYCODE_A); - mapper1->addSupportedKeyCode(AKEYCODE_B); - mapper1->setKeyCodeState(AKEYCODE_A, AKEY_STATE_DOWN); - mapper1->setKeyCodeState(AKEYCODE_B, AKEY_STATE_UP); - mapper1->setScanCodeState(2, AKEY_STATE_DOWN); - mapper1->setScanCodeState(3, AKEY_STATE_UP); - mapper1->setSwitchState(4, AKEY_STATE_DOWN); - mDevice->addMapper(mapper1); - - FakeInputMapper* mapper2 = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN); - mapper2->setMetaState(AMETA_SHIFT_ON); - mDevice->addMapper(mapper2); - - mDevice->configure(); - - String8 propertyValue; - ASSERT_TRUE(mDevice->getCalibration().tryGetProperty(String8("key"), propertyValue)) - << "Device should have read calibration during configuration phase."; - ASSERT_STREQ("value", propertyValue.string()); - - ASSERT_NO_FATAL_FAILURE(mapper1->assertConfigureWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper2->assertConfigureWasCalled()); - - // Metadata. - ASSERT_FALSE(mDevice->isIgnored()); - ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), mDevice->getSources()); - - InputDeviceInfo info; - mDevice->getDeviceInfo(&info); - ASSERT_EQ(DEVICE_ID, info.getId()); - ASSERT_STREQ(DEVICE_NAME, info.getName().string()); - ASSERT_EQ(AINPUT_KEYBOARD_TYPE_ALPHABETIC, info.getKeyboardType()); - ASSERT_EQ(uint32_t(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_TOUCHSCREEN), info.getSources()); - - // State queries. - ASSERT_EQ(AMETA_ALT_ON | AMETA_SHIFT_ON, mDevice->getMetaState()) - << "Should query mappers and combine meta states."; - - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown key code state when source not supported."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getScanCodeState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown scan code state when source not supported."; - ASSERT_EQ(AKEY_STATE_UNKNOWN, mDevice->getSwitchState(AINPUT_SOURCE_TRACKBALL, AKEYCODE_A)) - << "Should return unknown switch state when source not supported."; - - ASSERT_EQ(AKEY_STATE_DOWN, mDevice->getKeyCodeState(AINPUT_SOURCE_KEYBOARD, AKEYCODE_A)) - << "Should query mapper when source is supported."; - ASSERT_EQ(AKEY_STATE_UP, mDevice->getScanCodeState(AINPUT_SOURCE_KEYBOARD, 3)) - << "Should query mapper when source is supported."; - ASSERT_EQ(AKEY_STATE_DOWN, mDevice->getSwitchState(AINPUT_SOURCE_KEYBOARD, 4)) - << "Should query mapper when source is supported."; - - const int32_t keyCodes[4] = { AKEYCODE_A, AKEYCODE_B, AKEYCODE_1, AKEYCODE_2 }; - uint8_t flags[4] = { 0, 0, 0, 1 }; - ASSERT_FALSE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_TRACKBALL, 4, keyCodes, flags)) - << "Should do nothing when source is unsupported."; - ASSERT_EQ(0, flags[0]) << "Flag should be unchanged when source is unsupported."; - ASSERT_EQ(0, flags[1]) << "Flag should be unchanged when source is unsupported."; - ASSERT_EQ(0, flags[2]) << "Flag should be unchanged when source is unsupported."; - ASSERT_EQ(1, flags[3]) << "Flag should be unchanged when source is unsupported."; - - ASSERT_TRUE(mDevice->markSupportedKeyCodes(AINPUT_SOURCE_KEYBOARD, 4, keyCodes, flags)) - << "Should query mapper when source is supported."; - ASSERT_EQ(1, flags[0]) << "Flag for supported key should be set."; - ASSERT_EQ(1, flags[1]) << "Flag for supported key should be set."; - ASSERT_EQ(0, flags[2]) << "Flag for unsupported key should be unchanged."; - ASSERT_EQ(1, flags[3]) << "Flag for unsupported key should be unchanged."; - - // Event handling. - RawEvent event; - mDevice->process(&event); - - ASSERT_NO_FATAL_FAILURE(mapper1->assertProcessWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled()); - - // Reset. - mDevice->reset(); - - ASSERT_NO_FATAL_FAILURE(mapper1->assertResetWasCalled()); - ASSERT_NO_FATAL_FAILURE(mapper2->assertResetWasCalled()); -} - - -// --- InputMapperTest --- - -class InputMapperTest : public testing::Test { -protected: - static const char* DEVICE_NAME; - static const int32_t DEVICE_ID; - - sp<FakeEventHub> mFakeEventHub; - sp<FakeInputReaderPolicy> mFakePolicy; - sp<FakeInputDispatcher> mFakeDispatcher; - FakeInputReaderContext* mFakeContext; - InputDevice* mDevice; - - virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); - mFakePolicy = new FakeInputReaderPolicy(); - mFakeDispatcher = new FakeInputDispatcher(); - mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeDispatcher); - mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME)); - - mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0); - } - - virtual void TearDown() { - delete mDevice; - delete mFakeContext; - mFakeDispatcher.clear(); - mFakePolicy.clear(); - mFakeEventHub.clear(); - } - - void prepareCalibration(const char* key, const char* value) { - mFakePolicy->addInputDeviceCalibrationProperty(String8(DEVICE_NAME), - String8(key), String8(value)); - } - - void addMapperAndConfigure(InputMapper* mapper) { - mDevice->addMapper(mapper); - mDevice->configure(); - } - - static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type, - int32_t scanCode, int32_t keyCode, int32_t value, uint32_t flags) { - RawEvent event; - event.when = when; - event.deviceId = deviceId; - event.type = type; - event.scanCode = scanCode; - event.keyCode = keyCode; - event.value = value; - event.flags = flags; - mapper->process(&event); - } - - static void assertMotionRange(const InputDeviceInfo& info, - int32_t rangeType, float min, float max, float flat, float fuzz) { - const InputDeviceInfo::MotionRange* range = info.getMotionRange(rangeType); - ASSERT_TRUE(range != NULL) << "Range: " << rangeType; - ASSERT_NEAR(min, range->min, EPSILON) << "Range: " << rangeType; - ASSERT_NEAR(max, range->max, EPSILON) << "Range: " << rangeType; - ASSERT_NEAR(flat, range->flat, EPSILON) << "Range: " << rangeType; - ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Range: " << rangeType; - } - - static void assertPointerCoords(const PointerCoords& coords, - float x, float y, float pressure, float size, - float touchMajor, float touchMinor, float toolMajor, float toolMinor, - float orientation) { - ASSERT_NEAR(x, coords.x, 1); - ASSERT_NEAR(y, coords.y, 1); - ASSERT_NEAR(pressure, coords.pressure, EPSILON); - ASSERT_NEAR(size, coords.size, EPSILON); - ASSERT_NEAR(touchMajor, coords.touchMajor, 1); - ASSERT_NEAR(touchMinor, coords.touchMinor, 1); - ASSERT_NEAR(toolMajor, coords.toolMajor, 1); - ASSERT_NEAR(toolMinor, coords.toolMinor, 1); - ASSERT_NEAR(orientation, coords.orientation, EPSILON); - } -}; - -const char* InputMapperTest::DEVICE_NAME = "device"; -const int32_t InputMapperTest::DEVICE_ID = 1; - - -// --- SwitchInputMapperTest --- - -class SwitchInputMapperTest : public InputMapperTest { -protected: -}; - -TEST_F(SwitchInputMapperTest, GetSources) { - SwitchInputMapper* mapper = new SwitchInputMapper(mDevice); - addMapperAndConfigure(mapper); - - ASSERT_EQ(uint32_t(AINPUT_SOURCE_SWITCH), mapper->getSources()); -} - -TEST_F(SwitchInputMapperTest, GetSwitchState) { - SwitchInputMapper* mapper = new SwitchInputMapper(mDevice); - addMapperAndConfigure(mapper); - - mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 1); - ASSERT_EQ(1, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); - - mFakeEventHub->setSwitchState(DEVICE_ID, SW_LID, 0); - ASSERT_EQ(0, mapper->getSwitchState(AINPUT_SOURCE_ANY, SW_LID)); -} - -TEST_F(SwitchInputMapperTest, Process) { - SwitchInputMapper* mapper = new SwitchInputMapper(mDevice); - addMapperAndConfigure(mapper); - - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SW, SW_LID, 0, 1, 0); - - FakeInputDispatcher::NotifySwitchArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifySwitchWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.when); - ASSERT_EQ(SW_LID, args.switchCode); - ASSERT_EQ(1, args.switchValue); - ASSERT_EQ(uint32_t(0), args.policyFlags); -} - - -// --- KeyboardInputMapperTest --- - -class KeyboardInputMapperTest : public InputMapperTest { -protected: - void testDPadKeyRotation(KeyboardInputMapper* mapper, - int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode); -}; - -void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper, - int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) { - FakeInputDispatcher::NotifyKeyArgs args; - - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, originalKeyCode, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(originalScanCode, args.scanCode); - ASSERT_EQ(rotatedKeyCode, args.keyCode); - - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, originalScanCode, originalKeyCode, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(originalScanCode, args.scanCode); - ASSERT_EQ(rotatedKeyCode, args.keyCode); -} - - -TEST_F(KeyboardInputMapperTest, GetSources) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper->getSources()); -} - -TEST_F(KeyboardInputMapperTest, Process_SimpleKeyPress) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - // Key down. - process(mapper, ARBITRARY_TIME, DEVICE_ID, - EV_KEY, KEY_HOME, AKEYCODE_HOME, 1, POLICY_FLAG_WAKE); - FakeInputDispatcher::NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Key up. - process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, - EV_KEY, KEY_HOME, AKEYCODE_HOME, 0, POLICY_FLAG_WAKE); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); -} - -TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreNotDown_DoesNotSynthesizeKeyUp) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - // Key down. - process(mapper, ARBITRARY_TIME, DEVICE_ID, - EV_KEY, KEY_HOME, AKEYCODE_HOME, 1, POLICY_FLAG_WAKE); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Key up. - process(mapper, ARBITRARY_TIME, DEVICE_ID, - EV_KEY, KEY_HOME, AKEYCODE_HOME, 0, POLICY_FLAG_WAKE); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Reset. Since no keys still down, should not synthesize any key ups. - mapper->reset(); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); -} - -TEST_F(KeyboardInputMapperTest, Reset_WhenKeysAreDown_SynthesizesKeyUps) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - // Metakey down. - process(mapper, ARBITRARY_TIME, DEVICE_ID, - EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Key down. - process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, - EV_KEY, KEY_A, AKEYCODE_A, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Reset. Since two keys are still down, should synthesize two key ups in reverse order. - mapper->reset(); - - FakeInputDispatcher::NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEYCODE_A, args.keyCode); - ASSERT_EQ(KEY_A, args.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME + 1, args.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEYCODE_SHIFT_LEFT, args.keyCode); - ASSERT_EQ(KEY_LEFTSHIFT, args.scanCode); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM, args.flags); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(ARBITRARY_TIME + 1, args.downTime); - - // And that's it. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); -} - -TEST_F(KeyboardInputMapperTest, Process_ShouldUpdateMetaState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - // Initial metastate. - ASSERT_EQ(AMETA_NONE, mapper->getMetaState()); - - // Metakey down. - process(mapper, ARBITRARY_TIME, DEVICE_ID, - EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 1, 0); - FakeInputDispatcher::NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState()); - ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled()); - - // Key down. - process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, - EV_KEY, KEY_A, AKEYCODE_A, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState()); - - // Key up. - process(mapper, ARBITRARY_TIME + 2, DEVICE_ID, - EV_KEY, KEY_A, AKEYCODE_A, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, mapper->getMetaState()); - - // Metakey up. - process(mapper, ARBITRARY_TIME + 3, DEVICE_ID, - EV_KEY, KEY_LEFTSHIFT, AKEYCODE_SHIFT_LEFT, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AMETA_NONE, args.metaState); - ASSERT_EQ(AMETA_NONE, mapper->getMetaState()); - ASSERT_NO_FATAL_FAILURE(mFakeContext->assertUpdateGlobalMetaStateWasCalled()); -} - -TEST_F(KeyboardInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateDPad) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); -} - -TEST_F(KeyboardInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateDPad) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, DISPLAY_ID, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_0); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_90); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_180); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_270); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP)); - - // Special case: if orientation changes while key is down, we still emit the same keycode - // in the key up as we did in the key down. - FakeInputDispatcher::NotifyKeyArgs args; - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_270); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, AKEYCODE_DPAD_UP, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(KEY_UP, args.scanCode); - ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_180); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, KEY_UP, AKEYCODE_DPAD_UP, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(KEY_UP, args.scanCode); - ASSERT_EQ(AKEYCODE_DPAD_RIGHT, args.keyCode); -} - -TEST_F(KeyboardInputMapperTest, GetKeyCodeState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 1); - ASSERT_EQ(1, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); - - mFakeEventHub->setKeyCodeState(DEVICE_ID, AKEYCODE_A, 0); - ASSERT_EQ(0, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); -} - -TEST_F(KeyboardInputMapperTest, GetScanCodeState) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 1); - ASSERT_EQ(1, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); - - mFakeEventHub->setScanCodeState(DEVICE_ID, KEY_A, 0); - ASSERT_EQ(0, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); -} - -TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) { - KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, -1, - AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); - addMapperAndConfigure(mapper); - - mFakeEventHub->addKey(DEVICE_ID, KEY_A, AKEYCODE_A, 0); - - const int32_t keyCodes[2] = { AKEYCODE_A, AKEYCODE_B }; - uint8_t flags[2] = { 0, 0 }; - ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 1, keyCodes, flags)); - ASSERT_TRUE(flags[0]); - ASSERT_FALSE(flags[1]); -} - - -// --- TrackballInputMapperTest --- - -class TrackballInputMapperTest : public InputMapperTest { -protected: - static const int32_t TRACKBALL_MOVEMENT_THRESHOLD; - - void testMotionRotation(TrackballInputMapper* mapper, - int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY); -}; - -const int32_t TrackballInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6; - -void TrackballInputMapperTest::testMotionRotation(TrackballInputMapper* mapper, - int32_t originalX, int32_t originalY, int32_t rotatedX, int32_t rotatedY) { - FakeInputDispatcher::NotifyMotionArgs args; - - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, originalX, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, originalY, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD, - float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD, - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(TrackballInputMapperTest, GetSources) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper->getSources()); -} - -TEST_F(TrackballInputMapperTest, PopulateDeviceInfo) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - InputDeviceInfo info; - mapper->populateDeviceInfo(&info); - - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_X, - -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); - ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_Y, - -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD)); -} - -TEST_F(TrackballInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Button press. - // Mostly testing non x/y behavior here so we don't need to check again elsewhere. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.pointerCount); - ASSERT_EQ(0, args.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Button release. Should have same down time. - process(mapper, ARBITRARY_TIME + 1, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source); - ASSERT_EQ(uint32_t(0), args.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_EQ(0, args.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(0, args.edgeFlags); - ASSERT_EQ(uint32_t(1), args.pointerCount); - ASSERT_EQ(0, args.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision); - ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); -} - -TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentXYUpdates) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Motion in X but not Y. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 1, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - - // Motion in Y but not X. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, -2, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NEAR(0.0f, args.pointerCoords[0].x, EPSILON); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(TrackballInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Button press without following sync. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - - // Button release without following sync. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(TrackballInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Combined X, Y and Button. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 1, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, -2, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, - 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - - // Move X, Y a bit while pressed. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_X, 0, 2, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_REL, REL_Y, 0, 1, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, - 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - - // Release Button. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsNotDown_ShouldNotSynthesizeButtonUp) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Button press. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - - // Button release. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 0, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - - // Reset. Should not synthesize button up since button is not pressed. - mapper->reset(); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(TrackballInputMapperTest, Reset_WhenButtonIsDown_ShouldSynthesizeButtonUp) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Button press. - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_MOUSE, 0, 1, 0); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - - // Reset. Should synthesize button up. - mapper->reset(); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); -} - -TEST_F(TrackballInputMapperTest, Process_WhenNotAttachedToDisplay_ShouldNotRotateMotions) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, -1); - addMapperAndConfigure(mapper); - - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); -} - -TEST_F(TrackballInputMapperTest, Process_WhenAttachedToDisplay_ShouldRotateMotions) { - TrackballInputMapper* mapper = new TrackballInputMapper(mDevice, DISPLAY_ID); - addMapperAndConfigure(mapper); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_0); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_90); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_180); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1)); - - mFakePolicy->setDisplayInfo(DISPLAY_ID, - DISPLAY_WIDTH, DISPLAY_HEIGHT, - InputReaderPolicyInterface::ROTATION_270); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1)); -} - - -// --- TouchInputMapperTest --- - -class TouchInputMapperTest : public InputMapperTest { -protected: - static const int32_t RAW_X_MIN; - static const int32_t RAW_X_MAX; - static const int32_t RAW_Y_MIN; - static const int32_t RAW_Y_MAX; - static const int32_t RAW_TOUCH_MIN; - static const int32_t RAW_TOUCH_MAX; - static const int32_t RAW_TOOL_MIN; - static const int32_t RAW_TOOL_MAX; - static const int32_t RAW_PRESSURE_MIN; - static const int32_t RAW_PRESSURE_MAX; - static const int32_t RAW_ORIENTATION_MIN; - static const int32_t RAW_ORIENTATION_MAX; - static const int32_t RAW_ID_MIN; - static const int32_t RAW_ID_MAX; - static const float X_PRECISION; - static const float Y_PRECISION; - - static const VirtualKeyDefinition VIRTUAL_KEYS[2]; - - enum Axes { - POSITION = 1 << 0, - TOUCH = 1 << 1, - TOOL = 1 << 2, - PRESSURE = 1 << 3, - ORIENTATION = 1 << 4, - MINOR = 1 << 5, - ID = 1 << 6, - }; - - void prepareDisplay(int32_t orientation); - void prepareVirtualKeys(); - int32_t toRawX(float displayX); - int32_t toRawY(float displayY); - float toDisplayX(int32_t rawX); - float toDisplayY(int32_t rawY); -}; - -const int32_t TouchInputMapperTest::RAW_X_MIN = 25; -const int32_t TouchInputMapperTest::RAW_X_MAX = 1020; -const int32_t TouchInputMapperTest::RAW_Y_MIN = 30; -const int32_t TouchInputMapperTest::RAW_Y_MAX = 1010; -const int32_t TouchInputMapperTest::RAW_TOUCH_MIN = 0; -const int32_t TouchInputMapperTest::RAW_TOUCH_MAX = 31; -const int32_t TouchInputMapperTest::RAW_TOOL_MIN = 0; -const int32_t TouchInputMapperTest::RAW_TOOL_MAX = 15; -const int32_t TouchInputMapperTest::RAW_PRESSURE_MIN = RAW_TOUCH_MIN; -const int32_t TouchInputMapperTest::RAW_PRESSURE_MAX = RAW_TOUCH_MAX; -const int32_t TouchInputMapperTest::RAW_ORIENTATION_MIN = -7; -const int32_t TouchInputMapperTest::RAW_ORIENTATION_MAX = 7; -const int32_t TouchInputMapperTest::RAW_ID_MIN = 0; -const int32_t TouchInputMapperTest::RAW_ID_MAX = 9; -const float TouchInputMapperTest::X_PRECISION = float(RAW_X_MAX - RAW_X_MIN) / DISPLAY_WIDTH; -const float TouchInputMapperTest::Y_PRECISION = float(RAW_Y_MAX - RAW_Y_MIN) / DISPLAY_HEIGHT; - -const VirtualKeyDefinition TouchInputMapperTest::VIRTUAL_KEYS[2] = { - { KEY_HOME, 60, DISPLAY_HEIGHT + 15, 20, 20 }, - { KEY_MENU, DISPLAY_HEIGHT - 60, DISPLAY_WIDTH + 15, 20, 20 }, -}; - -void TouchInputMapperTest::prepareDisplay(int32_t orientation) { - mFakePolicy->setDisplayInfo(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation); -} - -void TouchInputMapperTest::prepareVirtualKeys() { - mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[0]); - mFakePolicy->addVirtualKeyDefinition(String8(DEVICE_NAME), VIRTUAL_KEYS[1]); - mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, AKEYCODE_HOME, POLICY_FLAG_WAKE); - mFakeEventHub->addKey(DEVICE_ID, KEY_MENU, AKEYCODE_MENU, POLICY_FLAG_WAKE); -} - -int32_t TouchInputMapperTest::toRawX(float displayX) { - return int32_t(displayX * (RAW_X_MAX - RAW_X_MIN) / DISPLAY_WIDTH + RAW_X_MIN); -} - -int32_t TouchInputMapperTest::toRawY(float displayY) { - return int32_t(displayY * (RAW_Y_MAX - RAW_Y_MIN) / DISPLAY_HEIGHT + RAW_Y_MIN); -} - -float TouchInputMapperTest::toDisplayX(int32_t rawX) { - return float(rawX - RAW_X_MIN) * DISPLAY_WIDTH / (RAW_X_MAX - RAW_X_MIN); -} - -float TouchInputMapperTest::toDisplayY(int32_t rawY) { - return float(rawY - RAW_Y_MIN) * DISPLAY_HEIGHT / (RAW_Y_MAX - RAW_Y_MIN); -} - - -// --- SingleTouchInputMapperTest --- - -class SingleTouchInputMapperTest : public TouchInputMapperTest { -protected: - void prepareAxes(int axes); - - void processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y); - void processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y); - void processUp(SingleTouchInputMapper* mappery); - void processPressure(SingleTouchInputMapper* mapper, int32_t pressure); - void processToolMajor(SingleTouchInputMapper* mapper, int32_t toolMajor); - void processSync(SingleTouchInputMapper* mapper); -}; - -void SingleTouchInputMapperTest::prepareAxes(int axes) { - if (axes & POSITION) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_X, RAW_X_MIN, RAW_X_MAX, 0, 0); - mFakeEventHub->addAxis(DEVICE_ID, ABS_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0); - } - if (axes & PRESSURE) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_PRESSURE, RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0); - } - if (axes & TOOL) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_TOOL_WIDTH, RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0); - } -} - -void SingleTouchInputMapperTest::processDown(SingleTouchInputMapper* mapper, int32_t x, int32_t y) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 0, 1, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, 0, x, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, 0, y, 0); -} - -void SingleTouchInputMapperTest::processMove(SingleTouchInputMapper* mapper, int32_t x, int32_t y) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_X, 0, x, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_Y, 0, y, 0); -} - -void SingleTouchInputMapperTest::processUp(SingleTouchInputMapper* mapper) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_KEY, BTN_TOUCH, 0, 0, 0); -} - -void SingleTouchInputMapperTest::processPressure( - SingleTouchInputMapper* mapper, int32_t pressure) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_PRESSURE, 0, pressure, 0); -} - -void SingleTouchInputMapperTest::processToolMajor( - SingleTouchInputMapper* mapper, int32_t toolMajor) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_TOOL_WIDTH, 0, toolMajor, 0); -} - -void SingleTouchInputMapperTest::processSync(SingleTouchInputMapper* mapper) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); -} - - -TEST_F(SingleTouchInputMapperTest, GetSources_WhenNotAttachedToADisplay_ReturnsTouchPad) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, -1); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - - ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper->getSources()); -} - -TEST_F(SingleTouchInputMapperTest, GetSources_WhenAttachedToADisplay_ReturnsTouchScreen) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper->getSources()); -} - -TEST_F(SingleTouchInputMapperTest, GetKeyCodeState) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - // Unknown key. - ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A)); - - // Virtual key is down. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME)); - - // Virtual key is up. - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - ASSERT_EQ(AKEY_STATE_UP, mapper->getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_HOME)); -} - -TEST_F(SingleTouchInputMapperTest, GetScanCodeState) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - // Unknown key. - ASSERT_EQ(AKEY_STATE_UNKNOWN, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_A)); - - // Virtual key is down. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - ASSERT_EQ(AKEY_STATE_VIRTUAL, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME)); - - // Virtual key is up. - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - ASSERT_EQ(AKEY_STATE_UP, mapper->getScanCodeState(AINPUT_SOURCE_ANY, KEY_HOME)); -} - -TEST_F(SingleTouchInputMapperTest, MarkSupportedKeyCodes) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - const int32_t keys[2] = { AKEYCODE_HOME, AKEYCODE_A }; - uint8_t flags[2] = { 0, 0 }; - ASSERT_TRUE(mapper->markSupportedKeyCodes(AINPUT_SOURCE_ANY, 2, keys, flags)); - ASSERT_TRUE(flags[0]); - ASSERT_FALSE(flags[1]); -} - -TEST_F(SingleTouchInputMapperTest, Reset_WhenVirtualKeysAreDown_SendsUp) { - // Note: Ideally we should send cancels but the implementation is more straightforward - // with up and this will only happen if a device is forcibly removed. - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - // Press virtual key. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Reset. Since key is down, synthesize key up. - mapper->reset(); - - FakeInputDispatcher::NotifyKeyArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - //ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); -} - -TEST_F(SingleTouchInputMapperTest, Reset_WhenNothingIsPressed_NothingMuchHappens) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - // Press virtual key. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Release virtual key. - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled()); - - // Reset. Since no key is down, nothing happens. - mapper->reset(); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndReleasedNormally_SendsKeyDownAndKeyUp) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyKeyArgs args; - - // Press virtual key. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Release virtual key. - processUp(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(ARBITRARY_TIME, args.eventTime); - ASSERT_EQ(DEVICE_ID, args.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, args.source); - ASSERT_EQ(POLICY_FLAG_VIRTUAL, args.policyFlags); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, args.flags); - ASSERT_EQ(AKEYCODE_HOME, args.keyCode); - ASSERT_EQ(KEY_HOME, args.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState); - ASSERT_EQ(ARBITRARY_TIME, args.downTime); - - // Should not have sent any motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_WhenVirtualKeyIsPressedAndMovedOutOfBounds_SendsKeyDownAndKeyCancel) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyKeyArgs keyArgs; - - // Press virtual key. - int32_t x = toRawX(VIRTUAL_KEYS[0].centerX); - int32_t y = toRawY(VIRTUAL_KEYS[0].centerY); - processDown(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(ARBITRARY_TIME, keyArgs.eventTime); - ASSERT_EQ(DEVICE_ID, keyArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyArgs.source); - ASSERT_EQ(POLICY_FLAG_VIRTUAL, keyArgs.policyFlags); - ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY, keyArgs.flags); - ASSERT_EQ(AKEYCODE_HOME, keyArgs.keyCode); - ASSERT_EQ(KEY_HOME, keyArgs.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, keyArgs.metaState); - ASSERT_EQ(ARBITRARY_TIME, keyArgs.downTime); - - // Move out of bounds. This should generate a cancel and a pointer down since we moved - // into the display area. - y -= 100; - processMove(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasCalled(&keyArgs)); - ASSERT_EQ(ARBITRARY_TIME, keyArgs.eventTime); - ASSERT_EQ(DEVICE_ID, keyArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, keyArgs.source); - ASSERT_EQ(POLICY_FLAG_VIRTUAL, keyArgs.policyFlags); - ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); - ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY - | AKEY_EVENT_FLAG_CANCELED, keyArgs.flags); - ASSERT_EQ(AKEYCODE_HOME, keyArgs.keyCode); - ASSERT_EQ(KEY_HOME, keyArgs.scanCode); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, keyArgs.metaState); - ASSERT_EQ(ARBITRARY_TIME, keyArgs.downTime); - - FakeInputDispatcher::NotifyMotionArgs motionArgs; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Keep moving out of bounds. Should generate a pointer move. - y -= 50; - processMove(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Release out of bounds. Should generate a pointer up. - processUp(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Should not have sent any more keys or motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_WhenTouchStartsOutsideDisplayAndMovesIn_SendsDownAsTouchEntersDisplay) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyMotionArgs motionArgs; - - // Initially go down out of bounds. - int32_t x = -10; - int32_t y = -10; - processDown(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); - - // Move into the display area. Should generate a pointer down. - x = 50; - y = 75; - processMove(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Release. Should generate a pointer up. - processUp(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Should not have sent any more keys or motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_NormalSingleTouchGesture) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyMotionArgs motionArgs; - - // Down. - int32_t x = 100; - int32_t y = 125; - processDown(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Move. - x += 50; - y += 75; - processMove(mapper, x, y); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Up. - processUp(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x), toDisplayY(y), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Should not have sent any more keys or motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_Rotation) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - - // Rotation 0. - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - processDown(mapper, toRawX(50), toRawY(75)); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(50, args.pointerCoords[0].x, 1); - ASSERT_NEAR(75, args.pointerCoords[0].y, 1); - - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled()); - - // Rotation 90. - prepareDisplay(InputReaderPolicyInterface::ROTATION_90); - processDown(mapper, toRawX(50), toRawY(75)); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(75, args.pointerCoords[0].x, 1); - ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].y, 1); - - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled()); - - // Rotation 180. - prepareDisplay(InputReaderPolicyInterface::ROTATION_180); - processDown(mapper, toRawX(50), toRawY(75)); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(DISPLAY_WIDTH - 50, args.pointerCoords[0].x, 1); - ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].y, 1); - - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled()); - - // Rotation 270. - prepareDisplay(InputReaderPolicyInterface::ROTATION_270); - processDown(mapper, toRawX(50), toRawY(75)); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NEAR(DISPLAY_HEIGHT - 75, args.pointerCoords[0].x, 1); - ASSERT_NEAR(50, args.pointerCoords[0].y, 1); - - processUp(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled()); -} - -TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { - SingleTouchInputMapper* mapper = new SingleTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | PRESSURE | TOOL); - addMapperAndConfigure(mapper); - - // These calculations are based on the input device calibration documentation. - int32_t rawX = 100; - int32_t rawY = 200; - int32_t rawPressure = 10; - int32_t rawToolMajor = 12; - - float x = toDisplayX(rawX); - float y = toDisplayY(rawY); - float pressure = float(rawPressure) / RAW_PRESSURE_MAX; - float size = float(rawToolMajor) / RAW_TOOL_MAX; - float tool = min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * size; - float touch = min(tool * pressure, tool); - - processDown(mapper, rawX, rawY); - processPressure(mapper, rawPressure); - processToolMajor(mapper, rawToolMajor); - processSync(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - x, y, pressure, size, touch, touch, tool, tool, 0)); -} - - -// --- MultiTouchInputMapperTest --- - -class MultiTouchInputMapperTest : public TouchInputMapperTest { -protected: - void prepareAxes(int axes); - - void processPosition(MultiTouchInputMapper* mapper, int32_t x, int32_t y); - void processTouchMajor(MultiTouchInputMapper* mapper, int32_t touchMajor); - void processTouchMinor(MultiTouchInputMapper* mapper, int32_t touchMinor); - void processToolMajor(MultiTouchInputMapper* mapper, int32_t toolMajor); - void processToolMinor(MultiTouchInputMapper* mapper, int32_t toolMinor); - void processOrientation(MultiTouchInputMapper* mapper, int32_t orientation); - void processPressure(MultiTouchInputMapper* mapper, int32_t pressure); - void processId(MultiTouchInputMapper* mapper, int32_t id); - void processMTSync(MultiTouchInputMapper* mapper); - void processSync(MultiTouchInputMapper* mapper); -}; - -void MultiTouchInputMapperTest::prepareAxes(int axes) { - if (axes & POSITION) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0, 0); - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX, 0, 0); - } - if (axes & TOUCH) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TOUCH_MAJOR, RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0); - if (axes & MINOR) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TOUCH_MINOR, - RAW_TOUCH_MIN, RAW_TOUCH_MAX, 0, 0); - } - } - if (axes & TOOL) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_WIDTH_MAJOR, RAW_TOOL_MIN, RAW_TOOL_MAX, 0, 0); - if (axes & MINOR) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_WIDTH_MINOR, - RAW_TOOL_MAX, RAW_TOOL_MAX, 0, 0); - } - } - if (axes & ORIENTATION) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_ORIENTATION, - RAW_ORIENTATION_MIN, RAW_ORIENTATION_MAX, 0, 0); - } - if (axes & PRESSURE) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_PRESSURE, - RAW_PRESSURE_MIN, RAW_PRESSURE_MAX, 0, 0); - } - if (axes & ID) { - mFakeEventHub->addAxis(DEVICE_ID, ABS_MT_TRACKING_ID, - RAW_ID_MIN, RAW_ID_MAX, 0, 0); - } -} - -void MultiTouchInputMapperTest::processPosition( - MultiTouchInputMapper* mapper, int32_t x, int32_t y) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_X, 0, x, 0); - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_POSITION_Y, 0, y, 0); -} - -void MultiTouchInputMapperTest::processTouchMajor( - MultiTouchInputMapper* mapper, int32_t touchMajor) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MAJOR, 0, touchMajor, 0); -} - -void MultiTouchInputMapperTest::processTouchMinor( - MultiTouchInputMapper* mapper, int32_t touchMinor) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TOUCH_MINOR, 0, touchMinor, 0); -} - -void MultiTouchInputMapperTest::processToolMajor( - MultiTouchInputMapper* mapper, int32_t toolMajor) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MAJOR, 0, toolMajor, 0); -} - -void MultiTouchInputMapperTest::processToolMinor( - MultiTouchInputMapper* mapper, int32_t toolMinor) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_WIDTH_MINOR, 0, toolMinor, 0); -} - -void MultiTouchInputMapperTest::processOrientation( - MultiTouchInputMapper* mapper, int32_t orientation) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_ORIENTATION, 0, orientation, 0); -} - -void MultiTouchInputMapperTest::processPressure( - MultiTouchInputMapper* mapper, int32_t pressure) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_PRESSURE, 0, pressure, 0); -} - -void MultiTouchInputMapperTest::processId( - MultiTouchInputMapper* mapper, int32_t id) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_ABS, ABS_MT_TRACKING_ID, 0, id, 0); -} - -void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_MT_REPORT, 0, 0, 0); -} - -void MultiTouchInputMapperTest::processSync(MultiTouchInputMapper* mapper) { - process(mapper, ARBITRARY_TIME, DEVICE_ID, EV_SYN, SYN_REPORT, 0, 0, 0); -} - - -TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithoutTrackingIds) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyMotionArgs motionArgs; - - // Two fingers down at once. - int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500; - processPosition(mapper, x1, y1); - processMTSync(mapper); - processPosition(mapper, x2, y2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_EQ(1, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Move. - x1 += 10; y1 += 15; x2 += 5; y2 -= 10; - processPosition(mapper, x1, y1); - processMTSync(mapper); - processPosition(mapper, x2, y2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_EQ(1, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // First finger up. - x2 += 15; y2 -= 20; - processPosition(mapper, x2, y2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_EQ(1, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Move. - x2 += 20; y2 -= 25; - processPosition(mapper, x2, y2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // New finger down. - int32_t x3 = 700, y3 = 300; - processPosition(mapper, x2, y2); - processMTSync(mapper); - processPosition(mapper, x3, y3); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_EQ(1, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Second finger up. - x3 += 30; y3 -= 20; - processPosition(mapper, x3, y3); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_EQ(1, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Last finger up. - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.eventTime); - ASSERT_EQ(DEVICE_ID, motionArgs.deviceId); - ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, motionArgs.source); - ASSERT_EQ(uint32_t(0), motionArgs.policyFlags); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(0, motionArgs.flags); - ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, motionArgs.metaState); - ASSERT_EQ(0, motionArgs.edgeFlags); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(0, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NEAR(X_PRECISION, motionArgs.xPrecision, EPSILON); - ASSERT_NEAR(Y_PRECISION, motionArgs.yPrecision, EPSILON); - ASSERT_EQ(ARBITRARY_TIME, motionArgs.downTime); - - // Should not have sent any more keys or motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(MultiTouchInputMapperTest, Process_NormalMultiTouchGesture_WithTrackingIds) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | ID); - prepareVirtualKeys(); - addMapperAndConfigure(mapper); - - mFakeContext->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON); - - FakeInputDispatcher::NotifyMotionArgs motionArgs; - - // Two fingers down at once. - int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500; - processPosition(mapper, x1, y1); - processId(mapper, 1); - processMTSync(mapper); - processPosition(mapper, x2, y2); - processId(mapper, 2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_EQ(2, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - - // Move. - x1 += 10; y1 += 15; x2 += 5; y2 -= 10; - processPosition(mapper, x1, y1); - processId(mapper, 1); - processMTSync(mapper); - processPosition(mapper, x2, y2); - processId(mapper, 2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_EQ(2, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - - // First finger up. - x2 += 15; y2 -= 20; - processPosition(mapper, x2, y2); - processId(mapper, 2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(1, motionArgs.pointerIds[0]); - ASSERT_EQ(2, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x1), toDisplayY(y1), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(2, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - - // Move. - x2 += 20; y2 -= 25; - processPosition(mapper, x2, y2); - processId(mapper, 2); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(2, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - - // New finger down. - int32_t x3 = 700, y3 = 300; - processPosition(mapper, x2, y2); - processId(mapper, 2); - processMTSync(mapper); - processPosition(mapper, x3, y3); - processId(mapper, 3); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(2, motionArgs.pointerIds[0]); - ASSERT_EQ(3, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - - // Second finger up. - x3 += 30; y3 -= 20; - processPosition(mapper, x3, y3); - processId(mapper, 3); - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - motionArgs.action); - ASSERT_EQ(size_t(2), motionArgs.pointerCount); - ASSERT_EQ(2, motionArgs.pointerIds[0]); - ASSERT_EQ(3, motionArgs.pointerIds[1]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x2), toDisplayY(y2), 1, 0, 0, 0, 0, 0, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[1], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(3, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - - // Last finger up. - processMTSync(mapper); - processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&motionArgs)); - ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); - ASSERT_EQ(size_t(1), motionArgs.pointerCount); - ASSERT_EQ(3, motionArgs.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(motionArgs.pointerCoords[0], - toDisplayX(x3), toDisplayY(y3), 1, 0, 0, 0, 0, 0, 0)); - - // Should not have sent any more keys or motions. - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyKeyWasNotCalled()); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasNotCalled()); -} - -TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | TOUCH | TOOL | PRESSURE | ORIENTATION | ID | MINOR); - addMapperAndConfigure(mapper); - - // These calculations are based on the input device calibration documentation. - int32_t rawX = 100; - int32_t rawY = 200; - int32_t rawTouchMajor = 7; - int32_t rawTouchMinor = 6; - int32_t rawToolMajor = 9; - int32_t rawToolMinor = 8; - int32_t rawPressure = 11; - int32_t rawOrientation = 3; - int32_t id = 5; - - float x = toDisplayX(rawX); - float y = toDisplayY(rawY); - float pressure = float(rawPressure) / RAW_PRESSURE_MAX; - float size = avg(rawToolMajor, rawToolMinor) / RAW_TOOL_MAX; - float toolMajor = float(min(DISPLAY_WIDTH, DISPLAY_HEIGHT)) * rawToolMajor / RAW_TOOL_MAX; - float toolMinor = float(min(DISPLAY_WIDTH, DISPLAY_HEIGHT)) * rawToolMinor / RAW_TOOL_MAX; - float touchMajor = min(toolMajor * pressure, toolMajor); - float touchMinor = min(toolMinor * pressure, toolMinor); - float orientation = float(rawOrientation) / RAW_ORIENTATION_MAX * M_PI_2; - - processPosition(mapper, rawX, rawY); - processTouchMajor(mapper, rawTouchMajor); - processTouchMinor(mapper, rawTouchMinor); - processToolMajor(mapper, rawToolMajor); - processToolMinor(mapper, rawToolMinor); - processPressure(mapper, rawPressure); - processOrientation(mapper, rawOrientation); - processId(mapper, id); - processMTSync(mapper); - processSync(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(id, args.pointerIds[0]); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, orientation)); -} - -TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | TOUCH | TOOL | MINOR); - prepareCalibration("touch.touchSize.calibration", "geometric"); - prepareCalibration("touch.toolSize.calibration", "geometric"); - addMapperAndConfigure(mapper); - - // These calculations are based on the input device calibration documentation. - int32_t rawX = 100; - int32_t rawY = 200; - int32_t rawTouchMajor = 140; - int32_t rawTouchMinor = 120; - int32_t rawToolMajor = 180; - int32_t rawToolMinor = 160; - - float x = toDisplayX(rawX); - float y = toDisplayY(rawY); - float pressure = float(rawTouchMajor) / RAW_TOUCH_MAX; - float size = avg(rawToolMajor, rawToolMinor) / RAW_TOOL_MAX; - float scale = avg(float(DISPLAY_WIDTH) / (RAW_X_MAX - RAW_X_MIN), - float(DISPLAY_HEIGHT) / (RAW_Y_MAX - RAW_Y_MIN)); - float toolMajor = float(rawToolMajor) * scale; - float toolMinor = float(rawToolMinor) * scale; - float touchMajor = min(float(rawTouchMajor) * scale, toolMajor); - float touchMinor = min(float(rawTouchMinor) * scale, toolMinor); - - processPosition(mapper, rawX, rawY); - processTouchMajor(mapper, rawTouchMajor); - processTouchMinor(mapper, rawTouchMinor); - processToolMajor(mapper, rawToolMajor); - processToolMinor(mapper, rawToolMinor); - processMTSync(mapper); - processSync(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, 0)); -} - -TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_SummedLinearCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | TOUCH | TOOL); - prepareCalibration("touch.touchSize.calibration", "pressure"); - prepareCalibration("touch.toolSize.calibration", "linear"); - prepareCalibration("touch.toolSize.linearScale", "10"); - prepareCalibration("touch.toolSize.linearBias", "160"); - prepareCalibration("touch.toolSize.isSummed", "1"); - prepareCalibration("touch.pressure.calibration", "amplitude"); - prepareCalibration("touch.pressure.source", "touch"); - prepareCalibration("touch.pressure.scale", "0.01"); - addMapperAndConfigure(mapper); - - // These calculations are based on the input device calibration documentation. - // Note: We only provide a single common touch/tool value because the device is assumed - // not to emit separate values for each pointer (isSummed = 1). - int32_t rawX = 100; - int32_t rawY = 200; - int32_t rawX2 = 150; - int32_t rawY2 = 250; - int32_t rawTouchMajor = 60; - int32_t rawToolMajor = 5; - - float x = toDisplayX(rawX); - float y = toDisplayY(rawY); - float x2 = toDisplayX(rawX2); - float y2 = toDisplayY(rawY2); - float pressure = float(rawTouchMajor) * 0.01f; - float size = float(rawToolMajor) / RAW_TOOL_MAX; - float tool = (float(rawToolMajor) * 10.0f + 160.0f) / 2; - float touch = min(tool * pressure, tool); - - processPosition(mapper, rawX, rawY); - processTouchMajor(mapper, rawTouchMajor); - processToolMajor(mapper, rawToolMajor); - processMTSync(mapper); - processPosition(mapper, rawX2, rawY2); - processTouchMajor(mapper, rawTouchMajor); - processToolMajor(mapper, rawToolMajor); - processMTSync(mapper); - processSync(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action); - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - args.action); - ASSERT_EQ(size_t(2), args.pointerCount); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - x, y, pressure, size, touch, touch, tool, tool, 0)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[1], - x2, y2, pressure, size, touch, touch, tool, tool, 0)); -} - -TEST_F(MultiTouchInputMapperTest, Process_TouchToolPressureSizeAxes_AreaCalibration) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice, DISPLAY_ID); - prepareDisplay(InputReaderPolicyInterface::ROTATION_0); - prepareAxes(POSITION | TOUCH | TOOL); - prepareCalibration("touch.touchSize.calibration", "pressure"); - prepareCalibration("touch.toolSize.calibration", "area"); - prepareCalibration("touch.toolSize.areaScale", "22"); - prepareCalibration("touch.toolSize.areaBias", "1"); - prepareCalibration("touch.toolSize.linearScale", "9.2"); - prepareCalibration("touch.toolSize.linearBias", "3"); - prepareCalibration("touch.pressure.calibration", "amplitude"); - prepareCalibration("touch.pressure.source", "touch"); - prepareCalibration("touch.pressure.scale", "0.01"); - addMapperAndConfigure(mapper); - - // These calculations are based on the input device calibration documentation. - int32_t rawX = 100; - int32_t rawY = 200; - int32_t rawTouchMajor = 60; - int32_t rawToolMajor = 5; - - float x = toDisplayX(rawX); - float y = toDisplayY(rawY); - float pressure = float(rawTouchMajor) * 0.01f; - float size = float(rawToolMajor) / RAW_TOOL_MAX; - float tool = sqrtf(float(rawToolMajor) * 22.0f + 1.0f) * 9.2f + 3.0f; - float touch = min(tool * pressure, tool); - - processPosition(mapper, rawX, rawY); - processTouchMajor(mapper, rawTouchMajor); - processToolMajor(mapper, rawToolMajor); - processMTSync(mapper); - processSync(mapper); - - FakeInputDispatcher::NotifyMotionArgs args; - ASSERT_NO_FATAL_FAILURE(mFakeDispatcher->assertNotifyMotionWasCalled(&args)); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - x, y, pressure, size, touch, touch, tool, tool, 0)); -} - -} // namespace android diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk index 5a7b30ed0351..300224a016d4 100644 --- a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk +++ b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk @@ -7,15 +7,28 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := accessorychat.c usbhost.c +LOCAL_SRC_FILES := accessorychat.c LOCAL_MODULE := accessorychat LOCAL_C_INCLUDES += bionic/libc/kernel/common -LOCAL_STATIC_LIBRARIES := libcutils +LOCAL_STATIC_LIBRARIES := libusbhost libcutils LOCAL_LDLIBS += -lpthread LOCAL_CFLAGS := -g -O0 include $(BUILD_HOST_EXECUTABLE) endif + +# Build for device +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := accessorychat.c + +LOCAL_MODULE := accessorychat + +LOCAL_SHARED_LIBRARIES := libusbhost libcutils + +include $(BUILD_EXECUTABLE) diff --git a/libs/usb/tests/AccessoryChat/accessorychat/usbhost.c b/libs/usb/tests/AccessoryChat/accessorychat/usbhost.c deleted file mode 100644 index f5a7c3f326ac..000000000000 --- a/libs/usb/tests/AccessoryChat/accessorychat/usbhost.c +++ /dev/null @@ -1,574 +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. - */ - -// #define DEBUG 1 -#if DEBUG - -#ifdef USE_LIBLOG -#define LOG_TAG "usbhost" -#include "utils/Log.h" -#define D LOGD -#else -#define D printf -#endif - -#else -#define D(...) -#endif - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> - -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/time.h> -#include <sys/inotify.h> -#include <dirent.h> -#include <fcntl.h> -#include <errno.h> -#include <ctype.h> -#include <pthread.h> - -#include <linux/usbdevice_fs.h> -#include <asm/byteorder.h> - -#include "usbhost/usbhost.h" - -#define USB_FS_DIR "/dev/bus/usb" -#define USB_FS_ID_SCANNER "/dev/bus/usb/%d/%d" -#define USB_FS_ID_FORMAT "/dev/bus/usb/%03d/%03d" - - -struct usb_host_context { - int fd; -}; - -struct usb_device { - char dev_name[64]; - unsigned char desc[4096]; - int desc_length; - int fd; - int writeable; -}; - -static inline int badname(const char *name) -{ - while(*name) { - if(!isdigit(*name++)) return 1; - } - return 0; -} - -/* returns true if one of the callbacks indicates we are done */ -static int find_existing_devices(usb_device_added_cb added_cb, - usb_device_removed_cb removed_cb, - void *client_data) -{ - char busname[32], devname[32]; - DIR *busdir , *devdir ; - struct dirent *de; - int done = 0; - - busdir = opendir(USB_FS_DIR); - if(busdir == 0) return 1; - - while ((de = readdir(busdir)) != 0 && !done) { - if(badname(de->d_name)) continue; - - snprintf(busname, sizeof busname, "%s/%s", USB_FS_DIR, de->d_name); - devdir = opendir(busname); - if(devdir == 0) continue; - - while ((de = readdir(devdir)) && !done) { - if(badname(de->d_name)) continue; - - snprintf(devname, sizeof devname, "%s/%s", busname, de->d_name); - done = added_cb(devname, client_data); - } // end of devdir while - closedir(devdir); - } //end of busdir while - closedir(busdir); - - return done; -} - -struct usb_host_context *usb_host_init() -{ - struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context)); - if (!context) { - fprintf(stderr, "out of memory in usb_host_context\n"); - return NULL; - } - context->fd = inotify_init(); - if (context->fd < 0) { - fprintf(stderr, "inotify_init failed\n"); - free(context); - return NULL; - } - return context; -} - -void usb_host_cleanup(struct usb_host_context *context) -{ - close(context->fd); - free(context); -} - -void usb_host_run(struct usb_host_context *context, - usb_device_added_cb added_cb, - usb_device_removed_cb removed_cb, - usb_discovery_done_cb discovery_done_cb, - void *client_data) -{ - struct inotify_event* event; - char event_buf[512]; - char path[100]; - int i, ret, done = 0; - int wd, wds[10]; - int wd_count = sizeof(wds) / sizeof(wds[0]); - - D("Created device discovery thread\n"); - - /* watch for files added and deleted within USB_FS_DIR */ - memset(wds, 0, sizeof(wds)); - /* watch the root for new subdirectories */ - wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE); - if (wds[0] < 0) { - fprintf(stderr, "inotify_add_watch failed\n"); - if (discovery_done_cb) - discovery_done_cb(client_data); - return; - } - - /* watch existing subdirectories of USB_FS_DIR */ - for (i = 1; i < wd_count; i++) { - snprintf(path, sizeof(path), "%s/%03d", USB_FS_DIR, i); - ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); - if (ret > 0) - wds[i] = ret; - } - - /* check for existing devices first, after we have inotify set up */ - done = find_existing_devices(added_cb, removed_cb, client_data); - if (discovery_done_cb) - done |= discovery_done_cb(client_data); - - while (!done) { - ret = read(context->fd, event_buf, sizeof(event_buf)); - if (ret >= (int)sizeof(struct inotify_event)) { - event = (struct inotify_event *)event_buf; - wd = event->wd; - if (wd == wds[0]) { - i = atoi(event->name); - snprintf(path, sizeof(path), "%s/%s", USB_FS_DIR, event->name); - D("new subdirectory %s: index: %d\n", path, i); - if (i > 0 && i < wd_count) { - ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE); - if (ret > 0) - wds[i] = ret; - } - } else { - for (i = 1; i < wd_count && !done; i++) { - if (wd == wds[i]) { - snprintf(path, sizeof(path), "%s/%03d/%s", USB_FS_DIR, i, event->name); - if (event->mask == IN_CREATE) { - D("new device %s\n", path); - done = added_cb(path, client_data); - } else if (event->mask == IN_DELETE) { - D("gone device %s\n", path); - done = removed_cb(path, client_data); - } - } - } - } - } - } -} - -struct usb_device *usb_device_open(const char *dev_name) -{ - int fd, did_retry = 0, writeable = 1; - - D("usb_device_open %s\n", dev_name); - -retry: - fd = open(dev_name, O_RDWR); - if (fd < 0) { - /* if we fail, see if have read-only access */ - fd = open(dev_name, O_RDONLY); - D("usb_device_open open returned %d errno %d\n", fd, errno); - if (fd < 0 && (errno == EACCES || errno == ENOENT) && !did_retry) { - /* work around race condition between inotify and permissions management */ - sleep(1); - did_retry = 1; - goto retry; - } - - if (fd < 0) - return NULL; - writeable = 0; - D("[ usb open read-only %s fd = %d]\n", dev_name, fd); - } - - struct usb_device* result = usb_device_new(dev_name, fd); - if (result) - result->writeable = writeable; - return result; -} - -void usb_device_close(struct usb_device *device) -{ - close(device->fd); - free(device); -} - -struct usb_device *usb_device_new(const char *dev_name, int fd) -{ - struct usb_device *device = calloc(1, sizeof(struct usb_device)); - int length; - - D("usb_device_new %s fd: %d\n", dev_name, fd); - - if (lseek(fd, 0, SEEK_SET) != 0) - goto failed; - length = read(fd, device->desc, sizeof(device->desc)); - D("usb_device_new read returned %d errno %d\n", length, errno); - if (length < 0) - goto failed; - - strncpy(device->dev_name, dev_name, sizeof(device->dev_name) - 1); - device->fd = fd; - device->desc_length = length; - // assume we are writeable, since usb_device_get_fd will only return writeable fds - device->writeable = 1; - return device; - -failed: - close(fd); - free(device); - return NULL; -} - -static int usb_device_reopen_writeable(struct usb_device *device) -{ - if (device->writeable) - return 1; - - int fd = open(device->dev_name, O_RDWR); - if (fd >= 0) { - close(device->fd); - device->fd = fd; - device->writeable = 1; - return 1; - } - D("usb_device_reopen_writeable failed errno %d\n", errno); - return 0; -} - -int usb_device_get_fd(struct usb_device *device) -{ - if (!usb_device_reopen_writeable(device)) - return -1; - return device->fd; -} - -const char* usb_device_get_name(struct usb_device *device) -{ - return device->dev_name; -} - -int usb_device_get_unique_id(struct usb_device *device) -{ - int bus = 0, dev = 0; - sscanf(device->dev_name, USB_FS_ID_SCANNER, &bus, &dev); - return bus * 1000 + dev; -} - -int usb_device_get_unique_id_from_name(const char* name) -{ - int bus = 0, dev = 0; - sscanf(name, USB_FS_ID_SCANNER, &bus, &dev); - return bus * 1000 + dev; -} - -char* usb_device_get_name_from_unique_id(int id) -{ - int bus = id / 1000; - int dev = id % 1000; - char* result = (char *)calloc(1, strlen(USB_FS_ID_FORMAT)); - snprintf(result, strlen(USB_FS_ID_FORMAT) - 1, USB_FS_ID_FORMAT, bus, dev); - return result; -} - -uint16_t usb_device_get_vendor_id(struct usb_device *device) -{ - struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc; - return __le16_to_cpu(desc->idVendor); -} - -uint16_t usb_device_get_product_id(struct usb_device *device) -{ - struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc; - return __le16_to_cpu(desc->idProduct); -} - -const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device) -{ - return (struct usb_device_descriptor*)device->desc; -} - -char* usb_device_get_string(struct usb_device *device, int id) -{ - char string[256]; - __u16 buffer[128]; - __u16 languages[128]; - int i, result; - int languageCount = 0; - - string[0] = 0; - memset(languages, 0, sizeof(languages)); - - // read list of supported languages - result = usb_device_control_transfer(device, - USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR, - (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages), 0); - if (result > 0) - languageCount = (result - 2) / 2; - - for (i = 1; i <= languageCount; i++) { - memset(buffer, 0, sizeof(buffer)); - - result = usb_device_control_transfer(device, - USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR, - (USB_DT_STRING << 8) | id, languages[i], buffer, sizeof(buffer), 0); - if (result > 0) { - int i; - // skip first word, and copy the rest to the string, changing shorts to bytes. - result /= 2; - for (i = 1; i < result; i++) - string[i - 1] = buffer[i]; - string[i - 1] = 0; - return strdup(string); - } - } - - return NULL; -} - -char* usb_device_get_manufacturer_name(struct usb_device *device) -{ - struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; - - if (desc->iManufacturer) - return usb_device_get_string(device, desc->iManufacturer); - else - return NULL; -} - -char* usb_device_get_product_name(struct usb_device *device) -{ - struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; - - if (desc->iProduct) - return usb_device_get_string(device, desc->iProduct); - else - return NULL; -} - -char* usb_device_get_serial(struct usb_device *device) -{ - struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc; - - if (desc->iSerialNumber) - return usb_device_get_string(device, desc->iSerialNumber); - else - return NULL; -} - -int usb_device_is_writeable(struct usb_device *device) -{ - return device->writeable; -} - -void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter) -{ - iter->config = device->desc; - iter->config_end = device->desc + device->desc_length; - iter->curr_desc = device->desc; -} - -struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter) -{ - struct usb_descriptor_header* next; - if (iter->curr_desc >= iter->config_end) - return NULL; - next = (struct usb_descriptor_header*)iter->curr_desc; - iter->curr_desc += next->bLength; - return next; -} - -int usb_device_claim_interface(struct usb_device *device, unsigned int interface) -{ - return ioctl(device->fd, USBDEVFS_CLAIMINTERFACE, &interface); -} - -int usb_device_release_interface(struct usb_device *device, unsigned int interface) -{ - return ioctl(device->fd, USBDEVFS_RELEASEINTERFACE, &interface); -} - -int usb_device_connect_kernel_driver(struct usb_device *device, - unsigned int interface, int connect) -{ - struct usbdevfs_ioctl ctl; - - ctl.ifno = interface; - ctl.ioctl_code = (connect ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT); - ctl.data = NULL; - return ioctl(device->fd, USBDEVFS_IOCTL, &ctl); -} - -int usb_device_control_transfer(struct usb_device *device, - int requestType, - int request, - int value, - int index, - void* buffer, - int length, - unsigned int timeout) -{ - struct usbdevfs_ctrltransfer ctrl; - - // this usually requires read/write permission - if (!usb_device_reopen_writeable(device)) - return -1; - - memset(&ctrl, 0, sizeof(ctrl)); - ctrl.bRequestType = requestType; - ctrl.bRequest = request; - ctrl.wValue = value; - ctrl.wIndex = index; - ctrl.wLength = length; - ctrl.data = buffer; - ctrl.timeout = timeout; - return ioctl(device->fd, USBDEVFS_CONTROL, &ctrl); -} - -int usb_device_bulk_transfer(struct usb_device *device, - int endpoint, - void* buffer, - int length, - unsigned int timeout) -{ - struct usbdevfs_bulktransfer ctrl; - - memset(&ctrl, 0, sizeof(ctrl)); - ctrl.ep = endpoint; - ctrl.len = length; - ctrl.data = buffer; - ctrl.timeout = timeout; - return ioctl(device->fd, USBDEVFS_BULK, &ctrl); -} - -struct usb_request *usb_request_new(struct usb_device *dev, - const struct usb_endpoint_descriptor *ep_desc) -{ - struct usbdevfs_urb *urb = calloc(1, sizeof(struct usbdevfs_urb)); - if (!urb) - return NULL; - - if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) - urb->type = USBDEVFS_URB_TYPE_BULK; - else if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) - urb->type = USBDEVFS_URB_TYPE_INTERRUPT; - else { - D("Unsupported endpoint type %d", ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - free(urb); - return NULL; - } - urb->endpoint = ep_desc->bEndpointAddress; - - struct usb_request *req = calloc(1, sizeof(struct usb_request)); - if (!req) { - free(urb); - return NULL; - } - - req->dev = dev; - req->max_packet_size = __le16_to_cpu(ep_desc->wMaxPacketSize); - req->private_data = urb; - req->endpoint = urb->endpoint; - urb->usercontext = req; - - return req; -} - -void usb_request_free(struct usb_request *req) -{ - free(req->private_data); - free(req); -} - -int usb_request_queue(struct usb_request *req) -{ - struct usbdevfs_urb *urb = (struct usbdevfs_urb*)req->private_data; - int res; - - urb->status = -1; - urb->buffer = req->buffer; - urb->buffer_length = req->buffer_length; - - do { - res = ioctl(req->dev->fd, USBDEVFS_SUBMITURB, urb); - } while((res < 0) && (errno == EINTR)); - - return res; -} - -struct usb_request *usb_request_wait(struct usb_device *dev) -{ - struct usbdevfs_urb *urb = NULL; - struct usb_request *req = NULL; - int res; - - while (1) { - int res = ioctl(dev->fd, USBDEVFS_REAPURB, &urb); - D("USBDEVFS_REAPURB returned %d\n", res); - if (res < 0) { - if(errno == EINTR) { - continue; - } - D("[ reap urb - error ]\n"); - return NULL; - } else { - D("[ urb @%p status = %d, actual = %d ]\n", - urb, urb->status, urb->actual_length); - req = (struct usb_request*)urb->usercontext; - req->actual_length = urb->actual_length; - } - break; - } - return req; -} - -int usb_request_cancel(struct usb_request *req) -{ - struct usbdevfs_urb *urb = ((struct usbdevfs_urb*)req->private_data); - return ioctl(req->dev->fd, USBDEVFS_DISCARDURB, &urb); -} - diff --git a/libs/usb/tests/AccessoryChat/accessorychat/usbhost/usbhost.h b/libs/usb/tests/AccessoryChat/accessorychat/usbhost/usbhost.h deleted file mode 100644 index 9a6b59c5f46f..000000000000 --- a/libs/usb/tests/AccessoryChat/accessorychat/usbhost/usbhost.h +++ /dev/null @@ -1,219 +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. - */ - -#ifndef __USB_HOST_H -#define __USB_HOST_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdint.h> - -#include <linux/version.h> -#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) -#include <linux/usb/ch9.h> -#else -#include <linux/usb_ch9.h> -#endif - -struct usb_host_context; -struct usb_endpoint_descriptor; - -struct usb_descriptor_iter { - unsigned char* config; - unsigned char* config_end; - unsigned char* curr_desc; -}; - -struct usb_request -{ - struct usb_device *dev; - void* buffer; - int buffer_length; - int actual_length; - int max_packet_size; - void *private_data; /* struct usbdevfs_urb* */ - int endpoint; - void *client_data; /* free for use by client */ -}; - -/* Callback for notification when new USB devices are attached. - * Return true to exit from usb_host_run. - */ -typedef int (* usb_device_added_cb)(const char *dev_name, void *client_data); - -/* Callback for notification when USB devices are removed. - * Return true to exit from usb_host_run. - */ -typedef int (* usb_device_removed_cb)(const char *dev_name, void *client_data); - -/* Callback indicating that initial device discovery is done. - * Return true to exit from usb_host_run. - */ -typedef int (* usb_discovery_done_cb)(void *client_data); - -/* Call this to initialize the USB host library. */ -struct usb_host_context *usb_host_init(void); - -/* Call this to cleanup the USB host library. */ -void usb_host_cleanup(struct usb_host_context *context); - -/* Call this to monitor the USB bus for new and removed devices. - * This is intended to be called from a dedicated thread, - * as it will not return until one of the callbacks returns true. - * added_cb will be called immediately for each existing USB device, - * and subsequently each time a new device is added. - * removed_cb is called when USB devices are removed from the bus. - * discovery_done_cb is called after the initial discovery of already - * connected devices is complete. - */ -void usb_host_run(struct usb_host_context *context, - usb_device_added_cb added_cb, - usb_device_removed_cb removed_cb, - usb_discovery_done_cb discovery_done_cb, - void *client_data); - -/* Creates a usb_device object for a USB device */ -struct usb_device *usb_device_open(const char *dev_name); - -/* Releases all resources associated with the USB device */ -void usb_device_close(struct usb_device *device); - -/* Creates a usb_device object for already open USB device */ -struct usb_device *usb_device_new(const char *dev_name, int fd); - -/* Returns the file descriptor for the usb_device */ -int usb_device_get_fd(struct usb_device *device); - -/* Returns the name for the USB device, which is the same as - * the dev_name passed to usb_device_open() - */ -const char* usb_device_get_name(struct usb_device *device); - -/* Returns a unique ID for the device. - *Currently this is generated from the dev_name path. - */ -int usb_device_get_unique_id(struct usb_device *device); - -/* Returns a unique ID for the device name. - * Currently this is generated from the device path. - */ -int usb_device_get_unique_id_from_name(const char* name); - -/* Returns the device name for the unique ID. - * Call free() to deallocate the returned string */ -char* usb_device_get_name_from_unique_id(int id); - -/* Returns the USB vendor ID from the device descriptor for the USB device */ -uint16_t usb_device_get_vendor_id(struct usb_device *device); - -/* Returns the USB product ID from the device descriptor for the USB device */ -uint16_t usb_device_get_product_id(struct usb_device *device); - -const struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device); - -/* Returns a USB descriptor string for the given string ID. - * Used to implement usb_device_get_manufacturer_name, - * usb_device_get_product_name and usb_device_get_serial. - * Call free() to free the result when you are done with it. - */ -char* usb_device_get_string(struct usb_device *device, int id); - -/* Returns the manufacturer name for the USB device. - * Call free() to free the result when you are done with it. - */ -char* usb_device_get_manufacturer_name(struct usb_device *device); - -/* Returns the product name for the USB device. - * Call free() to free the result when you are done with it. - */ -char* usb_device_get_product_name(struct usb_device *device); - -/* Returns the USB serial number for the USB device. - * Call free() to free the result when you are done with it. - */ -char* usb_device_get_serial(struct usb_device *device); - -/* Returns true if we have write access to the USB device, - * and false if we only have access to the USB device configuration. - */ -int usb_device_is_writeable(struct usb_device *device); - -/* Initializes a usb_descriptor_iter, which can be used to iterate through all - * the USB descriptors for a USB device. - */ -void usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter); - -/* Returns the next USB descriptor for a device, or NULL if we have reached the - * end of the list. - */ -struct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter); - -/* Claims the specified interface of a USB device */ -int usb_device_claim_interface(struct usb_device *device, unsigned int interface); - -/* Releases the specified interface of a USB device */ -int usb_device_release_interface(struct usb_device *device, unsigned int interface); - -/* Requests the kernel to connect or disconnect its driver for the specified interface. - * This can be used to ask the kernel to disconnect its driver for a device - * so usb_device_claim_interface can claim it instead. - */ -int usb_device_connect_kernel_driver(struct usb_device *device, - unsigned int interface, int connect); - -/* Sends a control message to the specified device on endpoint zero */ -int usb_device_control_transfer(struct usb_device *device, - int requestType, - int request, - int value, - int index, - void* buffer, - int length, - unsigned int timeout); - -/* Reads or writes on a bulk endpoint. - * Returns number of bytes transferred, or negative value for error. - */ -int usb_device_bulk_transfer(struct usb_device *device, - int endpoint, - void* buffer, - int length, - unsigned int timeout); - -/* Creates a new usb_request. */ -struct usb_request *usb_request_new(struct usb_device *dev, - const struct usb_endpoint_descriptor *ep_desc); - -/* Releases all resources associated with the request */ -void usb_request_free(struct usb_request *req); - -/* Submits a read or write request on the specified device */ -int usb_request_queue(struct usb_request *req); - - /* Waits for the results of a previous usb_request_queue operation. - * Returns a usb_request, or NULL for error. - */ -struct usb_request *usb_request_wait(struct usb_device *dev); - -/* Cancels a pending usb_request_queue() operation. */ -int usb_request_cancel(struct usb_request *req); - -#ifdef __cplusplus -} -#endif -#endif /* __USB_HOST_H */ diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index eb75ed89404e..e8d40ba08352 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -28,6 +28,7 @@ commonSources:= \ Flattenable.cpp \ ObbFile.cpp \ Pool.cpp \ + PropertyMap.cpp \ RefBase.cpp \ ResourceTypes.cpp \ SharedBuffer.cpp \ @@ -41,6 +42,8 @@ commonSources:= \ TextOutput.cpp \ Threads.cpp \ Timers.cpp \ + Tokenizer.cpp \ + Unicode.cpp \ VectorImpl.cpp \ ZipFileCRO.cpp \ ZipFileRO.cpp \ @@ -67,11 +70,6 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1 endif endif -ifeq ($(HOST_OS),darwin) -# MacOS doesn't have lseek64. However, off_t is 64-bit anyway. -LOCAL_CFLAGS += -DOFF_T_IS_64_BIT -endif - include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libs/utils/Asset.cpp b/libs/utils/Asset.cpp index cef7db492e55..a18294b18c8b 100644 --- a/libs/utils/Asset.cpp +++ b/libs/utils/Asset.cpp @@ -35,6 +35,9 @@ #include <fcntl.h> #include <errno.h> #include <assert.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> using namespace android; @@ -62,7 +65,7 @@ String8 Asset::getAssetAllocations() if (cur->isAllocated()) { res.append(" "); res.append(cur->getAssetSource()); - off_t size = (cur->getLength()+512)/1024; + off64_t size = (cur->getLength()+512)/1024; char buf[64]; sprintf(buf, ": %dK\n", (int)size); res.append(buf); @@ -119,7 +122,7 @@ Asset::~Asset(void) { _FileAsset* pAsset; status_t result; - off_t length; + off64_t length; int fd; fd = open(fileName, O_RDONLY | O_BINARY); @@ -132,12 +135,26 @@ Asset::~Asset(void) * always open things read-only it doesn't really matter, so there's * no value in incurring the extra overhead of an fstat() call. */ - length = lseek(fd, 0, SEEK_END); + // TODO(kroot): replace this with fstat despite the plea above. +#if 1 + length = lseek64(fd, 0, SEEK_END); if (length < 0) { ::close(fd); return NULL; } - (void) lseek(fd, 0, SEEK_SET); + (void) lseek64(fd, 0, SEEK_SET); +#else + struct stat st; + if (fstat(fd, &st) < 0) { + ::close(fd); + return NULL; + } + + if (!S_ISREG(st.st_mode)) { + ::close(fd); + return NULL; + } +#endif pAsset = new _FileAsset; result = pAsset->openChunk(fileName, fd, 0, length); @@ -162,7 +179,7 @@ Asset::~Asset(void) { _CompressedAsset* pAsset; status_t result; - off_t fileLen; + off64_t fileLen; bool scanResult; long offset; int method; @@ -215,7 +232,7 @@ Asset::~Asset(void) /* * Create a new Asset from part of an open file. */ -/*static*/ Asset* Asset::createFromFileSegment(int fd, off_t offset, +/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset, size_t length, AccessMode mode) { _FileAsset* pAsset; @@ -233,7 +250,7 @@ Asset::~Asset(void) /* * Create a new Asset from compressed data in an open file. */ -/*static*/ Asset* Asset::createFromCompressedData(int fd, off_t offset, +/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen, AccessMode mode) { @@ -295,9 +312,9 @@ Asset::~Asset(void) * * Returns the new chunk offset, or -1 if the seek is illegal. */ -off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) +off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn) { - off_t newOffset; + off64_t newOffset; switch (whence) { case SEEK_SET: @@ -311,15 +328,15 @@ off_t Asset::handleSeek(off_t offset, int whence, off_t curPosn, off_t maxPosn) break; default: LOGW("unexpected whence %d\n", whence); - // this was happening due to an off_t size mismatch + // this was happening due to an off64_t size mismatch assert(false); - return (off_t) -1; + return (off64_t) -1; } if (newOffset < 0 || newOffset > maxPosn) { LOGW("seek out of range: want %ld, end=%ld\n", (long) newOffset, (long) maxPosn); - return (off_t) -1; + return (off64_t) -1; } return newOffset; @@ -353,7 +370,7 @@ _FileAsset::~_FileAsset(void) * * Zero-length chunks are allowed. */ -status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_t length) +status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length) { assert(mFp == NULL); // no reopen assert(mMap == NULL); @@ -363,15 +380,15 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off_t offset, size_ /* * Seek to end to get file length. */ - off_t fileLength; - fileLength = lseek(fd, 0, SEEK_END); - if (fileLength == (off_t) -1) { + off64_t fileLength; + fileLength = lseek64(fd, 0, SEEK_END); + if (fileLength == (off64_t) -1) { // probably a bad file descriptor LOGD("failed lseek (errno=%d)\n", errno); return UNKNOWN_ERROR; } - if ((off_t) (offset + length) > fileLength) { + if ((off64_t) (offset + length) > fileLength) { LOGD("start (%ld) + len (%ld) > end (%ld)\n", (long) offset, (long) length, (long) fileLength); return BAD_INDEX; @@ -482,21 +499,21 @@ ssize_t _FileAsset::read(void* buf, size_t count) /* * Seek to a new position. */ -off_t _FileAsset::seek(off_t offset, int whence) +off64_t _FileAsset::seek(off64_t offset, int whence) { - off_t newPosn; - long actualOffset; + off64_t newPosn; + off64_t actualOffset; // compute new position within chunk newPosn = handleSeek(offset, whence, mOffset, mLength); - if (newPosn == (off_t) -1) + if (newPosn == (off64_t) -1) return newPosn; - actualOffset = (long) (mStart + newPosn); + actualOffset = mStart + newPosn; if (mFp != NULL) { if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) - return (off_t) -1; + return (off64_t) -1; } mOffset = actualOffset - mStart; @@ -603,7 +620,7 @@ const void* _FileAsset::getBuffer(bool wordAligned) } } -int _FileAsset::openFileDescriptor(off_t* outStart, off_t* outLength) const +int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const { if (mMap != NULL) { const char* fname = mMap->getFileName(); @@ -678,7 +695,7 @@ _CompressedAsset::~_CompressedAsset(void) * This currently just sets up some values and returns. On the first * read, we expand the entire file into a buffer and return data from it. */ -status_t _CompressedAsset::openChunk(int fd, off_t offset, +status_t _CompressedAsset::openChunk(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen) { assert(mFd < 0); // no re-open @@ -782,13 +799,13 @@ ssize_t _CompressedAsset::read(void* buf, size_t count) * expensive, because it requires plowing through a bunch of compressed * data. */ -off_t _CompressedAsset::seek(off_t offset, int whence) +off64_t _CompressedAsset::seek(off64_t offset, int whence) { - off_t newPosn; + off64_t newPosn; // compute new position within chunk newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); - if (newPosn == (off_t) -1) + if (newPosn == (off64_t) -1) return newPosn; if (mZipInflater) { diff --git a/libs/utils/FileMap.cpp b/libs/utils/FileMap.cpp index e1ba9b238990..c220a9016f79 100644 --- a/libs/utils/FileMap.cpp +++ b/libs/utils/FileMap.cpp @@ -63,16 +63,18 @@ FileMap::~FileMap(void) free(mFileName); } #ifdef HAVE_POSIX_FILEMAP - if (munmap(mBasePtr, mBaseLength) != 0) { + if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { LOGD("munmap(%p, %d) failed\n", mBasePtr, (int) mBaseLength); } #endif #ifdef HAVE_WIN32_FILEMAP - if ( UnmapViewOfFile(mBasePtr) == 0) { + if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { LOGD("UnmapViewOfFile(%p) failed, error = %ld\n", mBasePtr, GetLastError() ); } - CloseHandle(mFileMapping); + if (mFileMapping != INVALID_HANDLE_VALUE) { + CloseHandle(mFileMapping); + } CloseHandle(mFileHandle); #endif } @@ -86,11 +88,12 @@ FileMap::~FileMap(void) * * Returns "false" on failure. */ -bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t length, bool readOnly) +bool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length, + bool readOnly) { #ifdef HAVE_WIN32_FILEMAP int adjust; - off_t adjOffset; + off64_t adjOffset; size_t adjLength; if (mPageSize == -1) { @@ -129,7 +132,7 @@ bool FileMap::create(const char* origFileName, int fd, off_t offset, size_t leng #endif #ifdef HAVE_POSIX_FILEMAP int prot, flags, adjust; - off_t adjOffset; + off64_t adjOffset; size_t adjLength; void* ptr; diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp index a5363d6fcfd9..18f858b4b689 100644 --- a/libs/utils/Looper.cpp +++ b/libs/utils/Looper.cpp @@ -19,10 +19,27 @@ #include <unistd.h> #include <fcntl.h> +#include <limits.h> namespace android { +// --- WeakMessageHandler --- + +WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) : + mHandler(handler) { +} + +void WeakMessageHandler::handleMessage(const Message& message) { + sp<MessageHandler> handler = mHandler.promote(); + if (handler != NULL) { + handler->handleMessage(message); + } +} + + +// --- Looper --- + #ifdef LOOPER_USES_EPOLL // Hint for number of file descriptors to be associated with the epoll instance. static const int EPOLL_SIZE_HINT = 8; @@ -35,8 +52,8 @@ static pthread_once_t gTLSOnce = PTHREAD_ONCE_INIT; static pthread_key_t gTLSKey = 0; Looper::Looper(bool allowNonCallbacks) : - mAllowNonCallbacks(allowNonCallbacks), - mResponseIndex(0) { + mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false), + mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { int wakeFds[2]; int result = pipe(wakeFds); LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); @@ -161,17 +178,21 @@ int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outDa for (;;) { while (mResponseIndex < mResponses.size()) { const Response& response = mResponses.itemAt(mResponseIndex++); - if (! response.request.callback) { + ALooper_callbackFunc callback = response.request.callback; + if (!callback) { + int ident = response.request.ident; + int fd = response.request.fd; + int events = response.events; + void* data = response.request.data; #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - returning signalled identifier %d: " - "fd=%d, events=0x%x, data=%p", this, - response.request.ident, response.request.fd, - response.events, response.request.data); + "fd=%d, events=0x%x, data=%p", + this, ident, fd, events, data); #endif - if (outFd != NULL) *outFd = response.request.fd; - if (outEvents != NULL) *outEvents = response.events; - if (outData != NULL) *outData = response.request.data; - return response.request.ident; + if (outFd != NULL) *outFd = fd; + if (outEvents != NULL) *outEvents = events; + if (outData != NULL) *outData = data; + return ident; } } @@ -194,6 +215,25 @@ int Looper::pollInner(int timeoutMillis) { LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis); #endif + // Adjust the timeout based on when the next message is due. + if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (mNextMessageUptime <= now) { + timeoutMillis = 0; + } else { + uint64_t delay = (mNextMessageUptime - now + 999999LL) / 1000000LL; + if (delay < INT_MAX + && (timeoutMillis < 0 || int(delay) < timeoutMillis)) { + timeoutMillis = int(delay); + } + } +#if DEBUG_POLL_AND_WAKE + LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d", + this, mNextMessageUptime - now, timeoutMillis); +#endif + } + + // Poll. int result = ALOOPER_POLL_WAKE; mResponses.clear(); mResponseIndex = 0; @@ -205,7 +245,6 @@ int Looper::pollInner(int timeoutMillis) { #ifdef LOOPER_USES_EPOLL struct epoll_event eventItems[EPOLL_MAX_EVENTS]; int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis); - bool acquiredLock = false; #else // Wait for wakeAndLock() waiters to run then set mPolling to true. mLock.lock(); @@ -219,16 +258,20 @@ int Looper::pollInner(int timeoutMillis) { int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis); #endif + // Acquire lock. + mLock.lock(); + + // Check for poll error. if (eventCount < 0) { if (errno == EINTR) { goto Done; } - LOGW("Poll failed with an unexpected error, errno=%d", errno); result = ALOOPER_POLL_ERROR; goto Done; } + // Check for poll timeout. if (eventCount == 0) { #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - timeout", this); @@ -237,6 +280,7 @@ int Looper::pollInner(int timeoutMillis) { goto Done; } + // Handle all events. #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount); #endif @@ -252,11 +296,6 @@ int Looper::pollInner(int timeoutMillis) { LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents); } } else { - if (! acquiredLock) { - mLock.lock(); - acquiredLock = true; - } - ssize_t requestIndex = mRequests.indexOfKey(fd); if (requestIndex >= 0) { int events = 0; @@ -271,9 +310,6 @@ int Looper::pollInner(int timeoutMillis) { } } } - if (acquiredLock) { - mLock.unlock(); - } Done: ; #else for (size_t i = 0; i < requestedCount; i++) { @@ -301,15 +337,12 @@ Done: ; } } } - Done: // Set mPolling to false and wake up the wakeAndLock() waiters. - mLock.lock(); mPolling = false; if (mWaiters != 0) { mAwake.broadcast(); } - mLock.unlock(); #endif #ifdef LOOPER_STATISTICS @@ -335,19 +368,59 @@ Done: } #endif + // Invoke pending message callbacks. + mNextMessageUptime = LLONG_MAX; + while (mMessageEnvelopes.size() != 0) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0); + if (messageEnvelope.uptime <= now) { + // Remove the envelope from the list. + // We keep a strong reference to the handler until the call to handleMessage + // finishes. Then we drop it so that the handler can be deleted *before* + // we reacquire our lock. + { // obtain handler + sp<MessageHandler> handler = messageEnvelope.handler; + Message message = messageEnvelope.message; + mMessageEnvelopes.removeAt(0); + mSendingMessage = true; + mLock.unlock(); + +#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS + LOGD("%p ~ pollOnce - sending message: handler=%p, what=%d", + this, handler.get(), message.what); +#endif + handler->handleMessage(message); + } // release handler + + mLock.lock(); + mSendingMessage = false; + result = ALOOPER_POLL_CALLBACK; + } else { + // The last message left at the head of the queue determines the next wakeup time. + mNextMessageUptime = messageEnvelope.uptime; + break; + } + } + + // Release lock. + mLock.unlock(); + + // Invoke all response callbacks. for (size_t i = 0; i < mResponses.size(); i++) { const Response& response = mResponses.itemAt(i); - if (response.request.callback) { + ALooper_callbackFunc callback = response.request.callback; + if (callback) { + int fd = response.request.fd; + int events = response.events; + void* data = response.request.data; #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this, - response.request.fd, response.events, response.request.data); + LOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p", + this, callback, fd, events, data); #endif - int callbackResult = response.request.callback( - response.request.fd, response.events, response.request.data); + int callbackResult = callback(fd, events, data); if (callbackResult == 0) { - removeFd(response.request.fd); + removeFd(fd); } - result = ALOOPER_POLL_CALLBACK; } } @@ -593,4 +666,83 @@ void Looper::wakeAndLock() { } #endif +void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) { + sendMessageAtTime(LLONG_MIN, handler, message); +} + +void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler, + const Message& message) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sendMessageAtTime(now + uptimeDelay, handler, message); +} + +void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler, + const Message& message) { +#if DEBUG_CALLBACKS + LOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d", + this, uptime, handler.get(), message.what); +#endif + + size_t i = 0; + { // acquire lock + AutoMutex _l(mLock); + + size_t messageCount = mMessageEnvelopes.size(); + while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) { + i += 1; + } + + MessageEnvelope messageEnvelope(uptime, handler, message); + mMessageEnvelopes.insertAt(messageEnvelope, i, 1); + + // Optimization: If the Looper is currently sending a message, then we can skip + // the call to wake() because the next thing the Looper will do after processing + // messages is to decide when the next wakeup time should be. In fact, it does + // not even matter whether this code is running on the Looper thread. + if (mSendingMessage) { + return; + } + } // release lock + + // Wake the poll loop only when we enqueue a new message at the head. + if (i == 0) { + wake(); + } +} + +void Looper::removeMessages(const sp<MessageHandler>& handler) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeMessages - handler=%p", this, handler.get()); +#endif + + { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = mMessageEnvelopes.size(); i != 0; ) { + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i); + if (messageEnvelope.handler == handler) { + mMessageEnvelopes.removeAt(i); + } + } + } // release lock +} + +void Looper::removeMessages(const sp<MessageHandler>& handler, int what) { +#if DEBUG_CALLBACKS + LOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what); +#endif + + { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = mMessageEnvelopes.size(); i != 0; ) { + const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i); + if (messageEnvelope.handler == handler + && messageEnvelope.message.what == what) { + mMessageEnvelopes.removeAt(i); + } + } + } // release lock +} + } // namespace android diff --git a/libs/utils/ObbFile.cpp b/libs/utils/ObbFile.cpp index 2c3724c0af08..2907b5666b67 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/utils/ObbFile.cpp @@ -22,6 +22,8 @@ #include <unistd.h> #define LOG_TAG "ObbFile" + +#include <utils/Compat.h> #include <utils/Log.h> #include <utils/ObbFile.h> @@ -67,17 +69,6 @@ _rc; }) #endif -/* - * Work around situations where off_t is 64-bit and use off64_t in - * situations where it's 32-bit. - */ -#ifdef OFF_T_IS_64_BIT -#define my_lseek64 lseek -typedef off_t my_off64_t; -#else -#define my_lseek64 lseek64 -typedef off64_t my_off64_t; -#endif namespace android { @@ -125,7 +116,7 @@ bool ObbFile::readFrom(int fd) bool ObbFile::parseObbFile(int fd) { - my_off64_t fileLength = my_lseek64(fd, 0, SEEK_END); + off64_t fileLength = lseek64(fd, 0, SEEK_END); if (fileLength < kFooterMinSize) { if (fileLength < 0) { @@ -140,7 +131,7 @@ bool ObbFile::parseObbFile(int fd) size_t footerSize; { - my_lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); + lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); char *footer = new char[kFooterTagSize]; actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); @@ -171,8 +162,8 @@ bool ObbFile::parseObbFile(int fd) } } - my_off64_t fileOffset = fileLength - footerSize - kFooterTagSize; - if (my_lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { + off64_t fileOffset = fileLength - footerSize - kFooterTagSize; + if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { LOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); return false; } @@ -211,10 +202,10 @@ bool ObbFile::parseObbFile(int fd) memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt)); - uint32_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); - if (packageNameLen <= 0 + size_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); + if (packageNameLen == 0 || packageNameLen > (footerSize - kPackageNameOffset)) { - LOGW("bad ObbFile package name length (0x%04x; 0x%04x possible)\n", + LOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n", packageNameLen, footerSize - kPackageNameOffset); free(scanBuf); return false; @@ -257,7 +248,7 @@ bool ObbFile::writeTo(int fd) return false; } - my_lseek64(fd, 0, SEEK_END); + lseek64(fd, 0, SEEK_END); if (mPackageName.size() == 0 || mVersion == -1) { LOGW("tried to write uninitialized ObbFile data\n"); diff --git a/libs/utils/PropertyMap.cpp b/libs/utils/PropertyMap.cpp new file mode 100644 index 000000000000..d472d45c4dd1 --- /dev/null +++ b/libs/utils/PropertyMap.cpp @@ -0,0 +1,218 @@ +/* + * 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. + */ + +#define LOG_TAG "PropertyMap" + +#include <stdlib.h> +#include <string.h> + +#include <utils/PropertyMap.h> +#include <utils/Log.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r="; + + +// --- PropertyMap --- + +PropertyMap::PropertyMap() { +} + +PropertyMap::~PropertyMap() { +} + +void PropertyMap::clear() { + mProperties.clear(); +} + +void PropertyMap::addProperty(const String8& key, const String8& value) { + mProperties.add(key, value); +} + +bool PropertyMap::hasProperty(const String8& key) const { + return mProperties.indexOfKey(key) >= 0; +} + +bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const { + ssize_t index = mProperties.indexOfKey(key); + if (index < 0) { + return false; + } + + outValue = mProperties.valueAt(index); + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const { + int32_t intValue; + if (!tryGetProperty(key, intValue)) { + return false; + } + + outValue = intValue; + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const { + String8 stringValue; + if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + int value = strtol(stringValue.string(), & end, 10); + if (*end != '\0') { + LOGW("Property key '%s' has invalid value '%s'. Expected an integer.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { + String8 stringValue; + if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + float value = strtof(stringValue.string(), & end); + if (*end != '\0') { + LOGW("Property key '%s' has invalid value '%s'. Expected a float.", + key.string(), stringValue.string()); + return false; + } + outValue = value; + return true; +} + +void PropertyMap::addAll(const PropertyMap* map) { + for (size_t i = 0; i < map->mProperties.size(); i++) { + mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i)); + } +} + +status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { + *outMap = NULL; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + LOGE("Error %d opening property file %s.", status, filename.string()); + } else { + PropertyMap* map = new PropertyMap(); + if (!map) { + LOGE("Error allocating property map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + LOGD("Parsed property file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + + +// --- PropertyMap::Parser --- + +PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) : + mMap(map), mTokenizer(tokenizer) { +} + +PropertyMap::Parser::~Parser() { +} + +status_t PropertyMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + LOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + if (keyToken.isEmpty()) { + LOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + if (mTokenizer->nextChar() != '=') { + LOGE("%s: Expected '=' between property key and value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + String8 valueToken = mTokenizer->nextToken(WHITESPACE); + if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { + LOGE("%s: Found reserved character '\\' or '\"' in property value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + LOGE("%s: Expected end of line, got '%s'.", + mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + + if (mMap->hasProperty(keyToken)) { + LOGE("%s: Duplicate property value for key '%s'.", + mTokenizer->getLocation().string(), keyToken.string()); + return BAD_VALUE; + } + + mMap->addProperty(keyToken, valueToken); + } + + mTokenizer->nextLine(); + } + return NO_ERROR; +} + +} // namespace android diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index 0bd1af4ebf18..bb6c1255f38a 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -20,9 +20,9 @@ #include <utils/Atomic.h> #include <utils/CallStack.h> -#include <utils/KeyedVector.h> #include <utils/Log.h> #include <utils/threads.h> +#include <utils/TextOutput.h> #include <stdlib.h> #include <stdio.h> @@ -34,6 +34,7 @@ // compile with refcounting debugging enabled #define DEBUG_REFS 0 +#define DEBUG_REFS_FATAL_SANITY_CHECKS 0 #define DEBUG_REFS_ENABLED_BY_DEFAULT 1 #define DEBUG_REFS_CALLSTACK_ENABLED 1 @@ -69,8 +70,10 @@ public: void addStrongRef(const void* /*id*/) { } void removeStrongRef(const void* /*id*/) { } + void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { } void addWeakRef(const void* /*id*/) { } void removeWeakRef(const void* /*id*/) { } + void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { } void printRefs() const { } void trackMe(bool, bool) { } @@ -86,39 +89,91 @@ public: , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT) , mRetain(false) { - //LOGI("NEW weakref_impl %p for RefBase %p", this, base); } ~weakref_impl() { - LOG_ALWAYS_FATAL_IF(!mRetain && mStrongRefs != NULL, "Strong references remain!"); - LOG_ALWAYS_FATAL_IF(!mRetain && mWeakRefs != NULL, "Weak references remain!"); + bool dumpStack = false; + if (!mRetain && mStrongRefs != NULL) { + dumpStack = true; +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("Strong references remain!"); +#else + LOGE("Strong references remain:"); +#endif + ref_entry* refs = mStrongRefs; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); +#if DEBUG_REFS_CALLSTACK_ENABLED + refs->stack.dump(); +#endif; + refs = refs->next; + } + } + + if (!mRetain && mWeakRefs != NULL) { + dumpStack = true; +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("Weak references remain:"); +#else + LOGE("Weak references remain!"); +#endif + ref_entry* refs = mWeakRefs; + while (refs) { + char inc = refs->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); +#if DEBUG_REFS_CALLSTACK_ENABLED + refs->stack.dump(); +#endif; + refs = refs->next; + } + } + if (dumpStack) { + LOGE("above errors at:"); + CallStack stack; + stack.update(); + stack.dump(); + } } - void addStrongRef(const void* id) - { + void addStrongRef(const void* id) { + //LOGD_IF(mTrackEnabled, + // "addStrongRef: RefBase=%p, id=%p", mBase, id); addRef(&mStrongRefs, id, mStrong); } - void removeStrongRef(const void* id) - { - if (!mRetain) + void removeStrongRef(const void* id) { + //LOGD_IF(mTrackEnabled, + // "removeStrongRef: RefBase=%p, id=%p", mBase, id); + if (!mRetain) { removeRef(&mStrongRefs, id); - else + } else { addRef(&mStrongRefs, id, -mStrong); + } } - void addWeakRef(const void* id) - { + void renameStrongRefId(const void* old_id, const void* new_id) { + //LOGD_IF(mTrackEnabled, + // "renameStrongRefId: RefBase=%p, oid=%p, nid=%p", + // mBase, old_id, new_id); + renameRefsId(mStrongRefs, old_id, new_id); + } + + void addWeakRef(const void* id) { addRef(&mWeakRefs, id, mWeak); } - void removeWeakRef(const void* id) - { - if (!mRetain) + void removeWeakRef(const void* id) { + if (!mRetain) { removeRef(&mWeakRefs, id); - else + } else { addRef(&mWeakRefs, id, -mWeak); + } + } + + void renameWeakRefId(const void* old_id, const void* new_id) { + renameRefsId(mWeakRefs, old_id, new_id); } void trackMe(bool track, bool retain) @@ -132,8 +187,7 @@ public: String8 text; { - AutoMutex _l(const_cast<weakref_impl*>(this)->mMutex); - + Mutex::Autolock _l(const_cast<weakref_impl*>(this)->mMutex); char buf[128]; sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this); text.append(buf); @@ -172,6 +226,7 @@ private: { if (mTrackEnabled) { AutoMutex _l(mMutex); + ref_entry* ref = new ref_entry; // Reference count at the time of the snapshot, but before the // update. Positive value means we increment, negative--we @@ -181,7 +236,6 @@ private: #if DEBUG_REFS_CALLSTACK_ENABLED ref->stack.update(2); #endif - ref->next = *refs; *refs = ref; } @@ -192,20 +246,52 @@ private: if (mTrackEnabled) { AutoMutex _l(mMutex); - ref_entry* ref = *refs; + ref_entry* const head = *refs; + ref_entry* ref = head; while (ref != NULL) { if (ref->id == id) { *refs = ref->next; delete ref; return; } - refs = &ref->next; ref = *refs; } - - LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p (weakref_type %p) that doesn't exist!", - id, mBase, this); + +#if DEBUG_REFS_FATAL_SANITY_CHECKS + LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p" + "(weakref_type %p) that doesn't exist!", + id, mBase, this); +#endif + + LOGE("RefBase: removing id %p on RefBase %p" + "(weakref_type %p) that doesn't exist!", + id, mBase, this); + + ref = head; + while (ref) { + char inc = ref->ref >= 0 ? '+' : '-'; + LOGD("\t%c ID %p (ref %d):", inc, ref->id, ref->ref); + ref = ref->next; + } + + CallStack stack; + stack.update(); + stack.dump(); + } + } + + void renameRefsId(ref_entry* r, const void* old_id, const void* new_id) + { + if (mTrackEnabled) { + AutoMutex _l(mMutex); + ref_entry* ref = r; + while (ref != NULL) { + if (ref->id == old_id) { + ref->id = new_id; + } + ref = ref->next; + } } } @@ -235,44 +321,6 @@ private: // on removeref that match the address ones. bool mRetain; -#if 0 - void addRef(KeyedVector<const void*, int32_t>* refs, const void* id) - { - AutoMutex _l(mMutex); - ssize_t i = refs->indexOfKey(id); - if (i >= 0) { - ++(refs->editValueAt(i)); - } else { - i = refs->add(id, 1); - } - } - - void removeRef(KeyedVector<const void*, int32_t>* refs, const void* id) - { - AutoMutex _l(mMutex); - ssize_t i = refs->indexOfKey(id); - LOG_ALWAYS_FATAL_IF(i < 0, "RefBase: removing id %p that doesn't exist!", id); - if (i >= 0) { - int32_t val = --(refs->editValueAt(i)); - if (val == 0) { - refs->removeItemsAt(i); - } - } - } - - void printRefs(const KeyedVector<const void*, int32_t>& refs) - { - const size_t N=refs.size(); - for (size_t i=0; i<N; i++) { - printf("\tID %p: %d remain\n", refs.keyAt(i), refs.valueAt(i)); - } - } - - mutable Mutex mMutex; - KeyedVector<const void*, int32_t> mStrongRefs; - KeyedVector<const void*, int32_t> mWeakRefs; -#endif - #endif }; @@ -281,7 +329,6 @@ private: void RefBase::incStrong(const void* id) const { weakref_impl* const refs = mRefs; - refs->addWeakRef(id); refs->incWeak(id); refs->addStrongRef(id); @@ -313,14 +360,12 @@ void RefBase::decStrong(const void* id) const delete this; } } - refs->removeWeakRef(id); refs->decWeak(id); } void RefBase::forceIncStrong(const void* id) const { weakref_impl* const refs = mRefs; - refs->addWeakRef(id); refs->incWeak(id); refs->addStrongRef(id); @@ -372,7 +417,7 @@ void RefBase::weakref_type::decWeak(const void* id) if (impl->mStrong == INITIAL_STRONG_VALUE) delete impl->mBase; else { -// LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); + // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase); delete impl; } } else { @@ -432,7 +477,6 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) } } - impl->addWeakRef(id); impl->addStrongRef(id); #if PRINT_REFS @@ -450,7 +494,7 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) bool RefBase::weakref_type::attemptIncWeak(const void* id) { weakref_impl* const impl = static_cast<weakref_impl*>(this); - + int32_t curCount = impl->mWeak; LOG_ASSERT(curCount >= 0, "attemptIncWeak called on %p after underflow", this); @@ -497,14 +541,11 @@ RefBase::weakref_type* RefBase::getWeakRefs() const RefBase::RefBase() : mRefs(new weakref_impl(this)) { -// LOGV("Creating refs %p with RefBase %p\n", mRefs, this); } RefBase::~RefBase() { -// LOGV("Destroying RefBase %p (refs %p)\n", this, mRefs); if (mRefs->mWeak == 0) { -// LOGV("Freeing refs %p of old RefBase %p\n", mRefs, this); delete mRefs; } } @@ -530,5 +571,37 @@ bool RefBase::onIncStrongAttempted(uint32_t flags, const void* id) void RefBase::onLastWeakRef(const void* /*id*/) { } - + +// --------------------------------------------------------------------------- + +void RefBase::moveReferences(void* dst, void const* src, size_t n, + const ReferenceConverterBase& caster) +{ +#if DEBUG_REFS + const size_t itemSize = caster.getReferenceTypeSize(); + for (size_t i=0 ; i<n ; i++) { + void* d = reinterpret_cast<void *>(intptr_t(dst) + i*itemSize); + void const* s = reinterpret_cast<void const*>(intptr_t(src) + i*itemSize); + RefBase* ref(reinterpret_cast<RefBase*>(caster.getReferenceBase(d))); + ref->mRefs->renameStrongRefId(s, d); + ref->mRefs->renameWeakRefId(s, d); + } +#endif +} + +// --------------------------------------------------------------------------- + +TextOutput& printStrongPointer(TextOutput& to, const void* val) +{ + to << "sp<>(" << val << ")"; + return to; +} + +TextOutput& printWeakPointer(TextOutput& to, const void* val) +{ + to << "wp<>(" << val << ")"; + return to; +} + + }; // namespace android diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 7fb7ae3e9fc8..7197ad7a2b7e 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -317,6 +317,12 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) 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", @@ -438,15 +444,51 @@ void ResStringPool::uninit() } } -#define DECODE_LENGTH(str, chrsz, len) \ - len = *(str); \ - if (*(str)&(1<<(chrsz*8-1))) { \ - (str)++; \ - len = (((len)&((1<<(chrsz*8-1))-1))<<(chrsz*8)) + *(str); \ - } \ - (str)++; +/** + * Strings in UTF-16 format have length indicated by a length encoded in the + * stored data. It is either 1 or 2 characters of length data. This allows a + * maximum length of 0x7FFFFFF (2147483647 bytes), but if you're storing that + * much data in a string, you're abusing them. + * + * If the high bit is set, then there are two characters or 4 bytes of length + * data encoded. In that case, drop the high bit of the first character and + * add it together with the next character. + */ +static inline size_t +decodeLength(const char16_t** str) +{ + size_t len = **str; + if ((len & 0x8000) != 0) { + (*str)++; + len = ((len & 0x7FFF) << 16) | **str; + } + (*str)++; + return len; +} + +/** + * Strings in UTF-8 format have length indicated by a length encoded in the + * stored data. It is either 1 or 2 characters of length data. This allows a + * maximum length of 0x7FFF (32767 bytes), but you should consider storing + * text in another way if you're using that much data in a single string. + * + * If the high bit is set, then there are two characters or 2 bytes of length + * data encoded. In that case, drop the high bit of the first character and + * add it together with the next character. + */ +static inline size_t +decodeLength(const uint8_t** str) +{ + size_t len = **str; + if ((len & 0x80) != 0) { + (*str)++; + len = ((len & 0x7F) << 8) | **str; + } + (*str)++; + return len; +} -const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const +const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; @@ -455,37 +497,51 @@ const uint16_t* ResStringPool::stringAt(size_t idx, size_t* outLen) const if (!isUTF8) { const char16_t* strings = (char16_t*)mStrings; const char16_t* str = strings+off; - DECODE_LENGTH(str, sizeof(char16_t), *outLen) - if ((uint32_t)(str+*outLen-strings) < mStringPoolSize) { + + *u16len = decodeLength(&str); + if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { return str; } else { LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+*outLen-strings), (int)mStringPoolSize); + (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); } } else { const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; - DECODE_LENGTH(str, sizeof(uint8_t), *outLen) - size_t encLen; - DECODE_LENGTH(str, sizeof(uint8_t), encLen) - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + const uint8_t* u8str = strings+off; + + *u16len = decodeLength(&u8str); + size_t u8len = decodeLength(&u8str); + + // encLen must be less than 0x7FFF due to encoding. + if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { AutoMutex lock(mDecodeLock); + if (mCache[idx] != NULL) { return mCache[idx]; } - char16_t *u16str = (char16_t *)calloc(*outLen+1, sizeof(char16_t)); + + ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); + if (actualLen < 0 || (size_t)actualLen != *u16len) { + LOGW("Bad string block: string #%lld decoded length is not correct " + "%lld vs %llu\n", + (long long)idx, (long long)actualLen, (long long)*u16len); + return NULL; + } + + char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); if (!u16str) { LOGW("No memory when trying to allocate decode cache for string #%d\n", (int)idx); return NULL; } - const unsigned char *u8src = reinterpret_cast<const unsigned char *>(str); - utf8_to_utf16(u8src, encLen, u16str, *outLen); + + utf8_to_utf16(u8str, u8len, u16str); mCache[idx] = u16str; return u16str; } else { - LOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); + LOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", + (long long)idx, (long long)(u8str+u8len-strings), + (long long)mStringPoolSize); } } } else { @@ -506,9 +562,8 @@ const char* ResStringPool::string8At(size_t idx, size_t* outLen) const if (isUTF8) { const uint8_t* strings = (uint8_t*)mStrings; const uint8_t* str = strings+off; - DECODE_LENGTH(str, sizeof(uint8_t), *outLen) - size_t encLen; - DECODE_LENGTH(str, sizeof(uint8_t), encLen) + *outLen = decodeLength(&str); + size_t encLen = decodeLength(&str); if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { return (const char*)str; } else { @@ -1878,13 +1933,19 @@ bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const 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; } return false; } -ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, +ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density, uint32_t* outSpecFlags, ResTable_config* outConfig) const { if (mError != NO_ERROR) { @@ -1914,7 +1975,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up if (outSpecFlags != NULL) *outSpecFlags = 0; - + // Look through all resource packages, starting with the most // recently added. const PackageGroup* const grp = mPackageGroups[p]; @@ -1922,6 +1983,22 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag LOGW("Bad identifier when getting value for resource number 0x%08x", resID); return BAD_INDEX; } + + // Allow overriding density + const ResTable_config* desiredConfig = &mParams; + ResTable_config* overrideConfig = NULL; + if (density > 0) { + overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config)); + if (overrideConfig == NULL) { + LOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); + return BAD_INDEX; + } + memcpy(overrideConfig, &mParams, sizeof(ResTable_config)); + overrideConfig->density = density; + desiredConfig = overrideConfig; + } + + ssize_t rc = BAD_VALUE; size_t ip = grp->packages.size(); while (ip > 0) { ip--; @@ -1931,12 +2008,13 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag const ResTable_type* type; const ResTable_entry* entry; const Type* typeClass; - ssize_t offset = getEntry(package, t, e, &mParams, &type, &entry, &typeClass); + ssize_t offset = getEntry(package, t, e, desiredConfig, &type, &entry, &typeClass); if (offset <= 0) { if (offset < 0) { LOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", resID, t, e, ip, (int)offset); - return offset; + rc = offset; + goto out; } continue; } @@ -1951,13 +2029,14 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag TABLE_NOISY(aout << "Resource type data: " << HexDump(type, dtohl(type->header.size)) << endl); - + if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { LOGW("ResTable_item at %d is beyond type chunk data %d", (int)offset, dtohl(type->header.size)); - return BAD_TYPE; + rc = BAD_TYPE; + goto out; } - + const Res_value* item = (const Res_value*)(((const uint8_t*)type) + offset); ResTable_config thisConfig; @@ -1999,10 +2078,16 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag outValue->data, &len)).string() : "", outValue->data)); - return bestPackage->header->index; + rc = bestPackage->header->index; + goto out; + } + +out: + if (overrideConfig != NULL) { + free(overrideConfig); } - return BAD_VALUE; + return rc; } ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, @@ -2015,7 +2100,7 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, if (outLastRef) *outLastRef = value->data; uint32_t lastRef = value->data; uint32_t newFlags = 0; - const ssize_t newIndex = getResource(value->data, value, true, &newFlags, + const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags, outConfig); if (newIndex == BAD_INDEX) { return BAD_INDEX; @@ -2609,6 +2694,24 @@ bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, *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; } @@ -3600,9 +3703,9 @@ void ResTable::getConfigurations(Vector<ResTable_config>* configs) const void ResTable::getLocales(Vector<String8>* locales) const { Vector<ResTable_config> configs; - LOGD("calling getConfigurations"); + LOGV("calling getConfigurations"); getConfigurations(&configs); - LOGD("called getConfigurations size=%d", (int)configs.size()); + LOGV("called getConfigurations size=%d", (int)configs.size()); const size_t I = configs.size(); for (size_t i=0; i<I; i++) { char locale[6]; @@ -4159,13 +4262,16 @@ void ResTable::print(bool inclValues) const | (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++) { @@ -4372,11 +4478,14 @@ void ResTable::print(bool inclValues) const | (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; @@ -4434,18 +4543,19 @@ void ResTable::print(bool inclValues) const 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/libs/utils/StreamingZipInflater.cpp b/libs/utils/StreamingZipInflater.cpp index 1f62ac51896e..5a162cc49e9b 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/utils/StreamingZipInflater.cpp @@ -31,7 +31,7 @@ using namespace android; /* * Streaming access to compressed asset data in an open fd */ -StreamingZipInflater::StreamingZipInflater(int fd, off_t compDataStart, +StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize) { mFd = fd; mDataMap = NULL; @@ -210,7 +210,7 @@ int StreamingZipInflater::readNextChunk() { // seeking backwards requires uncompressing fom the beginning, so is very // expensive. seeking forwards only requires uncompressing from the current // position to the destination. -off_t StreamingZipInflater::seekAbsolute(off_t absoluteInputPosition) { +off64_t StreamingZipInflater::seekAbsolute(off64_t absoluteInputPosition) { if (absoluteInputPosition < mOutCurPosition) { // rewind and reprocess the data from the beginning if (!mStreamNeedsInit) { diff --git a/libs/utils/String16.cpp b/libs/utils/String16.cpp index eab7b2b9262a..4ce166426c11 100644 --- a/libs/utils/String16.cpp +++ b/libs/utils/String16.cpp @@ -18,228 +18,17 @@ #include <utils/Debug.h> #include <utils/Log.h> +#include <utils/Unicode.h> #include <utils/String8.h> #include <utils/TextOutput.h> #include <utils/threads.h> #include <private/utils/Static.h> -#ifdef HAVE_WINSOCK -# undef nhtol -# undef htonl -# undef nhtos -# undef htons - -# ifdef HAVE_LITTLE_ENDIAN -# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) -# define htonl(x) ntohl(x) -# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) -# define htons(x) ntohs(x) -# else -# define ntohl(x) (x) -# define htonl(x) (x) -# define ntohs(x) (x) -# define htons(x) (x) -# endif -#else -# include <netinet/in.h> -#endif - #include <memory.h> #include <stdio.h> #include <ctype.h> -// --------------------------------------------------------------------------- - -int strcmp16(const char16_t *s1, const char16_t *s2) -{ - char16_t ch; - int d = 0; - - while ( 1 ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) -{ - char16_t ch; - int d = 0; - - while ( n-- ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -char16_t *strcpy16(char16_t *dst, const char16_t *src) -{ - char16_t *q = dst; - const char16_t *p = src; - char16_t ch; - - do { - *q++ = ch = *p++; - } while ( ch ); - - return dst; -} - -size_t strlen16(const char16_t *s) -{ - const char16_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - - -char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) -{ - char16_t *q = dst; - const char16_t *p = src; - char ch; - - while (n) { - n--; - *q++ = ch = *p++; - if ( !ch ) - break; - } - - *q = 0; - - return dst; -} - -size_t strnlen16(const char16_t *s, size_t maxlen) -{ - const char16_t *ss = s; - - /* Important: the maxlen test must precede the reference through ss; - since the byte beyond the maximum may segfault */ - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) -{ - const char16_t* e1 = s1+n1; - const char16_t* e2 = s2+n2; - - while (s1 < e1 && s2 < e2) { - const int d = (int)*s1++ - (int)*s2++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)*s2) - : (n1 > n2 - ? ((int)*s1 - 0) - : 0); -} - -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) -{ - const char16_t* e1 = s1H+n1; - const char16_t* e2 = s2N+n2; - - while (s1H < e1 && s2N < e2) { - const char16_t c2 = ntohs(*s2N); - const int d = (int)*s1H++ - (int)c2; - s2N++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)ntohs(*s2N)) - : (n1 > n2 - ? ((int)*s1H - 0) - : 0); -} - -static inline size_t -utf8_char_len(uint8_t ch) -{ - return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; -} - -#define UTF8_SHIFT_AND_MASK(unicode, byte) (unicode)<<=6; (unicode) |= (0x3f & (byte)); - -static inline uint32_t -utf8_to_utf32(const uint8_t *src, size_t length) -{ - uint32_t unicode; - - switch (length) - { - case 1: - return src[0]; - case 2: - unicode = src[0] & 0x1f; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - return unicode; - case 3: - unicode = src[0] & 0x0f; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - UTF8_SHIFT_AND_MASK(unicode, src[2]) - return unicode; - case 4: - unicode = src[0] & 0x07; - UTF8_SHIFT_AND_MASK(unicode, src[1]) - UTF8_SHIFT_AND_MASK(unicode, src[2]) - UTF8_SHIFT_AND_MASK(unicode, src[3]) - return unicode; - default: - return 0xffff; - } - - //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); -} - -void -utf8_to_utf16(const uint8_t *src, size_t srcLen, - char16_t* dst, const size_t dstLen) -{ - const uint8_t* const end = src + srcLen; - const char16_t* const dstEnd = dst + dstLen; - while (src < end && dst < dstEnd) { - size_t len = utf8_char_len(*src); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)src, len); - - // Convert the UTF32 codepoint to one or more UTF16 codepoints - if (codepoint <= 0xFFFF) { - // Single UTF16 character - *dst++ = (char16_t) codepoint; - } else { - // Multiple UTF16 characters with surrogates - codepoint = codepoint - 0x10000; - *dst++ = (char16_t) ((codepoint >> 10) + 0xD800); - *dst++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); - } - - src += len; - } - if (dst < dstEnd) { - *dst = 0; - } -} - -// --------------------------------------------------------------------------- namespace android { @@ -270,37 +59,33 @@ void terminate_string16() // --------------------------------------------------------------------------- -static char16_t* allocFromUTF8(const char* in, size_t len) +static char16_t* allocFromUTF8(const char* u8str, size_t u8len) { - if (len == 0) return getEmptyString(); - - size_t chars = 0; - const char* end = in+len; - const char* p = in; - - while (p < end) { - chars++; - int utf8len = utf8_char_len(*p); - uint32_t codepoint = utf8_to_utf32((const uint8_t*)p, utf8len); - if (codepoint > 0xFFFF) chars++; // this will be a surrogate pair in utf16 - p += utf8len; + if (u8len == 0) return getEmptyString(); + + const uint8_t* u8cur = (const uint8_t*) u8str; + + const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len); + if (u16len < 0) { + return getEmptyString(); } - - size_t bufSize = (chars+1)*sizeof(char16_t); - SharedBuffer* buf = SharedBuffer::alloc(bufSize); + + const uint8_t* const u8end = u8cur + u8len; + + SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1)); if (buf) { - p = in; - char16_t* str = (char16_t*)buf->data(); - - utf8_to_utf16((const uint8_t*)p, len, str, bufSize); + u8cur = (const uint8_t*) u8str; + char16_t* u16str = (char16_t*)buf->data(); + + utf8_to_utf16(u8cur, u8len, u16str); //printf("Created UTF-16 string from UTF-8 \"%s\":", in); //printHexData(1, str, buf->size(), 16, 1); //printf("\n"); - return str; + return u16str; } - + return getEmptyString(); } diff --git a/libs/utils/String8.cpp b/libs/utils/String8.cpp index 1c4f80c1f80d..0bc5aff2216b 100644 --- a/libs/utils/String8.cpp +++ b/libs/utils/String8.cpp @@ -17,6 +17,8 @@ #include <utils/String8.h> #include <utils/Log.h> +#include <utils/Unicode.h> +#include <utils/SharedBuffer.h> #include <utils/String16.h> #include <utils/TextOutput.h> #include <utils/threads.h> @@ -34,94 +36,10 @@ namespace android { -static const char32_t kByteMask = 0x000000BF; -static const char32_t kByteMark = 0x00000080; - -// Surrogates aren't valid for UTF-32 characters, so define some -// constants that will let us screen them out. -static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; -static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; -static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; - -// Mask used to set appropriate bits in first byte of UTF-8 sequence, -// indexed by number of bytes in the sequence. -// 0xxxxxxx -// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 -// 110yyyyx 10xxxxxx -// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 -// 1110yyyy 10yxxxxx 10xxxxxx -// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 -// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx -// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 -static const char32_t kFirstByteMark[] = { - 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 -}; - // Separator used by resource paths. This is not platform dependent contrary // to OS_PATH_SEPARATOR. #define RES_PATH_SEPARATOR '/' -// Return number of utf8 bytes required for the character. -static size_t utf32_to_utf8_bytes(char32_t srcChar) -{ - size_t bytesToWrite; - - // Figure out how many bytes the result will require. - if (srcChar < 0x00000080) - { - bytesToWrite = 1; - } - else if (srcChar < 0x00000800) - { - bytesToWrite = 2; - } - else if (srcChar < 0x00010000) - { - if ((srcChar < kUnicodeSurrogateStart) - || (srcChar > kUnicodeSurrogateEnd)) - { - bytesToWrite = 3; - } - else - { - // Surrogates are invalid UTF-32 characters. - return 0; - } - } - // Max code point for Unicode is 0x0010FFFF. - else if (srcChar <= kUnicodeMaxCodepoint) - { - bytesToWrite = 4; - } - else - { - // Invalid UTF-32 character. - return 0; - } - - return bytesToWrite; -} - -// Write out the source character to <dstP>. - -static void utf32_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) -{ - dstP += bytes; - switch (bytes) - { /* note: everything falls through. */ - case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); - } -} - -// --------------------------------------------------------------------------- - static SharedBuffer* gEmptyStringBuf = NULL; static char* gEmptyString = NULL; @@ -175,62 +93,47 @@ static char* allocFromUTF8(const char* in, size_t len) return getEmptyString(); } -template<typename T, typename L> -static char* allocFromUTF16OrUTF32(const T* in, L len) +static char* allocFromUTF16(const char16_t* in, size_t len) { if (len == 0) return getEmptyString(); - size_t bytes = 0; - const T* end = in+len; - const T* p = in; - - while (p < end) { - bytes += utf32_to_utf8_bytes(*p); - p++; + const ssize_t bytes = utf16_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); } SharedBuffer* buf = SharedBuffer::alloc(bytes+1); LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - p = in; - char* str = (char*)buf->data(); - char* d = str; - while (p < end) { - const T c = *p++; - size_t len = utf32_to_utf8_bytes(c); - utf32_to_utf8((uint8_t*)d, c, len); - d += len; - } - *d = 0; - - return str; + if (!buf) { + return getEmptyString(); } - return getEmptyString(); + char* str = (char*)buf->data(); + utf16_to_utf8(in, len, str); + return str; } -static char* allocFromUTF16(const char16_t* in, size_t len) +static char* allocFromUTF32(const char32_t* in, size_t len) { - if (len == 0) return getEmptyString(); + if (len == 0) { + return getEmptyString(); + } - const size_t bytes = utf8_length_from_utf16(in, len); + const ssize_t bytes = utf32_to_utf8_length(in, len); + if (bytes < 0) { + return getEmptyString(); + } SharedBuffer* buf = SharedBuffer::alloc(bytes+1); LOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char* str = (char*)buf->data(); - - utf16_to_utf8(in, len, str, bytes+1); - - return str; + if (!buf) { + return getEmptyString(); } - return getEmptyString(); -} + char* str = (char*) buf->data(); + utf32_to_utf8(in, len, str); -static char* allocFromUTF32(const char32_t* in, size_t len) -{ - return allocFromUTF16OrUTF32<char32_t, size_t>(in, len); + return str; } // --------------------------------------------------------------------------- @@ -292,6 +195,29 @@ String8::~String8() SharedBuffer::bufferFromData(mString)->release(); } +String8 String8::format(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + String8 result(formatV(fmt, args)); + + va_end(args); + return result; +} + +String8 String8::formatV(const char* fmt, va_list args) +{ + String8 result; + result.appendFormatV(fmt, args); + return result; +} + +void String8::clear() { + SharedBuffer::bufferFromData(mString)->release(); + mString = getEmptyString(); +} + void String8::setTo(const String8& other) { SharedBuffer::bufferFromData(other.mString)->acquire(); @@ -374,22 +300,28 @@ status_t String8::append(const char* other, size_t otherLen) status_t String8::appendFormat(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); + va_list args; + va_start(args, fmt); + + status_t result = appendFormatV(fmt, args); + va_end(args); + return result; +} + +status_t String8::appendFormatV(const char* fmt, va_list args) +{ int result = NO_ERROR; - int n = vsnprintf(NULL, 0, fmt, ap); + int n = vsnprintf(NULL, 0, fmt, args); if (n != 0) { size_t oldLength = length(); char* buf = lockBuffer(oldLength + n); if (buf) { - vsnprintf(buf + oldLength, n + 1, fmt, ap); + vsnprintf(buf + oldLength, n + 1, fmt, args); } else { result = NO_MEMORY; } } - - va_end(ap); return result; } @@ -505,17 +437,17 @@ void String8::toUpper(size_t start, size_t length) size_t String8::getUtf32Length() const { - return utf32_length(mString, length()); + return utf8_to_utf32_length(mString, length()); } int32_t String8::getUtf32At(size_t index, size_t *next_index) const { - return utf32_at(mString, length(), index, next_index); + return utf32_from_utf8_at(mString, length(), index, next_index); } -size_t String8::getUtf32(char32_t* dst, size_t dst_len) const +void String8::getUtf32(char32_t* dst) const { - return utf8_to_utf32(mString, length(), dst, dst_len); + utf8_to_utf32(mString, length(), dst); } TextOutput& operator<<(TextOutput& to, const String8& val) @@ -700,241 +632,3 @@ String8& String8::convertToResPath() } }; // namespace android - -// --------------------------------------------------------------------------- - -size_t strlen32(const char32_t *s) -{ - const char32_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - -size_t strnlen32(const char32_t *s, size_t maxlen) -{ - const char32_t *ss = s; - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -size_t utf8_length(const char *src) -{ - const char *cur = src; - size_t ret = 0; - while (*cur != '\0') { - const char first_char = *cur++; - if ((first_char & 0x80) == 0) { // ASCII - ret += 1; - continue; - } - // (UTF-8's character must not be like 10xxxxxx, - // but 110xxxxx, 1110xxxx, ... or 1111110x) - if ((first_char & 0x40) == 0) { - return 0; - } - - int32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = 0; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; - num_to_read < 5 && (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx - return 0; - } - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - // "first_char" must be (110xxxxx - 11110xxx) - if (num_to_read == 5) { - return 0; - } - to_ignore_mask |= mask; - utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); - if (utf32 > android::kUnicodeMaxCodepoint) { - return 0; - } - - ret += num_to_read; - } - return ret; -} - -size_t utf32_length(const char *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char* cur; - const char* end; - size_t num_to_skip; - for (cur = src, end = src + src_len, num_to_skip = 1; - cur < end; - cur += num_to_skip, ret++) { - const char first_char = *cur; - num_to_skip = 1; - if ((first_char & 0x80) == 0) { // ASCII - continue; - } - int32_t mask; - - for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { - } - } - return ret; -} - -size_t utf8_length_from_utf32(const char32_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char32_t *end = src + src_len; - while (src < end) { - ret += android::utf32_to_utf8_bytes(*src++); - } - return ret; -} - -size_t utf8_length_from_utf16(const char16_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char16_t* const end = src + src_len; - while (src < end) { - if ((*src & 0xFC00) == 0xD800 && (src + 1) < end - && (*++src & 0xFC00) == 0xDC00) { - // surrogate pairs are always 4 bytes. - ret += 4; - src++; - } else { - ret += android::utf32_to_utf8_bytes((char32_t) *src++); - } - } - return ret; -} - -static int32_t utf32_at_internal(const char* cur, size_t *num_read) -{ - const char first_char = *cur; - if ((first_char & 0x80) == 0) { // ASCII - *num_read = 1; - return *cur; - } - cur++; - char32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = first_char; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; - (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - to_ignore_mask |= mask; - utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); - - *num_read = num_to_read; - return static_cast<int32_t>(utf32); -} - -int32_t utf32_at(const char *src, size_t src_len, - size_t index, size_t *next_index) -{ - if (index >= src_len) { - return -1; - } - size_t dummy_index; - if (next_index == NULL) { - next_index = &dummy_index; - } - size_t num_read; - int32_t ret = utf32_at_internal(src + index, &num_read); - if (ret >= 0) { - *next_index = index + num_read; - } - - return ret; -} - -size_t utf8_to_utf32(const char* src, size_t src_len, - char32_t* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - - const char* cur = src; - const char* end = src + src_len; - char32_t* cur_utf32 = dst; - const char32_t* end_utf32 = dst + dst_len; - while (cur_utf32 < end_utf32 && cur < end) { - size_t num_read; - *cur_utf32++ = - static_cast<char32_t>(utf32_at_internal(cur, &num_read)); - cur += num_read; - } - if (cur_utf32 < end_utf32) { - *cur_utf32 = 0; - } - return static_cast<size_t>(cur_utf32 - dst); -} - -size_t utf32_to_utf8(const char32_t* src, size_t src_len, - char* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - const char32_t *cur_utf32 = src; - const char32_t *end_utf32 = src + src_len; - char *cur = dst; - const char *end = dst + dst_len; - while (cur_utf32 < end_utf32 && cur < end) { - size_t len = android::utf32_to_utf8_bytes(*cur_utf32); - android::utf32_to_utf8((uint8_t *)cur, *cur_utf32++, len); - cur += len; - } - if (cur < end) { - *cur = '\0'; - } - return cur - dst; -} - -size_t utf16_to_utf8(const char16_t* src, size_t src_len, - char* dst, size_t dst_len) -{ - if (src == NULL || src_len == 0 || dst == NULL || dst_len == 0) { - return 0; - } - const char16_t* cur_utf16 = src; - const char16_t* const end_utf16 = src + src_len; - char *cur = dst; - const char* const end = dst + dst_len; - while (cur_utf16 < end_utf16 && cur < end) { - char32_t utf32; - // surrogate pairs - if ((*cur_utf16 & 0xFC00) == 0xD800 && (cur_utf16 + 1) < end_utf16) { - utf32 = (*cur_utf16++ - 0xD800) << 10; - utf32 |= *cur_utf16++ - 0xDC00; - utf32 += 0x10000; - } else { - utf32 = (char32_t) *cur_utf16++; - } - size_t len = android::utf32_to_utf8_bytes(utf32); - android::utf32_to_utf8((uint8_t*)cur, utf32, len); - cur += len; - } - if (cur < end) { - *cur = '\0'; - } - return cur - dst; -} diff --git a/libs/utils/SystemClock.cpp b/libs/utils/SystemClock.cpp index 2bdc0ce278a4..062e6d76bfb8 100644 --- a/libs/utils/SystemClock.cpp +++ b/libs/utils/SystemClock.cpp @@ -19,7 +19,7 @@ * System clock functions. */ -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS #include <linux/ioctl.h> #include <linux/rtc.h> #include <utils/Atomic.h> @@ -50,7 +50,7 @@ int setCurrentTimeMillis(int64_t millis) return -1; #else struct timeval tv; -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS struct timespec ts; int fd; int res; @@ -66,7 +66,7 @@ int setCurrentTimeMillis(int64_t millis) LOGD("Setting time of day to sec=%d\n", (int) tv.tv_sec); -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS fd = open("/dev/alarm", O_RDWR); if(fd < 0) { LOGW("Unable to open alarm driver: %s\n", strerror(errno)); @@ -106,7 +106,7 @@ int64_t uptimeMillis() */ int64_t elapsedRealtime() { -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS static int s_fd = -1; if (s_fd == -1) { diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index b1bd8284430f..8b5da0e58014 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -675,6 +675,9 @@ Thread::Thread(bool canCallJava) mLock("Thread::mLock"), mStatus(NO_ERROR), mExitPending(false), mRunning(false) +#ifdef HAVE_ANDROID_OS + , mTid(-1) +#endif { } @@ -715,6 +718,7 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack) res = androidCreateRawThreadEtc(_threadLoop, this, name, priority, stack, &mThread); } + // The new thread wakes up at _threadLoop, but immediately blocks on mLock if (res == false) { mStatus = UNKNOWN_ERROR; // something happened! @@ -730,16 +734,24 @@ status_t Thread::run(const char* name, int32_t priority, size_t stack) // here merely indicates successfully starting the thread and does not // imply successful termination/execution. return NO_ERROR; + + // Exiting scope of mLock is a memory barrier and allows new thread to run } int Thread::_threadLoop(void* user) { Thread* const self = static_cast<Thread*>(user); + + // force a memory barrier before reading any fields, in particular mHoldSelf + { + Mutex::Autolock _l(self->mLock); + } + sp<Thread> strong(self->mHoldSelf); wp<Thread> weak(strong); self->mHoldSelf.clear(); -#if HAVE_ANDROID_OS +#ifdef HAVE_ANDROID_OS // this is very useful for debugging with gdb self->mTid = gettid(); #endif @@ -753,7 +765,7 @@ int Thread::_threadLoop(void* user) self->mStatus = self->readyToRun(); result = (self->mStatus == NO_ERROR); - if (result && !self->mExitPending) { + if (result && !self->exitPending()) { // Binder threads (and maybe others) rely on threadLoop // running at least once after a successful ::readyToRun() // (unless, of course, the thread has already been asked to exit @@ -770,18 +782,21 @@ int Thread::_threadLoop(void* user) result = self->threadLoop(); } + // establish a scope for mLock + { + Mutex::Autolock _l(self->mLock); if (result == false || self->mExitPending) { self->mExitPending = true; - self->mLock.lock(); self->mRunning = false; // clear thread ID so that requestExitAndWait() does not exit if // called by a new thread using the same thread ID as this one. self->mThread = thread_id_t(-1); + // note that interested observers blocked in requestExitAndWait are + // awoken by broadcast, but blocked on mLock until break exits scope self->mThreadExitedCondition.broadcast(); - self->mThread = thread_id_t(-1); // thread id could be reused - self->mLock.unlock(); break; } + } // Release our strong reference, to let a chance to the thread // to die a peaceful death. @@ -795,6 +810,7 @@ int Thread::_threadLoop(void* user) void Thread::requestExit() { + Mutex::Autolock _l(mLock); mExitPending = true; } @@ -815,6 +831,8 @@ status_t Thread::requestExitAndWait() while (mRunning == true) { mThreadExitedCondition.wait(mLock); } + // This next line is probably not needed any more, but is being left for + // historical reference. Note that each interested party will clear flag. mExitPending = false; return mStatus; @@ -822,6 +840,7 @@ status_t Thread::requestExitAndWait() bool Thread::exitPending() const { + Mutex::Autolock _l(mLock); return mExitPending; } diff --git a/libs/utils/Tokenizer.cpp b/libs/utils/Tokenizer.cpp new file mode 100644 index 000000000000..b3445b797f75 --- /dev/null +++ b/libs/utils/Tokenizer.cpp @@ -0,0 +1,163 @@ +/* + * 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. + */ + +#define LOG_TAG "Tokenizer" + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <utils/Log.h> +#include <utils/Tokenizer.h> + +// Enables debug output for the tokenizer. +#define DEBUG_TOKENIZER 0 + + +namespace android { + +static inline bool isDelimiter(char ch, const char* delimiters) { + return strchr(delimiters, ch) != NULL; +} + +Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length) : + mFilename(filename), mFileMap(fileMap), + mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) { +} + +Tokenizer::~Tokenizer() { + if (mFileMap) { + mFileMap->release(); + } else { + delete[] mBuffer; + } +} + +status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { + *outTokenizer = NULL; + + int result = NO_ERROR; + int fd = ::open(filename.string(), O_RDONLY); + if (fd < 0) { + result = -errno; + LOGE("Error opening file '%s', %s.", filename.string(), strerror(errno)); + } else { + struct stat stat; + if (fstat(fd, &stat)) { + result = -errno; + LOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno)); + } else { + size_t length = size_t(stat.st_size); + + FileMap* fileMap = new FileMap(); + char* buffer; + if (fileMap->create(NULL, fd, 0, length, true)) { + fileMap->advise(FileMap::SEQUENTIAL); + buffer = static_cast<char*>(fileMap->getDataPtr()); + } else { + fileMap->release(); + fileMap = NULL; + + // Fall back to reading into a buffer since we can't mmap files in sysfs. + // The length we obtained from stat is wrong too (it will always be 4096) + // so we must trust that read will read the entire file. + buffer = new char[length]; + ssize_t nrd = read(fd, buffer, length); + if (nrd < 0) { + result = -errno; + LOGE("Error reading file '%s', %s.", filename.string(), strerror(errno)); + delete[] buffer; + buffer = NULL; + } else { + length = size_t(nrd); + } + } + + if (!result) { + *outTokenizer = new Tokenizer(filename, fileMap, buffer, length); + } + } + close(fd); + } + return result; +} + +String8 Tokenizer::getLocation() const { + String8 result; + result.appendFormat("%s:%d", mFilename.string(), mLineNumber); + return result; +} + +String8 Tokenizer::peekRemainderOfLine() const { + const char* end = getEnd(); + const char* eol = mCurrent; + while (eol != end) { + char ch = *eol; + if (ch == '\n') { + break; + } + eol += 1; + } + return String8(mCurrent, eol - mCurrent); +} + +String8 Tokenizer::nextToken(const char* delimiters) { +#if DEBUG_TOKENIZER + LOGD("nextToken"); +#endif + const char* end = getEnd(); + const char* tokenStart = mCurrent; + while (mCurrent != end) { + char ch = *mCurrent; + if (ch == '\n' || isDelimiter(ch, delimiters)) { + break; + } + mCurrent += 1; + } + return String8(tokenStart, mCurrent - tokenStart); +} + +void Tokenizer::nextLine() { +#if DEBUG_TOKENIZER + LOGD("nextLine"); +#endif + const char* end = getEnd(); + while (mCurrent != end) { + char ch = *(mCurrent++); + if (ch == '\n') { + mLineNumber += 1; + break; + } + } +} + +void Tokenizer::skipDelimiters(const char* delimiters) { +#if DEBUG_TOKENIZER + LOGD("skipDelimiters"); +#endif + const char* end = getEnd(); + while (mCurrent != end) { + char ch = *mCurrent; + if (ch == '\n' || !isDelimiter(ch, delimiters)) { + break; + } + mCurrent += 1; + } +} + +} // namespace android diff --git a/libs/utils/Unicode.cpp b/libs/utils/Unicode.cpp new file mode 100644 index 000000000000..78c61b4fc632 --- /dev/null +++ b/libs/utils/Unicode.cpp @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2005 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 <utils/Unicode.h> + +#include <stddef.h> + +#ifdef HAVE_WINSOCK +# undef nhtol +# undef htonl +# undef nhtos +# undef htons + +# ifdef HAVE_LITTLE_ENDIAN +# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) +# define htonl(x) ntohl(x) +# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) +# define htons(x) ntohs(x) +# else +# define ntohl(x) (x) +# define htonl(x) (x) +# define ntohs(x) (x) +# define htons(x) (x) +# endif +#else +# include <netinet/in.h> +#endif + +extern "C" { + +static const char32_t kByteMask = 0x000000BF; +static const char32_t kByteMark = 0x00000080; + +// Surrogates aren't valid for UTF-32 characters, so define some +// constants that will let us screen them out. +static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; +static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; +static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; +static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; +static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; +static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; +static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; + +// Mask used to set appropriate bits in first byte of UTF-8 sequence, +// indexed by number of bytes in the sequence. +// 0xxxxxxx +// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 +// 110yyyyx 10xxxxxx +// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 +// 1110yyyy 10yxxxxx 10xxxxxx +// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 +// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx +// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 +static const char32_t kFirstByteMark[] = { + 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 +}; + +// -------------------------------------------------------------------------- +// UTF-32 +// -------------------------------------------------------------------------- + +/** + * Return number of UTF-8 bytes required for the character. If the character + * is invalid, return size of 0. + */ +static inline size_t utf32_codepoint_utf8_length(char32_t srcChar) +{ + // Figure out how many bytes the result will require. + if (srcChar < 0x00000080) { + return 1; + } else if (srcChar < 0x00000800) { + return 2; + } else if (srcChar < 0x00010000) { + if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) { + return 3; + } else { + // Surrogates are invalid UTF-32 characters. + return 0; + } + } + // Max code point for Unicode is 0x0010FFFF. + else if (srcChar <= kUnicodeMaxCodepoint) { + return 4; + } else { + // Invalid UTF-32 character. + return 0; + } +} + +// Write out the source character to <dstP>. + +static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) +{ + dstP += bytes; + switch (bytes) + { /* note: everything falls through. */ + case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; + case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); + } +} + +size_t strlen32(const char32_t *s) +{ + const char32_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + +size_t strnlen32(const char32_t *s, size_t maxlen) +{ + const char32_t *ss = s; + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +static inline int32_t utf32_at_internal(const char* cur, size_t *num_read) +{ + const char first_char = *cur; + if ((first_char & 0x80) == 0) { // ASCII + *num_read = 1; + return *cur; + } + cur++; + char32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = first_char; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; + (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + to_ignore_mask |= mask; + utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); + + *num_read = num_to_read; + return static_cast<int32_t>(utf32); +} + +int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index) +{ + if (index >= src_len) { + return -1; + } + size_t dummy_index; + if (next_index == NULL) { + next_index = &dummy_index; + } + size_t num_read; + int32_t ret = utf32_at_internal(src + index, &num_read); + if (ret >= 0) { + *next_index = index + num_read; + } + + return ret; +} + +ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char32_t *end = src + src_len; + while (src < end) { + ret += utf32_codepoint_utf8_length(*src++); + } + return ret; +} + +void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char32_t *cur_utf32 = src; + const char32_t *end_utf32 = src + src_len; + char *cur = dst; + while (cur_utf32 < end_utf32) { + size_t len = utf32_codepoint_utf8_length(*cur_utf32); + utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-16 +// -------------------------------------------------------------------------- + +int strcmp16(const char16_t *s1, const char16_t *s2) +{ + char16_t ch; + int d = 0; + + while ( 1 ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) +{ + char16_t ch; + int d = 0; + + while ( n-- ) { + d = (int)(ch = *s1++) - (int)*s2++; + if ( d || !ch ) + break; + } + + return d; +} + +char16_t *strcpy16(char16_t *dst, const char16_t *src) +{ + char16_t *q = dst; + const char16_t *p = src; + char16_t ch; + + do { + *q++ = ch = *p++; + } while ( ch ); + + return dst; +} + +size_t strlen16(const char16_t *s) +{ + const char16_t *ss = s; + while ( *ss ) + ss++; + return ss-s; +} + + +char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) +{ + char16_t *q = dst; + const char16_t *p = src; + char ch; + + while (n) { + n--; + *q++ = ch = *p++; + if ( !ch ) + break; + } + + *q = 0; + + return dst; +} + +size_t strnlen16(const char16_t *s, size_t maxlen) +{ + const char16_t *ss = s; + + /* Important: the maxlen test must precede the reference through ss; + since the byte beyond the maximum may segfault */ + while ((maxlen > 0) && *ss) { + ss++; + maxlen--; + } + return ss-s; +} + +int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) +{ + const char16_t* e1 = s1+n1; + const char16_t* e2 = s2+n2; + + while (s1 < e1 && s2 < e2) { + const int d = (int)*s1++ - (int)*s2++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)*s2) + : (n1 > n2 + ? ((int)*s1 - 0) + : 0); +} + +int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) +{ + const char16_t* e1 = s1H+n1; + const char16_t* e2 = s2N+n2; + + while (s1H < e1 && s2N < e2) { + const char16_t c2 = ntohs(*s2N); + const int d = (int)*s1H++ - (int)c2; + s2N++; + if (d) { + return d; + } + } + + return n1 < n2 + ? (0 - (int)ntohs(*s2N)) + : (n1 > n2 + ? ((int)*s1H - 0) + : 0); +} + +void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char16_t* cur_utf16 = src; + const char16_t* const end_utf16 = src + src_len; + char *cur = dst; + while (cur_utf16 < end_utf16) { + char32_t utf32; + // surrogate pairs + if ((*cur_utf16 & 0xFC00) == 0xD800) { + utf32 = (*cur_utf16++ - 0xD800) << 10; + utf32 |= *cur_utf16++ - 0xDC00; + utf32 += 0x10000; + } else { + utf32 = (char32_t) *cur_utf16++; + } + const size_t len = utf32_codepoint_utf8_length(utf32); + utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len); + cur += len; + } + *cur = '\0'; +} + +// -------------------------------------------------------------------------- +// UTF-8 +// -------------------------------------------------------------------------- + +ssize_t utf8_length(const char *src) +{ + const char *cur = src; + size_t ret = 0; + while (*cur != '\0') { + const char first_char = *cur++; + if ((first_char & 0x80) == 0) { // ASCII + ret += 1; + continue; + } + // (UTF-8's character must not be like 10xxxxxx, + // but 110xxxxx, 1110xxxx, ... or 1111110x) + if ((first_char & 0x40) == 0) { + return -1; + } + + int32_t mask, to_ignore_mask; + size_t num_to_read = 0; + char32_t utf32 = 0; + for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; + num_to_read < 5 && (first_char & mask); + num_to_read++, to_ignore_mask |= mask, mask >>= 1) { + if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx + return -1; + } + // 0x3F == 00111111 + utf32 = (utf32 << 6) + (*cur++ & 0x3F); + } + // "first_char" must be (110xxxxx - 11110xxx) + if (num_to_read == 5) { + return -1; + } + to_ignore_mask |= mask; + utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); + if (utf32 > kUnicodeMaxCodepoint) { + return -1; + } + + ret += num_to_read; + } + return ret; +} + +ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return -1; + } + + size_t ret = 0; + const char16_t* const end = src + src_len; + while (src < end) { + if ((*src & 0xFC00) == 0xD800 && (src + 1) < end + && (*++src & 0xFC00) == 0xDC00) { + // surrogate pairs are always 4 bytes. + ret += 4; + src++; + } else { + ret += utf32_codepoint_utf8_length((char32_t) *src++); + } + } + return ret; +} + +/** + * Returns 1-4 based on the number of leading bits. + * + * 1111 -> 4 + * 1110 -> 3 + * 110x -> 2 + * 10xx -> 1 + * 0xxx -> 1 + */ +static inline size_t utf8_codepoint_len(uint8_t ch) +{ + return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; +} + +static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte) +{ + *codePoint <<= 6; + *codePoint |= 0x3F & byte; +} + +size_t utf8_to_utf32_length(const char *src, size_t src_len) +{ + if (src == NULL || src_len == 0) { + return 0; + } + size_t ret = 0; + const char* cur; + const char* end; + size_t num_to_skip; + for (cur = src, end = src + src_len, num_to_skip = 1; + cur < end; + cur += num_to_skip, ret++) { + const char first_char = *cur; + num_to_skip = 1; + if ((first_char & 0x80) == 0) { // ASCII + continue; + } + int32_t mask; + + for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { + } + } + return ret; +} + +void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst) +{ + if (src == NULL || src_len == 0 || dst == NULL) { + return; + } + + const char* cur = src; + const char* const end = src + src_len; + char32_t* cur_utf32 = dst; + while (cur < end) { + size_t num_read; + *cur_utf32++ = static_cast<char32_t>(utf32_at_internal(cur, &num_read)); + cur += num_read; + } + *cur_utf32 = 0; +} + +static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length) +{ + uint32_t unicode; + + switch (length) + { + case 1: + return src[0]; + case 2: + unicode = src[0] & 0x1f; + utf8_shift_and_mask(&unicode, src[1]); + return unicode; + case 3: + unicode = src[0] & 0x0f; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + return unicode; + case 4: + unicode = src[0] & 0x07; + utf8_shift_and_mask(&unicode, src[1]); + utf8_shift_and_mask(&unicode, src[2]); + utf8_shift_and_mask(&unicode, src[3]); + return unicode; + default: + return 0xffff; + } + + //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); +} + +ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + + /* Validate that the UTF-8 is the correct len */ + size_t u16measuredLen = 0; + while (u8cur < u8end) { + u16measuredLen++; + int u8charLen = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen); + if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16 + u8cur += u8charLen; + } + + /** + * Make sure that we ended where we thought we would and the output UTF-16 + * will be exactly how long we were told it would be. + */ + if (u8cur != u8end) { + return -1; + } + + return u16measuredLen; +} + +/** + * Convert a UTF-8 string to UTF-16. The destination UTF-16 buffer must have + * space for NULL at the end. + */ +void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) +{ + const uint8_t* const u8end = u8str + u8len; + const uint8_t* u8cur = u8str; + char16_t* u16cur = u16str; + + while (u8cur < u8end) { + size_t u8len = utf8_codepoint_len(*u8cur); + uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len); + + // Convert the UTF32 codepoint to one or more UTF16 codepoints + if (codepoint <= 0xFFFF) { + // Single UTF16 character + *u16cur++ = (char16_t) codepoint; + } else { + // Multiple UTF16 characters with surrogates + codepoint = codepoint - 0x10000; + *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); + *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); + } + + u8cur += u8len; + } + *u16cur = 0; +} + +} diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index 16b219cad4a2..55dfd9f873b8 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -40,7 +40,7 @@ ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) { ZipFileRO* zip = (ZipFileRO*)zipToken; ZipEntryRO entry = (ZipEntryRO)entryToken; return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 42611964b9f5..b18c383aeeaf 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -146,7 +146,7 @@ status_t ZipFileRO::open(const char* zipFileName) return NAME_NOT_FOUND; } - mFileLength = lseek(fd, 0, SEEK_END); + mFileLength = lseek64(fd, 0, SEEK_END); if (mFileLength < kEOCDLen) { TEMP_FAILURE_RETRY(close(fd)); return UNKNOWN_ERROR; @@ -202,7 +202,7 @@ bool ZipFileRO::mapCentralDirectory(void) /* * Make sure this is a Zip archive. */ - if (lseek(mFd, 0, SEEK_SET) != 0) { + if (lseek64(mFd, 0, SEEK_SET) != 0) { LOGW("seek to start failed: %s", strerror(errno)); free(scanBuf); return false; @@ -240,9 +240,9 @@ bool ZipFileRO::mapCentralDirectory(void) * * We start by pulling in the last part of the file. */ - off_t searchStart = mFileLength - readAmount; + off64_t searchStart = mFileLength - readAmount; - if (lseek(mFd, searchStart, SEEK_SET) != searchStart) { + if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) { LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); free(scanBuf); return false; @@ -274,7 +274,7 @@ bool ZipFileRO::mapCentralDirectory(void) return false; } - off_t eocdOffset = searchStart + i; + off64_t eocdOffset = searchStart + i; const unsigned char* eocdPtr = scanBuf + i; assert(eocdOffset < mFileLength); @@ -473,7 +473,7 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const * appear to be bogus. */ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, - size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const + size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const { bool ret = false; @@ -489,7 +489,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, * so we can just subtract back from that. */ const unsigned char* ptr = (const unsigned char*) hashEntry.name; - off_t cdOffset = mDirectoryOffset; + off64_t cdOffset = mDirectoryOffset; ptr -= kCDELen; @@ -536,12 +536,12 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, #ifdef HAVE_PREAD /* * This file descriptor might be from zygote's preloaded assets, - * so we need to do an pread() instead of a lseek() + read() to + * so we need to do an pread64() instead of a lseek64() + read() to * guarantee atomicity across the processes with the shared file * descriptors. */ ssize_t actual = - TEMP_FAILURE_RETRY(pread(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); + TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset)); if (actual != sizeof(lfhBuf)) { LOGW("failed reading lfh from offset %ld\n", localHdrOffset); @@ -556,17 +556,17 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } #else /* HAVE_PREAD */ /* - * For hosts don't have pread() we cannot guarantee atomic reads from + * For hosts don't have pread64() we cannot guarantee atomic reads from * an offset in a file. Android should never run on those platforms. * File descriptors inherited from a fork() share file offsets and * there would be nothing to protect from two different processes - * calling lseek() concurrently. + * calling lseek64() concurrently. */ { AutoMutex _l(mFdLock); - if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); return false; } @@ -579,7 +579,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } if (get4LE(lfhBuf) != kLFHSignature) { - off_t actualOffset = lseek(mFd, 0, SEEK_CUR); + off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR); LOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; " "got: offset=" ZD " data=0x%08lx\n", localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf)); @@ -588,7 +588,7 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } #endif /* HAVE_PREAD */ - off_t dataOffset = localHdrOffset + kLFHLen + off64_t dataOffset = localHdrOffset + kLFHLen + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); if (dataOffset >= cdOffset) { LOGW("bad data offset %ld in zip\n", (long) dataOffset); @@ -596,14 +596,14 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } /* check lengths */ - if ((off_t)(dataOffset + compLen) > cdOffset) { + if ((off64_t)(dataOffset + compLen) > cdOffset) { LOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n", (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset); return false; } if (method == kCompressStored && - (off_t)(dataOffset + uncompLen) > cdOffset) + (off64_t)(dataOffset + uncompLen) > cdOffset) { LOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n", (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset); @@ -649,7 +649,7 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const FileMap* newMap; size_t compLen; - off_t offset; + off64_t offset; if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) return NULL; @@ -679,7 +679,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const int method; size_t uncompLen, compLen; - off_t offset; + off64_t offset; const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); @@ -739,7 +739,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const int method; size_t uncompLen, compLen; - off_t offset; + off64_t offset; const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 00077eecf369..72d48769a589 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -8,7 +8,8 @@ ifneq ($(TARGET_SIMULATOR),true) test_src_files := \ ObbFile_test.cpp \ Looper_test.cpp \ - String8_test.cpp + String8_test.cpp \ + Unicode_test.cpp shared_libraries := \ libz \ diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp index cea1313a4986..8bf2ba29722b 100644 --- a/libs/utils/tests/Looper_test.cpp +++ b/libs/utils/tests/Looper_test.cpp @@ -16,6 +16,13 @@ namespace android { +enum { + MSG_TEST1 = 1, + MSG_TEST2 = 2, + MSG_TEST3 = 3, + MSG_TEST4 = 4, +}; + class DelayedWake : public DelayedTask { sp<Looper> mLooper; @@ -82,6 +89,15 @@ protected: } }; +class StubMessageHandler : public MessageHandler { +public: + Vector<Message> messages; + + virtual void handleMessage(const Message& message) { + messages.push(message); + } +}; + class LooperTest : public testing::Test { protected: sp<Looper> mLooper; @@ -421,5 +437,257 @@ TEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInv << "replacement handler callback should be invoked"; } +TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) { + sp<StubMessageHandler> handler1 = new StubMessageHandler(); + sp<StubMessageHandler> handler2 = new StubMessageHandler(); + mLooper->sendMessage(handler1, Message(MSG_TEST1)); + mLooper->sendMessage(handler2, Message(MSG_TEST2)); + mLooper->sendMessage(handler1, Message(MSG_TEST3)); + mLooper->sendMessage(handler1, Message(MSG_TEST4)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(3), handler1->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler1->messages[0].what) + << "handled message"; + EXPECT_EQ(MSG_TEST3, handler1->messages[1].what) + << "handled message"; + EXPECT_EQ(MSG_TEST4, handler1->messages[2].what) + << "handled message"; + EXPECT_EQ(size_t(1), handler2->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST2, handler2->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "first poll should end quickly because next message timeout was computed"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no message handled yet"; + + result = mLooper->pollOnce(1000); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "second poll should end around the time of the delayed message dispatch"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + + result = mLooper->pollOnce(100); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) + << "third poll should timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(1000); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "first poll should end quickly because next message timeout was computed"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no message handled yet"; + + result = mLooper->pollOnce(1000); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; + EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) + << "second poll should end around the time of the delayed message dispatch"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + + result = mLooper->pollOnce(100); + elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS) + << "third poll should timeout"; + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1)); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(100); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was already sent"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent"; + EXPECT_EQ(size_t(1), handler->messages.size()) + << "handled message"; + EXPECT_EQ(MSG_TEST1, handler->messages[0].what) + << "handled message"; +} + +TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + mLooper->sendMessage(handler, Message(MSG_TEST2)); + mLooper->sendMessage(handler, Message(MSG_TEST3)); + mLooper->removeMessages(handler); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was sent so looper was awoken"; + EXPECT_EQ(ALOOPER_POLL_WAKE, result) + << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no messages to handle"; + + result = mLooper->pollOnce(0); + + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; + EXPECT_EQ(size_t(0), handler->messages.size()) + << "no messages to handle"; +} + +TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) { + sp<StubMessageHandler> handler = new StubMessageHandler(); + mLooper->sendMessage(handler, Message(MSG_TEST1)); + mLooper->sendMessage(handler, Message(MSG_TEST2)); + mLooper->sendMessage(handler, Message(MSG_TEST3)); + mLooper->sendMessage(handler, Message(MSG_TEST4)); + mLooper->removeMessages(handler, MSG_TEST3); + mLooper->removeMessages(handler, MSG_TEST1); + + StopWatch stopWatch("pollOnce"); + int result = mLooper->pollOnce(0); + int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); + + EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) + << "elapsed time should approx. zero because message was sent so looper was awoken"; + EXPECT_EQ(ALOOPER_POLL_CALLBACK, result) + << "pollOnce result should be ALOOPER_POLL_CALLBACK because two messages were sent"; + EXPECT_EQ(size_t(2), handler->messages.size()) + << "no messages to handle"; + EXPECT_EQ(MSG_TEST2, handler->messages[0].what) + << "handled message"; + EXPECT_EQ(MSG_TEST4, handler->messages[1].what) + << "handled message"; + + result = mLooper->pollOnce(0); + + EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result) + << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do"; + EXPECT_EQ(size_t(2), handler->messages.size()) + << "no more messages to handle"; +} } // namespace android diff --git a/libs/utils/tests/Unicode_test.cpp b/libs/utils/tests/Unicode_test.cpp new file mode 100644 index 000000000000..18c130c55474 --- /dev/null +++ b/libs/utils/tests/Unicode_test.cpp @@ -0,0 +1,115 @@ +/* + * 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. + */ + +#define LOG_TAG "Unicode_test" +#include <utils/Log.h> +#include <utils/Unicode.h> + +#include <gtest/gtest.h> + +namespace android { + +class UnicodeTest : public testing::Test { +protected: + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(UnicodeTest, UTF8toUTF16ZeroLength) { + ssize_t measured; + + const uint8_t str[] = { }; + + measured = utf8_to_utf16_length(str, 0); + EXPECT_EQ(0, measured) + << "Zero length input should return zero length output."; +} + +TEST_F(UnicodeTest, UTF8toUTF16ASCIILength) { + ssize_t measured; + + // U+0030 or ASCII '0' + const uint8_t str[] = { 0x30 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(1, measured) + << "ASCII glyphs should have a length of 1 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16Plane1Length) { + ssize_t measured; + + // U+2323 SMILE + const uint8_t str[] = { 0xE2, 0x8C, 0xA3 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(1, measured) + << "Plane 1 glyphs should have a length of 1 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16SurrogateLength) { + ssize_t measured; + + // U+10000 + const uint8_t str[] = { 0xF0, 0x90, 0x80, 0x80 }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(2, measured) + << "Surrogate pairs should have a length of 2 char16_t"; +} + +TEST_F(UnicodeTest, UTF8toUTF16TruncatedUTF8) { + ssize_t measured; + + // Truncated U+2323 SMILE + // U+2323 SMILE + const uint8_t str[] = { 0xE2, 0x8C }; + + measured = utf8_to_utf16_length(str, sizeof(str)); + EXPECT_EQ(-1, measured) + << "Truncated UTF-8 should return -1 to indicate invalid"; +} + +TEST_F(UnicodeTest, UTF8toUTF16Normal) { + const uint8_t str[] = { + 0x30, // U+0030, 1 UTF-16 character + 0xC4, 0x80, // U+0100, 1 UTF-16 character + 0xE2, 0x8C, 0xA3, // U+2323, 1 UTF-16 character + 0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character + }; + + char16_t output[1 + 1 + 1 + 2 + 1]; // Room for NULL + + utf8_to_utf16(str, sizeof(str), output); + + EXPECT_EQ(0x0030, output[0]) + << "should be U+0030"; + EXPECT_EQ(0x0100, output[1]) + << "should be U+0100"; + EXPECT_EQ(0x2323, output[2]) + << "should be U+2323"; + EXPECT_EQ(0xD800, output[3]) + << "should be first half of surrogate U+10000"; + EXPECT_EQ(0xDC00, output[4]) + << "should be second half of surrogate U+10000"; + EXPECT_EQ(NULL, output[5]) + << "should be NULL terminated"; +} + +} |