diff options
Diffstat (limited to 'libs')
61 files changed, 1802 insertions, 773 deletions
diff --git a/libs/binder/CursorWindow.cpp b/libs/binder/CursorWindow.cpp index 60681c4..0733378 100644 --- a/libs/binder/CursorWindow.cpp +++ b/libs/binder/CursorWindow.cpp @@ -40,11 +40,9 @@ CursorWindow::~CursorWindow() { ::close(mAshmemFd); } -status_t CursorWindow::create(const String8& name, size_t size, bool localOnly, - CursorWindow** outCursorWindow) { +status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) { String8 ashmemName("CursorWindow: "); ashmemName.append(name); - ashmemName.append(localOnly ? " (local)" : " (remote)"); status_t result; int ashmemFd = ashmem_create_region(ashmemName.string(), size); diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 5ccf87f..5d34787 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -773,6 +773,7 @@ status_t IPCThreadState::talkWithDriver(bool doReceive) bwr.read_buffer = (long unsigned int)mIn.data(); } else { bwr.read_size = 0; + bwr.read_buffer = 0; } IF_LOG_COMMANDS() { diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk index ed319f5..9767568 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -32,6 +32,10 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= libgui +ifeq ($(TARGET_BOARD_PLATFORM), tegra) + LOCAL_CFLAGS += -DALLOW_DEQUEUE_CURRENT_BUFFER +endif + include $(BUILD_SHARED_LIBRARY) ifeq (,$(ONE_SHOT_MAKEFILE)) diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index eb90147..86bc62a 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -79,7 +79,7 @@ public: } virtual void setTransactionState(const Vector<ComposerState>& state, - int orientation) + int orientation, uint32_t flags) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -90,6 +90,7 @@ public: b->write(data); } data.writeInt32(orientation); + data.writeInt32(flags); remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply); } @@ -204,7 +205,8 @@ status_t BnSurfaceComposer::onTransact( state.add(s); } int orientation = data.readInt32(); - setTransactionState(state, orientation); + uint32_t flags = data.readInt32(); + setTransactionState(state, orientation, flags); } break; case BOOT_FINISHED: { CHECK_INTERFACE(ISurfaceComposer, data, reply); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 5f3d608..4ad6c22 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -92,11 +92,14 @@ class Composer : public Singleton<Composer> mutable Mutex mLock; SortedVector<ComposerState> mStates; int mOrientation; + uint32_t mForceSynchronous; Composer() : Singleton<Composer>(), - mOrientation(ISurfaceComposer::eOrientationUnchanged) { } + mOrientation(ISurfaceComposer::eOrientationUnchanged), + mForceSynchronous(0) + { } - void closeGlobalTransactionImpl(); + void closeGlobalTransactionImpl(bool synchronous); layer_state_t* getLayerStateLocked( const sp<SurfaceComposerClient>& client, SurfaceID id); @@ -123,8 +126,8 @@ public: uint32_t tint); status_t setOrientation(int orientation); - static void closeGlobalTransaction() { - Composer::getInstance().closeGlobalTransactionImpl(); + static void closeGlobalTransaction(bool synchronous) { + Composer::getInstance().closeGlobalTransactionImpl(synchronous); } }; @@ -132,11 +135,12 @@ ANDROID_SINGLETON_STATIC_INSTANCE(Composer); // --------------------------------------------------------------------------- -void Composer::closeGlobalTransactionImpl() { +void Composer::closeGlobalTransactionImpl(bool synchronous) { sp<ISurfaceComposer> sm(getComposerService()); Vector<ComposerState> transaction; int orientation; + uint32_t flags = 0; { // scope for the lock Mutex::Autolock _l(mLock); @@ -145,9 +149,14 @@ void Composer::closeGlobalTransactionImpl() { orientation = mOrientation; mOrientation = ISurfaceComposer::eOrientationUnchanged; + + if (synchronous || mForceSynchronous) { + flags |= ISurfaceComposer::eSynchronous; + } + mForceSynchronous = false; } - sm->setTransactionState(transaction, orientation); + sm->setTransactionState(transaction, orientation, flags); } layer_state_t* Composer::getLayerStateLocked( @@ -188,6 +197,10 @@ status_t Composer::setSize(const sp<SurfaceComposerClient>& client, s->what |= ISurfaceComposer::eSizeChanged; s->w = w; s->h = h; + + // Resizing a surface makes the transaction synchronous. + mForceSynchronous = true; + return NO_ERROR; } @@ -270,6 +283,10 @@ status_t Composer::setFreezeTint(const sp<SurfaceComposerClient>& client, status_t Composer::setOrientation(int orientation) { Mutex::Autolock _l(mLock); mOrientation = orientation; + + // Changing the orientation makes the transaction synchronous. + mForceSynchronous = true; + return NO_ERROR; } @@ -375,8 +392,8 @@ void SurfaceComposerClient::openGlobalTransaction() { // Currently a no-op } -void SurfaceComposerClient::closeGlobalTransaction() { - Composer::closeGlobalTransaction(); +void SurfaceComposerClient::closeGlobalTransaction(bool synchronous) { + Composer::closeGlobalTransaction(synchronous); } // ---------------------------------------------------------------------------- diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index c72a45b..4772189 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -36,8 +36,28 @@ #include <utils/Log.h> #include <utils/String8.h> - -#define ALLOW_DEQUEUE_CURRENT_BUFFER false +// This compile option causes SurfaceTexture to return the buffer that is currently +// attached to the GL texture from dequeueBuffer when no other buffers are +// available. It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do +// implicit cross-process synchronization to prevent the buffer from being +// written to before the buffer has (a) been detached from the GL texture and +// (b) all GL reads from the buffer have completed. +#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER +#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true +#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled" +#else +#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false +#endif + +// This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension +// to synchronize access to the buffers. It will cause dequeueBuffer to stall, +// waiting for the GL reads for the buffer being dequeued to complete before +// allowing the buffer to be dequeued. +#ifdef USE_FENCE_SYNC +#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER +#error "USE_FENCE_SYNC and ALLOW_DEQUEUE_CURRENT_BUFFER are incompatible" +#endif +#endif // Macros for including the SurfaceTexture name in log messages #define ST_LOGV(x, ...) LOGV("[%s] "x, mName.string(), ##__VA_ARGS__) @@ -95,7 +115,7 @@ static int32_t createProcessUniqueId() { } SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, - GLenum texTarget) : + GLenum texTarget, bool useFenceSync) : mDefaultWidth(1), mDefaultHeight(1), mPixelFormat(PIXEL_FORMAT_RGBA_8888), @@ -112,11 +132,17 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, mAllowSynchronousMode(allowSynchronousMode), mConnectedApi(NO_CONNECTED_API), mAbandoned(false), - mTexTarget(texTarget) { +#ifdef USE_FENCE_SYNC + mUseFenceSync(useFenceSync), +#else + mUseFenceSync(false), +#endif + mTexTarget(texTarget), + mFrameCounter(0) { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); - ST_LOGV("SurfaceTexture::SurfaceTexture"); + ST_LOGV("SurfaceTexture"); sp<ISurfaceComposer> composer(ComposerService::getComposerService()); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); mNextCrop.makeInvalid(); @@ -125,7 +151,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, } SurfaceTexture::~SurfaceTexture() { - ST_LOGV("SurfaceTexture::~SurfaceTexture"); + ST_LOGV("~SurfaceTexture"); freeAllBuffersLocked(); } @@ -169,7 +195,7 @@ status_t SurfaceTexture::setBufferCountServer(int bufferCount) { } status_t SurfaceTexture::setBufferCount(int bufferCount) { - ST_LOGV("SurfaceTexture::setBufferCount"); + ST_LOGV("setBufferCount: count=%d", bufferCount); Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -217,6 +243,7 @@ status_t SurfaceTexture::setBufferCount(int bufferCount) { status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) { + ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h); if (!w || !h) { ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)", w, h); @@ -230,7 +257,7 @@ status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h) } status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) { - ST_LOGV("SurfaceTexture::requestBuffer"); + ST_LOGV("requestBuffer: slot=%d", slot); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!"); @@ -248,189 +275,237 @@ status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) { status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { - ST_LOGV("SurfaceTexture::dequeueBuffer"); + ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); if ((w && !h) || (!w && h)) { ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); return BAD_VALUE; } - Mutex::Autolock lock(mMutex); - status_t returnFlags(OK); + EGLDisplay dpy = EGL_NO_DISPLAY; + EGLSyncKHR fence = EGL_NO_SYNC_KHR; - int found, foundSync; - int dequeuedCount = 0; - bool tryAgain = true; - while (tryAgain) { - if (mAbandoned) { - ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } + { // Scope for the lock + Mutex::Autolock lock(mMutex); - // We need to wait for the FIFO to drain if the number of buffer - // needs to change. - // - // The condition "number of buffers needs to change" is true if - // - the client doesn't care about how many buffers there are - // - AND the actual number of buffer is different from what was - // set in the last setBufferCountServer() - // - OR - - // setBufferCountServer() was set to a value incompatible with - // the synchronization mode (for instance because the sync mode - // changed since) - // - // As long as this condition is true AND the FIFO is not empty, we - // wait on mDequeueCondition. - - const int minBufferCountNeeded = mSynchronousMode ? - MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; - - const bool numberOfBuffersNeedsToChange = !mClientBufferCount && - ((mServerBufferCount != mBufferCount) || - (mServerBufferCount < minBufferCountNeeded)); - - if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { - // wait for the FIFO to drain - mDequeueCondition.wait(mMutex); - // NOTE: we continue here because we need to reevaluate our - // whole state (eg: we could be abandoned or disconnected) - continue; - } + int found = -1; + int foundSync = -1; + int dequeuedCount = 0; + bool tryAgain = true; + while (tryAgain) { + if (mAbandoned) { + ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } - if (numberOfBuffersNeedsToChange) { - // here we're guaranteed that mQueue is empty - freeAllBuffersLocked(); - mBufferCount = mServerBufferCount; - if (mBufferCount < minBufferCountNeeded) - mBufferCount = minBufferCountNeeded; - mCurrentTexture = INVALID_BUFFER_SLOT; - returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; - } + // We need to wait for the FIFO to drain if the number of buffer + // needs to change. + // + // The condition "number of buffers needs to change" is true if + // - the client doesn't care about how many buffers there are + // - AND the actual number of buffer is different from what was + // set in the last setBufferCountServer() + // - OR - + // setBufferCountServer() was set to a value incompatible with + // the synchronization mode (for instance because the sync mode + // changed since) + // + // As long as this condition is true AND the FIFO is not empty, we + // wait on mDequeueCondition. + + const int minBufferCountNeeded = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + + const bool numberOfBuffersNeedsToChange = !mClientBufferCount && + ((mServerBufferCount != mBufferCount) || + (mServerBufferCount < minBufferCountNeeded)); + + if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { + // wait for the FIFO to drain + mDequeueCondition.wait(mMutex); + // NOTE: we continue here because we need to reevaluate our + // whole state (eg: we could be abandoned or disconnected) + continue; + } - // look for a free buffer to give to the client - found = INVALID_BUFFER_SLOT; - foundSync = INVALID_BUFFER_SLOT; - dequeuedCount = 0; - for (int i = 0; i < mBufferCount; i++) { - const int state = mSlots[i].mBufferState; - if (state == BufferSlot::DEQUEUED) { - dequeuedCount++; + if (numberOfBuffersNeedsToChange) { + // here we're guaranteed that mQueue is empty + freeAllBuffersLocked(); + mBufferCount = mServerBufferCount; + if (mBufferCount < minBufferCountNeeded) + mBufferCount = minBufferCountNeeded; + mCurrentTexture = INVALID_BUFFER_SLOT; + returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; } - // if buffer is FREE it CANNOT be current - LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), - "dequeueBuffer: buffer %d is both FREE and current!", i); + // look for a free buffer to give to the client + found = INVALID_BUFFER_SLOT; + foundSync = INVALID_BUFFER_SLOT; + dequeuedCount = 0; + for (int i = 0; i < mBufferCount; i++) { + const int state = mSlots[i].mBufferState; + if (state == BufferSlot::DEQUEUED) { + dequeuedCount++; + } - if (ALLOW_DEQUEUE_CURRENT_BUFFER) { - if (state == BufferSlot::FREE || i == mCurrentTexture) { - foundSync = i; - if (i != mCurrentTexture) { - found = i; - break; + // if buffer is FREE it CANNOT be current + LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), + "dequeueBuffer: buffer %d is both FREE and current!", + i); + + if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { + if (state == BufferSlot::FREE || i == mCurrentTexture) { + foundSync = i; + if (i != mCurrentTexture) { + found = i; + break; + } + } + } else { + if (state == BufferSlot::FREE) { + /* We return the oldest of the free buffers to avoid + * stalling the producer if possible. This is because + * the consumer may still have pending reads of the + * buffers in flight. + */ + bool isOlder = mSlots[i].mFrameNumber < + mSlots[found].mFrameNumber; + if (found < 0 || isOlder) { + foundSync = i; + found = i; + } } } - } else { - if (state == BufferSlot::FREE) { - foundSync = i; - found = i; - break; + } + + // clients are not allowed to dequeue more than one buffer + // if they didn't set a buffer count. + if (!mClientBufferCount && dequeuedCount) { + ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " + "setting the buffer count"); + return -EINVAL; + } + + // See whether a buffer has been queued since the last + // setBufferCount so we know whether to perform the + // MIN_UNDEQUEUED_BUFFERS check below. + bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; + if (bufferHasBeenQueued) { + // make sure the client is not trying to dequeue more buffers + // than allowed. + const int avail = mBufferCount - (dequeuedCount+1); + if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { + ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " + "(dequeued=%d)", + MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), + dequeuedCount); + return -EBUSY; } } - } - // clients are not allowed to dequeue more than one buffer - // if they didn't set a buffer count. - if (!mClientBufferCount && dequeuedCount) { - return -EINVAL; + // we're in synchronous mode and didn't find a buffer, we need to + // wait for some buffers to be consumed + tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); + if (tryAgain) { + mDequeueCondition.wait(mMutex); + } } - // See whether a buffer has been queued since the last setBufferCount so - // we know whether to perform the MIN_UNDEQUEUED_BUFFERS check below. - bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; - if (bufferHasBeenQueued) { - // make sure the client is not trying to dequeue more buffers - // than allowed. - const int avail = mBufferCount - (dequeuedCount+1); - if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { - ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " - "(dequeued=%d)", - MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), - dequeuedCount); - return -EBUSY; - } + if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { + // foundSync guaranteed to be != INVALID_BUFFER_SLOT + found = foundSync; } - // we're in synchronous mode and didn't find a buffer, we need to wait - // for some buffers to be consumed - tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); - if (tryAgain) { - mDequeueCondition.wait(mMutex); + if (found == INVALID_BUFFER_SLOT) { + // This should not happen. + ST_LOGE("dequeueBuffer: no available buffer slots"); + return -EBUSY; } - } - if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { - // foundSync guaranteed to be != INVALID_BUFFER_SLOT - found = foundSync; - } + const int buf = found; + *outBuf = found; - if (found == INVALID_BUFFER_SLOT) { - return -EBUSY; - } + const bool useDefaultSize = !w && !h; + if (useDefaultSize) { + // use the default size + w = mDefaultWidth; + h = mDefaultHeight; + } - const int buf = found; - *outBuf = found; + const bool updateFormat = (format != 0); + if (!updateFormat) { + // keep the current (or default) format + format = mPixelFormat; + } - const bool useDefaultSize = !w && !h; - if (useDefaultSize) { - // use the default size - w = mDefaultWidth; - h = mDefaultHeight; - } + // buffer is now in DEQUEUED (but can also be current at the same time, + // if we're in synchronous mode) + mSlots[buf].mBufferState = BufferSlot::DEQUEUED; + + const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); + if ((buffer == NULL) || + (uint32_t(buffer->width) != w) || + (uint32_t(buffer->height) != h) || + (uint32_t(buffer->format) != format) || + ((uint32_t(buffer->usage) & usage) != usage)) + { + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + status_t error; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer( + w, h, format, usage, &error)); + if (graphicBuffer == 0) { + ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " + "failed"); + return error; + } + if (updateFormat) { + mPixelFormat = format; + } + mSlots[buf].mGraphicBuffer = graphicBuffer; + mSlots[buf].mRequestBufferCalled = false; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; + 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; + } + if (mCurrentTexture == buf) { + // The current texture no longer references the buffer in this slot + // since we just allocated a new buffer. + mCurrentTexture = INVALID_BUFFER_SLOT; + } + returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + } - const bool updateFormat = (format != 0); - if (!updateFormat) { - // keep the current (or default) format - format = mPixelFormat; + dpy = mSlots[buf].mEglDisplay; + fence = mSlots[buf].mFence; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; } - // buffer is now in DEQUEUED (but can also be current at the same time, - // if we're in synchronous mode) - mSlots[buf].mBufferState = BufferSlot::DEQUEUED; - - const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); - if ((buffer == NULL) || - (uint32_t(buffer->width) != w) || - (uint32_t(buffer->height) != h) || - (uint32_t(buffer->format) != format) || - ((uint32_t(buffer->usage) & usage) != usage)) - { - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - status_t error; - sp<GraphicBuffer> graphicBuffer( - mGraphicBufferAlloc->createGraphicBuffer( - w, h, format, usage, &error)); - if (graphicBuffer == 0) { - ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " - "failed"); - return error; - } - if (updateFormat) { - mPixelFormat = format; + if (fence != EGL_NO_SYNC_KHR) { + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + // If something goes wrong, log the error, but return the buffer without + // synchronizing access to it. It's too late at this point to abort the + // dequeue operation. + if (result == EGL_FALSE) { + LOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + LOGE("dequeueBuffer: timeout waiting for fence"); } - mSlots[buf].mGraphicBuffer = graphicBuffer; - mSlots[buf].mRequestBufferCalled = false; - 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; - } - returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + eglDestroySyncKHR(dpy, fence); } + + ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, + mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); + return returnFlags; } status_t SurfaceTexture::setSynchronousMode(bool enabled) { + ST_LOGV("setSynchronousMode: enabled=%d", enabled); Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -462,7 +537,7 @@ status_t SurfaceTexture::setSynchronousMode(bool enabled) { status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp, uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { - ST_LOGV("SurfaceTexture::queueBuffer"); + ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp); sp<FrameAvailableListener> listener; @@ -519,6 +594,9 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp, mSlots[buf].mTransform = mNextTransform; mSlots[buf].mScalingMode = mNextScalingMode; mSlots[buf].mTimestamp = timestamp; + mFrameCounter++; + mSlots[buf].mFrameNumber = mFrameCounter; + mDequeueCondition.signal(); *outWidth = mDefaultWidth; @@ -534,7 +612,7 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp, } void SurfaceTexture::cancelBuffer(int buf) { - ST_LOGV("SurfaceTexture::cancelBuffer"); + ST_LOGV("cancelBuffer: slot=%d", buf); Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -552,11 +630,14 @@ void SurfaceTexture::cancelBuffer(int buf) { return; } mSlots[buf].mBufferState = BufferSlot::FREE; + mSlots[buf].mFrameNumber = 0; mDequeueCondition.signal(); } status_t SurfaceTexture::setCrop(const Rect& crop) { - ST_LOGV("SurfaceTexture::setCrop"); + ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right, + crop.bottom); + Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("setCrop: SurfaceTexture has been abandoned!"); @@ -567,7 +648,7 @@ status_t SurfaceTexture::setCrop(const Rect& crop) { } status_t SurfaceTexture::setTransform(uint32_t transform) { - ST_LOGV("SurfaceTexture::setTransform"); + ST_LOGV("setTransform: xform=%#x", transform); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("setTransform: SurfaceTexture has been abandoned!"); @@ -579,7 +660,7 @@ status_t SurfaceTexture::setTransform(uint32_t transform) { status_t SurfaceTexture::connect(int api, uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { - ST_LOGV("SurfaceTexture::connect(this=%p, %d)", this, api); + ST_LOGV("connect: api=%d", api); Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -612,12 +693,13 @@ status_t SurfaceTexture::connect(int api, } status_t SurfaceTexture::disconnect(int api) { - ST_LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api); + ST_LOGV("disconnect: api=%d", api); Mutex::Autolock lock(mMutex); if (mAbandoned) { - ST_LOGE("disconnect: SurfaceTexture has been abandoned!"); - return NO_INIT; + // it is not really an error to disconnect after the surface + // has been abandoned, it should just be a no-op. + return NO_ERROR; } int err = NO_ERROR; @@ -640,6 +722,7 @@ status_t SurfaceTexture::disconnect(int api) { } break; default: + ST_LOGE("disconnect: unknown API %d", api); err = -EINVAL; break; } @@ -647,13 +730,14 @@ status_t SurfaceTexture::disconnect(int api) { } status_t SurfaceTexture::setScalingMode(int mode) { - ST_LOGV("SurfaceTexture::setScalingMode(%d)", mode); + ST_LOGV("setScalingMode: mode=%d", mode); switch (mode) { case NATIVE_WINDOW_SCALING_MODE_FREEZE: case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: break; default: + ST_LOGE("unknown scaling mode: %d", mode); return BAD_VALUE; } @@ -663,7 +747,7 @@ status_t SurfaceTexture::setScalingMode(int mode) { } status_t SurfaceTexture::updateTexImage() { - ST_LOGV("SurfaceTexture::updateTexImage"); + ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { @@ -679,8 +763,8 @@ status_t SurfaceTexture::updateTexImage() { // Update the GL texture object. EGLImageKHR image = mSlots[buf].mEglImage; + EGLDisplay dpy = eglGetCurrentDisplay(); if (image == EGL_NO_IMAGE_KHR) { - EGLDisplay dpy = eglGetCurrentDisplay(); if (mSlots[buf].mGraphicBuffer == 0) { ST_LOGE("buffer at slot %d is null", buf); return BAD_VALUE; @@ -714,11 +798,31 @@ status_t SurfaceTexture::updateTexImage() { } if (mCurrentTexture != INVALID_BUFFER_SLOT) { + if (mUseFenceSync) { + EGLSyncKHR fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, + NULL); + if (fence == EGL_NO_SYNC_KHR) { + LOGE("updateTexImage: error creating fence: %#x", + eglGetError()); + return -EINVAL; + } + glFlush(); + mSlots[mCurrentTexture].mFence = fence; + } + } + + ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", + mCurrentTexture, + mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, + buf, mSlots[buf].mGraphicBuffer->handle); + + if (mCurrentTexture != INVALID_BUFFER_SLOT) { // The current buffer becomes FREE if it was still in the queued // state. If it has already been given to the client // (synchronous mode), then it stays in DEQUEUED state. - if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) + if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) { mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE; + } } // Update the SurfaceTexture state. @@ -771,7 +875,7 @@ void SurfaceTexture::getTransformMatrix(float mtx[16]) { } void SurfaceTexture::computeCurrentTransformMatrix() { - ST_LOGV("SurfaceTexture::computeCurrentTransformMatrix"); + ST_LOGV("computeCurrentTransformMatrix"); float xform[16]; for (int i = 0; i < 16; i++) { @@ -862,14 +966,14 @@ void SurfaceTexture::computeCurrentTransformMatrix() { } nsecs_t SurfaceTexture::getTimestamp() { - ST_LOGV("SurfaceTexture::getTimestamp"); + ST_LOGV("getTimestamp"); Mutex::Autolock lock(mMutex); return mCurrentTimestamp; } void SurfaceTexture::setFrameAvailableListener( const sp<FrameAvailableListener>& listener) { - ST_LOGV("SurfaceTexture::setFrameAvailableListener"); + ST_LOGV("setFrameAvailableListener"); Mutex::Autolock lock(mMutex); mFrameAvailableListener = listener; } @@ -877,6 +981,7 @@ void SurfaceTexture::setFrameAvailableListener( void SurfaceTexture::freeBufferLocked(int i) { mSlots[i].mGraphicBuffer = 0; mSlots[i].mBufferState = BufferSlot::FREE; + mSlots[i].mFrameNumber = 0; if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; @@ -972,6 +1077,11 @@ uint32_t SurfaceTexture::getCurrentScalingMode() const { return mCurrentScalingMode; } +bool SurfaceTexture::isSynchronousMode() const { + Mutex::Autolock lock(mMutex); + return mSynchronousMode; +} + int SurfaceTexture::query(int what, int* outValue) { Mutex::Autolock lock(mMutex); diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index 98fa171..48070d6 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -36,6 +36,12 @@ SurfaceTextureClient::SurfaceTextureClient() { SurfaceTextureClient::init(); } +SurfaceTextureClient::~SurfaceTextureClient() { + if (mConnectedToCpu) { + SurfaceTextureClient::disconnect(NATIVE_WINDOW_API_CPU); + } +} + void SurfaceTextureClient::init() { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = hook_setSwapInterval; diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index b8bc454..c313904 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -334,7 +334,7 @@ protected: class SurfaceTextureGLTest : public GLTest { protected: - static const GLint TEX_ID = 123; + enum { TEX_ID = 123 }; virtual void SetUp() { GLTest::SetUp(); @@ -396,7 +396,8 @@ protected: 1.0f, 1.0f, }; - glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices); + 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()); @@ -410,13 +411,17 @@ protected: // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as // they're setting the defautls for that target, but when hacking things // to use GL_TEXTURE_2D they are needed to achieve the same behavior. - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, + GL_LINEAR); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, + GL_LINEAR); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); GLfloat texMatrix[16]; @@ -531,6 +536,20 @@ void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { } } +void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r, + uint8_t g, uint8_t b, uint8_t a) { + const size_t PIXEL_SIZE = 4; + for (int y = 0; y < h; y++) { + for (int x = 0; x < h; x++) { + off_t offset = (y * stride + x) * PIXEL_SIZE; + buf[offset + 0] = r; + buf[offset + 1] = g; + buf[offset + 2] = b; + buf[offset + 3] = a; + } + } +} + TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { const int texWidth = 64; const int texHeight = 66; @@ -640,8 +659,8 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { 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()); + 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)); @@ -650,13 +669,15 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { ASSERT_TRUE(anb != NULL); sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + 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, texWidth, texHeight, buf->getStride(), crop); buf->unlock(); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), + buf->getNativeBuffer())); mST->updateTexImage(); @@ -708,7 +729,8 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { class ProducerThread : public Thread { public: - ProducerThread(const sp<ANativeWindow>& anw, const TestPixel* testPixels): + ProducerThread(const sp<ANativeWindow>& anw, + const TestPixel* testPixels): mANW(anw), mTestPixels(testPixels) { } @@ -940,21 +962,173 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) { EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35)); } -TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { +TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { + class ProducerThread : public Thread { + public: + ProducerThread(const sp<ANativeWindow>& anw): + mANW(anw), + mDequeueError(NO_ERROR) { + } + + virtual ~ProducerThread() { + } + + virtual bool threadLoop() { + Mutex::Autolock lock(mMutex); + ANativeWindowBuffer* anb; + + // Frame 1 + if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb) + != NO_ERROR) { + return false; + } + + // Frame 2 + if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb) + != NO_ERROR) { + return false; + } + + // Frame 3 - error expected + mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb); + return false; + } + + status_t getDequeueError() { + Mutex::Autolock lock(mMutex); + return mDequeueError; + } + + private: + sp<ANativeWindow> mANW; + status_t mDequeueError; + Mutex mMutex; + }; + + sp<FrameWaiter> fw(new FrameWaiter); + mST->setFrameAvailableListener(fw); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, mST->setBufferCountServer(2)); + + sp<Thread> pt(new ProducerThread(mANW)); + pt->run(); + + fw->waitForFrame(); + fw->waitForFrame(); + + // Sleep for 100ms to allow the producer thread's dequeueBuffer call to + // block waiting for a buffer to become available. + usleep(100000); + + mST->abandon(); + + pt->requestExitAndWait(); + ASSERT_EQ(NO_INIT, + reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError()); +} + +TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) { + int texHeight = 16; + ANativeWindowBuffer* anb; + + GLint maxTextureSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + + // make sure it works with small textures + mST->setDefaultBufferSize(16, texHeight); + EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + EXPECT_EQ(16, anb->width); + EXPECT_EQ(texHeight, anb->height); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ(NO_ERROR, mST->updateTexImage()); + + // make sure it works with GL_MAX_TEXTURE_SIZE + mST->setDefaultBufferSize(maxTextureSize, texHeight); + EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + EXPECT_EQ(maxTextureSize, anb->width); + EXPECT_EQ(texHeight, anb->height); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); + EXPECT_EQ(NO_ERROR, mST->updateTexImage()); + + // make sure it fails with GL_MAX_TEXTURE_SIZE+1 + mST->setDefaultBufferSize(maxTextureSize+1, texHeight); + EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + EXPECT_EQ(maxTextureSize+1, anb->width); + EXPECT_EQ(texHeight, anb->height); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb)); + ASSERT_NE(NO_ERROR, mST->updateTexImage()); +} + +/* + * This test fixture is for testing GL -> GL texture streaming. It creates an + * EGLSurface and an EGLContext for the image producer to use. + */ +class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest { +protected: + SurfaceTextureGLToGLTest(): + mProducerEglSurface(EGL_NO_SURFACE), + mProducerEglContext(EGL_NO_CONTEXT) { + } + + virtual void SetUp() { + SurfaceTextureGLTest::SetUp(); + + EGLConfig myConfig = {0}; + EGLint numConfigs = 0; + EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig, + 1, &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig, + mANW.get(), NULL); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface); + + mProducerEglContext = eglCreateContext(mEglDisplay, myConfig, + EGL_NO_CONTEXT, getContextAttribs()); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext); + } + + virtual void TearDown() { + if (mProducerEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mProducerEglContext); + } + if (mProducerEglSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEglDisplay, mProducerEglSurface); + } + SurfaceTextureGLTest::TearDown(); + } + + EGLSurface mProducerEglSurface; + EGLContext mProducerEglContext; +}; + +TEST_F(SurfaceTextureGLToGLTest, TexturingFromGLFilledRGBABufferPow2) { const int texWidth = 64; const int texHeight = 64; mST->setDefaultBufferSize(texWidth, texHeight); // Do the producer side of things - EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, - mANW.get(), NULL); + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_SURFACE, stcEglSurface); - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); + // This is needed to ensure we pick up a buffer of the correct size. + eglSwapBuffers(mEglDisplay, mProducerEglSurface); glClearColor(0.6, 0.6, 0.6, 0.6); glClear(GL_COLOR_BUFFER_BIT); @@ -972,7 +1146,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { glClearColor(0.0, 0.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(mEglDisplay, stcEglSurface); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); // Do the consumer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, @@ -981,12 +1155,9 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { glDisable(GL_SCISSOR_TEST); + mST->updateTexImage(); // Skip the first frame, which was empty mST->updateTexImage(); - // We must wait until updateTexImage has been called to destroy the - // EGLSurface because we're in synchronous mode. - eglDestroySurface(mEglDisplay, stcEglSurface); - glClearColor(0.2, 0.2, 0.2, 0.2); glClear(GL_COLOR_BUFFER_BIT); @@ -1016,90 +1187,136 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); } -TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { - class ProducerThread : public Thread { - public: - ProducerThread(const sp<ANativeWindow>& anw): - mANW(anw), - mDequeueError(NO_ERROR) { - } +TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) { + sp<GraphicBuffer> buffers[3]; - virtual ~ProducerThread() { - } + // This test requires async mode to run on a single thread. + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); - virtual bool threadLoop() { - Mutex::Autolock lock(mMutex); - ANativeWindowBuffer* anb; + for (int i = 0; i < 3; i++) { + // Produce a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); - // Frame 1 - if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { - return false; - } - if (anb == NULL) { - return false; - } - if (mANW->queueBuffer(mANW.get(), anb) - != NO_ERROR) { - return false; - } + // Consume a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mST->updateTexImage(); + buffers[i] = mST->getCurrentBuffer(); + } - // Frame 2 - if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { - return false; - } - if (anb == NULL) { - return false; - } - if (mANW->queueBuffer(mANW.get(), anb) - != NO_ERROR) { - return false; - } + // Destroy the GL texture object to release its ref on buffers[2]. + GLuint texID = TEX_ID; + glDeleteTextures(1, &texID); - // Frame 3 - error expected - mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb); - return false; - } + // Destroy the EGLSurface + EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); - status_t getDequeueError() { - Mutex::Autolock lock(mMutex); - return mDequeueError; - } + // Release the ref that the SurfaceTexture has on buffers[2]. + mST->abandon(); - private: - sp<ANativeWindow> mANW; - status_t mDequeueError; - Mutex mMutex; - }; + EXPECT_EQ(1, buffers[0]->getStrongCount()); + EXPECT_EQ(1, buffers[1]->getStrongCount()); - sp<FrameWaiter> fw(new FrameWaiter); - mST->setFrameAvailableListener(fw); - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, mST->setBufferCountServer(2)); + // Depending on how lazily the GL driver dequeues buffers, we may end up + // with either two or three total buffers. If there are three, make sure + // the last one was properly down-ref'd. + if (buffers[2] != buffers[0]) { + EXPECT_EQ(1, buffers[2]->getStrongCount()); + } +} - sp<Thread> pt(new ProducerThread(mANW)); - pt->run(); +TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) { + sp<GraphicBuffer> buffers[3]; - fw->waitForFrame(); - fw->waitForFrame(); + // This test requires async mode to run on a single thread. + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); - // Sleep for 100ms to allow the producer thread's dequeueBuffer call to - // block waiting for a buffer to become available. - usleep(100000); + for (int i = 0; i < 3; i++) { + // Produce a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Consume a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + buffers[i] = mST->getCurrentBuffer(); + } + // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has + // on buffers[2]. mST->abandon(); - pt->requestExitAndWait(); - ASSERT_EQ(NO_INIT, - reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError()); + // Destroy the GL texture object to release its ref on buffers[2]. + GLuint texID = TEX_ID; + glDeleteTextures(1, &texID); + + // Destroy the EGLSurface. + EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EXPECT_EQ(1, buffers[0]->getStrongCount()); + EXPECT_EQ(1, buffers[1]->getStrongCount()); + + // Depending on how lazily the GL driver dequeues buffers, we may end up + // with either two or three total buffers. If there are three, make sure + // the last one was properly down-ref'd. + if (buffers[2] != buffers[0]) { + EXPECT_EQ(1, buffers[2]->getStrongCount()); + } +} + +TEST_F(SurfaceTextureGLToGLTest, EglSurfaceDefaultsToSynchronousMode) { + // This test requires 3 buffers to run on a single thread. + mST->setBufferCountServer(3); + + ASSERT_TRUE(mST->isSynchronousMode()); + + for (int i = 0; i < 10; i++) { + // Produce a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Consume a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + } + + ASSERT_TRUE(mST->isSynchronousMode()); } /* - * This test is for testing GL -> GL texture streaming via SurfaceTexture. It - * contains functionality to create a producer thread that will perform GL - * rendering to an ANativeWindow that feeds frames to a SurfaceTexture. - * Additionally it supports interlocking the producer and consumer threads so - * that a specific sequence of calls can be deterministically created by the - * test. + * This test fixture is for testing GL -> GL texture streaming from one thread + * to another. It contains functionality to create a producer thread that will + * perform GL rendering to an ANativeWindow that feeds frames to a + * SurfaceTexture. Additionally it supports interlocking the producer and + * consumer threads so that a specific sequence of calls can be + * deterministically created by the test. * * The intended usage is as follows: * @@ -1122,7 +1339,7 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { * } * */ -class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest { +class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest { protected: // ProducerThread is an abstract base class to simplify the creation of @@ -1223,30 +1440,8 @@ protected: Condition mFrameFinishCondition; }; - SurfaceTextureGLToGLTest(): - mProducerEglSurface(EGL_NO_SURFACE), - mProducerEglContext(EGL_NO_CONTEXT) { - } - virtual void SetUp() { - SurfaceTextureGLTest::SetUp(); - - EGLConfig myConfig = {0}; - EGLint numConfigs = 0; - EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig, - 1, &numConfigs)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig, - mANW.get(), NULL); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface); - - mProducerEglContext = eglCreateContext(mEglDisplay, myConfig, - EGL_NO_CONTEXT, getContextAttribs()); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext); - + SurfaceTextureGLToGLTest::SetUp(); mFC = new FrameCondition(); mST->setFrameAvailableListener(mFC); } @@ -1255,15 +1450,9 @@ protected: if (mProducerThread != NULL) { mProducerThread->requestExitAndWait(); } - if (mProducerEglContext != EGL_NO_CONTEXT) { - eglDestroyContext(mEglDisplay, mProducerEglContext); - } - if (mProducerEglSurface != EGL_NO_SURFACE) { - eglDestroySurface(mEglDisplay, mProducerEglSurface); - } mProducerThread.clear(); mFC.clear(); - SurfaceTextureGLTest::TearDown(); + SurfaceTextureGLToGLTest::TearDown(); } void runProducerThread(const sp<ProducerThread> producerThread) { @@ -1274,13 +1463,12 @@ protected: producerThread->run(); } - EGLSurface mProducerEglSurface; - EGLContext mProducerEglContext; sp<ProducerThread> mProducerThread; sp<FrameCondition> mFC; }; -TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + UpdateTexImageBeforeFrameFinishedCompletes) { class PT : public ProducerThread { virtual void render() { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); @@ -1298,7 +1486,8 @@ TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) { // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } -TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + UpdateTexImageAfterFrameFinishedCompletes) { class PT : public ProducerThread { virtual void render() { glClearColor(0.0f, 1.0f, 0.0f, 1.0f); @@ -1316,7 +1505,8 @@ TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) { // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! } -TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + RepeatedUpdateTexImageBeforeFrameFinishedCompletes) { enum { NUM_ITERATIONS = 1024 }; class PT : public ProducerThread { @@ -1344,7 +1534,8 @@ TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedComple } } -TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + RepeatedUpdateTexImageAfterFrameFinishedCompletes) { enum { NUM_ITERATIONS = 1024 }; class PT : public ProducerThread { @@ -1373,7 +1564,8 @@ TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedComplet } // XXX: This test is disabled because it is currently hanging on some devices. -TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) { enum { NUM_ITERATIONS = 64 }; class PT : public ProducerThread { @@ -1438,4 +1630,101 @@ TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalled } } +class SurfaceTextureFBOTest : public SurfaceTextureGLTest { +protected: + + virtual void SetUp() { + SurfaceTextureGLTest::SetUp(); + + glGenFramebuffers(1, &mFbo); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + glGenTextures(1, &mFboTex); + glBindTexture(GL_TEXTURE_2D, mFboTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(), + getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, mFboTex, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + } + + virtual void TearDown() { + SurfaceTextureGLTest::TearDown(); + + glDeleteTextures(1, &mFboTex); + glDeleteFramebuffers(1, &mFbo); + } + + GLuint mFbo; + GLuint mFboTex; +}; + +// This test is intended to verify that proper synchronization is done when +// rendering into an FBO. +TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { + const int texWidth = 64; + const int texHeight = 64; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); + 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 green + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255, + 0, 255); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + drawTexture(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + for (int i = 0; i < 4; i++) { + SCOPED_TRACE(String8::format("frame %d", i).string()); + + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + buf = new GraphicBuffer(anb, false); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), + buf->getNativeBuffer())); + + // Fill the buffer with red + ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, + (void**)(&img))); + fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 255, 0, + 0, 255); + ASSERT_EQ(NO_ERROR, buf->unlock()); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), + buf->getNativeBuffer())); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + drawTexture(); + + EXPECT_TRUE(checkPixel( 24, 39, 255, 0, 0, 255)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + + EXPECT_TRUE(checkPixel( 24, 39, 0, 255, 0, 255)); +} + } // namespace android diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index a98e4cd..9bfc94c 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -39,6 +39,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) external/skia/include/utils LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER + LOCAL_CFLAGS += -fvisibility=hidden LOCAL_MODULE_CLASS := SHARED_LIBRARIES LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui LOCAL_MODULE := libhwui diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 24ec4e8..f293cba 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -46,22 +46,16 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO), - lastDstMode(GL_ZERO), currentProgram(NULL) { +Caches::Caches(): Singleton<Caches>(), mInitialized(false) { 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; + init(); mDebugLevel = readDebugLevel(); LOGD("Enabling debug mode %d", mDebugLevel); @@ -71,8 +65,40 @@ Caches::Caches(): Singleton<Caches>(), blend(false), lastSrcMode(GL_ZERO), #endif } -Caches::~Caches() { +void Caches::init() { + if (mInitialized) return; + + glGenBuffers(1, &meshBuffer); + glBindBuffer(GL_ARRAY_BUFFER, meshBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW); + + mCurrentBuffer = meshBuffer; + mRegionMesh = NULL; + + blend = false; + lastSrcMode = GL_ZERO; + lastDstMode = GL_ZERO; + currentProgram = NULL; + + mInitialized = true; +} + +void Caches::terminate() { + if (!mInitialized) return; + + glDeleteBuffers(1, &meshBuffer); + mCurrentBuffer = 0; + + glDeleteBuffers(1, &mRegionMeshIndices); delete[] mRegionMesh; + mRegionMesh = NULL; + + fboCache.clear(); + + programCache.clear(); + currentProgram = NULL; + + mInitialized = false; } /////////////////////////////////////////////////////////////////////////////// @@ -170,8 +196,11 @@ void Caches::flush(FlushMode mode) { patchCache.clear(); dropShadowCache.clear(); gradientCache.clear(); + fontRenderer.clear(); // fall through case kFlushMode_Moderate: + fontRenderer.flush(); + textureCache.flush(); pathCache.clear(); roundRectShapeCache.clear(); circleShapeCache.clear(); diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index cdcbf21..5e58a9e 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -23,6 +23,8 @@ #include <utils/Singleton.h> +#include <cutils/compiler.h> + #include "Extensions.h" #include "FontRenderer.h" #include "GammaFontRenderer.h" @@ -82,9 +84,8 @@ struct CacheLogger { // Caches /////////////////////////////////////////////////////////////////////////////// -class Caches: public Singleton<Caches> { +class ANDROID_API Caches: public Singleton<Caches> { Caches(); - ~Caches(); friend class Singleton<Caches>; @@ -107,6 +108,11 @@ public: }; /** + * Initializes the cache. + */ + void init(); + + /** * Flush the cache. * * @param mode Indicates how much of the cache should be flushed @@ -114,6 +120,12 @@ public: void flush(FlushMode mode); /** + * Destroys all resources associated with this cache. This should + * be called after a flush(kFlushMode_Full). + */ + void terminate(); + + /** * Indicates whether the renderer is in debug mode. * This debug mode provides limited information to app developers. */ @@ -192,6 +204,7 @@ public: private: DebugLevel mDebugLevel; + bool mInitialized; }; // class Caches }; // namespace uirenderer diff --git a/libs/hwui/DisplayListLogBuffer.h b/libs/hwui/DisplayListLogBuffer.h index bf16f29..5d689bb 100644 --- a/libs/hwui/DisplayListLogBuffer.h +++ b/libs/hwui/DisplayListLogBuffer.h @@ -18,6 +18,7 @@ #define ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H #include <utils/Singleton.h> + #include <stdio.h> namespace android { diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index cedf456..3372d1c 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -382,12 +382,13 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { xDivs = getInts(xDivsCount); yDivs = getInts(yDivsCount); colors = getUInts(numColors); - DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); - getFloat(); - getFloat(); - getFloat(); - getFloat(); - getPaint(); + float left = getFloat(); + float top = getFloat(); + float right = getFloat(); + float bottom = getFloat(); + SkPaint* paint = getPaint(); + LOGD("%s%s %.2f, %.2f, %.2f, %.2f", (char*) indent, OP_NAMES[op], + left, top, right, bottom); } break; case DrawColor: { diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 8cd7fea..ab475bf 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -26,6 +26,8 @@ #include <SkTDArray.h> #include <SkTSearch.h> +#include <cutils/compiler.h> + #include "DisplayListLogBuffer.h" #include "OpenGLRenderer.h" #include "utils/Functor.h" @@ -58,7 +60,7 @@ class DisplayListRenderer; class DisplayList { public: DisplayList(const DisplayListRenderer& recorder); - ~DisplayList(); + ANDROID_API ~DisplayList(); // IMPORTANT: Update the intialization of OP_NAMES in the .cpp file // when modifying this file @@ -107,13 +109,13 @@ public: void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false); - size_t getSize(); + ANDROID_API size_t getSize(); bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0); void output(OpenGLRenderer& renderer, uint32_t level = 0); - static void outputLogBuffer(int fd); + ANDROID_API static void outputLogBuffer(int fd); void setRenderable(bool renderable) { mIsRenderable = renderable; @@ -230,75 +232,76 @@ private: */ class DisplayListRenderer: public OpenGLRenderer { public: - DisplayListRenderer(); - ~DisplayListRenderer(); + ANDROID_API DisplayListRenderer(); + virtual ~DisplayListRenderer(); - DisplayList* getDisplayList(DisplayList* displayList); + ANDROID_API DisplayList* getDisplayList(DisplayList* displayList); - void setViewport(int width, int height); - void prepareDirty(float left, float top, float right, float bottom, bool opaque); - void finish(); + virtual void setViewport(int width, int height); + virtual void prepareDirty(float left, float top, float right, float bottom, bool opaque); + virtual void finish(); - bool callDrawGLFunction(Functor *functor, Rect& dirty); + virtual bool callDrawGLFunction(Functor *functor, Rect& dirty); - void interrupt(); - void resume(); + virtual void interrupt(); + virtual void resume(); - int save(int flags); - void restore(); - void restoreToCount(int saveCount); + virtual int save(int flags); + virtual void restore(); + virtual void restoreToCount(int saveCount); - int saveLayer(float left, float top, float right, float bottom, + virtual int saveLayer(float left, float top, float right, float bottom, SkPaint* p, int flags); - int saveLayerAlpha(float left, float top, float right, float bottom, + virtual 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); + 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); - void setMatrix(SkMatrix* matrix); - void concatMatrix(SkMatrix* matrix); + virtual void setMatrix(SkMatrix* matrix); + virtual void concatMatrix(SkMatrix* matrix); - bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); + virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); - bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, + virtual 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, + 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); - void drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, + virtual 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, + 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); - 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, + 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); - 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, + 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); - void drawPath(SkPath* path, SkPaint* paint); - void drawLines(float* points, int count, SkPaint* paint); - void drawPoints(float* points, int count, SkPaint* paint); - void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint); + virtual void drawPath(SkPath* path, SkPaint* paint); + virtual void drawLines(float* points, int count, SkPaint* paint); + virtual void drawPoints(float* points, int count, SkPaint* paint); + virtual void drawText(const char* text, int bytesCount, int count, float x, float y, + SkPaint* paint); - void resetShader(); - void setupShader(SkiaShader* shader); + virtual void resetShader(); + virtual void setupShader(SkiaShader* shader); - void resetColorFilter(); - void setupColorFilter(SkiaColorFilter* filter); + virtual void resetColorFilter(); + virtual void setupColorFilter(SkiaColorFilter* filter); - void resetShadow(); - void setupShadow(float radius, float dx, float dy, int color); + virtual void resetShadow(); + virtual void setupShadow(float radius, float dx, float dy, int color); - void reset(); + ANDROID_API void reset(); const SkWriter32& writeStream() const { return mWriter; diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index a077cbc..158f785 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -163,7 +163,6 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL); } - } void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, @@ -615,7 +614,8 @@ void FontRenderer::issueDrawCommand() { 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) { + if (mClip && + (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { return; } @@ -723,11 +723,16 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch return image; } + mClip = NULL; + mBounds = NULL; + Rect bounds; mCurrentFont->measure(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; } @@ -765,8 +770,11 @@ bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text mDrawn = false; mBounds = bounds; mClip = clip; + mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); + mBounds = NULL; + mClip = NULL; if (mCurrentQuadIndex != 0) { issueDrawCommand(); diff --git a/libs/hwui/GammaFontRenderer.cpp b/libs/hwui/GammaFontRenderer.cpp index e8362dc..eb863e9 100644 --- a/libs/hwui/GammaFontRenderer.cpp +++ b/libs/hwui/GammaFontRenderer.cpp @@ -67,20 +67,63 @@ GammaFontRenderer::GammaFontRenderer() { const float whiteGamma = 1.0f / gamma; for (uint32_t i = 0; i <= 255; i++) { - mDefault[i] = i; + mGammaTable[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)); + mGammaTable[256 + i] = uint8_t((float)::floor(black * 255.0f + 0.5f)); + mGammaTable[512 + 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]); + memset(mRenderers, 0, sizeof(FontRenderer*) * kGammaCount); + memset(mRenderersUsageCount, 0, sizeof(uint32_t) * kGammaCount); +} + +GammaFontRenderer::~GammaFontRenderer() { + for (int i = 0; i < kGammaCount; i++) { + delete mRenderers[i]; + } +} + +void GammaFontRenderer::clear() { + for (int i = 0; i < kGammaCount; i++) { + delete mRenderers[i]; + mRenderers[i] = NULL; + } +} + +void GammaFontRenderer::flush() { + int count = 0; + int min = -1; + uint32_t minCount = UINT_MAX; + + for (int i = 0; i < kGammaCount; i++) { + if (mRenderers[i]) { + count++; + if (mRenderersUsageCount[i] < minCount) { + minCount = mRenderersUsageCount[i]; + min = i; + } + } + } + + if (count <= 1 || min < 0) return; + + delete mRenderers[min]; + mRenderers[min] = NULL; +} + +FontRenderer* GammaFontRenderer::getRenderer(Gamma gamma) { + FontRenderer* renderer = mRenderers[gamma]; + if (!renderer) { + renderer = new FontRenderer(); + mRenderers[gamma] = renderer; + renderer->setGammaTable(&mGammaTable[gamma * 256]); + } + mRenderersUsageCount[gamma]++; + return renderer; } FontRenderer& GammaFontRenderer::getFontRenderer(const SkPaint* paint) { @@ -92,12 +135,12 @@ FontRenderer& GammaFontRenderer::getFontRenderer(const SkPaint* paint) { const int luminance = (r * 2 + g * 5 + b) >> 3; if (luminance <= mBlackThreshold) { - return mBlackGammaRenderer; + return *getRenderer(kGammaBlack); } else if (luminance >= mWhiteThreshold) { - return mWhiteGammaRenderer; + return *getRenderer(kGammaWhite); } } - return mDefaultRenderer; + return *getRenderer(kGammaDefault); } }; // namespace uirenderer diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h index 96d960c..54c208e 100644 --- a/libs/hwui/GammaFontRenderer.h +++ b/libs/hwui/GammaFontRenderer.h @@ -26,36 +26,43 @@ namespace uirenderer { struct GammaFontRenderer { GammaFontRenderer(); + ~GammaFontRenderer(); + + enum Gamma { + kGammaDefault = 0, + kGammaBlack = 1, + kGammaWhite = 2, + kGammaCount = 3 + }; + + void clear(); + void flush(); FontRenderer& getFontRenderer(const SkPaint* paint); uint32_t getFontRendererCount() const { - return 3; + return kGammaCount; } 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; + if (fontRenderer >= kGammaCount) return 0; + + FontRenderer* renderer = mRenderers[fontRenderer]; + if (!renderer) return 0; + + return renderer->getCacheHeight() * renderer->getCacheWidth(); } private: - FontRenderer mDefaultRenderer; - FontRenderer mBlackGammaRenderer; - FontRenderer mWhiteGammaRenderer; + FontRenderer* getRenderer(Gamma gamma); + + uint32_t mRenderersUsageCount[kGammaCount]; + FontRenderer* mRenderers[kGammaCount]; int mBlackThreshold; int mWhiteThreshold; - uint8_t mDefault[256]; - uint8_t mBlackGamma[256]; - uint8_t mWhiteGamma[256]; + uint8_t mGammaTable[256 * kGammaCount]; }; }; // namespace uirenderer diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 349b9e3..e38b479 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -31,6 +31,12 @@ namespace uirenderer { // Rendering /////////////////////////////////////////////////////////////////////////////// +LayerRenderer::LayerRenderer(Layer* layer): mLayer(layer) { +} + +LayerRenderer::~LayerRenderer() { +} + void LayerRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo()); @@ -210,7 +216,8 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE); if (glGetError() != GL_NO_ERROR) { - LOGD("Could not allocate texture"); + LOGD("Could not allocate texture for layer (fbo=%d %dx%d)", + fbo, width, height); glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); Caches::getInstance().fboCache.put(fbo); @@ -264,7 +271,7 @@ Layer* LayerRenderer::createTextureLayer(bool isOpaque) { layer->setFbo(0); layer->setAlpha(255, SkXfermode::kSrcOver_Mode); layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f); - layer->texCoords.set(0.0f, 1.0f, 0.0f, 1.0f); + layer->texCoords.set(0.0f, 1.0f, 1.0f, 0.0f); layer->region.clear(); layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer() @@ -400,6 +407,18 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { renderer.setViewport(bitmap->width(), bitmap->height()); renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f, bitmap->width(), bitmap->height(), !layer->isBlend()); + + glDisable(GL_SCISSOR_TEST); + renderer.translate(0.0f, bitmap->height()); + renderer.scale(1.0f, -1.0f); + + mat4 texTransform(layer->getTexTransform()); + + mat4 invert; + invert.translate(0.0f, 1.0f, 0.0f); + invert.scale(1.0f, -1.0f, 1.0f); + layer->getTexTransform().multiply(invert); + if ((error = glGetError()) != GL_NO_ERROR) goto error; { @@ -413,6 +432,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { if ((error = glGetError()) != GL_NO_ERROR) goto error; } + layer->getTexTransform().load(texTransform); status = true; } diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index 2246573..6104301 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -17,6 +17,8 @@ #ifndef ANDROID_HWUI_LAYER_RENDERER_H #define ANDROID_HWUI_LAYER_RENDERER_H +#include <cutils/compiler.h> + #include "OpenGLRenderer.h" #include "Layer.h" @@ -42,27 +44,24 @@ namespace uirenderer { class LayerRenderer: public OpenGLRenderer { public: - LayerRenderer(Layer* layer): mLayer(layer) { - } - - ~LayerRenderer() { - } + ANDROID_API LayerRenderer(Layer* layer); + virtual ~LayerRenderer(); - void prepareDirty(float left, float top, float right, float bottom, bool opaque); - void finish(); + virtual void prepareDirty(float left, float top, float right, float bottom, bool opaque); + virtual void finish(); - bool hasLayer(); - Region* getRegion(); - GLint getTargetFbo(); + virtual bool hasLayer(); + virtual Region* getRegion(); + virtual GLint getTargetFbo(); - static Layer* createTextureLayer(bool isOpaque); - 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 updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, + ANDROID_API static Layer* createTextureLayer(bool isOpaque); + ANDROID_API static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false); + ANDROID_API static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height); + ANDROID_API static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, bool isOpaque, GLenum renderTarget, float* transform); - static void destroyLayer(Layer* layer); - static void destroyLayerDeferred(Layer* layer); - static bool copyLayer(Layer* layer, SkBitmap* bitmap); + ANDROID_API static void destroyLayer(Layer* layer); + ANDROID_API static void destroyLayerDeferred(Layer* layer); + ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap); private: void generateMesh(); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index 56fd37d..22220a9 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -19,6 +19,8 @@ #include <SkMatrix.h> +#include <cutils/compiler.h> + #include "Rect.h" namespace android { @@ -28,7 +30,7 @@ namespace uirenderer { // Classes /////////////////////////////////////////////////////////////////////////////// -class Matrix4 { +class ANDROID_API Matrix4 { public: float data[16]; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 32595e4..4d22646 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -201,14 +201,16 @@ void OpenGLRenderer::interrupt() { } void OpenGLRenderer::resume() { - glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight()); + sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot; + + glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); glEnable(GL_SCISSOR_TEST); dirtyClip(); glDisable(GL_DITHER); - glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo()); + glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); mCaches.blend = true; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 14b22b3..2fc88e1 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -31,6 +31,8 @@ #include <utils/RefBase.h> #include <utils/Vector.h> +#include <cutils/compiler.h> + #include "Debug.h" #include "Extensions.h" #include "Matrix.h" @@ -57,12 +59,12 @@ class DisplayList; */ class OpenGLRenderer { public: - OpenGLRenderer(); + ANDROID_API OpenGLRenderer(); virtual ~OpenGLRenderer(); virtual void setViewport(int width, int height); - void prepare(bool opaque); + ANDROID_API void prepare(bool opaque); virtual void prepareDirty(float left, float top, float right, float bottom, bool opaque); virtual void finish(); @@ -72,7 +74,7 @@ public: virtual bool callDrawGLFunction(Functor *functor, Rect& dirty); - int getSaveCount() const; + ANDROID_API int getSaveCount() const; virtual int save(int flags); virtual void restore(); virtual void restoreToCount(int saveCount); @@ -87,12 +89,12 @@ public: virtual void scale(float sx, float sy); virtual void skew(float sx, float sy); - void getMatrix(SkMatrix* matrix); + ANDROID_API 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); + ANDROID_API const Rect& getClipBounds(); + ANDROID_API 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, diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index f7dacae..47a2c99 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -157,14 +157,17 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, for (uint32_t i = 0; i < mYCount; i++) { float stepY = mYDivs[i]; + const float segment = stepY - previousStepY; if (i & 1) { - const float segment = stepY - previousStepY; y2 = y1 + floorf(segment * stretchY + 0.5f); } else { - y2 = y1 + stepY - previousStepY; + y2 = y1 + segment; } - float v2 = fmax(0.0f, stepY - 0.5f) / bitmapHeight; + + float vOffset = y1 == y2 ? 0.0f : 0.5 - (0.5 * segment / (y2 - y1)); + float v2 = fmax(0.0f, stepY - vOffset) / bitmapHeight; + v1 += vOffset / bitmapHeight; if (stepY > 0.0f) { #if DEBUG_EXPLODE_PATCHES @@ -179,7 +182,7 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, } y1 = y2; - v1 = (stepY + 0.5f) / bitmapHeight; + v1 = stepY / bitmapHeight; previousStepY = stepY; } @@ -190,8 +193,7 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, y1 += mYCount * EXPLODE_GAP; y2 += mYCount * EXPLODE_GAP; #endif - generateRow(vertex, y1, y2, v1, 1.0f, stretchX, right - left, - bitmapWidth, quadCount); + generateRow(vertex, y1, y2, v1, 1.0f, stretchX, right - left, bitmapWidth, quadCount); } if (verticesCount > 0) { @@ -220,14 +222,17 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl // Generate the row quad by quad for (uint32_t i = 0; i < mXCount; i++) { float stepX = mXDivs[i]; + const float segment = stepX - previousStepX; if (i & 1) { - const float segment = stepX - previousStepX; x2 = x1 + floorf(segment * stretchX + 0.5f); } else { - x2 = x1 + stepX - previousStepX; + x2 = x1 + segment; } - float u2 = fmax(0.0f, stepX - 0.5f) / bitmapWidth; + + float uOffset = x1 == x2 ? 0.0f : 0.5 - (0.5 * segment / (x2 - x1)); + float u2 = fmax(0.0f, stepX - uOffset) / bitmapWidth; + u1 += uOffset / bitmapWidth; if (stepX > 0.0f) { #if DEBUG_EXPLODE_PATCHES @@ -241,7 +246,7 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl } x1 = x2; - u1 = (stepX + 0.5f) / bitmapWidth; + u1 = stepX / bitmapWidth; previousStepX = stepX; } @@ -265,8 +270,8 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f 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); + PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.4f, %.4f", x1, y1, u1, v1); + PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.4f, %.4f", x2, y2, u2, v2); #endif return; } @@ -294,8 +299,8 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f #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); + PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.4f, %.4f", x1, y1, u1, v1); + PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.4f, %.4f", x2, y2, u2, v2); #endif } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 5bd0d4f..8c01e3a 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -61,6 +61,9 @@ enum DebugLevel { #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 percentage (range 0..1) +#define PROPERTY_TEXTURE_CACHE_FLUSH_RATE "ro.hwui.texture_cache_flush_rate" + // 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" @@ -82,6 +85,8 @@ enum DebugLevel { #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f #define DEFAULT_FBO_CACHE_SIZE 16 +#define DEFAULT_TEXTURE_CACHE_FLUSH_RATE 0.6f + #define DEFAULT_TEXT_GAMMA 1.4f #define DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD 64 #define DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD 192 diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h index 2a38910..8cf466b 100644 --- a/libs/hwui/ResourceCache.h +++ b/libs/hwui/ResourceCache.h @@ -17,6 +17,8 @@ #ifndef ANDROID_HWUI_RESOURCE_CACHE_H #define ANDROID_HWUI_RESOURCE_CACHE_H +#include <cutils/compiler.h> + #include <SkBitmap.h> #include <SkiaColorFilter.h> #include <SkiaShader.h> @@ -49,7 +51,7 @@ public: ResourceType resourceType; }; -class ResourceCache { +class ANDROID_API ResourceCache { KeyedVector<void *, ResourceReference *>* mCache; public: ResourceCache(); diff --git a/libs/hwui/SkiaColorFilter.h b/libs/hwui/SkiaColorFilter.h index 1bf475c..2feb834 100644 --- a/libs/hwui/SkiaColorFilter.h +++ b/libs/hwui/SkiaColorFilter.h @@ -20,6 +20,8 @@ #include <GLES2/gl2.h> #include <SkColorFilter.h> +#include <cutils/compiler.h> + #include "ProgramCache.h" #include "Extensions.h" @@ -45,7 +47,7 @@ struct SkiaColorFilter { kBlend, }; - SkiaColorFilter(SkColorFilter *skFilter, Type type, bool blend); + ANDROID_API SkiaColorFilter(SkColorFilter *skFilter, Type type, bool blend); virtual ~SkiaColorFilter(); virtual void describe(ProgramDescription& description, const Extensions& extensions) = 0; @@ -79,7 +81,7 @@ private: * 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); + ANDROID_API SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector); ~SkiaColorMatrixFilter(); void describe(ProgramDescription& description, const Extensions& extensions); @@ -95,7 +97,7 @@ private: * another fixed value. Ignores the alpha channel of both arguments. */ struct SkiaLightingFilter: public SkiaColorFilter { - SkiaLightingFilter(SkColorFilter *skFilter, int multiply, int add); + ANDROID_API SkiaLightingFilter(SkColorFilter *skFilter, int multiply, int add); void describe(ProgramDescription& description, const Extensions& extensions); void setupProgram(Program* program); @@ -110,7 +112,7 @@ private: * and PorterDuff blending mode. */ struct SkiaBlendFilter: public SkiaColorFilter { - SkiaBlendFilter(SkColorFilter *skFilter, int color, SkXfermode::Mode mode); + ANDROID_API SkiaBlendFilter(SkColorFilter *skFilter, int color, SkXfermode::Mode mode); void describe(ProgramDescription& description, const Extensions& extensions); void setupProgram(Program* program); diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index 89dd131..2de9a93 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -22,6 +22,8 @@ #include <GLES2/gl2.h> +#include <cutils/compiler.h> + #include "Extensions.h" #include "ProgramCache.h" #include "TextureCache.h" @@ -52,8 +54,8 @@ struct SkiaShader { kCompose }; - SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, SkShader::TileMode tileY, - SkMatrix* matrix, bool blend); + ANDROID_API SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX, + SkShader::TileMode tileY, SkMatrix* matrix, bool blend); virtual ~SkiaShader(); virtual SkiaShader* copy() = 0; @@ -139,7 +141,7 @@ private: * A shader that draws a bitmap. */ struct SkiaBitmapShader: public SkiaShader { - SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX, + ANDROID_API SkiaBitmapShader(SkBitmap* bitmap, SkShader* key, SkShader::TileMode tileX, SkShader::TileMode tileY, SkMatrix* matrix, bool blend); SkiaShader* copy(); @@ -169,8 +171,8 @@ private: * 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); + ANDROID_API SkiaLinearGradientShader(float* bounds, uint32_t* colors, float* positions, + int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); ~SkiaLinearGradientShader(); SkiaShader* copy(); @@ -193,8 +195,8 @@ private: * 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); + ANDROID_API SkiaSweepGradientShader(float x, float y, uint32_t* colors, float* positions, + int count, SkShader* key, SkMatrix* matrix, bool blend); ~SkiaSweepGradientShader(); SkiaShader* copy(); @@ -218,8 +220,9 @@ protected: * 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); + ANDROID_API 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); @@ -233,7 +236,8 @@ private: * A shader that draws two shaders, composited with an xfermode. */ struct SkiaComposeShader: public SkiaShader { - SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode, SkShader* key); + ANDROID_API SkiaComposeShader(SkiaShader* first, SkiaShader* second, SkXfermode::Mode mode, + SkShader* key); ~SkiaComposeShader(); SkiaShader* copy(); diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index d51154d..aff7b93 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -213,7 +213,8 @@ public: Layer* layer; /** - * Only set when the flag kFlagIsFboLayer is set. + * Target FBO used for rendering. Set to 0 when rendering directly + * into the framebuffer. */ GLuint fbo; diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index fbdbf92..018ce3e 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -34,7 +34,8 @@ namespace uirenderer { TextureCache::TextureCache(): mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) { + mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)), + mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) { char property[PROPERTY_VALUE_MAX]; if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) { INIT_LOGD(" Setting texture cache size to %sMB", property); @@ -43,6 +44,15 @@ TextureCache::TextureCache(): INIT_LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE); } + if (property_get(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, property, NULL) > 0) { + float flushRate = atof(property); + INIT_LOGD(" Setting texture cache flush rate to %.2f%%", flushRate * 100.0f); + setFlushRate(flushRate); + } else { + INIT_LOGD(" Using default texture cache flush rate of %.2f%%", + DEFAULT_TEXTURE_CACHE_FLUSH_RATE * 100.0f); + } + init(); } @@ -84,6 +94,10 @@ void TextureCache::setMaxSize(uint32_t maxSize) { } } +void TextureCache::setFlushRate(float flushRate) { + mFlushRate = fmaxf(0.0f, fminf(1.0f, flushRate)); +} + /////////////////////////////////////////////////////////////////////////////// // Callbacks /////////////////////////////////////////////////////////////////////////////// @@ -168,6 +182,21 @@ void TextureCache::clear() { TEXTURE_LOGD("TextureCache:clear(), mSize = %d", mSize); } +void TextureCache::flush() { + if (mFlushRate >= 1.0f || mCache.size() == 0) return; + if (mFlushRate <= 0.0f) { + clear(); + return; + } + + uint32_t targetSize = uint32_t(mSize * mFlushRate); + TEXTURE_LOGD("TextureCache::flush: target size: %d", targetSize); + + while (mSize > targetSize) { + mCache.removeOldest(); + } +} + void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) { SkAutoLockPixels alp(*bitmap); diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index f7707f7..ce924b4 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -98,6 +98,17 @@ public: */ uint32_t getSize(); + /** + * Partially flushes the cache. The amount of memory freed by a flush + * is defined by the flush rate. + */ + void flush(); + /** + * Indicates the percentage of the cache to retain when a + * memory trim is requested (see Caches::flush). + */ + void setFlushRate(float flushRate); + private: /** * Generates the texture from a bitmap into the specified texture structure. @@ -119,6 +130,8 @@ private: uint32_t mMaxSize; GLint mMaxTextureSize; + float mFlushRate; + bool mDebugEnabled; Vector<SkBitmap*> mGarbage; diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h index 3ba0123..6d54268 100644 --- a/libs/rs/RenderScript.h +++ b/libs/rs/RenderScript.h @@ -34,26 +34,30 @@ 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 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); +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); +void rsaElementGetSubElements(RsContext, RsElement, uint32_t *ids, const char **names, + uint32_t *arraySizes, uint32_t dataSize); RsDevice rsDeviceCreate(); void rsDeviceDestroy(RsDevice dev); void rsDeviceSetConfig(RsDevice dev, RsDeviceParam p, int32_t value); RsContext rsContextCreate(RsDevice dev, uint32_t version, uint32_t sdkVersion); -RsContext rsContextCreateGL(RsDevice dev, uint32_t version, uint32_t sdkVersion, RsSurfaceConfig sc, uint32_t dpi); +RsContext rsContextCreateGL(RsDevice dev, uint32_t version, uint32_t sdkVersion, + RsSurfaceConfig sc, uint32_t dpi); #include "rsgApiFuncDecl.h" diff --git a/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp index 5fd5c35..4ecf8e8 100644 --- a/libs/rs/driver/rsdBcc.cpp +++ b/libs/rs/driver/rsdBcc.cpp @@ -226,6 +226,7 @@ static void wc_xy(void *usr, uint32_t idx) { RsdHal * dc = (RsdHal *)mtls->rsc->mHal.drv; uint32_t sig = mtls->sig; + outer_foreach_t fn = dc->mForEachLaunch[sig]; while (1) { uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum); uint32_t yStart = mtls->yStart + slice * mtls->mSliceSize; @@ -239,16 +240,10 @@ static void wc_xy(void *usr, uint32_t idx) { //LOGE("usr ptr in %p, out %p", mtls->ptrIn, mtls->ptrOut); for (p.y = yStart; p.y < yEnd; p.y++) { uint32_t offset = mtls->dimX * p.y; - uint8_t *xPtrOut = mtls->ptrOut + (mtls->eStrideOut * offset); - const uint8_t *xPtrIn = mtls->ptrIn + (mtls->eStrideIn * offset); - - for (p.x = mtls->xStart; p.x < mtls->xEnd; p.x++) { - p.in = xPtrIn; - p.out = xPtrOut; - dc->mForEachLaunch[sig](&mtls->script->mHal.info.root, &p); - xPtrIn += mtls->eStrideIn; - xPtrOut += mtls->eStrideOut; - } + p.out = mtls->ptrOut + (mtls->eStrideOut * offset); + p.in = mtls->ptrIn + (mtls->eStrideIn * offset); + fn(&mtls->script->mHal.info.root, &p, mtls->xStart, mtls->xEnd, + mtls->eStrideIn, mtls->eStrideOut); } } } @@ -262,6 +257,7 @@ static void wc_x(void *usr, uint32_t idx) { RsdHal * dc = (RsdHal *)mtls->rsc->mHal.drv; uint32_t sig = mtls->sig; + outer_foreach_t fn = dc->mForEachLaunch[sig]; while (1) { uint32_t slice = (uint32_t)android_atomic_inc(&mtls->mSliceNum); uint32_t xStart = mtls->xStart + slice * mtls->mSliceSize; @@ -271,17 +267,12 @@ static void wc_x(void *usr, uint32_t idx) { return; } - //LOGE("usr idx %i, x %i,%i y %i,%i", idx, mtls->xStart, mtls->xEnd, yStart, yEnd); + //LOGE("usr slice %i idx %i, x %i,%i", slice, idx, xStart, xEnd); //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 (p.x = xStart; p.x < xEnd; p.x++) { - p.in = xPtrIn; - p.out = xPtrOut; - dc->mForEachLaunch[sig](&mtls->script->mHal.info.root, &p); - xPtrIn += mtls->eStrideIn; - xPtrOut += mtls->eStrideOut; - } + + p.out = mtls->ptrOut + (mtls->eStrideOut * xStart); + p.in = mtls->ptrIn + (mtls->eStrideIn * xStart); + fn(&mtls->script->mHal.info.root, &p, xStart, xEnd, mtls->eStrideIn, mtls->eStrideOut); } } @@ -392,22 +383,17 @@ void rsdScriptInvokeForEach(const Context *rsc, uint32_t sig = mtls.sig; //LOGE("launch 3"); + outer_foreach_t fn = dc->mForEachLaunch[sig]; for (p.ar[0] = mtls.arrayStart; p.ar[0] < mtls.arrayEnd; p.ar[0]++) { for (p.z = mtls.zStart; p.z < mtls.zEnd; p.z++) { for (p.y = mtls.yStart; p.y < mtls.yEnd; p.y++) { uint32_t offset = mtls.dimX * mtls.dimY * mtls.dimZ * p.ar[0] + mtls.dimX * mtls.dimY * p.z + mtls.dimX * p.y; - uint8_t *xPtrOut = mtls.ptrOut + (mtls.eStrideOut * offset); - const uint8_t *xPtrIn = mtls.ptrIn + (mtls.eStrideIn * offset); - - for (p.x = mtls.xStart; p.x < mtls.xEnd; p.x++) { - p.in = xPtrIn; - p.out = xPtrOut; - dc->mForEachLaunch[sig](&s->mHal.info.root, &p); - xPtrIn += mtls.eStrideIn; - xPtrOut += mtls.eStrideOut; - } + p.out = mtls.ptrOut + (mtls.eStrideOut * offset); + p.in = mtls.ptrIn + (mtls.eStrideIn * offset); + fn(&mtls.script->mHal.info.root, &p, mtls.xStart, mtls.xEnd, + mtls.eStrideIn, mtls.eStrideOut); } } } diff --git a/libs/rs/driver/rsdCore.cpp b/libs/rs/driver/rsdCore.cpp index f8107d9..247f4dc 100644 --- a/libs/rs/driver/rsdCore.cpp +++ b/libs/rs/driver/rsdCore.cpp @@ -292,75 +292,136 @@ void Shutdown(Context *rsc) { } static void rsdForEach17(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(const void *, uint32_t); (*(fe*)vRoot)(p->in, p->y); } static void rsdForEach18(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(void *, uint32_t); (*(fe*)vRoot)(p->out, p->y); } static void rsdForEach19(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(const void *, void *, uint32_t); (*(fe*)vRoot)(p->in, p->out, p->y); } static void rsdForEach21(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(const void *, const void *, uint32_t); (*(fe*)vRoot)(p->in, p->usr, p->y); } static void rsdForEach22(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(void *, const void *, uint32_t); (*(fe*)vRoot)(p->out, p->usr, p->y); } static void rsdForEach23(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(const void *, void *, const void *, uint32_t); (*(fe*)vRoot)(p->in, p->out, p->usr, p->y); } static void rsdForEach25(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(const void *, uint32_t, uint32_t); - (*(fe*)vRoot)(p->in, p->x, p->y); + const uint8_t *pin = (const uint8_t *)p->in; + uint32_t y = p->y; + for (uint32_t x = x1; x < x2; x++) { + (*(fe*)vRoot)(pin, x, y); + pin += instep; + } } static void rsdForEach26(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(void *, uint32_t, uint32_t); - (*(fe*)vRoot)(p->out, p->x, p->y); + uint8_t *pout = (uint8_t *)p->out; + uint32_t y = p->y; + for (uint32_t x = x1; x < x2; x++) { + (*(fe*)vRoot)(pout, x, y); + pout += outstep; + } } static void rsdForEach27(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(const void *, void *, uint32_t, uint32_t); - (*(fe*)vRoot)(p->in, p->out, p->x, p->y); + uint8_t *pout = (uint8_t *)p->out; + const uint8_t *pin = (const uint8_t *)p->in; + uint32_t y = p->y; + for (uint32_t x = x1; x < x2; x++) { + (*(fe*)vRoot)(pin, pout, x, y); + pin += instep; + pout += outstep; + } } static void rsdForEach29(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(const void *, const void *, uint32_t, uint32_t); - (*(fe*)vRoot)(p->in, p->usr, p->x, p->y); + const uint8_t *pin = (const uint8_t *)p->in; + const void *usr = p->usr; + const uint32_t y = p->y; + for (uint32_t x = x1; x < x2; x++) { + (*(fe*)vRoot)(pin, usr, x, y); + pin += instep; + } } static void rsdForEach30(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(void *, const void *, uint32_t, uint32_t); - (*(fe*)vRoot)(p->out, p->usr, p->x, p->y); + uint8_t *pout = (uint8_t *)p->out; + const void *usr = p->usr; + const uint32_t y = p->y; + for (uint32_t x = x1; x < x2; x++) { + (*(fe*)vRoot)(pout, usr, x, y); + pout += outstep; + } } static void rsdForEach31(const void *vRoot, - const android::renderscript::RsForEachStubParamStruct *p) { + const android::renderscript::RsForEachStubParamStruct *p, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep) { typedef void (*fe)(const void *, void *, const void *, uint32_t, uint32_t); - (*(fe*)vRoot)(p->in, p->out, p->usr, p->x, p->y); + uint8_t *pout = (uint8_t *)p->out; + const uint8_t *pin = (const uint8_t *)p->in; + const void *usr = p->usr; + const uint32_t y = p->y; + for (uint32_t x = x1; x < x2; x++) { + (*(fe*)vRoot)(pin, pout, usr, x, y); + pin += instep; + pout += outstep; + } } diff --git a/libs/rs/driver/rsdCore.h b/libs/rs/driver/rsdCore.h index 159b72a..ce86d11 100644 --- a/libs/rs/driver/rsdCore.h +++ b/libs/rs/driver/rsdCore.h @@ -28,7 +28,9 @@ typedef void (* InvokeFunc_t)(void); typedef void (*WorkerCallback_t)(void *usr, uint32_t idx); typedef void (*outer_foreach_t)(const void *, - const android::renderscript::RsForEachStubParamStruct *); + const android::renderscript::RsForEachStubParamStruct *, + uint32_t x1, uint32_t x2, + uint32_t instep, uint32_t outstep); typedef struct RsdSymbolTableRec { const char * mName; diff --git a/libs/rs/driver/rsdFrameBuffer.cpp b/libs/rs/driver/rsdFrameBuffer.cpp index 8c1b12d..bb07d29 100644 --- a/libs/rs/driver/rsdFrameBuffer.cpp +++ b/libs/rs/driver/rsdFrameBuffer.cpp @@ -33,11 +33,11 @@ void setDepthAttachment(const Context *rsc, const FBOCache *fb) { RsdFrameBufferObj *fbo = (RsdFrameBufferObj*)fb->mHal.drv; DrvAllocation *depth = NULL; - if (fb->mHal.state.depthTarget.get() != NULL) { + if (fb->mHal.state.depthTarget != NULL) { depth = (DrvAllocation *)fb->mHal.state.depthTarget->mHal.drv; if (depth->uploadDeferred) { - rsdAllocationSyncAll(rsc, fb->mHal.state.depthTarget.get(), + rsdAllocationSyncAll(rsc, fb->mHal.state.depthTarget, RS_ALLOCATION_USAGE_SCRIPT); } } @@ -49,11 +49,11 @@ void setColorAttachment(const Context *rsc, const FBOCache *fb) { // Now attach color targets for (uint32_t i = 0; i < fb->mHal.state.colorTargetsCount; i ++) { DrvAllocation *color = NULL; - if (fb->mHal.state.colorTargets[i].get() != NULL) { + if (fb->mHal.state.colorTargets[i] != NULL) { color = (DrvAllocation *)fb->mHal.state.colorTargets[i]->mHal.drv; if (color->uploadDeferred) { - rsdAllocationSyncAll(rsc, fb->mHal.state.colorTargets[i].get(), + rsdAllocationSyncAll(rsc, fb->mHal.state.colorTargets[i], RS_ALLOCATION_USAGE_SCRIPT); } } @@ -79,10 +79,10 @@ void rsdFrameBufferSetActive(const Context *rsc, const FBOCache *fb) { setColorAttachment(rsc, fb); RsdFrameBufferObj *fbo = (RsdFrameBufferObj *)fb->mHal.drv; - if (fb->mHal.state.colorTargets[0].get()) { + if (fb->mHal.state.colorTargets[0]) { fbo->setDimensions(fb->mHal.state.colorTargets[0]->getType()->getDimX(), fb->mHal.state.colorTargets[0]->getType()->getDimY()); - } else if (fb->mHal.state.depthTarget.get()) { + } else if (fb->mHal.state.depthTarget) { fbo->setDimensions(fb->mHal.state.depthTarget->getType()->getDimX(), fb->mHal.state.depthTarget->getType()->getDimY()); } diff --git a/libs/rs/driver/rsdMeshObj.cpp b/libs/rs/driver/rsdMeshObj.cpp index 019167b..24a7183 100644 --- a/libs/rs/driver/rsdMeshObj.cpp +++ b/libs/rs/driver/rsdMeshObj.cpp @@ -138,7 +138,7 @@ void RsdMeshObj::renderPrimitiveRange(const Context *rsc, uint32_t primIndex, } for (uint32_t ct=0; ct < mRSMesh->mHal.state.vertexBuffersCount; ct++) { - const Allocation *alloc = mRSMesh->mHal.state.vertexBuffers[ct].get(); + const Allocation *alloc = mRSMesh->mHal.state.vertexBuffers[ct]; DrvAllocation *drv = (DrvAllocation *)alloc->mHal.drv; if (drv->uploadDeferred) { rsdAllocationSyncAll(rsc, alloc, RS_ALLOCATION_USAGE_SCRIPT); @@ -148,7 +148,7 @@ void RsdMeshObj::renderPrimitiveRange(const Context *rsc, uint32_t primIndex, // 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 = mRSMesh->mHal.state.vertexBuffers[allocIndex].get(); + Allocation *alloc = mRSMesh->mHal.state.vertexBuffers[allocIndex]; DrvAllocation *drvAlloc = (DrvAllocation *)alloc->mHal.drv; if (drvAlloc->bufferID) { @@ -163,8 +163,7 @@ void RsdMeshObj::renderPrimitiveRange(const Context *rsc, uint32_t primIndex, RsdVertexArray va(mAttribs, mAttribCount); va.setup(rsc); - Mesh::Primitive_t *prim = mRSMesh->mHal.state.primitives[primIndex]; - const Allocation *idxAlloc = prim->mIndexBuffer.get(); + const Allocation *idxAlloc = mRSMesh->mHal.state.indexBuffers[primIndex]; if (idxAlloc) { DrvAllocation *drvAlloc = (DrvAllocation *)idxAlloc->mHal.drv; if (drvAlloc->uploadDeferred) { @@ -190,7 +189,7 @@ void RsdMeshObj::renderPrimitiveRange(const Context *rsc, uint32_t primIndex, void RsdMeshObj::updateGLPrimitives() { mGLPrimitives = new uint32_t[mRSMesh->mHal.state.primitivesCount]; for (uint32_t i = 0; i < mRSMesh->mHal.state.primitivesCount; i ++) { - switch (mRSMesh->mHal.state.primitives[i]->mPrimitive) { + switch (mRSMesh->mHal.state.primitives[i]) { case RS_PRIMITIVE_POINT: mGLPrimitives[i] = GL_POINTS; break; case RS_PRIMITIVE_LINE: mGLPrimitives[i] = GL_LINES; break; case RS_PRIMITIVE_LINE_STRIP: mGLPrimitives[i] = GL_LINE_STRIP; break; diff --git a/libs/rs/driver/rsdProgram.cpp b/libs/rs/driver/rsdProgram.cpp index 27a6663..7556e50 100644 --- a/libs/rs/driver/rsdProgram.cpp +++ b/libs/rs/driver/rsdProgram.cpp @@ -43,7 +43,7 @@ bool rsdProgramVertexInit(const Context *rsc, const ProgramVertex *pv, static void SyncProgramConstants(const Context *rsc, const Program *p) { for (uint32_t ct=0; ct < p->mHal.state.texturesCount; ct++) { - const Allocation *a = p->mHal.state.textures[ct].get(); + const Allocation *a = p->mHal.state.textures[ct]; if (!a) { continue; } diff --git a/libs/rs/driver/rsdShader.cpp b/libs/rs/driver/rsdShader.cpp index 16ff063..bdb60c2 100644 --- a/libs/rs/driver/rsdShader.cpp +++ b/libs/rs/driver/rsdShader.cpp @@ -69,7 +69,7 @@ void RsdShader::init() { uint32_t attribCount = 0; uint32_t uniformCount = 0; for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) { - initAddUserElement(mRSProgram->mHal.state.inputElements[ct].get(), mAttribNames, NULL, &attribCount, RS_SHADER_ATTR); + initAddUserElement(mRSProgram->mHal.state.inputElements[ct], mAttribNames, NULL, &attribCount, RS_SHADER_ATTR); } for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) { initAddUserElement(mRSProgram->mHal.state.constantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI); @@ -89,7 +89,7 @@ void RsdShader::init() { String8 RsdShader::getGLSLInputString() const { String8 s; for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) { - const Element *e = mRSProgram->mHal.state.inputElements[ct].get(); + const Element *e = mRSProgram->mHal.state.inputElements[ct]; for (uint32_t field=0; field < e->getFieldCount(); field++) { const Element *f = e->getField(field); @@ -113,7 +113,7 @@ String8 RsdShader::getGLSLInputString() const { void RsdShader::appendAttributes() { for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) { - const Element *e = mRSProgram->mHal.state.inputElements[ct].get(); + const Element *e = mRSProgram->mHal.state.inputElements[ct]; for (uint32_t field=0; field < e->getFieldCount(); field++) { const Element *f = e->getField(field); const char *fn = e->getFieldName(field); @@ -414,7 +414,7 @@ void RsdShader::setupTextures(const Context *rsc, RsdShaderCache *sc) { RSD_CALL_GL(glActiveTexture, GL_TEXTURE0 + ct); RSD_CALL_GL(glUniform1i, sc->fragUniformSlot(mTextureUniformIndexStart + ct), ct); - if (!mRSProgram->mHal.state.textures[ct].get()) { + if (!mRSProgram->mHal.state.textures[ct]) { // if nothing is bound, reset to default GL texture RSD_CALL_GL(glBindTexture, mTextureTargets[ct], 0); continue; @@ -427,9 +427,9 @@ void RsdShader::setupTextures(const Context *rsc, RsdShaderCache *sc) { } RSD_CALL_GL(glBindTexture, drvTex->glTarget, drvTex->textureID); rsdGLCheckError(rsc, "ProgramFragment::setup tex bind"); - if (mRSProgram->mHal.state.samplers[ct].get()) { - setupSampler(rsc, mRSProgram->mHal.state.samplers[ct].get(), - mRSProgram->mHal.state.textures[ct].get()); + if (mRSProgram->mHal.state.samplers[ct]) { + setupSampler(rsc, mRSProgram->mHal.state.samplers[ct], + mRSProgram->mHal.state.textures[ct]); } else { RSD_CALL_GL(glTexParameteri, drvTex->glTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); RSD_CALL_GL(glTexParameteri, drvTex->glTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -448,7 +448,7 @@ void RsdShader::setupTextures(const Context *rsc, RsdShaderCache *sc) { void RsdShader::setupUserConstants(const Context *rsc, RsdShaderCache *sc, bool isFragment) { uint32_t uidx = 0; for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) { - Allocation *alloc = mRSProgram->mHal.state.constants[ct].get(); + Allocation *alloc = mRSProgram->mHal.state.constants[ct]; if (!alloc) { LOGE("Attempting to set constants on shader id %u, but alloc at slot %u is not set", (uint32_t)this, ct); @@ -504,7 +504,7 @@ void RsdShader::setup(const android::renderscript::Context *rsc, RsdShaderCache void RsdShader::initAttribAndUniformArray() { mAttribCount = 0; for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) { - const Element *elem = mRSProgram->mHal.state.inputElements[ct].get(); + const Element *elem = mRSProgram->mHal.state.inputElements[ct]; for (uint32_t field=0; field < elem->getFieldCount(); field++) { if (elem->getFieldName(field)[0] != '#') { mAttribCount ++; diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp index 4359d95..e732630 100644 --- a/libs/rs/rsAllocation.cpp +++ b/libs/rs/rsAllocation.cpp @@ -30,7 +30,7 @@ Allocation::Allocation(Context *rsc, const Type *type, uint32_t usages, mHal.state.usageFlags = usages; mHal.state.mipmapControl = mc; - mHal.state.type.set(type); + setType(type); updateCache(); } @@ -47,7 +47,7 @@ Allocation * Allocation::createAllocation(Context *rsc, const Type *type, uint32 } void Allocation::updateCache() { - const Type *type = mHal.state.type.get(); + const Type *type = mHal.state.type; mHal.state.dimensionX = type->getDimX(); mHal.state.dimensionY = type->getDimY(); mHal.state.dimensionZ = type->getDimZ(); @@ -187,7 +187,7 @@ void Allocation::dumpLOGV(const char *prefix) const { String8 s(prefix); s.append(" type "); - if (mHal.state.type.get()) { + if (mHal.state.type) { mHal.state.type->dumpLOGV(s.string()); } @@ -314,7 +314,7 @@ void Allocation::resize1D(Context *rsc, uint32_t dimX) { decRefs(getPtr(), oldDimX - dimX, dimX); } rsc->mHal.funcs.allocation.resize(rsc, this, t.get(), mHal.state.hasReferences); - mHal.state.type.set(t.get()); + setType(t.get()); updateCache(); } diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h index 67fc3b5..714798a 100644 --- a/libs/rs/rsAllocation.h +++ b/libs/rs/rsAllocation.h @@ -41,7 +41,7 @@ public: void * drv; struct State { - ObjectBaseRef<const Type> type; + const Type * type; uint32_t usageFlags; RsAllocationMipmapControl mipmapControl; @@ -71,7 +71,7 @@ public: void updateCache(); void * getPtr() const {return mHal.drvState.mallocPtr;} - const Type * getType() const {return mHal.state.type.get();} + const Type * getType() const {return mHal.state.type;} void syncAll(Context *rsc, RsAllocationUsageType src); @@ -126,6 +126,11 @@ public: protected: Vector<const Program *> mToDirtyList; + ObjectBaseRef<const Type> mType; + void setType(const Type *t) { + mType.set(t); + mHal.state.type = t; + } private: void freeChildrenUnlocked(); diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index d6ab0da..71e1b91 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -364,7 +364,7 @@ void rsaElementGetNativeData(RsContext con, RsElement elem, } void rsaElementGetSubElements(RsContext con, RsElement elem, uint32_t *ids, - const char **names, uint32_t dataSize) { + const char **names, uint32_t *arraySizes, uint32_t dataSize) { Element *e = static_cast<Element *>(elem); rsAssert(e->getFieldCount() == dataSize); @@ -372,5 +372,6 @@ void rsaElementGetSubElements(RsContext con, RsElement elem, uint32_t *ids, e->getField(i)->incUserRef(); ids[i] = (uint32_t)e->getField(i); names[i] = e->getFieldName(i); + arraySizes[i] = e->getFieldArraySize(i); } } diff --git a/libs/rs/rsFBOCache.cpp b/libs/rs/rsFBOCache.cpp index c5c64c2..f4a8bc6 100644 --- a/libs/rs/rsFBOCache.cpp +++ b/libs/rs/rsFBOCache.cpp @@ -26,11 +26,14 @@ using namespace android::renderscript; FBOCache::FBOCache() { mDirty = true; mHal.state.colorTargetsCount = 1; - mHal.state.colorTargets = new ObjectBaseRef<Allocation>[mHal.state.colorTargetsCount]; + mHal.state.colorTargets = new Allocation*[mHal.state.colorTargetsCount]; + mColorTargets = new ObjectBaseRef<Allocation>[mHal.state.colorTargetsCount]; + resetAll(NULL); } FBOCache::~FBOCache() { delete[] mHal.state.colorTargets; + delete[] mColorTargets; } void FBOCache::init(Context *rsc) { @@ -52,7 +55,8 @@ void FBOCache::bindColorTarget(Context *rsc, Allocation *a, uint32_t slot) { return; } } - mHal.state.colorTargets[slot].set(a); + mColorTargets[slot].set(a); + mHal.state.colorTargets[slot] = a; mDirty = true; } @@ -63,15 +67,18 @@ void FBOCache::bindDepthTarget(Context *rsc, Allocation *a) { return; } } - mHal.state.depthTarget.set(a); + mDepthTarget.set(a); + mHal.state.depthTarget = a; mDirty = true; } void FBOCache::resetAll(Context *) { for (uint32_t i = 0; i < mHal.state.colorTargetsCount; i ++) { - mHal.state.colorTargets[i].set(NULL); + mColorTargets[i].set(NULL); + mHal.state.colorTargets[i] = NULL; } - mHal.state.depthTarget.set(NULL); + mDepthTarget.set(NULL); + mHal.state.depthTarget = NULL; mDirty = true; } diff --git a/libs/rs/rsFBOCache.h b/libs/rs/rsFBOCache.h index 5d58ba4..abb84de 100644 --- a/libs/rs/rsFBOCache.h +++ b/libs/rs/rsFBOCache.h @@ -44,15 +44,17 @@ public: mutable void *drv; struct State { - ObjectBaseRef<Allocation> *colorTargets; + Allocation **colorTargets; uint32_t colorTargetsCount; - ObjectBaseRef<Allocation> depthTarget; + Allocation *depthTarget; }; State state; }; Hal mHal; protected: + ObjectBaseRef<Allocation> *mColorTargets; + ObjectBaseRef<Allocation> mDepthTarget; bool mDirty; void checkError(Context *); void setColorAttachment(Context *rsc); diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp index 359d09f..bf9284f 100644 --- a/libs/rs/rsMesh.cpp +++ b/libs/rs/rsMesh.cpp @@ -23,9 +23,14 @@ Mesh::Mesh(Context *rsc) : ObjectBase(rsc) { mHal.drv = NULL; mHal.state.primitives = NULL; mHal.state.primitivesCount = 0; + mHal.state.indexBuffers = NULL; + mHal.state.indexBuffersCount = 0; mHal.state.vertexBuffers = NULL; mHal.state.vertexBuffersCount = 0; mInitialized = false; + + mVertexBuffers = NULL; + mIndexBuffers = NULL; } Mesh::Mesh(Context *rsc, @@ -33,12 +38,23 @@ Mesh::Mesh(Context *rsc, uint32_t primitivesCount) : ObjectBase(rsc) { mHal.drv = NULL; mHal.state.primitivesCount = primitivesCount; - mHal.state.primitives = new Primitive_t *[mHal.state.primitivesCount]; + mHal.state.indexBuffersCount = primitivesCount; + mHal.state.primitives = new RsPrimitive[mHal.state.primitivesCount]; + mHal.state.indexBuffers = new Allocation *[mHal.state.indexBuffersCount]; for (uint32_t i = 0; i < mHal.state.primitivesCount; i ++) { - mHal.state.primitives[i] = new Primitive_t; + mHal.state.primitives[i] = RS_PRIMITIVE_POINT; + } + for (uint32_t i = 0; i < mHal.state.indexBuffersCount; i ++) { + mHal.state.indexBuffers[i] = NULL; } mHal.state.vertexBuffersCount = vertexBuffersCount; - mHal.state.vertexBuffers = new ObjectBaseRef<Allocation>[mHal.state.vertexBuffersCount]; + mHal.state.vertexBuffers = new Allocation *[mHal.state.vertexBuffersCount]; + for (uint32_t i = 0; i < mHal.state.vertexBuffersCount; i ++) { + mHal.state.vertexBuffers[i] = NULL; + } + + mVertexBuffers = new ObjectBaseRef<Allocation>[mHal.state.vertexBuffersCount]; + mIndexBuffers = new ObjectBaseRef<Allocation>[mHal.state.primitivesCount]; } Mesh::~Mesh() { @@ -46,17 +62,12 @@ Mesh::~Mesh() { mRSC->mHal.funcs.mesh.destroy(mRSC, this); #endif - if (mHal.state.vertexBuffers) { - delete[] mHal.state.vertexBuffers; - } + delete[] mHal.state.vertexBuffers; + delete[] mHal.state.primitives; + delete[] mHal.state.indexBuffers; - if (mHal.state.primitives) { - for (uint32_t i = 0; i < mHal.state.primitivesCount; i ++) { - mHal.state.primitives[i]->mIndexBuffer.clear(); - delete mHal.state.primitives[i]; - } - delete[] mHal.state.primitives; - } + delete[] mVertexBuffers; + delete[] mIndexBuffers; } void Mesh::init() { @@ -81,13 +92,11 @@ void Mesh::serialize(OStream *stream) const { stream->addU32(mHal.state.primitivesCount); // Store the primitives for (uint32_t pCount = 0; pCount < mHal.state.primitivesCount; pCount ++) { - Primitive_t * prim = mHal.state.primitives[pCount]; + stream->addU8((uint8_t)mHal.state.primitives[pCount]); - stream->addU8((uint8_t)prim->mPrimitive); - - if (prim->mIndexBuffer.get()) { + if (mHal.state.indexBuffers[pCount]) { stream->addU32(1); - prim->mIndexBuffer->serialize(stream); + mHal.state.indexBuffers[pCount]->serialize(stream); } else { stream->addU32(0); } @@ -173,10 +182,8 @@ void Mesh::renderPrimitive(Context *rsc, uint32_t primIndex) const { return; } - Primitive_t *prim = mHal.state.primitives[primIndex]; - - if (prim->mIndexBuffer.get()) { - renderPrimitiveRange(rsc, primIndex, 0, prim->mIndexBuffer->getType()->getDimX()); + if (mHal.state.indexBuffers[primIndex]) { + renderPrimitiveRange(rsc, primIndex, 0, mHal.state.indexBuffers[primIndex]->getType()->getDimX()); return; } @@ -194,14 +201,14 @@ void Mesh::renderPrimitiveRange(Context *rsc, uint32_t primIndex, uint32_t start void Mesh::uploadAll(Context *rsc) { for (uint32_t ct = 0; ct < mHal.state.vertexBuffersCount; ct ++) { - if (mHal.state.vertexBuffers[ct].get()) { - rsc->mHal.funcs.allocation.markDirty(rsc, mHal.state.vertexBuffers[ct].get()); + if (mHal.state.vertexBuffers[ct]) { + rsc->mHal.funcs.allocation.markDirty(rsc, mHal.state.vertexBuffers[ct]); } } for (uint32_t ct = 0; ct < mHal.state.primitivesCount; ct ++) { - if (mHal.state.primitives[ct]->mIndexBuffer.get()) { - rsc->mHal.funcs.allocation.markDirty(rsc, mHal.state.primitives[ct]->mIndexBuffer.get()); + if (mHal.state.indexBuffers[ct]) { + rsc->mHal.funcs.allocation.markDirty(rsc, mHal.state.indexBuffers[ct]); } } } @@ -290,7 +297,7 @@ void rsaMeshGetVertices(RsContext con, RsMesh mv, RsAllocation *vtxData, uint32_ rsAssert(vtxDataCount == sm->mHal.state.vertexBuffersCount); for (uint32_t ct = 0; ct < vtxDataCount; ct ++) { - vtxData[ct] = sm->mHal.state.vertexBuffers[ct].get(); + vtxData[ct] = sm->mHal.state.vertexBuffers[ct]; sm->mHal.state.vertexBuffers[ct]->incUserRef(); } } @@ -300,10 +307,10 @@ void rsaMeshGetIndices(RsContext con, RsMesh mv, RsAllocation *va, uint32_t *pri rsAssert(idxDataCount == sm->mHal.state.primitivesCount); for (uint32_t ct = 0; ct < idxDataCount; ct ++) { - va[ct] = sm->mHal.state.primitives[ct]->mIndexBuffer.get(); - primType[ct] = sm->mHal.state.primitives[ct]->mPrimitive; - if (sm->mHal.state.primitives[ct]->mIndexBuffer.get()) { - sm->mHal.state.primitives[ct]->mIndexBuffer->incUserRef(); + va[ct] = sm->mHal.state.indexBuffers[ct]; + primType[ct] = sm->mHal.state.primitives[ct]; + if (sm->mHal.state.indexBuffers[ct]) { + sm->mHal.state.indexBuffers[ct]->incUserRef(); } } } diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h index ed1e93d..0fc73fb 100644 --- a/libs/rs/rsMesh.h +++ b/libs/rs/rsMesh.h @@ -32,13 +32,6 @@ public: Mesh(Context *, uint32_t vertexBuffersCount, uint32_t primitivesCount); ~Mesh(); - // 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 { - ObjectBaseRef<Allocation> mIndexBuffer; - RsPrimitive mPrimitive; - }; - virtual void serialize(OStream *stream) const; virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_MESH; } static Mesh *createFromStream(Context *rsc, IStream *stream); @@ -51,10 +44,13 @@ public: // Contains vertex data // Position, normal, texcoord, etc could either be strided in one allocation // of provided separetely in multiple ones - ObjectBaseRef<Allocation> *vertexBuffers; + Allocation **vertexBuffers; uint32_t vertexBuffersCount; - Primitive_t ** primitives; + // indexBuffers[i] could be NULL, in which case only primitives[i] is used + Allocation **indexBuffers; + uint32_t indexBuffersCount; + RsPrimitive *primitives; uint32_t primitivesCount; }; State state; @@ -62,12 +58,14 @@ public: Hal mHal; void setVertexBuffer(Allocation *vb, uint32_t index) { - mHal.state.vertexBuffers[index].set(vb); + mVertexBuffers[index].set(vb); + mHal.state.vertexBuffers[index] = vb; } void setPrimitive(Allocation *idx, RsPrimitive prim, uint32_t index) { - mHal.state.primitives[index]->mIndexBuffer.set(idx); - mHal.state.primitives[index]->mPrimitive = prim; + mIndexBuffers[index].set(idx); + mHal.state.indexBuffers[index] = idx; + mHal.state.primitives[index] = prim; } void render(Context *) const; @@ -80,6 +78,8 @@ public: float mBBoxMax[3]; void computeBBox(); protected: + ObjectBaseRef<Allocation> *mVertexBuffers; + ObjectBaseRef<Allocation> *mIndexBuffers; bool mInitialized; }; diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp index 4178aa5..a9fd877 100644 --- a/libs/rs/rsProgram.cpp +++ b/libs/rs/rsProgram.cpp @@ -37,22 +37,33 @@ Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength, } } - mHal.state.textures = new ObjectBaseRef<Allocation>[mHal.state.texturesCount]; - mHal.state.samplers = new ObjectBaseRef<Sampler>[mHal.state.texturesCount]; + mTextures = new ObjectBaseRef<Allocation>[mHal.state.texturesCount]; + mSamplers = new ObjectBaseRef<Sampler>[mHal.state.texturesCount]; + mInputElements = new ObjectBaseRef<Element>[mHal.state.inputElementsCount]; + mConstantTypes = new ObjectBaseRef<Type>[mHal.state.constantsCount]; + mConstants = new ObjectBaseRef<Allocation>[mHal.state.constantsCount]; + + mHal.state.textures = new Allocation*[mHal.state.texturesCount]; + mHal.state.samplers = new Sampler*[mHal.state.texturesCount]; mHal.state.textureTargets = new RsTextureTarget[mHal.state.texturesCount]; - mHal.state.inputElements = new ObjectBaseRef<Element>[mHal.state.inputElementsCount]; - mHal.state.constantTypes = new ObjectBaseRef<Type>[mHal.state.constantsCount]; - mHal.state.constants = new ObjectBaseRef<Allocation>[mHal.state.constantsCount]; + mHal.state.inputElements = new Element*[mHal.state.inputElementsCount]; + mHal.state.constantTypes = new Type*[mHal.state.constantsCount]; + mHal.state.constants = new Allocation*[mHal.state.constantsCount]; + + // Will initialize everything + freeChildren(); uint32_t input = 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) { - mHal.state.inputElements[input++].set(reinterpret_cast<Element *>(params[ct+1])); + mInputElements[input].set(reinterpret_cast<Element *>(params[ct+1])); + mHal.state.inputElements[input++] = reinterpret_cast<Element *>(params[ct+1]); } if (params[ct] == RS_PROGRAM_PARAM_CONSTANT) { - mHal.state.constantTypes[constant++].set(reinterpret_cast<Type *>(params[ct+1])); + mConstantTypes[constant].set(reinterpret_cast<Type *>(params[ct+1])); + mHal.state.constantTypes[constant++] = reinterpret_cast<Type *>(params[ct+1]); } if (params[ct] == RS_PROGRAM_PARAM_TEXTURE_TYPE) { mHal.state.textureTargets[texture++] = (RsTextureTarget)params[ct+1]; @@ -72,6 +83,12 @@ Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength, Program::~Program() { freeChildren(); + delete[] mTextures; + delete[] mSamplers; + delete[] mInputElements; + delete[] mConstantTypes; + delete[] mConstants; + delete[] mHal.state.textures; delete[] mHal.state.samplers; delete[] mHal.state.textureTargets; @@ -110,6 +127,12 @@ void Program::initMemberVars() { mHal.state.constantsCount = 0; mHal.state.texturesCount = 0; + mTextures = NULL; + mSamplers = NULL; + mInputElements = NULL; + mConstantTypes = NULL; + mConstants = NULL; + mIsInternal = false; } @@ -121,20 +144,21 @@ void Program::bindAllocation(Context *rsc, Allocation *alloc, uint32_t slot) { rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation"); return; } - if (alloc->getType() != mHal.state.constantTypes[slot].get()) { + if (alloc->getType() != 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 (mHal.state.constants[slot].get() == alloc) { + if (mConstants[slot].get() == alloc) { return; } - if (mHal.state.constants[slot].get()) { - mHal.state.constants[slot].get()->removeProgramToDirty(this); + if (mConstants[slot].get()) { + mConstants[slot]->removeProgramToDirty(this); } - mHal.state.constants[slot].set(alloc); + mConstants[slot].set(alloc); + mHal.state.constants[slot] = alloc; if (alloc) { alloc->addProgramToDirty(this); } @@ -154,7 +178,9 @@ void Program::bindTexture(Context *rsc, uint32_t slot, Allocation *a) { return; } - mHal.state.textures[slot].set(a); + mTextures[slot].set(a); + mHal.state.textures[slot] = a; + mDirty = true; } @@ -165,7 +191,8 @@ void Program::bindSampler(Context *rsc, uint32_t slot, Sampler *s) { return; } - mHal.state.samplers[slot].set(s); + mSamplers[slot].set(s); + mHal.state.samplers[slot] = s; mDirty = true; } diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h index 3237a72..06fc3ec 100644 --- a/libs/rs/rsProgram.h +++ b/libs/rs/rsProgram.h @@ -30,19 +30,6 @@ namespace renderscript { class Program : public ProgramBase { public: - - Program(Context *, const char * shaderText, uint32_t shaderLength, - const uint32_t * params, uint32_t paramLength); - virtual ~Program(); - virtual bool freeChildren(); - - void bindAllocation(Context *, Allocation *, uint32_t slot); - - bool isUserProgram() const {return !mIsInternal;} - - void bindTexture(Context *, uint32_t slot, Allocation *); - void bindSampler(Context *, uint32_t slot, Sampler *); - struct Hal { mutable void *drv; @@ -53,25 +40,43 @@ public: // and filtered. // // Constants are strictly accessed by the shader code - ObjectBaseRef<Allocation> *textures; + Allocation **textures; RsTextureTarget *textureTargets; uint32_t texturesCount; - ObjectBaseRef<Sampler> *samplers; + Sampler **samplers; uint32_t samplersCount; - ObjectBaseRef<Allocation> *constants; - ObjectBaseRef<Type> *constantTypes; + Allocation **constants; + Type **constantTypes; uint32_t constantsCount; - ObjectBaseRef<Element> *inputElements; + Element **inputElements; uint32_t inputElementsCount; }; State state; }; Hal mHal; + Program(Context *, const char * shaderText, uint32_t shaderLength, + const uint32_t * params, uint32_t paramLength); + virtual ~Program(); + virtual bool freeChildren(); + + void bindAllocation(Context *, Allocation *, uint32_t slot); + + bool isUserProgram() const {return !mIsInternal;} + + void bindTexture(Context *, uint32_t slot, Allocation *); + void bindSampler(Context *, uint32_t slot, Sampler *); + protected: + ObjectBaseRef<Allocation> *mTextures; + ObjectBaseRef<Sampler> *mSamplers; + ObjectBaseRef<Allocation> *mConstants; + ObjectBaseRef<Type> *mConstantTypes; + ObjectBaseRef<Element> *mInputElements; + bool mIsInternal; String8 mUserShader; void initMemberVars(); diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp index ff29520..81eedc4 100644 --- a/libs/rs/rsProgramFragment.cpp +++ b/libs/rs/rsProgramFragment.cpp @@ -42,7 +42,7 @@ void ProgramFragment::setConstantColor(Context *rsc, float r, float g, float b, rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set fixed function emulation color on user program"); return; } - if (mHal.state.constants[0].get() == NULL) { + if (mHal.state.constants[0] == 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; @@ -62,7 +62,7 @@ void ProgramFragment::setup(Context *rsc, ProgramFragmentState *state) { state->mLast.set(this); for (uint32_t ct=0; ct < mHal.state.texturesCount; ct++) { - if (!mHal.state.textures[ct].get()) { + if (!mHal.state.textures[ct]) { LOGE("No texture bound for shader id %u, texture unit %u", (uint)this, ct); rsc->setError(RS_ERROR_BAD_SHADER, "No texture bound"); continue; diff --git a/libs/rs/rsProgramRaster.h b/libs/rs/rsProgramRaster.h index 20af30a..c552ea3 100644 --- a/libs/rs/rsProgramRaster.h +++ b/libs/rs/rsProgramRaster.h @@ -24,17 +24,16 @@ namespace android { namespace renderscript { class ProgramRasterState; - +/***************************************************************************** + * CAUTION + * + * Any layout changes for this class may require a corresponding change to be + * made to frameworks/compile/libbcc/lib/ScriptCRT/rs_core.c, which contains + * a partial copy of the information below. + * + *****************************************************************************/ class ProgramRaster : public ProgramBase { public: - virtual void setup(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); - - static ObjectBaseRef<ProgramRaster> getProgramRaster(Context *rsc, - bool pointSprite, - RsCullMode cull); struct Hal { mutable void *drv; @@ -46,6 +45,14 @@ public: }; Hal mHal; + virtual void setup(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); + + static ObjectBaseRef<ProgramRaster> getProgramRaster(Context *rsc, + bool pointSprite, + RsCullMode cull); protected: virtual void preDestroy() const; virtual ~ProgramRaster(); diff --git a/libs/rs/rsProgramStore.h b/libs/rs/rsProgramStore.h index e21f039..9bb2795 100644 --- a/libs/rs/rsProgramStore.h +++ b/libs/rs/rsProgramStore.h @@ -25,23 +25,16 @@ namespace android { namespace renderscript { class ProgramStoreState; - +/***************************************************************************** + * CAUTION + * + * Any layout changes for this class may require a corresponding change to be + * made to frameworks/compile/libbcc/lib/ScriptCRT/rs_core.c, which contains + * a partial copy of the information below. + * + *****************************************************************************/ class ProgramStore : public ProgramBase { public: - virtual void setup(const Context *, ProgramStoreState *); - - virtual void serialize(OStream *stream) const; - virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_STORE; } - static ProgramStore *createFromStream(Context *rsc, IStream *stream); - static ObjectBaseRef<ProgramStore> getProgramStore(Context *, - bool colorMaskR, bool colorMaskG, - bool colorMaskB, bool colorMaskA, - bool depthMask, bool ditherEnable, - RsBlendSrcFunc srcFunc, RsBlendDstFunc destFunc, - RsDepthFunc depthFunc); - - void init(); - struct Hal { mutable void *drv; @@ -64,6 +57,18 @@ public: }; Hal mHal; + virtual void setup(const Context *, ProgramStoreState *); + + virtual void serialize(OStream *stream) const; + virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_STORE; } + static ProgramStore *createFromStream(Context *rsc, IStream *stream); + static ObjectBaseRef<ProgramStore> getProgramStore(Context *, + bool colorMaskR, bool colorMaskG, + bool colorMaskB, bool colorMaskA, + bool depthMask, bool ditherEnable, + RsBlendSrcFunc srcFunc, RsBlendDstFunc destFunc, + RsDepthFunc depthFunc); + void init(); protected: virtual void preDestroy() const; virtual ~ProgramStore(); diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp index 51cb2a8a..4a13622 100644 --- a/libs/rs/rsProgramVertex.cpp +++ b/libs/rs/rsProgramVertex.cpp @@ -38,7 +38,7 @@ void ProgramVertex::setup(Context *rsc, ProgramVertexState *state) { } if (!isUserProgram()) { - if (mHal.state.constants[0].get() == NULL) { + if (mHal.state.constants[0] == NULL) { rsc->setError(RS_ERROR_FATAL_UNKNOWN, "Unable to set fixed function emulation matrices because allocation is missing"); return; @@ -65,7 +65,7 @@ void ProgramVertex::setProjectionMatrix(Context *rsc, const rsc_Matrix *m) const "Attempting to set fixed function emulation matrix projection on user program"); return; } - if (mHal.state.constants[0].get() == NULL) { + if (mHal.state.constants[0] == NULL) { rsc->setError(RS_ERROR_FATAL_UNKNOWN, "Unable to set fixed function emulation matrix projection because allocation is missing"); return; @@ -81,7 +81,7 @@ void ProgramVertex::setModelviewMatrix(Context *rsc, const rsc_Matrix *m) const "Attempting to set fixed function emulation matrix modelview on user program"); return; } - if (mHal.state.constants[0].get() == NULL) { + if (mHal.state.constants[0] == NULL) { rsc->setError(RS_ERROR_FATAL_UNKNOWN, "Unable to set fixed function emulation matrix modelview because allocation is missing"); return; @@ -97,7 +97,7 @@ void ProgramVertex::setTextureMatrix(Context *rsc, const rsc_Matrix *m) const { "Attempting to set fixed function emulation matrix texture on user program"); return; } - if (mHal.state.constants[0].get() == NULL) { + if (mHal.state.constants[0] == NULL) { rsc->setError(RS_ERROR_FATAL_UNKNOWN, "Unable to set fixed function emulation matrix texture because allocation is missing"); return; @@ -113,7 +113,7 @@ void ProgramVertex::getProjectionMatrix(Context *rsc, rsc_Matrix *m) const { "Attempting to get fixed function emulation matrix projection on user program"); return; } - if (mHal.state.constants[0].get() == NULL) { + if (mHal.state.constants[0] == NULL) { rsc->setError(RS_ERROR_FATAL_UNKNOWN, "Unable to get fixed function emulation matrix projection because allocation is missing"); return; diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h index e698132..654cd9c 100644 --- a/libs/rs/rsSampler.h +++ b/libs/rs/rsSampler.h @@ -27,23 +27,16 @@ namespace renderscript { const static uint32_t RS_MAX_SAMPLER_SLOT = 16; class SamplerState; - +/***************************************************************************** + * CAUTION + * + * Any layout changes for this class may require a corresponding change to be + * made to frameworks/compile/libbcc/lib/ScriptCRT/rs_core.c, which contains + * a partial copy of the information below. + * + *****************************************************************************/ class Sampler : public ObjectBase { public: - static ObjectBaseRef<Sampler> getSampler(Context *, - RsSamplerValue magFilter, - RsSamplerValue minFilter, - RsSamplerValue wrapS, - RsSamplerValue wrapT, - RsSamplerValue wrapR, - float aniso = 1.0f); - 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); - struct Hal { mutable void *drv; @@ -59,6 +52,20 @@ public: }; Hal mHal; + static ObjectBaseRef<Sampler> getSampler(Context *, + RsSamplerValue magFilter, + RsSamplerValue minFilter, + RsSamplerValue wrapS, + RsSamplerValue wrapT, + RsSamplerValue wrapR, + float aniso = 1.0f); + 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: int32_t mBoundSlot; diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index 2e7f213..cd7b3a7 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -21,6 +21,7 @@ #ifndef ANDROID_RS_SERIALIZE #include <bcinfo/BitcodeTranslator.h> +#include <bcinfo/BitcodeWrapper.h> #endif using namespace android; @@ -196,7 +197,24 @@ bool ScriptC::runCompiler(Context *rsc, //LOGE("runCompiler %p %p %p %p %p %i", rsc, this, resName, cacheDir, bitcode, bitcodeLen); #ifndef ANDROID_RS_SERIALIZE - uint32_t sdkVersion = rsc->getTargetSdkVersion(); + uint32_t sdkVersion = 0; + bcinfo::BitcodeWrapper bcWrapper((const char *)bitcode, bitcodeLen); + if (!bcWrapper.unwrap()) { + LOGE("Bitcode is not in proper container format (raw or wrapper)"); + return false; + } + + rsAssert(bcWrapper.getHeaderVersion() == 0); + if (bcWrapper.getBCFileType() == bcinfo::BC_WRAPPER) { + sdkVersion = bcWrapper.getTargetAPI(); + } + + if (sdkVersion == 0) { + // This signals that we didn't have a wrapper containing information + // about the bitcode. + sdkVersion = rsc->getTargetSdkVersion(); + } + if (BT) { delete BT; } diff --git a/libs/rs/scriptc/rs_allocation.rsh b/libs/rs/scriptc/rs_allocation.rsh index 154a099..9ec03bf 100644 --- a/libs/rs/scriptc/rs_allocation.rsh +++ b/libs/rs/scriptc/rs_allocation.rsh @@ -14,6 +14,31 @@ * limitations under the License. */ +/*! \mainpage notitle + * + * Renderscript is a high-performance runtime that provides graphics rendering and + * compute operations at the native level. Renderscript code is compiled on devices + * at runtime to allow platform-independence as well. + * This reference documentation describes the Renderscript runtime APIs, which you + * can utilize to write Renderscript code in C99. The Renderscript header + * files are automatically included for you, except for the rs_graphics.rsh header. If + * you are doing graphics rendering, include the graphics header file like this: + * + * <code>#include "rs_graphics.rsh"</code> + * + * To use Renderscript, you need to utilize the Renderscript runtime APIs documented here + * as well as the Android framework APIs for Renderscript. + * For documentation on the Android framework APIs, see the <a target="_parent" href= + * "http://developer.android.com/reference/android/renderscript/package-summary.html"> + * android.renderscript</a> package reference. + * For more information on how to develop with Renderscript and how the runtime and + * Android framework APIs interact, see the <a target="_parent" href= + * "http://developer.android.com/guide/topics/renderscript/index.html">Renderscript + * developer guide</a> and the <a target="_parent" href= + * "http://developer.android.com/resources/samples/RenderScript/index.html"> + * Renderscript samples</a>. + */ + /** @file rs_allocation.rsh * \brief Allocation routines * diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh index 3e9339e..2581953 100644 --- a/libs/rs/scriptc/rs_graphics.rsh +++ b/libs/rs/scriptc/rs_graphics.rsh @@ -22,7 +22,6 @@ */ #ifndef __RS_GRAPHICS_RSH__ #define __RS_GRAPHICS_RSH__ - #if (defined(RS_VERSION) && (RS_VERSION >= 14)) /** * Set the color target used for all subsequent rendering calls diff --git a/libs/rs/scriptc/rs_quaternion.rsh b/libs/rs/scriptc/rs_quaternion.rsh index 23945ae..4e08d2f 100644 --- a/libs/rs/scriptc/rs_quaternion.rsh +++ b/libs/rs/scriptc/rs_quaternion.rsh @@ -66,19 +66,6 @@ rsQuaternionMultiply(rs_quaternion *q, float s) { } /** - * Multiply quaternion by another quaternion - * @param q destination quaternion - * @param rhs right hand side quaternion to multiply by - */ -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; -} - -/** * Add two quaternions * @param q destination quaternion to add to * @param rsh right hand side quaternion to add @@ -168,6 +155,23 @@ rsQuaternionNormalize(rs_quaternion *q) { } /** + * Multiply quaternion by another quaternion + * @param q destination quaternion + * @param rhs right hand side quaternion to multiply by + */ +static void __attribute__((overloadable)) +rsQuaternionMultiply(rs_quaternion *q, const rs_quaternion *rhs) { + rs_quaternion qtmp; + rsQuaternionSet(&qtmp, q); + + q->w = qtmp.w*rhs->w - qtmp.x*rhs->x - qtmp.y*rhs->y - qtmp.z*rhs->z; + q->x = qtmp.w*rhs->x + qtmp.x*rhs->w + qtmp.y*rhs->z - qtmp.z*rhs->y; + q->y = qtmp.w*rhs->y + qtmp.y*rhs->w + qtmp.z*rhs->x - qtmp.x*rhs->z; + q->z = qtmp.w*rhs->z + qtmp.z*rhs->w + qtmp.x*rhs->y - qtmp.y*rhs->x; + rsQuaternionNormalize(q); +} + +/** * Performs spherical linear interpolation between two quaternions * @param q result quaternion from interpolation * @param q0 first param @@ -222,34 +226,26 @@ rsQuaternionSlerp(rs_quaternion *q, const rs_quaternion *q0, const rs_quaternion * @param p normalized quaternion */ 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; + float xx = q->x * q->x; + float xy = q->x * q->y; + float xz = q->x * q->z; + float xw = q->x * q->w; + float yy = q->y * q->y; + float yz = q->y * q->z; + float yw = q->y * q->w; + float zz = q->z * q->z; + float zw = q->z * q->w; + + m->m[0] = 1.0f - 2.0f * ( yy + zz ); + m->m[4] = 2.0f * ( xy - zw ); + m->m[8] = 2.0f * ( xz + yw ); + m->m[1] = 2.0f * ( xy + zw ); + m->m[5] = 1.0f - 2.0f * ( xx + zz ); + m->m[9] = 2.0f * ( yz - xw ); + m->m[2] = 2.0f * ( xz - yw ); + m->m[6] = 2.0f * ( yz + xw ); + m->m[10] = 1.0f - 2.0f * ( xx + yy ); + m->m[3] = m->m[7] = m->m[11] = m->m[12] = m->m[13] = m->m[14] = 0.0f; m->m[15] = 1.0f; } diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp index 2decfe9..77f18de 100644 --- a/libs/ui/KeyCharacterMap.cpp +++ b/libs/ui/KeyCharacterMap.cpp @@ -124,17 +124,6 @@ status_t KeyCharacterMap::load(const String8& filename, KeyCharacterMap** outMap return status; } -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; } diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp index 600a951..10bb39c 100644 --- a/libs/ui/Keyboard.cpp +++ b/libs/ui/Keyboard.cpp @@ -173,50 +173,6 @@ bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, 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) { diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 638f72f..831d9e3 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -71,6 +71,10 @@ LOCAL_CFLAGS += -DMB_CUR_MAX=1 endif endif +ifeq ($(TARGET_OS),linux) +LOCAL_LDLIBS += -lrt -ldl +endif + include $(BUILD_HOST_STATIC_LIBRARY) diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp index 590576a..d38aae9 100644 --- a/libs/utils/BlobCache.cpp +++ b/libs/utils/BlobCache.cpp @@ -21,10 +21,20 @@ #include <string.h> #include <utils/BlobCache.h> +#include <utils/Errors.h> #include <utils/Log.h> namespace android { +// BlobCache::Header::mMagicNumber value +static const uint32_t blobCacheMagic = '_Bb$'; + +// BlobCache::Header::mBlobCacheVersion value +static const uint32_t blobCacheVersion = 1; + +// BlobCache::Header::mDeviceVersion value +static const uint32_t blobCacheDeviceVersion = 1; + BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize): mMaxKeySize(maxKeySize), mMaxValueSize(maxValueSize), @@ -67,12 +77,10 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, return; } - Mutex::Autolock lock(mMutex); sp<Blob> dummyKey(new Blob(key, keySize, false)); CacheEntry dummyEntry(dummyKey, NULL); while (true) { - ssize_t index = mCacheEntries.indexOf(dummyEntry); if (index < 0) { // Create a new cache entry. @@ -129,7 +137,6 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, keySize, mMaxKeySize); return 0; } - Mutex::Autolock lock(mMutex); sp<Blob> dummyKey(new Blob(key, keySize, false)); CacheEntry dummyEntry(dummyKey, NULL); ssize_t index = mCacheEntries.indexOf(dummyEntry); @@ -152,6 +159,133 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, return valueBlobSize; } +static inline size_t align4(size_t size) { + return (size + 3) & ~3; +} + +size_t BlobCache::getFlattenedSize() const { + size_t size = sizeof(Header); + for (size_t i = 0; i < mCacheEntries.size(); i++) { + const CacheEntry& e(mCacheEntries[i]); + sp<Blob> keyBlob = e.getKey(); + sp<Blob> valueBlob = e.getValue(); + size = align4(size); + size += sizeof(EntryHeader) + keyBlob->getSize() + + valueBlob->getSize(); + } + return size; +} + +size_t BlobCache::getFdCount() const { + return 0; +} + +status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count) + const { + if (count != 0) { + LOGE("flatten: nonzero fd count: %d", count); + return BAD_VALUE; + } + + // Write the cache header + if (size < sizeof(Header)) { + LOGE("flatten: not enough room for cache header"); + return BAD_VALUE; + } + Header* header = reinterpret_cast<Header*>(buffer); + header->mMagicNumber = blobCacheMagic; + header->mBlobCacheVersion = blobCacheVersion; + header->mDeviceVersion = blobCacheDeviceVersion; + header->mNumEntries = mCacheEntries.size(); + + // Write cache entries + uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer); + off_t byteOffset = align4(sizeof(Header)); + for (size_t i = 0; i < mCacheEntries.size(); i++) { + const CacheEntry& e(mCacheEntries[i]); + sp<Blob> keyBlob = e.getKey(); + sp<Blob> valueBlob = e.getValue(); + size_t keySize = keyBlob->getSize(); + size_t valueSize = valueBlob->getSize(); + + size_t entrySize = sizeof(EntryHeader) + keySize + valueSize; + if (byteOffset + entrySize > size) { + LOGE("flatten: not enough room for cache entries"); + return BAD_VALUE; + } + + EntryHeader* eheader = reinterpret_cast<EntryHeader*>( + &byteBuffer[byteOffset]); + eheader->mKeySize = keySize; + eheader->mValueSize = valueSize; + + memcpy(eheader->mData, keyBlob->getData(), keySize); + memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize); + + byteOffset += align4(entrySize); + } + + return OK; +} + +status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[], + size_t count) { + // All errors should result in the BlobCache being in an empty state. + mCacheEntries.clear(); + + if (count != 0) { + LOGE("unflatten: nonzero fd count: %d", count); + return BAD_VALUE; + } + + // Read the cache header + if (size < sizeof(Header)) { + LOGE("unflatten: not enough room for cache header"); + return BAD_VALUE; + } + const Header* header = reinterpret_cast<const Header*>(buffer); + if (header->mMagicNumber != blobCacheMagic) { + LOGE("unflatten: bad magic number: %d", header->mMagicNumber); + return BAD_VALUE; + } + if (header->mBlobCacheVersion != blobCacheVersion || + header->mDeviceVersion != blobCacheDeviceVersion) { + // We treat version mismatches as an empty cache. + return OK; + } + + // Read cache entries + const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer); + off_t byteOffset = align4(sizeof(Header)); + size_t numEntries = header->mNumEntries; + for (size_t i = 0; i < numEntries; i++) { + if (byteOffset + sizeof(EntryHeader) > size) { + mCacheEntries.clear(); + LOGE("unflatten: not enough room for cache entry headers"); + return BAD_VALUE; + } + + const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>( + &byteBuffer[byteOffset]); + size_t keySize = eheader->mKeySize; + size_t valueSize = eheader->mValueSize; + size_t entrySize = sizeof(EntryHeader) + keySize + valueSize; + + if (byteOffset + entrySize > size) { + mCacheEntries.clear(); + LOGE("unflatten: not enough room for cache entry headers"); + return BAD_VALUE; + } + + const uint8_t* data = eheader->mData; + set(data, keySize, data + keySize, valueSize); + + byteOffset += align4(entrySize); + } + + return OK; +} + long int BlobCache::blob_random() { #ifdef _WIN32 return rand(); @@ -179,7 +313,7 @@ BlobCache::Blob::Blob(const void* data, size_t size, bool copyData): mData(copyData ? malloc(size) : data), mSize(size), mOwnsData(copyData) { - if (copyData) { + if (data != NULL && copyData) { memcpy(const_cast<void*>(mData), data, size); } } diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp index 653ea5e..b64cc39 100644 --- a/libs/utils/tests/BlobCache_test.cpp +++ b/libs/utils/tests/BlobCache_test.cpp @@ -14,9 +14,13 @@ ** limitations under the License. */ +#include <fcntl.h> +#include <stdio.h> + #include <gtest/gtest.h> #include <utils/BlobCache.h> +#include <utils/Errors.h> namespace android { @@ -254,4 +258,164 @@ TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) { ASSERT_EQ(maxEntries/2 + 1, numCached); } +class BlobCacheFlattenTest : public BlobCacheTest { +protected: + virtual void SetUp() { + BlobCacheTest::SetUp(); + mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE); + } + + virtual void TearDown() { + mBC2.clear(); + BlobCacheTest::TearDown(); + } + + void roundTrip() { + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); + delete[] flat; + } + + sp<BlobCache> mBC2; +}; + +TEST_F(BlobCacheFlattenTest, FlattenOneValue) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + roundTrip(); + ASSERT_EQ(size_t(4), mBC2->get("abcd", 4, buf, 4)); + ASSERT_EQ('e', buf[0]); + ASSERT_EQ('f', buf[1]); + ASSERT_EQ('g', buf[2]); + ASSERT_EQ('h', buf[3]); +} + +TEST_F(BlobCacheFlattenTest, FlattenFullCache) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, &k, 1); + } + + roundTrip(); + + // Verify the deserialized cache + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + uint8_t v = 0xee; + ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1)); + ASSERT_EQ(k, v); + } +} + +TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, &k, 1); + } + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + delete[] flat; + + // Verify the cache that we just serialized + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + uint8_t v = 0xee; + ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1)); + ASSERT_EQ(k, v); + } +} + +TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) { + // Fill up the entire cache with 1 char key/value pairs. + const int maxEntries = MAX_TOTAL_SIZE / 2; + for (int i = 0; i < maxEntries; i++) { + uint8_t k = i; + mBC->set(&k, 1, &k, 1); + } + + size_t size = mBC->getFlattenedSize() - 1; + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size, NULL, 0)); + delete[] flat; +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + flat[1] = ~flat[1]; + + // Bad magic should cause an error. + ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size, NULL, 0)); + delete[] flat; + + // The error should cause the unflatten to result in an empty cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + flat[5] = ~flat[5]; + + // Version mismatches shouldn't cause errors, but should not use the + // serialized entries + ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); + delete[] flat; + + // The version mismatch should cause the unflatten to result in an empty + // cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + flat[10] = ~flat[10]; + + // Version mismatches shouldn't cause errors, but should not use the + // serialized entries + ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0)); + delete[] flat; + + // The version mismatch should cause the unflatten to result in an empty + // cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + +TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { + char buf[4] = { 0xee, 0xee, 0xee, 0xee }; + mBC->set("abcd", 4, "efgh", 4); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0)); + + // A buffer truncation shouldt cause an error + ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1, NULL, 0)); + delete[] flat; + + // The error should cause the unflatten to result in an empty cache + ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); +} + } // namespace android |