diff options
28 files changed, 1255 insertions, 754 deletions
diff --git a/camera/tests/CameraServiceTest/CameraServiceTest.cpp b/camera/tests/CameraServiceTest/CameraServiceTest.cpp index 41670af52a..cb695341e8 100644 --- a/camera/tests/CameraServiceTest/CameraServiceTest.cpp +++ b/camera/tests/CameraServiceTest/CameraServiceTest.cpp @@ -298,6 +298,7 @@ public: virtual sp<OverlayRef> createOverlay( uint32_t w, uint32_t h, int32_t format, int32_t orientation); virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage); + virtual status_t setBufferCount(int bufferCount); // new functions void clearStat(); @@ -339,6 +340,11 @@ sp<GraphicBuffer> MSurface::requestBuffer(int bufferIdx, int usage) { return NULL; } +status_t MSurface::setBufferCount(int bufferCount) { + INFO(__func__); + return NULL; +} + void MSurface::clearStat() { Mutex::Autolock _l(mLock); registerBuffersCount = 0; diff --git a/common/java/com/android/common/GoogleLogTags.logtags b/common/java/com/android/common/GoogleLogTags.logtags index a5c9bb0530..f848ddfd21 100644 --- a/common/java/com/android/common/GoogleLogTags.logtags +++ b/common/java/com/android/common/GoogleLogTags.logtags @@ -80,7 +80,7 @@ option java_package com.android.common 204004 gtalk_heartbeat_reset (interval_and_nt|1),(ip|3) # This event is logged when an Rmq v2 packet is sent or received. -204005 push_messaging (packet_type|1),(persistent_id|3),(stream_id|1),(last_stream_id|1) +204005 c2dm (packet_type|1),(persistent_id|3),(stream_id|1),(last_stream_id|1) ##### # Google Login Service and Setup Wizard diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h index 2504d39992..c23832d492 100644 --- a/include/private/surfaceflinger/SharedBufferStack.h +++ b/include/private/surfaceflinger/SharedBufferStack.h @@ -129,7 +129,7 @@ public: // ---------------------------------------------------------------------------- -// 4 KB max +// 32 KB max class SharedClient { public: @@ -166,7 +166,7 @@ public: protected: SharedClient* const mSharedClient; SharedBufferStack* const mSharedStack; - const int mNumBuffers; + int mNumBuffers; const int mIdentity; int32_t computeTail() const; @@ -217,6 +217,7 @@ public: bool needNewBuffer(int buffer) const; status_t setDirtyRegion(int buffer, const Region& reg); status_t setCrop(int buffer, const Rect& reg); + status_t setBufferCount(int bufferCount); private: friend struct Condition; @@ -269,13 +270,61 @@ public: status_t reallocate(); status_t assertReallocate(int buffer); int32_t getQueuedCount() const; - Region getDirtyRegion(int buffer) const; + status_t resize(int newNumBuffers); + SharedBufferStack::Statistics getStats() const; private: + /* + * BufferList is basically a fixed-capacity sorted-vector of + * unsigned 5-bits ints using a 32-bits int as storage. + * it has efficient iterators to find items in the list and not in the list. + */ + class BufferList { + size_t mCapacity; + uint32_t mList; + public: + BufferList(size_t c = NUM_BUFFER_MAX) : mCapacity(c), mList(0) { } + status_t add(int value); + status_t remove(int value); + + class const_iterator { + friend class BufferList; + uint32_t mask, curr; + const_iterator(uint32_t mask) : + mask(mask), curr(31 - __builtin_clz(mask)) { } + public: + inline bool operator == (const const_iterator& rhs) const { + return mask == rhs.mask; + } + inline bool operator != (const const_iterator& rhs) const { + return mask != rhs.mask; + } + inline int operator *() const { return curr; } + inline const const_iterator& operator ++(int) { + mask &= ~curr; + curr = 31 - __builtin_clz(mask); + return *this; + } + }; + + inline const_iterator begin() const { + return const_iterator(mList); + } + inline const_iterator end() const { + return const_iterator(0); + } + inline const_iterator free_begin() const { + uint32_t mask = (1 << (32-mCapacity)) - 1; + return const_iterator( ~(mList | mask) ); + } + }; + + BufferList mBufferList; + struct UnlockUpdate : public UpdateBase { const int lockedBuffer; inline UnlockUpdate(SharedBufferBase* sbb, int lockedBuffer); diff --git a/include/surfaceflinger/ISurface.h b/include/surfaceflinger/ISurface.h index 472f759237..9476686420 100644 --- a/include/surfaceflinger/ISurface.h +++ b/include/surfaceflinger/ISurface.h @@ -47,12 +47,14 @@ protected: POST_BUFFER, // one-way transaction CREATE_OVERLAY, REQUEST_BUFFER, + SET_BUFFER_COUNT, }; public: DECLARE_META_INTERFACE(Surface); virtual sp<GraphicBuffer> requestBuffer(int bufferIdx, int usage) = 0; + virtual status_t setBufferCount(int bufferCount) = 0; class BufferHeap { public: diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h index 7ab3a00144..973780f774 100644 --- a/include/surfaceflinger/Surface.h +++ b/include/surfaceflinger/Surface.h @@ -217,6 +217,7 @@ private: int connect(int api); int disconnect(int api); int crop(Rect const* rect); + int setBufferCount(int bufferCount); /* * private stuff... diff --git a/include/utils/ZipFileCRO.h b/include/utils/ZipFileCRO.h index 30e00368e9..e38bf669d7 100644 --- a/include/utils/ZipFileCRO.h +++ b/include/utils/ZipFileCRO.h @@ -47,8 +47,8 @@ extern ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zip, const char* fileName); extern bool ZipFileCRO_getEntryInfo(ZipFileCRO zip, ZipEntryCRO entry, - int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); + int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32); extern bool ZipFileCRO_uncompressEntry(ZipFileCRO zip, ZipEntryCRO entry, int fd); diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index 51c4f2fb6a..97d31f4db4 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -58,14 +58,19 @@ typedef void* ZipEntryRO; class ZipFileRO { public: ZipFileRO() - : mFd(-1), mFileMap(NULL), mHashTableSize(-1), mHashTable(NULL) + : mFd(-1), mFileName(NULL), mFileLength(-1), + mDirectoryMap(NULL), + mNumEntries(-1), mDirectoryOffset(-1), + mHashTableSize(-1), mHashTable(NULL) {} ~ZipFileRO() { free(mHashTable); - if (mFileMap) - mFileMap->release(); + if (mDirectoryMap) + mDirectoryMap->release(); if (mFd >= 0) close(mFd); + if (mFileName) + free(mFileName); } /* @@ -118,8 +123,8 @@ public: * Returns "false" if "entry" is bogus or if the data in the Zip file * appears to be bad. */ - bool getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; + bool getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const; /* * Create a new FileMap object that maps a subset of the archive. For @@ -155,13 +160,13 @@ public: * Utility function: uncompress deflated data, buffer to buffer. */ static bool inflateBuffer(void* outBuf, const void* inBuf, - long uncompLen, long compLen); + size_t uncompLen, size_t compLen); /* * Utility function: uncompress deflated data, buffer to fd. */ static bool inflateBuffer(int fd, const void* inBuf, - long uncompLen, long compLen); + size_t uncompLen, size_t compLen); /* * Some basic functions for raw data manipulation. "LE" means @@ -179,6 +184,9 @@ private: ZipFileRO(const ZipFileRO& src); ZipFileRO& operator=(const ZipFileRO& src); + /* locate and parse the central directory */ + bool mapCentralDirectory(void); + /* parse the archive, prepping internal structures */ bool parseZipArchive(void); @@ -203,12 +211,21 @@ private: /* open Zip archive */ int mFd; + /* zip file name */ + char* mFileName; + + /* length of file */ + size_t mFileLength; + /* mapped file */ - FileMap* mFileMap; + FileMap* mDirectoryMap; /* number of entries in the Zip archive */ int mNumEntries; + /* CD directory offset in the Zip archive */ + off_t mDirectoryOffset; + /* * We know how many entries are in the Zip archive, so we have a * fixed-size hash table. We probe for an empty slot. diff --git a/libs/audioflinger/AudioDumpInterface.cpp b/libs/audioflinger/AudioDumpInterface.cpp index a018b4c225..6c111148a9 100644 --- a/libs/audioflinger/AudioDumpInterface.cpp +++ b/libs/audioflinger/AudioDumpInterface.cpp @@ -32,7 +32,7 @@ namespace android { // ---------------------------------------------------------------------------- AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) - : mFirstHwOutput(true), mPolicyCommands(String8("")), mFileName(String8("")) + : mPolicyCommands(String8("")), mFileName(String8("")) { if(hw == 0) { LOGE("Dump construct hw = 0"); @@ -47,6 +47,11 @@ AudioDumpInterface::~AudioDumpInterface() for (size_t i = 0; i < mOutputs.size(); i++) { closeOutputStream((AudioStreamOut *)mOutputs[i]); } + + for (size_t i = 0; i < mInputs.size(); i++) { + closeInputStream((AudioStreamIn *)mInputs[i]); + } + if(mFinalInterface) delete mFinalInterface; } @@ -60,31 +65,32 @@ AudioStreamOut* AudioDumpInterface::openOutputStream( uint32_t lRate = 44100; - if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices) || mFirstHwOutput) { - outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status); - if (outFinal != 0) { - lFormat = outFinal->format(); - lChannels = outFinal->channels(); - lRate = outFinal->sampleRate(); - if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) { - mFirstHwOutput = false; - } - } + outFinal = mFinalInterface->openOutputStream(devices, format, channels, sampleRate, status); + if (outFinal != 0) { + lFormat = outFinal->format(); + lChannels = outFinal->channels(); + lRate = outFinal->sampleRate(); } else { - if (format != 0 && *format != 0) { - lFormat = *format; - } else { - lFormat = AudioSystem::PCM_16_BIT; + if (format != 0) { + if (*format != 0) { + lFormat = *format; + } else { + *format = lFormat; + } } - if (channels != 0 && *channels != 0) { - lChannels = *channels; - } else { - lChannels = AudioSystem::CHANNEL_OUT_STEREO; + if (channels != 0) { + if (*channels != 0) { + lChannels = *channels; + } else { + *channels = lChannels; + } } - if (sampleRate != 0 && *sampleRate != 0) { - lRate = *sampleRate; - } else { - lRate = 44100; + if (sampleRate != 0) { + if (*sampleRate != 0) { + lRate = *sampleRate; + } else { + *sampleRate = lRate; + } } if (status) *status = NO_ERROR; } @@ -111,7 +117,6 @@ void AudioDumpInterface::closeOutputStream(AudioStreamOut* out) dumpOut->standby(); if (dumpOut->finalStream() != NULL) { mFinalInterface->closeOutputStream(dumpOut->finalStream()); - mFirstHwOutput = true; } mOutputs.remove(dumpOut); @@ -126,18 +131,33 @@ AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO; uint32_t lRate = 8000; - - if (mInputs.size() == 0) { - inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); - if (inFinal == 0) return 0; - + inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); + if (inFinal != 0) { lFormat = inFinal->format(); lChannels = inFinal->channels(); lRate = inFinal->sampleRate(); } else { - if (format != 0 && *format != 0) lFormat = *format; - if (channels != 0 && *channels != 0) lChannels = *channels; - if (sampleRate != 0 && *sampleRate != 0) lRate = *sampleRate; + if (format != 0) { + if (*format != 0) { + lFormat = *format; + } else { + *format = lFormat; + } + } + if (channels != 0) { + if (*channels != 0) { + lChannels = *channels; + } else { + *channels = lChannels; + } + } + if (sampleRate != 0) { + if (*sampleRate != 0) { + lRate = *sampleRate; + } else { + *sampleRate = lRate; + } + } if (status) *status = NO_ERROR; } LOGV("openInputStream(), inFinal %p", inFinal); @@ -223,6 +243,15 @@ String8 AudioDumpInterface::getParameters(const String8& keys) return keyValuePairs; } +status_t AudioDumpInterface::setMode(int mode) +{ + return mFinalInterface->setMode(mode); +} + +size_t AudioDumpInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) +{ + return mFinalInterface->getInputBufferSize(sampleRate, format, channelCount); +} // ---------------------------------------------------------------------------- @@ -235,7 +264,7 @@ AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface, uint32_t sampleRate) : mInterface(interface), mId(id), mSampleRate(sampleRate), mFormat(format), mChannels(channels), mLatency(0), mDevice(devices), - mBufferSize(1024), mFinalStream(finalStream), mOutFile(0), mFileCount(0) + mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0) { LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); } @@ -254,26 +283,26 @@ ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) if (mFinalStream) { ret = mFinalStream->write(buffer, bytes); } else { - usleep((bytes * 1000000) / frameSize() / sampleRate()); + usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000); ret = bytes; } - if(!mOutFile) { + if(!mFile) { if (mInterface->fileName() != "") { char name[255]; - sprintf(name, "%s_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount); - mOutFile = fopen(name, "wb"); - LOGV("Opening dump file %s, fh %p", name, mOutFile); + sprintf(name, "%s_out_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount); + mFile = fopen(name, "wb"); + LOGV("Opening dump file %s, fh %p", name, mFile); } } - if (mOutFile) { - fwrite(buffer, bytes, 1, mOutFile); + if (mFile) { + fwrite(buffer, bytes, 1, mFile); } return ret; } status_t AudioStreamOutDump::standby() { - LOGV("AudioStreamOutDump standby(), mOutFile %p, mFinalStream %p", mOutFile, mFinalStream); + LOGV("AudioStreamOutDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream); Close(); if (mFinalStream != 0 ) return mFinalStream->standby(); @@ -330,7 +359,7 @@ status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs) } if (param.getInt(String8("format"), valueInt) == NO_ERROR) { - if (mOutFile == 0) { + if (mFile == 0) { mFormat = valueInt; } else { status = INVALID_OPERATION; @@ -345,7 +374,7 @@ status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs) } if (param.getInt(String8("sampling_rate"), valueInt) == NO_ERROR) { if (valueInt > 0 && valueInt <= 48000) { - if (mOutFile == 0) { + if (mFile == 0) { mSampleRate = valueInt; } else { status = INVALID_OPERATION; @@ -373,9 +402,9 @@ status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args) void AudioStreamOutDump::Close() { - if(mOutFile) { - fclose(mOutFile); - mOutFile = 0; + if(mFile) { + fclose(mFile); + mFile = 0; } } @@ -396,7 +425,7 @@ AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface, uint32_t sampleRate) : mInterface(interface), mId(id), mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices), - mBufferSize(1024), mFinalStream(finalStream), mInFile(0) + mBufferSize(1024), mFinalStream(finalStream), mFile(0), mFileCount(0) { LOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); } @@ -409,55 +438,68 @@ AudioStreamInDump::~AudioStreamInDump() ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes) { - if (mFinalStream) { - return mFinalStream->read(buffer, bytes); - } - - usleep((bytes * 1000000) / frameSize() / sampleRate()); + ssize_t ret; - if(!mInFile) { - char name[255]; - strcpy(name, "/sdcard/music/sine440"); - if (channels() == AudioSystem::CHANNEL_IN_MONO) { - strcat(name, "_mo"); - } else { - strcat(name, "_st"); + if (mFinalStream) { + ret = mFinalStream->read(buffer, bytes); + if(!mFile) { + if (mInterface->fileName() != "") { + char name[255]; + sprintf(name, "%s_in_%d_%d.pcm", mInterface->fileName().string(), mId, ++mFileCount); + mFile = fopen(name, "wb"); + LOGV("Opening input dump file %s, fh %p", name, mFile); + } } - if (format() == AudioSystem::PCM_16_BIT) { - strcat(name, "_16b"); - } else { - strcat(name, "_8b"); + if (mFile) { + fwrite(buffer, bytes, 1, mFile); } - if (sampleRate() < 16000) { - strcat(name, "_8k"); - } else if (sampleRate() < 32000) { - strcat(name, "_22k"); - } else if (sampleRate() < 48000) { - strcat(name, "_44k"); - } else { - strcat(name, "_48k"); - } - strcat(name, ".wav"); - mInFile = fopen(name, "rb"); - LOGV("Opening dump file %s, fh %p", name, mInFile); - if (mInFile) { - fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); + } else { + usleep((((bytes * 1000) / frameSize()) / sampleRate()) * 1000); + ret = bytes; + if(!mFile) { + char name[255]; + strcpy(name, "/sdcard/music/sine440"); + if (channels() == AudioSystem::CHANNEL_IN_MONO) { + strcat(name, "_mo"); + } else { + strcat(name, "_st"); + } + if (format() == AudioSystem::PCM_16_BIT) { + strcat(name, "_16b"); + } else { + strcat(name, "_8b"); + } + if (sampleRate() < 16000) { + strcat(name, "_8k"); + } else if (sampleRate() < 32000) { + strcat(name, "_22k"); + } else if (sampleRate() < 48000) { + strcat(name, "_44k"); + } else { + strcat(name, "_48k"); + } + strcat(name, ".wav"); + mFile = fopen(name, "rb"); + LOGV("Opening input read file %s, fh %p", name, mFile); + if (mFile) { + fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); + } } - - } - if (mInFile) { - ssize_t bytesRead = fread(buffer, bytes, 1, mInFile); - if (bytesRead != bytes) { - fseek(mInFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); - fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mInFile); + if (mFile) { + ssize_t bytesRead = fread(buffer, bytes, 1, mFile); + if (bytesRead >=0 && bytesRead < bytes) { + fseek(mFile, AUDIO_DUMP_WAVE_HDR_SIZE, SEEK_SET); + fread((uint8_t *)buffer+bytesRead, bytes-bytesRead, 1, mFile); + } } } - return bytes; + + return ret; } status_t AudioStreamInDump::standby() { - LOGV("AudioStreamInDump standby(), mInFile %p, mFinalStream %p", mInFile, mFinalStream); + LOGV("AudioStreamInDump standby(), mFile %p, mFinalStream %p", mFile, mFinalStream); Close(); if (mFinalStream != 0 ) return mFinalStream->standby(); @@ -523,9 +565,9 @@ status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args) void AudioStreamInDump::Close() { - if(mInFile) { - fclose(mInFile); - mInFile = 0; + if(mFile) { + fclose(mFile); + mFile = 0; } } }; // namespace android diff --git a/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h index 4c62b3ed78..814ce5f717 100644 --- a/libs/audioflinger/AudioDumpInterface.h +++ b/libs/audioflinger/AudioDumpInterface.h @@ -69,7 +69,7 @@ private: uint32_t mDevice; // current device this output is routed to size_t mBufferSize; AudioStreamOut *mFinalStream; - FILE *mOutFile; // output file + FILE *mFile; // output file int mFileCount; }; @@ -109,7 +109,8 @@ private: uint32_t mDevice; // current device this output is routed to size_t mBufferSize; AudioStreamIn *mFinalStream; - FILE *mInFile; // output file + FILE *mFile; // output file + int mFileCount; }; class AudioDumpInterface : public AudioHardwareBase @@ -134,6 +135,8 @@ public: virtual status_t setMasterVolume(float volume) {return mFinalInterface->setMasterVolume(volume);} + virtual status_t setMode(int mode); + // mic mute virtual status_t setMicMute(bool state) {return mFinalInterface->setMicMute(state);} @@ -143,6 +146,8 @@ public: virtual status_t setParameters(const String8& keyValuePairs); virtual String8 getParameters(const String8& keys); + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); + virtual AudioStreamIn* openInputStream(uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics); virtual void closeInputStream(AudioStreamIn* in); @@ -153,8 +158,7 @@ public: protected: AudioHardwareInterface *mFinalInterface; - SortedVector<AudioStreamOutDump *> mOutputs; - bool mFirstHwOutput; + SortedVector<AudioStreamOutDump *> mOutputs; SortedVector<AudioStreamInDump *> mInputs; Mutex mLock; String8 mPolicyCommands; diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index 86eb78d8dd..b8a0630ac6 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -13,6 +13,7 @@ LOCAL_SRC_FILES:= \ LayerDim.cpp \ MessageQueue.cpp \ SurfaceFlinger.cpp \ + TextureManager.cpp \ Tokenizer.cpp \ Transform.cpp diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index d979f00c3b..af89e9a9da 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -125,7 +125,6 @@ void DisplayHardware::init(uint32_t dpy) EGLint numConfigs=0; EGLSurface surface; EGLContext context; - mFlags = CACHED_BUFFERS; // TODO: all the extensions below should be queried through // eglGetProcAddress(). @@ -253,15 +252,6 @@ void DisplayHardware::init(uint32_t dpy) LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); -#if 0 - // for drivers that don't have proper support for flushing cached buffers - // on gralloc unlock, uncomment this block and test for the specific - // renderer substring - if (strstr(gl_renderer, "<some vendor string>")) { - LOGD("Assuming uncached graphics buffers."); - mFlags &= ~CACHED_BUFFERS; - } -#endif if (strstr(gl_extensions, "GL_ARB_texture_non_power_of_two")) { mFlags |= NPOT_EXTENSION; diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h index 897a6edd8b..ebd7c4271c 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -50,7 +50,6 @@ public: PARTIAL_UPDATES = 0x00020000, // video driver feature SLOW_CONFIG = 0x00040000, // software SWAP_RECTANGLE = 0x00080000, - CACHED_BUFFERS = 0x00100000 }; DisplayHardware( diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index e6658fa7ce..1fe997d415 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -48,41 +48,48 @@ template <typename T> inline T min(T a, T b) { // --------------------------------------------------------------------------- Layer::Layer(SurfaceFlinger* flinger, DisplayID display, - const sp<Client>& c, int32_t i) - : LayerBaseClient(flinger, display, c, i), + const sp<Client>& client, int32_t i) + : LayerBaseClient(flinger, display, client, i), + lcblk(NULL), mSecure(false), - mNoEGLImageForSwBuffers(false), mNeedsBlending(true), - mNeedsDithering(false) + mNeedsDithering(false), + mTextureManager(mFlags), + mBufferManager(mTextureManager) { // no OpenGL operation is possible here, since we might not be // in the OpenGL thread. - mFrontBufferIndex = lcblk->getFrontBuffer(); + lcblk = new SharedBufferServer( + client->ctrlblk, i, mBufferManager.getBufferCount(), + getIdentity()); + + mBufferManager.setActiveBufferIndex( lcblk->getFrontBuffer() ); } Layer::~Layer() { destroy(); // the actual buffers will be destroyed here + delete lcblk; +} + +// called with SurfaceFlinger::mStateLock as soon as the layer is entered +// in the purgatory list +void Layer::onRemoved() +{ + // wake up the condition + lcblk->setStatus(NO_INIT); } void Layer::destroy() { - for (size_t i=0 ; i<NUM_BUFFERS ; i++) { - if (mTextures[i].name != -1U) { - glDeleteTextures(1, &mTextures[i].name); - mTextures[i].name = -1U; - } - if (mTextures[i].image != EGL_NO_IMAGE_KHR) { - EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); - eglDestroyImageKHR(dpy, mTextures[i].image); - mTextures[i].image = EGL_NO_IMAGE_KHR; - } - Mutex::Autolock _l(mLock); - mBuffers[i].clear(); - mWidth = mHeight = 0; - } + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + mBufferManager.destroy(dpy); + mSurface.clear(); + + Mutex::Autolock _l(mLock); + mWidth = mHeight = 0; } sp<LayerBaseClient::Surface> Layer::createSurface() const @@ -126,24 +133,19 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, mHeight = h; mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; - mNoEGLImageForSwBuffers = !(hwFlags & DisplayHardware::CACHED_BUFFERS); // we use the red index int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED); int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED); mNeedsDithering = layerRedsize > displayRedSize; - for (size_t i=0 ; i<NUM_BUFFERS ; i++) { - mBuffers[i] = new GraphicBuffer(); - } mSurface = new SurfaceLayer(mFlinger, clientIndex(), this); return NO_ERROR; } void Layer::reloadTexture(const Region& dirty) { - Mutex::Autolock _l(mLock); - sp<GraphicBuffer> buffer(getFrontBufferLocked()); + sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer()); if (buffer == NULL) { // this situation can happen if we ran out of memory for instance. // not much we can do. continue to use whatever texture was bound @@ -151,118 +153,24 @@ void Layer::reloadTexture(const Region& dirty) return; } - const int index = mFrontBufferIndex; - - // create the new texture name if needed - if (UNLIKELY(mTextures[index].name == -1U)) { - mTextures[index].name = createTexture(); - mTextures[index].width = 0; - mTextures[index].height = 0; - } - #ifdef EGL_ANDROID_image_native_buffer if (mFlags & DisplayHardware::DIRECT_TEXTURE) { - if (buffer->usage & GraphicBuffer::USAGE_HW_TEXTURE) { - if (mTextures[index].dirty) { - if (initializeEglImage(buffer, &mTextures[index]) != NO_ERROR) { - // not sure what we can do here... - mFlags &= ~DisplayHardware::DIRECT_TEXTURE; - goto slowpath; - } - } - } else { - if (mHybridBuffer==0 || (mHybridBuffer->width != buffer->width || - mHybridBuffer->height != buffer->height)) { - mHybridBuffer.clear(); - mHybridBuffer = new GraphicBuffer( - buffer->width, buffer->height, buffer->format, - GraphicBuffer::USAGE_SW_WRITE_OFTEN | - GraphicBuffer::USAGE_HW_TEXTURE); - if (initializeEglImage( - mHybridBuffer, &mTextures[0]) != NO_ERROR) { - // not sure what we can do here... - mFlags &= ~DisplayHardware::DIRECT_TEXTURE; - mHybridBuffer.clear(); - goto slowpath; - } - } - - GGLSurface t; - status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); - LOGE_IF(res, "error %d (%s) locking buffer %p", - res, strerror(res), buffer.get()); - if (res == NO_ERROR) { - Texture* const texture(&mTextures[0]); - - glBindTexture(GL_TEXTURE_2D, texture->name); - - sp<GraphicBuffer> buf(mHybridBuffer); - void* vaddr; - res = buf->lock(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &vaddr); - if (res == NO_ERROR) { - int bpp = 0; - switch (t.format) { - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_RGBA_4444: - bpp = 2; - break; - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - bpp = 4; - break; - default: - if (isSupportedYuvFormat(t.format)) { - // just show the Y plane of YUV buffers - bpp = 1; - break; - } - // oops, we don't handle this format! - LOGE("layer %p, texture=%d, using format %d, which is not " - "supported by the GL", this, texture->name, t.format); - } - if (bpp) { - const Rect bounds(dirty.getBounds()); - size_t src_stride = t.stride; - size_t dst_stride = buf->stride; - if (src_stride == dst_stride && - bounds.width() == t.width && - bounds.height() == t.height) - { - memcpy(vaddr, t.data, t.height * t.stride * bpp); - } else { - GLubyte const * src = t.data + - (bounds.left + bounds.top * src_stride) * bpp; - GLubyte * dst = (GLubyte *)vaddr + - (bounds.left + bounds.top * dst_stride) * bpp; - const size_t length = bounds.width() * bpp; - size_t h = bounds.height(); - src_stride *= bpp; - dst_stride *= bpp; - while (h--) { - memcpy(dst, src, length); - dst += dst_stride; - src += src_stride; - } - } - } - buf->unlock(); - } - buffer->unlock(); - } + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + if (mBufferManager.initEglImage(dpy, buffer) != NO_ERROR) { + // not sure what we can do here... + mFlags &= ~DisplayHardware::DIRECT_TEXTURE; + goto slowpath; } } else #endif { slowpath: - for (size_t i=0 ; i<NUM_BUFFERS ; i++) { - mTextures[i].image = EGL_NO_IMAGE_KHR; - } GGLSurface t; status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_OFTEN); LOGE_IF(res, "error %d (%s) locking buffer %p", res, strerror(res), buffer.get()); if (res == NO_ERROR) { - loadTexture(&mTextures[0], dirty, t); + mBufferManager.loadTexture(dirty, t); buffer->unlock(); } } @@ -270,11 +178,8 @@ slowpath: void Layer::onDraw(const Region& clip) const { - int index = mFrontBufferIndex; - if (mTextures[index].image == EGL_NO_IMAGE_KHR) - index = 0; - GLuint textureName = mTextures[index].name; - if (UNLIKELY(textureName == -1LU)) { + Texture tex(mBufferManager.getActiveTexture()); + if (tex.name == -1LU) { // the texture has not been created yet, this Layer has // in fact never been drawn into. This happens frequently with // SurfaceView because the WindowManager can't know when the client @@ -300,7 +205,31 @@ void Layer::onDraw(const Region& clip) const } return; } - drawWithOpenGL(clip, mTextures[index]); + drawWithOpenGL(clip, tex); +} + + +status_t Layer::setBufferCount(int bufferCount) +{ + // this ensures our client doesn't go away while we're accessing + // the shared area. + sp<Client> ourClient(client.promote()); + if (ourClient == 0) { + // oops, the client is already gone + return DEAD_OBJECT; + } + + status_t err; + + // FIXME: resize() below is NOT thread-safe, we need to synchronize + // the users of lcblk in our process (ie: retire), and we assume the + // client is not mucking with the SharedStack, which is only enforced + // by construction, therefore we need to protect ourselves against + // buggy and malicious client (as always) + + err = lcblk->resize(bufferCount); + + return err; } sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) @@ -337,15 +266,7 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) Mutex::Autolock _l(mLock); w = mWidth; h = mHeight; - buffer = mBuffers[index]; - - // destroy() could have been called before we get here, we log it - // because it's uncommon, and the code below should handle it - LOGW_IF(buffer==0, - "mBuffers[%d] is null (mWidth=%d, mHeight=%d)", - index, w, h); - - mBuffers[index].clear(); + buffer = mBufferManager.detachBuffer(index); } const uint32_t effectiveUsage = getEffectiveUsage(usage); @@ -373,10 +294,7 @@ sp<GraphicBuffer> Layer::requestBuffer(int index, int usage) if (err == NO_ERROR && buffer->handle != 0) { Mutex::Autolock _l(mLock); if (mWidth && mHeight) { - // and we have new buffer - mBuffers[index] = buffer; - // texture is now dirty... - mTextures[index].dirty = true; + mBufferManager.attachBuffer(index, buffer); } else { // oops we got killed while we were allocating the buffer buffer.clear(); @@ -406,15 +324,8 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const } else { // it's allowed to modify the usage flags here, but generally // the requested flags should be honored. - if (mNoEGLImageForSwBuffers) { - if (usage & GraphicBuffer::USAGE_HW_MASK) { - // request EGLImage for h/w buffers only - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - } - } else { - // request EGLImage for all buffers - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - } + // request EGLImage for all buffers + usage |= GraphicBuffer::USAGE_HW_TEXTURE; } return usage; } @@ -428,13 +339,10 @@ uint32_t Layer::doTransaction(uint32_t flags) (front.requested_h != temp.requested_h)) { // the size changed, we need to ask our client to request a new buffer LOGD_IF(DEBUG_RESIZE, - "resize (layer=%p), requested (%dx%d), " - "drawing (%d,%d), (%dx%d), (%dx%d)", + "resize (layer=%p), requested (%dx%d), drawing (%d,%d)", this, int(temp.requested_w), int(temp.requested_h), - int(front.requested_w), int(front.requested_h), - int(mBuffers[0]->getWidth()), int(mBuffers[0]->getHeight()), - int(mBuffers[1]->getWidth()), int(mBuffers[1]->getHeight())); + int(front.requested_w), int(front.requested_h)); // we're being resized and there is a freeze display request, // acquire a freeze lock, so that the screen stays put @@ -486,22 +394,25 @@ void Layer::setDrawingSize(uint32_t w, uint32_t h) { void Layer::lockPageFlip(bool& recomputeVisibleRegions) { ssize_t buf = lcblk->retireAndLock(); - if (buf < NO_ERROR) { - //LOGW("nothing to retire (%s)", strerror(-buf)); - // NOTE: here the buffer is locked because we will used + if (buf == NOT_ENOUGH_DATA) { + // NOTE: This is not an error, it simply means there is nothing to + // retire. The buffer is locked because we will use it // for composition later in the loop return; } - // ouch, this really should never happen - if (uint32_t(buf)>=NUM_BUFFERS) { + if (buf < NO_ERROR) { LOGE("retireAndLock() buffer index (%d) out of range", buf); mPostedDirtyRegion.clear(); return; } // we retired a buffer, which becomes the new front buffer - mFrontBufferIndex = buf; + if (mBufferManager.setActiveBufferIndex(buf) < NO_ERROR) { + LOGE("retireAndLock() buffer index (%d) out of range", buf); + mPostedDirtyRegion.clear(); + return; + } // get the dirty region sp<GraphicBuffer> newFrontBuffer(getBuffer(buf)); @@ -593,10 +504,9 @@ void Layer::unlockPageFlip( void Layer::finishPageFlip() { - status_t err = lcblk->unlock( mFrontBufferIndex ); - LOGE_IF(err!=NO_ERROR, - "layer %p, buffer=%d wasn't locked!", - this, mFrontBufferIndex); + int buf = mBufferManager.getActiveBufferIndex(); + status_t err = lcblk->unlock( buf ); + LOGE_IF(err!=NO_ERROR, "layer %p, buffer=%d wasn't locked!", this, buf); } @@ -633,6 +543,109 @@ void Layer::dump(String8& result, char* buffer, size_t SIZE) const // --------------------------------------------------------------------------- +Layer::BufferManager::BufferManager(TextureManager& tm) + : mTextureManager(tm), mActiveBuffer(0), mFailover(false) +{ +} + +size_t Layer::BufferManager::getBufferCount() const { + return NUM_BUFFERS; +} + +// only for debugging +sp<GraphicBuffer> Layer::BufferManager::getBuffer(size_t index) const { + return mBufferData[index].buffer; +} + +status_t Layer::BufferManager::setActiveBufferIndex(size_t index) { + // TODO: need to validate 'index' + mActiveBuffer = index; + return NO_ERROR; +} + +size_t Layer::BufferManager::getActiveBufferIndex() const { + return mActiveBuffer; +} + +Texture Layer::BufferManager::getActiveTexture() const { + return mFailover ? mFailoverTexture : mBufferData[mActiveBuffer].texture; +} + +sp<GraphicBuffer> Layer::BufferManager::getActiveBuffer() const { + Mutex::Autolock _l(mLock); + return mBufferData[mActiveBuffer].buffer; +} + +sp<GraphicBuffer> Layer::BufferManager::detachBuffer(size_t index) +{ + sp<GraphicBuffer> buffer; + Mutex::Autolock _l(mLock); + buffer = mBufferData[index].buffer; + mBufferData[index].buffer = 0; + return buffer; +} + +status_t Layer::BufferManager::attachBuffer(size_t index, + const sp<GraphicBuffer>& buffer) +{ + Mutex::Autolock _l(mLock); + mBufferData[index].buffer = buffer; + mBufferData[index].texture.dirty = true; + return NO_ERROR; +} + +status_t Layer::BufferManager::destroyTexture(Texture* tex, EGLDisplay dpy) +{ + if (tex->name != -1U) { + glDeleteTextures(1, &tex->name); + tex->name = -1U; + } + if (tex->image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, tex->image); + tex->image = EGL_NO_IMAGE_KHR; + } + return NO_ERROR; +} + +status_t Layer::BufferManager::destroy(EGLDisplay dpy) +{ + Mutex::Autolock _l(mLock); + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + destroyTexture(&mBufferData[i].texture, dpy); + mBufferData[i].buffer = 0; + } + destroyTexture(&mFailoverTexture, dpy); + return NO_ERROR; +} + +status_t Layer::BufferManager::initEglImage(EGLDisplay dpy, + const sp<GraphicBuffer>& buffer) +{ + size_t index = mActiveBuffer; + Texture& texture(mBufferData[index].texture); + status_t err = mTextureManager.initEglImage(&texture, dpy, buffer); + // if EGLImage fails, we switch to regular texture mode, and we + // free all resources associated with using EGLImages. + if (err == NO_ERROR) { + mFailover = false; + destroyTexture(&mFailoverTexture, dpy); + } else { + mFailover = true; + for (size_t i=0 ; i<NUM_BUFFERS ; i++) { + destroyTexture(&mBufferData[i].texture, dpy); + } + } + return err; +} + +status_t Layer::BufferManager::loadTexture( + const Region& dirty, const GGLSurface& t) +{ + return mTextureManager.loadTexture(&mFailoverTexture, dirty, t); +} + +// --------------------------------------------------------------------------- + Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger, SurfaceID id, const sp<Layer>& owner) : Surface(flinger, id, owner->getIdentity(), owner) @@ -648,15 +661,21 @@ sp<GraphicBuffer> Layer::SurfaceLayer::requestBuffer(int index, int usage) sp<GraphicBuffer> buffer; sp<Layer> owner(getOwner()); if (owner != 0) { - LOGE_IF(uint32_t(index)>=NUM_BUFFERS, - "getBuffer() index (%d) out of range", index); - if (uint32_t(index) < NUM_BUFFERS) { - buffer = owner->requestBuffer(index, usage); - } + buffer = owner->requestBuffer(index, usage); } return buffer; } +status_t Layer::SurfaceLayer::setBufferCount(int bufferCount) +{ + status_t err = DEAD_OBJECT; + sp<Layer> owner(getOwner()); + if (owner != 0) { + err = owner->setBufferCount(bufferCount); + } + return err; +} + // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h index 98e30d7597..80fbd6aca3 100644 --- a/libs/surfaceflinger/Layer.h +++ b/libs/surfaceflinger/Layer.h @@ -31,6 +31,7 @@ #include "LayerBase.h" #include "Transform.h" +#include "TextureManager.h" namespace android { @@ -41,11 +42,13 @@ class FreezeLock; // --------------------------------------------------------------------------- -const size_t NUM_BUFFERS = 2; - class Layer : public LayerBaseClient { -public: +public: + // lcblk is (almost) only accessed from the main SF thread, in the places + // where it's not, a reference to Client must be held + SharedBufferServer* lcblk; + Layer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i); @@ -66,15 +69,14 @@ public: virtual bool isSecure() const { return mSecure; } virtual sp<Surface> createSurface() const; virtual status_t ditch(); + virtual void onRemoved(); // only for debugging - inline sp<GraphicBuffer> getBuffer(int i) const { return mBuffers[i]; } + inline sp<GraphicBuffer> getBuffer(int i) const { return mBufferManager.getBuffer(i); } // only for debugging inline const sp<FreezeLock>& getFreezeLock() const { return mFreezeLock; } // only for debugging inline PixelFormat pixelFormat() const { return mFormat; } - // only for debugging - inline int getFrontBufferIndex() const { return mFrontBufferIndex; } virtual const char* getTypeId() const { return "Layer"; } @@ -82,15 +84,12 @@ protected: virtual void dump(String8& result, char* scratch, size_t size) const; private: - inline sp<GraphicBuffer> getFrontBufferLocked() { - return mBuffers[mFrontBufferIndex]; - } - void reloadTexture(const Region& dirty); uint32_t getEffectiveUsage(uint32_t usage) const; sp<GraphicBuffer> requestBuffer(int index, int usage); + status_t setBufferCount(int bufferCount); void destroy(); class SurfaceLayer : public LayerBaseClient::Surface { @@ -100,6 +99,7 @@ private: ~SurfaceLayer(); private: virtual sp<GraphicBuffer> requestBuffer(int index, int usage); + virtual status_t setBufferCount(int bufferCount); sp<Layer> getOwner() const { return static_cast<Layer*>(Surface::getOwner().get()); } @@ -109,22 +109,70 @@ private: sp<Surface> mSurface; bool mSecure; - bool mNoEGLImageForSwBuffers; int32_t mFrontBufferIndex; bool mNeedsBlending; bool mNeedsDithering; Region mPostedDirtyRegion; sp<FreezeLock> mFreezeLock; PixelFormat mFormat; - - // protected by mLock - sp<GraphicBuffer> mBuffers[NUM_BUFFERS]; - Texture mTextures[NUM_BUFFERS]; - sp<GraphicBuffer> mHybridBuffer; - uint32_t mWidth; - uint32_t mHeight; - - mutable Mutex mLock; + + class BufferManager { + static const size_t NUM_BUFFERS = 2; + struct BufferData { + sp<GraphicBuffer> buffer; + Texture texture; + }; + mutable Mutex mLock; + BufferData mBufferData[NUM_BUFFERS]; + Texture mFailoverTexture; + TextureManager& mTextureManager; + ssize_t mActiveBuffer; + bool mFailover; + static status_t destroyTexture(Texture* tex, EGLDisplay dpy); + + public: + BufferManager(TextureManager& tm); + + size_t getBufferCount() const; + + // detach/attach buffer from/to given index + sp<GraphicBuffer> detachBuffer(size_t index); + status_t attachBuffer(size_t index, const sp<GraphicBuffer>& buffer); + + // ---------------------------------------------- + // must be called from GL thread + + // set/get active buffer index + status_t setActiveBufferIndex(size_t index); + size_t getActiveBufferIndex() const; + + // return the active buffer + sp<GraphicBuffer> getActiveBuffer() const; + + // return the active texture (or fail-over) + Texture getActiveTexture() const; + + // frees resources associated with all buffers + status_t destroy(EGLDisplay dpy); + + // load bitmap data into the active buffer + status_t loadTexture(const Region& dirty, const GGLSurface& t); + + // make active buffer an EGLImage if needed + status_t initEglImage(EGLDisplay dpy, + const sp<GraphicBuffer>& buffer); + + // ---------------------------------------------- + // only for debugging + sp<GraphicBuffer> getBuffer(size_t index) const; + }; + + TextureManager mTextureManager; + BufferManager mBufferManager; + + mutable Mutex mLock; + uint32_t mWidth; + uint32_t mHeight; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp index 48b0e47a83..63b9520e03 100644 --- a/libs/surfaceflinger/LayerBase.cpp +++ b/libs/surfaceflinger/LayerBase.cpp @@ -32,6 +32,7 @@ #include "LayerBase.h" #include "SurfaceFlinger.h" #include "DisplayHardware/DisplayHardware.h" +#include "TextureManager.h" namespace android { @@ -340,18 +341,6 @@ void LayerBase::draw(const Region& inClip) const */ } -GLuint LayerBase::createTexture() const -{ - GLuint textureName = -1; - glGenTextures(1, &textureName); - glBindTexture(GL_TEXTURE_2D, textureName); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - return textureName; -} - void LayerBase::clearWithOpenGL(const Region& clip, GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) const @@ -492,187 +481,6 @@ void LayerBase::validateTexture(GLint textureName) const } } -bool LayerBase::isSupportedYuvFormat(int format) const -{ - switch (format) { - case HAL_PIXEL_FORMAT_YCbCr_422_SP: - case HAL_PIXEL_FORMAT_YCbCr_420_SP: - case HAL_PIXEL_FORMAT_YCbCr_422_P: - case HAL_PIXEL_FORMAT_YCbCr_420_P: - case HAL_PIXEL_FORMAT_YCbCr_422_I: - case HAL_PIXEL_FORMAT_YCbCr_420_I: - case HAL_PIXEL_FORMAT_YCrCb_420_SP: - return true; - } - return false; -} - -void LayerBase::loadTexture(Texture* texture, - const Region& dirty, const GGLSurface& t) const -{ - if (texture->name == -1U) { - // uh? - return; - } - - glBindTexture(GL_TEXTURE_2D, texture->name); - - /* - * In OpenGL ES we can't specify a stride with glTexImage2D (however, - * GL_UNPACK_ALIGNMENT is a limited form of stride). - * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we - * need to do something reasonable (here creating a bigger texture). - * - * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); - * - * This situation doesn't happen often, but some h/w have a limitation - * for their framebuffer (eg: must be multiple of 8 pixels), and - * we need to take that into account when using these buffers as - * textures. - * - * This should never be a problem with POT textures - */ - - int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format)); - unpack = 1 << ((unpack > 3) ? 3 : unpack); - glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); - - /* - * round to POT if needed - */ - if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { - texture->NPOTAdjust = true; - } - - if (texture->NPOTAdjust) { - // find the smallest power-of-two that will accommodate our surface - texture->potWidth = 1 << (31 - clz(t.width)); - texture->potHeight = 1 << (31 - clz(t.height)); - if (texture->potWidth < t.width) texture->potWidth <<= 1; - if (texture->potHeight < t.height) texture->potHeight <<= 1; - texture->wScale = float(t.width) / texture->potWidth; - texture->hScale = float(t.height) / texture->potHeight; - } else { - texture->potWidth = t.width; - texture->potHeight = t.height; - } - - Rect bounds(dirty.bounds()); - GLvoid* data = 0; - if (texture->width != t.width || texture->height != t.height) { - texture->width = t.width; - texture->height = t.height; - - // texture size changed, we need to create a new one - bounds.set(Rect(t.width, t.height)); - if (t.width == texture->potWidth && - t.height == texture->potHeight) { - // we can do it one pass - data = t.data; - } - - if (t.format == HAL_PIXEL_FORMAT_RGB_565) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGB, texture->potWidth, texture->potHeight, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, texture->potWidth, texture->potHeight, 0, - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || - t.format == HAL_PIXEL_FORMAT_RGBX_8888) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, texture->potWidth, texture->potHeight, 0, - GL_RGBA, GL_UNSIGNED_BYTE, data); - } else if (isSupportedYuvFormat(t.format)) { - // just show the Y plane of YUV buffers - glTexImage2D(GL_TEXTURE_2D, 0, - GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, - GL_LUMINANCE, GL_UNSIGNED_BYTE, data); - } else { - // oops, we don't handle this format! - LOGE("layer %p, texture=%d, using format %d, which is not " - "supported by the GL", this, texture->name, t.format); - } - } - if (!data) { - if (t.format == HAL_PIXEL_FORMAT_RGB_565) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, - t.data + bounds.top*t.stride*2); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, - t.data + bounds.top*t.stride*2); - } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || - t.format == HAL_PIXEL_FORMAT_RGBX_8888) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGBA, GL_UNSIGNED_BYTE, - t.data + bounds.top*t.stride*4); - } else if (isSupportedYuvFormat(t.format)) { - // just show the Y plane of YUV buffers - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_LUMINANCE, GL_UNSIGNED_BYTE, - t.data + bounds.top*t.stride); - } - } -} - -status_t LayerBase::initializeEglImage( - const sp<GraphicBuffer>& buffer, Texture* texture) -{ - status_t err = NO_ERROR; - - // we need to recreate the texture - EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); - - // free the previous image - if (texture->image != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(dpy, texture->image); - texture->image = EGL_NO_IMAGE_KHR; - } - - // construct an EGL_NATIVE_BUFFER_ANDROID - android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); - - // create the new EGLImageKHR - const EGLint attrs[] = { - EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, - EGL_NONE, EGL_NONE - }; - texture->image = eglCreateImageKHR( - dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - (EGLClientBuffer)clientBuf, attrs); - - if (texture->image != EGL_NO_IMAGE_KHR) { - glBindTexture(GL_TEXTURE_2D, texture->name); - glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, - (GLeglImageOES)texture->image); - GLint error = glGetError(); - if (UNLIKELY(error != GL_NO_ERROR)) { - LOGE("layer=%p, glEGLImageTargetTexture2DOES(%p) " - "failed err=0x%04x", - this, texture->image, error); - err = INVALID_OPERATION; - } else { - // Everything went okay! - texture->NPOTAdjust = false; - texture->dirty = false; - texture->width = clientBuf->width; - texture->height = clientBuf->height; - } - } else { - LOGE("layer=%p, eglCreateImageKHR() failed. err=0x%4x", - this, eglGetError()); - err = INVALID_OPERATION; - } - return err; -} - void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const { const Layer::State& s(drawingState()); @@ -696,12 +504,9 @@ int32_t LayerBaseClient::sIdentity = 0; LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i) - : LayerBase(flinger, display), lcblk(NULL), client(client), mIndex(i), + : LayerBase(flinger, display), client(client), mIndex(i), mIdentity(uint32_t(android_atomic_inc(&sIdentity))) { - lcblk = new SharedBufferServer( - client->ctrlblk, i, NUM_BUFFERS, - mIdentity); } void LayerBaseClient::onFirstRef() @@ -718,7 +523,6 @@ LayerBaseClient::~LayerBaseClient() if (client != 0) { client->free(mIndex); } - delete lcblk; } ssize_t LayerBaseClient::serverIndex() const @@ -748,14 +552,6 @@ sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const const_cast<LayerBaseClient *>(this)); } -// called with SurfaceFlinger::mStateLock as soon as the layer is entered -// in the purgatory list -void LayerBaseClient::onRemoved() -{ - // wake up the condition - lcblk->setStatus(NO_INIT); -} - void LayerBaseClient::dump(String8& result, char* buffer, size_t SIZE) const { LayerBase::dump(result, buffer, SIZE); @@ -825,6 +621,11 @@ sp<GraphicBuffer> LayerBaseClient::Surface::requestBuffer(int index, int usage) return NULL; } +status_t LayerBaseClient::Surface::setBufferCount(int bufferCount) +{ + return INVALID_OPERATION; +} + status_t LayerBaseClient::Surface::registerBuffers( const ISurface::BufferHeap& buffers) { diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h index 219a53c2d7..53b848f143 100644 --- a/libs/surfaceflinger/LayerBase.h +++ b/libs/surfaceflinger/LayerBase.h @@ -46,6 +46,7 @@ class Client; class GraphicBuffer; class GraphicPlane; class SurfaceFlinger; +class Texture; // --------------------------------------------------------------------------- @@ -221,35 +222,10 @@ protected: const GraphicPlane& graphicPlane(int dpy) const; GraphicPlane& graphicPlane(int dpy); - GLuint createTexture() const; - - struct Texture { - Texture() : name(-1U), width(0), height(0), - image(EGL_NO_IMAGE_KHR), transform(0), - NPOTAdjust(false), dirty(true) { } - GLuint name; - GLuint width; - GLuint height; - GLuint potWidth; - GLuint potHeight; - GLfloat wScale; - GLfloat hScale; - EGLImageKHR image; - uint32_t transform; - bool NPOTAdjust; - bool dirty; - }; - void clearWithOpenGL(const Region& clip, GLclampx r, GLclampx g, GLclampx b, GLclampx alpha) const; void clearWithOpenGL(const Region& clip) const; void drawWithOpenGL(const Region& clip, const Texture& texture) const; - void loadTexture(Texture* texture, - const Region& dirty, const GGLSurface& t) const; - status_t initializeEglImage( - const sp<GraphicBuffer>& buffer, Texture* texture); - - bool isSupportedYuvFormat(int format) const; sp<SurfaceFlinger> mFlinger; uint32_t mFlags; @@ -294,10 +270,6 @@ class LayerBaseClient : public LayerBase public: class Surface; - // lcblk is (almost) only accessed from the main SF thread, in the places - // where it's not, a reference to Client must be held - SharedBufferServer* lcblk; - LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& client, int32_t i); virtual ~LayerBaseClient(); @@ -311,7 +283,6 @@ public: sp<Surface> getSurface(); virtual sp<Surface> createSurface() const; virtual ssize_t serverIndex() const; - virtual void onRemoved(); virtual const char* getTypeId() const { return "LayerBaseClient"; } class Surface : public BnSurface @@ -331,6 +302,8 @@ public: private: virtual sp<GraphicBuffer> requestBuffer(int index, int usage); + virtual status_t setBufferCount(int bufferCount); + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp index 0041a0fdc0..dfcc80fb83 100644 --- a/libs/surfaceflinger/LayerBuffer.cpp +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -328,7 +328,7 @@ bool LayerBuffer::Source::transformed() const { LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers) : Source(layer), mStatus(NO_ERROR), mBufferSize(0), - mUseEGLImageDirectly(true) + mTextureManager(layer.mFlags) { if (buffers.heap == NULL) { // this is allowed, but in this case, it is illegal to receive @@ -460,35 +460,10 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const NativeBuffer src(ourBuffer->getBuffer()); const Rect transformedBounds(mLayer.getTransformedBounds()); - if (UNLIKELY(mTexture.name == -1LU)) { - mTexture.name = mLayer.createTexture(); - } - #if defined(EGL_ANDROID_image_native_buffer) if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) { err = INVALID_OPERATION; if (ourBuffer->supportsCopybit()) { - - // there are constraints on buffers used by the GPU and these may not - // be honored here. We need to change the API so the buffers - // are allocated with gralloc. For now disable this code-path -#if 0 - // First, try to use the buffer as an EGLImage directly - if (mUseEGLImageDirectly) { - // NOTE: Assume the buffer is allocated with the proper USAGE flags - - sp<GraphicBuffer> buffer = new GraphicBuffer( - src.img.w, src.img.h, src.img.format, - GraphicBuffer::USAGE_HW_TEXTURE, - src.img.w, src.img.handle, false); - - err = mLayer.initializeEglImage(buffer, &mTexture); - if (err != NO_ERROR) { - mUseEGLImageDirectly = false; - } - } -#endif - copybit_device_t* copybit = mLayer.mBlitEngine; if (copybit && err != NO_ERROR) { // create our EGLImageKHR the first time @@ -525,7 +500,7 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const t.format = src.img.format; t.data = (GGLubyte*)src.img.base; const Region dirty(Rect(t.width, t.height)); - mLayer.loadTexture(&mTexture, dirty, t); + mTextureManager.loadTexture(&mTexture, dirty, t); } mTexture.transform = mBufferHeap.transform; @@ -591,7 +566,8 @@ status_t LayerBuffer::BufferSource::initTempBuffer() const dst.crop.r = w; dst.crop.b = h; - err = mLayer.initializeEglImage(buffer, &mTexture); + EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay()); + err = mTextureManager.initEglImage(&mTexture, dpy, buffer); } return err; @@ -607,7 +583,6 @@ void LayerBuffer::BufferSource::clearTempBufferImage() const glDeleteTextures(1, &mTexture.name); Texture defaultTexture; mTexture = defaultTexture; - mTexture.name = mLayer.createTexture(); } // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h index 243cc43865..869c74f55c 100644 --- a/libs/surfaceflinger/LayerBuffer.h +++ b/libs/surfaceflinger/LayerBuffer.h @@ -21,6 +21,7 @@ #include <sys/types.h> #include "LayerBase.h" +#include "TextureManager.h" struct copybit_device_t; @@ -139,9 +140,9 @@ private: status_t mStatus; ISurface::BufferHeap mBufferHeap; size_t mBufferSize; - mutable LayerBase::Texture mTexture; + mutable Texture mTexture; mutable NativeBuffer mTempBuffer; - mutable bool mUseEGLImageDirectly; + mutable TextureManager mTextureManager; }; class OverlaySource : public Source { diff --git a/libs/surfaceflinger/TextureManager.cpp b/libs/surfaceflinger/TextureManager.cpp new file mode 100644 index 0000000000..e5d5302b7d --- /dev/null +++ b/libs/surfaceflinger/TextureManager.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/GraphicBuffer.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include <hardware/hardware.h> + +#include "clz.h" +#include "DisplayHardware/DisplayHardware.h" +#include "TextureManager.h" + +namespace android { + +// --------------------------------------------------------------------------- + +TextureManager::TextureManager(uint32_t flags) + : mFlags(flags) +{ +} + +GLuint TextureManager::createTexture() +{ + GLuint textureName = -1; + glGenTextures(1, &textureName); + glBindTexture(GL_TEXTURE_2D, textureName); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + return textureName; +} + +bool TextureManager::isSupportedYuvFormat(int format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + case HAL_PIXEL_FORMAT_YCbCr_420_SP: + case HAL_PIXEL_FORMAT_YCbCr_422_P: + case HAL_PIXEL_FORMAT_YCbCr_420_P: + case HAL_PIXEL_FORMAT_YCbCr_422_I: + case HAL_PIXEL_FORMAT_YCbCr_420_I: + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + return true; + } + return false; +} + +status_t TextureManager::initEglImage(Texture* texture, + EGLDisplay dpy, const sp<GraphicBuffer>& buffer) +{ + status_t err = NO_ERROR; + if (!texture->dirty) return err; + + // free the previous image + if (texture->image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, texture->image); + texture->image = EGL_NO_IMAGE_KHR; + } + + // construct an EGL_NATIVE_BUFFER_ANDROID + android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); + + // create the new EGLImageKHR + const EGLint attrs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE, EGL_NONE + }; + texture->image = eglCreateImageKHR( + dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + (EGLClientBuffer)clientBuf, attrs); + + if (texture->image != EGL_NO_IMAGE_KHR) { + if (texture->name == -1UL) { + texture->name = createTexture(); + texture->width = 0; + texture->height = 0; + } + glBindTexture(GL_TEXTURE_2D, texture->name); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, + (GLeglImageOES)texture->image); + GLint error = glGetError(); + if (error != GL_NO_ERROR) { + LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x", + texture->image, error); + err = INVALID_OPERATION; + } else { + // Everything went okay! + texture->NPOTAdjust = false; + texture->dirty = false; + texture->width = clientBuf->width; + texture->height = clientBuf->height; + } + } else { + LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); + err = INVALID_OPERATION; + } + return err; +} + +status_t TextureManager::loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t) +{ + if (texture->name == -1UL) { + texture->name = createTexture(); + texture->width = 0; + texture->height = 0; + } + + glBindTexture(GL_TEXTURE_2D, texture->name); + + /* + * In OpenGL ES we can't specify a stride with glTexImage2D (however, + * GL_UNPACK_ALIGNMENT is a limited form of stride). + * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we + * need to do something reasonable (here creating a bigger texture). + * + * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); + * + * This situation doesn't happen often, but some h/w have a limitation + * for their framebuffer (eg: must be multiple of 8 pixels), and + * we need to take that into account when using these buffers as + * textures. + * + * This should never be a problem with POT textures + */ + + int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format)); + unpack = 1 << ((unpack > 3) ? 3 : unpack); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); + + /* + * round to POT if needed + */ + if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { + texture->NPOTAdjust = true; + } + + if (texture->NPOTAdjust) { + // find the smallest power-of-two that will accommodate our surface + texture->potWidth = 1 << (31 - clz(t.width)); + texture->potHeight = 1 << (31 - clz(t.height)); + if (texture->potWidth < t.width) texture->potWidth <<= 1; + if (texture->potHeight < t.height) texture->potHeight <<= 1; + texture->wScale = float(t.width) / texture->potWidth; + texture->hScale = float(t.height) / texture->potHeight; + } else { + texture->potWidth = t.width; + texture->potHeight = t.height; + } + + Rect bounds(dirty.bounds()); + GLvoid* data = 0; + if (texture->width != t.width || texture->height != t.height) { + texture->width = t.width; + texture->height = t.height; + + // texture size changed, we need to create a new one + bounds.set(Rect(t.width, t.height)); + if (t.width == texture->potWidth && + t.height == texture->potHeight) { + // we can do it one pass + data = t.data; + } + + if (t.format == HAL_PIXEL_FORMAT_RGB_565) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGB, texture->potWidth, texture->potHeight, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || + t.format == HAL_PIXEL_FORMAT_RGBX_8888) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture->potWidth, texture->potHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + } else if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + glTexImage2D(GL_TEXTURE_2D, 0, + GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, + GL_LUMINANCE, GL_UNSIGNED_BYTE, data); + } else { + // oops, we don't handle this format! + LOGE("texture=%d, using format %d, which is not " + "supported by the GL", texture->name, t.format); + } + } + if (!data) { + if (t.format == HAL_PIXEL_FORMAT_RGB_565) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, + t.data + bounds.top*t.stride*2); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, + t.data + bounds.top*t.stride*2); + } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || + t.format == HAL_PIXEL_FORMAT_RGBX_8888) { + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_RGBA, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.stride*4); + } else if (isSupportedYuvFormat(t.format)) { + // just show the Y plane of YUV buffers + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, bounds.top, t.width, bounds.height(), + GL_LUMINANCE, GL_UNSIGNED_BYTE, + t.data + bounds.top*t.stride); + } + } + return NO_ERROR; +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/TextureManager.h b/libs/surfaceflinger/TextureManager.h new file mode 100644 index 0000000000..90cb62b292 --- /dev/null +++ b/libs/surfaceflinger/TextureManager.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_TEXTURE_MANAGER_H +#define ANDROID_TEXTURE_MANAGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> + +#include <ui/Region.h> + +#include <pixelflinger/pixelflinger.h> + +namespace android { + +// --------------------------------------------------------------------------- + +class GraphicBuffer; + +// --------------------------------------------------------------------------- + +struct Texture { + Texture() : name(-1U), width(0), height(0), + image(EGL_NO_IMAGE_KHR), transform(0), + NPOTAdjust(false), dirty(true) { } + GLuint name; + GLuint width; + GLuint height; + GLuint potWidth; + GLuint potHeight; + GLfloat wScale; + GLfloat hScale; + EGLImageKHR image; + uint32_t transform; + bool NPOTAdjust; + bool dirty; +}; + +// --------------------------------------------------------------------------- + +class TextureManager { + uint32_t mFlags; + GLuint createTexture(); + static bool isSupportedYuvFormat(int format); +public: + + TextureManager(uint32_t flags); + + // load bitmap data into the active buffer + status_t loadTexture(Texture* texture, + const Region& dirty, const GGLSurface& t); + + // make active buffer an EGLImage if needed + status_t initEglImage(Texture* texture, + EGLDisplay dpy, const sp<GraphicBuffer>& buffer); +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_TEXTURE_MANAGER_H diff --git a/libs/surfaceflinger_client/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp index bb86199876..c5d0c0ecc4 100644 --- a/libs/surfaceflinger_client/ISurface.cpp +++ b/libs/surfaceflinger_client/ISurface.cpp @@ -83,6 +83,16 @@ public: return buffer; } + virtual status_t setBufferCount(int bufferCount) + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + data.writeInt32(bufferCount); + remote()->transact(SET_BUFFER_COUNT, data, &reply); + status_t err = reply.readInt32(); + return err; + } + virtual status_t registerBuffers(const BufferHeap& buffers) { Parcel data, reply; @@ -146,6 +156,13 @@ status_t BnSurface::onTransact( return BAD_VALUE; return reply->write(*buffer); } + case SET_BUFFER_COUNT: { + CHECK_INTERFACE(ISurface, data, reply); + int bufferCount = data.readInt32(); + status_t err = setBufferCount(bufferCount); + reply->writeInt32(err); + return NO_ERROR; + } case REGISTER_BUFFERS: { CHECK_INTERFACE(ISurface, data, reply); BufferHeap buffer; diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp index c42cd5315b..4a980268f2 100644 --- a/libs/surfaceflinger_client/SharedBufferStack.cpp +++ b/libs/surfaceflinger_client/SharedBufferStack.cpp @@ -305,7 +305,6 @@ SharedBufferServer::RetireUpdate::RetireUpdate( : UpdateBase(sbb), numBuffers(numBuffers) { } ssize_t SharedBufferServer::RetireUpdate::operator()() { - // head is only written in this function, which is single-thread. int32_t head = stack.head; if (uint32_t(head) >= NUM_BUFFER_MAX) return BAD_VALUE; @@ -322,16 +321,14 @@ ssize_t SharedBufferServer::RetireUpdate::operator()() { } } while (android_atomic_cmpxchg(queued, queued-1, &stack.queued)); - // update the head pointer - head = ((head+1 >= numBuffers) ? 0 : head+1); - // lock the buffer before advancing head, which automatically unlocks // the buffer we preventively locked upon entering this function + head = (head + 1) % numBuffers; android_atomic_write(stack.index[head], &stack.inUse); - // advance head + // head is only modified here, so we don't need to use cmpxchg android_atomic_write(head, &stack.head); - + // now that head has moved, we can increment the number of available buffers android_atomic_inc(&stack.available); return head; @@ -450,6 +447,14 @@ status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg) return stack.setDirtyRegion(buf, reg); } +status_t SharedBufferClient::setBufferCount(int bufferCount) +{ + if (uint32_t(bufferCount) >= NUM_BUFFER_MAX) + return BAD_VALUE; + mNumBuffers = bufferCount; + return NO_ERROR; +} + // ---------------------------------------------------------------------------- SharedBufferServer::SharedBufferServer(SharedClient* sharedClient, @@ -463,6 +468,7 @@ SharedBufferServer::SharedBufferServer(SharedClient* sharedClient, mSharedStack->reallocMask = 0; memset(mSharedStack->buffers, 0, sizeof(mSharedStack->buffers)); for (int i=0 ; i<num ; i++) { + mBufferList.add(i); mSharedStack->index[i] = i; } } @@ -513,6 +519,7 @@ int32_t SharedBufferServer::getQueuedCount() const status_t SharedBufferServer::assertReallocate(int buf) { + // TODO: need to validate "buf" ReallocateCondition condition(this, buf); status_t err = waitForCondition(condition); return err; @@ -524,12 +531,85 @@ Region SharedBufferServer::getDirtyRegion(int buf) const return stack.getDirtyRegion(buf); } + +/* + * + * NOTE: this is not thread-safe on the server-side, meaning + * 'head' cannot move during this operation. The client-side + * can safely operate an usual. + * + */ +status_t SharedBufferServer::resize(int newNumBuffers) +{ + if (uint32_t(newNumBuffers) >= NUM_BUFFER_MAX) + return BAD_VALUE; + + // for now we're not supporting shrinking + const int numBuffers = mNumBuffers; + if (newNumBuffers < numBuffers) + return BAD_VALUE; + + SharedBufferStack& stack( *mSharedStack ); + const int extra = newNumBuffers - numBuffers; + + // read the head, make sure it's valid + int32_t head = stack.head; + if (uint32_t(head) >= NUM_BUFFER_MAX) + return BAD_VALUE; + + int base = numBuffers; + int32_t avail = stack.available; + int tail = head - avail + 1; + if (tail >= 0) { + int8_t* const index = const_cast<int8_t*>(stack.index); + const int nb = numBuffers - head; + memmove(&index[head + extra], &index[head], nb); + base = head; + // move head 'extra' ahead, this doesn't impact stack.index[head]; + stack.head = head + extra; + } + stack.available += extra; + + // fill the new free space with unused buffers + BufferList::const_iterator curr(mBufferList.free_begin()); + for (int i=0 ; i<extra ; i++) { + stack.index[base+i] = *curr++; + mBufferList.add(stack.index[base+i]); + } + + mNumBuffers = newNumBuffers; + return NO_ERROR; +} + SharedBufferStack::Statistics SharedBufferServer::getStats() const { SharedBufferStack& stack( *mSharedStack ); return stack.stats; } +// --------------------------------------------------------------------------- +status_t SharedBufferServer::BufferList::add(int value) +{ + if (uint32_t(value) >= mCapacity) + return BAD_VALUE; + uint32_t mask = 1<<(31-value); + if (mList & mask) + return ALREADY_EXISTS; + mList |= mask; + return NO_ERROR; +} + +status_t SharedBufferServer::BufferList::remove(int value) +{ + if (uint32_t(value) >= mCapacity) + return BAD_VALUE; + uint32_t mask = 1<<(31-value); + if (!(mList & mask)) + return NAME_NOT_FOUND; + mList &= ~mask; + return NO_ERROR; +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp index eee4dae3d6..afbeafbaca 100644 --- a/libs/surfaceflinger_client/Surface.cpp +++ b/libs/surfaceflinger_client/Surface.cpp @@ -673,6 +673,27 @@ int Surface::crop(Rect const* rect) return NO_ERROR; } +int Surface::setBufferCount(int bufferCount) +{ + sp<ISurface> s(mSurface); + if (s == 0) return NO_INIT; + + // FIXME: this needs to be synchronized dequeue/queue + + status_t err = s->setBufferCount(bufferCount); + LOGE_IF(err, "ISurface::setBufferCount(%d) returned %s", + bufferCount, strerror(-err)); + if (err == NO_ERROR) { + err = mSharedBufferClient->getStatus(); + LOGE_IF(err, "Surface (identity=%d) state = %d", mIdentity, err); + if (!err) { + // update our local copy of the buffer count + mSharedBufferClient->setBufferCount(bufferCount); + } + } + return err; +} + // ---------------------------------------------------------------------------- diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 5a05e6a61f..60a0d82e25 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -824,7 +824,7 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, // TODO: look for previously-created shared memory slice? int method; - long uncompressedLen; + size_t uncompressedLen; //printf("USING Zip '%s'\n", pEntry->getFileName()); diff --git a/libs/utils/ZipFileCRO.cpp b/libs/utils/ZipFileCRO.cpp index 45f6c8baa7..16b219cad4 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/utils/ZipFileCRO.cpp @@ -39,8 +39,8 @@ ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken, } bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken, - int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { + int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) { ZipFileRO* zip = (ZipFileRO*)zipToken; ZipEntryRO entry = (ZipEntryRO)entryToken; return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 6c701dd0b6..28dc512bb6 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -29,6 +29,22 @@ #include <fcntl.h> #include <errno.h> #include <assert.h> +#include <unistd.h> + +/* + * TEMP_FAILURE_RETRY is defined by some, but not all, versions of + * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's + * not already defined, then define it here. + */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) ({ \ + typeof (exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; }) +#endif using namespace android; @@ -38,6 +54,7 @@ using namespace android; #define kEOCDSignature 0x06054b50 #define kEOCDLen 22 #define kEOCDNumEntries 8 // offset to #of entries in file +#define kEOCDSize 12 // size of the central directory #define kEOCDFileOffset 16 // offset to central directory #define kMaxCommentLen 65535 // longest possible in ushort @@ -90,9 +107,8 @@ int ZipFileRO::entryToIndex(const ZipEntryRO entry) const status_t ZipFileRO::open(const char* zipFileName) { int fd = -1; - off_t length; - assert(mFileMap == NULL); + assert(mDirectoryMap == NULL); /* * Open and map the specified file. @@ -103,172 +119,240 @@ status_t ZipFileRO::open(const char* zipFileName) return NAME_NOT_FOUND; } - length = lseek(fd, 0, SEEK_END); - if (length < 0) { + mFileLength = lseek(fd, 0, SEEK_END); + if (mFileLength < kEOCDLen) { close(fd); return UNKNOWN_ERROR; } - mFileMap = new FileMap(); - if (mFileMap == NULL) { - close(fd); - return NO_MEMORY; - } - if (!mFileMap->create(zipFileName, fd, 0, length, true)) { - LOGW("Unable to map '%s': %s\n", zipFileName, strerror(errno)); - close(fd); - return UNKNOWN_ERROR; + if (mFileName != NULL) { + free(mFileName); } + mFileName = strdup(zipFileName); mFd = fd; /* - * Got it mapped, verify it and create data structures for fast access. + * Find the Central Directory and store its size and number of entries. + */ + if (!mapCentralDirectory()) { + goto bail; + } + + /* + * Verify Central Directory and create data structures for fast access. */ if (!parseZipArchive()) { - mFileMap->release(); - mFileMap = NULL; - return UNKNOWN_ERROR; + goto bail; } return OK; + +bail: + free(mFileName); + mFileName = NULL; + close(fd); + return UNKNOWN_ERROR; } /* * Parse the Zip archive, verifying its contents and initializing internal * data structures. */ -bool ZipFileRO::parseZipArchive(void) +bool ZipFileRO::mapCentralDirectory(void) { -#define CHECK_OFFSET(_off) { \ - if ((unsigned int) (_off) >= maxOffset) { \ - LOGE("ERROR: bad offset %u (max %d): %s\n", \ - (unsigned int) (_off), maxOffset, #_off); \ - goto bail; \ - } \ - } - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - const unsigned char* ptr; - size_t length = mFileMap->getDataLength(); - bool result = false; - unsigned int i, numEntries, cdOffset; - unsigned int val; + size_t readAmount = kMaxEOCDSearch; + if (readAmount > (size_t) mFileLength) + readAmount = mFileLength; + + unsigned char* scanBuf = (unsigned char*) malloc(readAmount); + if (scanBuf == NULL) { + LOGW("couldn't allocate scanBuf: %s", strerror(errno)); + free(scanBuf); + return false; + } /* - * The first 4 bytes of the file will either be the local header - * signature for the first file (kLFHSignature) or, if the archive doesn't - * have any files in it, the end-of-central-directory signature - * (kEOCDSignature). + * Make sure this is a Zip archive. */ - val = get4LE(basePtr); - if (val == kEOCDSignature) { - LOGI("Found Zip archive, but it looks empty\n"); - goto bail; - } else if (val != kLFHSignature) { - LOGV("Not a Zip archive (found 0x%08x)\n", val); - goto bail; + if (lseek(mFd, 0, SEEK_SET) != 0) { + LOGW("seek to start failed: %s", strerror(errno)); + free(scanBuf); + return false; + } + + ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t))); + if (actual != (ssize_t) sizeof(int32_t)) { + LOGI("couldn't read first signature from zip archive: %s", strerror(errno)); + free(scanBuf); + return false; + } + + { + unsigned int header = get4LE(scanBuf); + if (header == kEOCDSignature) { + LOGI("Found Zip archive, but it looks empty\n"); + free(scanBuf); + return false; + } else if (header != kLFHSignature) { + LOGV("Not a Zip archive (found 0x%08x)\n", val); + free(scanBuf); + return false; + } } /* - * Find the EOCD. We'll find it immediately unless they have a file - * comment. + * Perform the traditional EOCD snipe hunt. + * + * We're searching for the End of Central Directory magic number, + * which appears at the start of the EOCD block. It's followed by + * 18 bytes of EOCD stuff and up to 64KB of archive comment. We + * need to read the last part of the file into a buffer, dig through + * it to find the magic number, parse some values out, and use those + * to determine the extent of the CD. + * + * We start by pulling in the last part of the file. */ - ptr = basePtr + length - kEOCDLen; + off_t searchStart = mFileLength - readAmount; - while (ptr >= basePtr) { - if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature) + if (lseek(mFd, searchStart, SEEK_SET) != searchStart) { + LOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno)); + free(scanBuf); + return false; + } + actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount)); + if (actual != (ssize_t) readAmount) { + LOGW("Zip: read %zd failed: %s\n", readAmount, strerror(errno)); + free(scanBuf); + return false; + } + + /* + * Scan backward for the EOCD magic. In an archive without a trailing + * comment, we'll find it on the first try. (We may want to consider + * doing an initial minimal read; if we don't find it, retry with a + * second read as above.) + */ + int i; + for (i = readAmount - kEOCDLen; i >= 0; i--) { + if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) { + LOGV("+++ Found EOCD at buf+%d\n", i); break; - ptr--; + } } - if (ptr < basePtr) { - LOGI("Could not find end-of-central-directory in Zip\n"); - goto bail; + if (i < 0) { + LOGD("Zip: EOCD not found, %s is not zip\n", mFileName); + free(scanBuf); + return false; } + off_t eocdOffset = searchStart + i; + const unsigned char* eocdPtr = scanBuf + i; + + assert(eocdOffset < mFileLength); + /* - * There are two interesting items in the EOCD block: the number of - * entries in the file, and the file offset of the start of the - * central directory. - * - * (There's actually a count of the #of entries in this file, and for - * all files which comprise a spanned archive, but for our purposes - * we're only interested in the current file. Besides, we expect the - * two to be equivalent for our stuff.) + * Grab the CD offset and size, and the number of entries in the + * archive. Verify that they look reasonable. */ - numEntries = get2LE(ptr + kEOCDNumEntries); - cdOffset = get4LE(ptr + kEOCDFileOffset); + unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries); + unsigned int dirSize = get4LE(eocdPtr + kEOCDSize); + unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset); + + if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) { + LOGW("bad offsets (dir %ld, size %u, eocd %ld)\n", + (long) dirOffset, dirSize, (long) eocdOffset); + free(scanBuf); + return false; + } + if (numEntries == 0) { + LOGW("empty archive?\n"); + free(scanBuf); + return false; + } - /* valid offsets are [0,EOCD] */ - unsigned int maxOffset; - maxOffset = (ptr - basePtr) +1; + LOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n", + numEntries, dirSize, dirOffset); - LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset); - if (numEntries == 0 || cdOffset >= length) { - LOGW("Invalid entries=%d offset=%d (len=%zd)\n", - numEntries, cdOffset, length); - goto bail; + mDirectoryMap = new FileMap(); + if (mDirectoryMap == NULL) { + LOGW("Unable to create directory map: %s", strerror(errno)); + free(scanBuf); + return false; } + if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) { + LOGW("Unable to map '%s' (%zd to %zd): %s\n", mFileName, + dirOffset, dirOffset + dirSize, strerror(errno)); + free(scanBuf); + return false; + } + + mNumEntries = numEntries; + mDirectoryOffset = dirOffset; + + return true; +} + +bool ZipFileRO::parseZipArchive(void) +{ + bool result = false; + const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr(); + size_t cdLength = mDirectoryMap->getDataLength(); + int numEntries = mNumEntries; + /* * Create hash table. We have a minimum 75% load factor, possibly as * low as 50% after we round off to a power of 2. */ - mNumEntries = numEntries; - mHashTableSize = roundUpPower2(1 + ((numEntries * 4) / 3)); - mHashTable = (HashEntry*) calloc(1, sizeof(HashEntry) * mHashTableSize); + mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3); + mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry)); /* * Walk through the central directory, adding entries to the hash * table. */ - ptr = basePtr + cdOffset; - for (i = 0; i < numEntries; i++) { - unsigned int fileNameLen, extraLen, commentLen, localHdrOffset; - const unsigned char* localHdr; - unsigned int hash; - + const unsigned char* ptr = cdPtr; + for (int i = 0; i < numEntries; i++) { if (get4LE(ptr) != kCDESignature) { LOGW("Missed a central dir sig (at %d)\n", i); goto bail; } - if (ptr + kCDELen > basePtr + length) { + if (ptr + kCDELen > cdPtr + cdLength) { LOGW("Ran off the end (at %d)\n", i); goto bail; } - localHdrOffset = get4LE(ptr + kCDELocalOffset); - CHECK_OFFSET(localHdrOffset); + long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); + if (localHdrOffset >= mDirectoryOffset) { + LOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i); + goto bail; + } + + unsigned int fileNameLen, extraLen, commentLen, hash; + fileNameLen = get2LE(ptr + kCDENameLen); extraLen = get2LE(ptr + kCDEExtraLen); commentLen = get2LE(ptr + kCDECommentLen); - //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n", - // i, localHdrOffset, fileNameLen, extraLen, commentLen); - //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen); - /* add the CDE filename to the hash table */ hash = computeHash((const char*)ptr + kCDELen, fileNameLen); addToHash((const char*)ptr + kCDELen, fileNameLen, hash); - localHdr = basePtr + localHdrOffset; - if (get4LE(localHdr) != kLFHSignature) { - LOGW("Bad offset to local header: %d (at %d)\n", - localHdrOffset, i); + ptr += kCDELen + fileNameLen + extraLen + commentLen; + if ((size_t)(ptr - cdPtr) > cdLength) { + LOGW("bad CD advance (%d vs %zd) at entry %d\n", + (int) (ptr - cdPtr), cdLength, i); goto bail; } - - ptr += kCDELen + fileNameLen + extraLen + commentLen; - CHECK_OFFSET(ptr - basePtr); } - + LOGV("+++ zip good scan %d entries\n", numEntries); result = true; bail: return result; -#undef CHECK_OFFSET } - /* * Simple string hash function for non-null-terminated strings. */ @@ -315,7 +399,7 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const memcmp(mHashTable[ent].name, fileName, nameLen) == 0) { /* match */ - return (ZipEntryRO) (ent + kZipEntryAdj); + return (ZipEntryRO)(long)(ent + kZipEntryAdj); } ent = (ent + 1) & (mHashTableSize-1); @@ -354,20 +438,24 @@ ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const * Returns "false" if the offsets to the fields or the contents of the fields * appear to be bogus. */ -bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, - long* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, + size_t* pCompLen, off_t* pOffset, long* pModWhen, long* pCrc32) const { - int ent = entryToIndex(entry); + bool ret = false; + + const int ent = entryToIndex(entry); if (ent < 0) return false; + HashEntry hashEntry = mHashTable[ent]; + /* * Recover the start of the central directory entry from the filename - * pointer. + * pointer. The filename is the first entry past the fixed-size data, + * so we can just subtract back from that. */ - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); - const unsigned char* ptr = (const unsigned char*) mHashTable[ent].name; - size_t zipLength = mFileMap->getDataLength(); + const unsigned char* ptr = (const unsigned char*) hashEntry.name; + off_t cdOffset = mDirectoryOffset; ptr -= kCDELen; @@ -380,48 +468,78 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, long* pUncompLen, if (pCrc32 != NULL) *pCrc32 = get4LE(ptr + kCDECRC); + size_t compLen = get4LE(ptr + kCDECompLen); + if (pCompLen != NULL) + *pCompLen = compLen; + size_t uncompLen = get4LE(ptr + kCDEUncompLen); + if (pUncompLen != NULL) + *pUncompLen = uncompLen; + /* - * We need to make sure that the lengths are not so large that somebody - * trying to map the compressed or uncompressed data runs off the end - * of the mapped region. + * If requested, determine the offset of the start of the data. All we + * have is the offset to the Local File Header, which is variable size, + * so we have to read the contents of the struct to figure out where + * the actual data starts. + * + * We also need to make sure that the lengths are not so large that + * somebody trying to map the compressed or uncompressed data runs + * off the end of the mapped region. + * + * Note we don't verify compLen/uncompLen if they don't request the + * dataOffset, because dataOffset is expensive to determine. However, + * if they don't have the file offset, they're not likely to be doing + * anything with the contents. */ - unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset); - if (localHdrOffset + kLFHLen >= zipLength) { - LOGE("ERROR: bad local hdr offset in zip\n"); - return false; - } - const unsigned char* localHdr = basePtr + localHdrOffset; - off_t dataOffset = localHdrOffset + kLFHLen - + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen); - if ((unsigned long) dataOffset >= zipLength) { - LOGE("ERROR: bad data offset in zip\n"); - return false; - } + if (pOffset != NULL) { + long localHdrOffset = get4LE(ptr + kCDELocalOffset); + if (localHdrOffset + kLFHLen >= cdOffset) { + LOGE("ERROR: bad local hdr offset in zip\n"); + return false; + } - if (pCompLen != NULL) { - *pCompLen = get4LE(ptr + kCDECompLen); - if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) { - LOGE("ERROR: bad compressed length in zip\n"); + unsigned char lfhBuf[kLFHLen]; + if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); return false; } - } - if (pUncompLen != NULL) { - *pUncompLen = get4LE(ptr + kCDEUncompLen); - if (*pUncompLen < 0) { - LOGE("ERROR: negative uncompressed length in zip\n"); + ssize_t actual = + TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); + if (actual != sizeof(lfhBuf)) { + LOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } + + if (get4LE(lfhBuf) != kLFHSignature) { + LOGW("didn't find signature at start of lfh, offset=%ld\n", + localHdrOffset); return false; } + + off_t dataOffset = localHdrOffset + kLFHLen + + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen); + if (dataOffset >= cdOffset) { + LOGW("bad data offset %ld in zip\n", (long) dataOffset); + return false; + } + + /* check lengths */ + if ((off_t)(dataOffset + compLen) > cdOffset) { + LOGW("bad compressed length in zip (%ld + %zd > %ld)\n", + (long) dataOffset, compLen, (long) cdOffset); + return false; + } + if (method == kCompressStored && - (size_t)(dataOffset + *pUncompLen) >= zipLength) + (off_t)(dataOffset + uncompLen) > cdOffset) { - LOGE("ERROR: bad uncompressed length in zip\n"); + LOGE("ERROR: bad uncompressed length in zip (%ld + %zd > %ld)\n", + (long) dataOffset, uncompLen, (long) cdOffset); return false; } - } - if (pOffset != NULL) { *pOffset = dataOffset; } + return true; } @@ -457,14 +575,14 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const */ FileMap* newMap; - long compLen; + size_t compLen; off_t offset; if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL)) return NULL; newMap = new FileMap(); - if (!newMap->create(mFileMap->getFileName(), mFd, offset, compLen, true)) { + if (!newMap->create(mFileName, mFd, offset, compLen, true)) { newMap->release(); return NULL; } @@ -480,19 +598,26 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const */ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const { - const int kSequentialMin = 32768; + const size_t kSequentialMin = 32768; bool result = false; int ent = entryToIndex(entry); if (ent < 0) return -1; - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); int method; - long uncompLen, compLen; + size_t uncompLen, compLen; off_t offset; + const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); + FileMap* file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } + + ptr = (const unsigned char*) file->getDataPtr(); + /* * Experiment with madvise hint. When we want to uncompress a file, * we pull some stuff out of the central dir entry and then hit a @@ -507,17 +632,17 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const * pair of system calls are negated by a reduction in page faults. */ if (compLen > kSequentialMin) - mFileMap->advise(FileMap::SEQUENTIAL); + file->advise(FileMap::SEQUENTIAL); if (method == kCompressStored) { - memcpy(buffer, basePtr + offset, uncompLen); + memcpy(buffer, ptr, uncompLen); } else { - if (!inflateBuffer(buffer, basePtr + offset, uncompLen, compLen)) + if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) goto bail; } if (compLen > kSequentialMin) - mFileMap->advise(FileMap::NORMAL); + file->advise(FileMap::NORMAL); result = true; @@ -537,29 +662,34 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const if (ent < 0) return -1; - const unsigned char* basePtr = (const unsigned char*)mFileMap->getDataPtr(); int method; - long uncompLen, compLen; + size_t uncompLen, compLen; off_t offset; + const unsigned char* ptr; getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - if (method == kCompressStored) { - ssize_t actual; + const FileMap* file = createEntryFileMap(entry); + if (file == NULL) { + goto bail; + } + + ptr = (const unsigned char*) file->getDataPtr(); - actual = write(fd, basePtr + offset, uncompLen); + if (method == kCompressStored) { + ssize_t actual = write(fd, ptr, uncompLen); if (actual < 0) { LOGE("Write failed: %s\n", strerror(errno)); goto bail; - } else if (actual != uncompLen) { - LOGE("Partial write during uncompress (%d of %ld)\n", - (int)actual, uncompLen); + } else if ((size_t) actual != uncompLen) { + LOGE("Partial write during uncompress (%zd of %zd)\n", + actual, uncompLen); goto bail; } else { LOGI("+++ successful write\n"); } } else { - if (!inflateBuffer(fd, basePtr+offset, uncompLen, compLen)) + if (!inflateBuffer(fd, ptr, uncompLen, compLen)) goto bail; } @@ -573,7 +703,7 @@ bail: * Uncompress "deflate" data from one buffer to another. */ /*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf, - long uncompLen, long compLen) + size_t uncompLen, size_t compLen) { bool result = false; z_stream zstream; @@ -582,7 +712,7 @@ bail: /* * Initialize the zlib stream struct. */ - memset(&zstream, 0, sizeof(zstream)); + memset(&zstream, 0, sizeof(zstream)); zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; @@ -592,10 +722,10 @@ bail: zstream.avail_out = uncompLen; zstream.data_type = Z_UNKNOWN; - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { @@ -619,8 +749,8 @@ bail: } /* paranoia */ - if ((long) zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + if (zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %zd)\n", zstream.total_out, uncompLen); goto z_bail; } @@ -638,10 +768,10 @@ bail: * Uncompress "deflate" data from one buffer to an open file descriptor. */ /*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf, - long uncompLen, long compLen) + size_t uncompLen, size_t compLen) { bool result = false; - const int kWriteBufSize = 32768; + const size_t kWriteBufSize = 32768; unsigned char writeBuf[kWriteBufSize]; z_stream zstream; int zerr; @@ -649,7 +779,7 @@ bail: /* * Initialize the zlib stream struct. */ - memset(&zstream, 0, sizeof(zstream)); + memset(&zstream, 0, sizeof(zstream)); zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; @@ -659,10 +789,10 @@ bail: zstream.avail_out = sizeof(writeBuf); zstream.data_type = Z_UNKNOWN; - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ + /* + * Use the undocumented "negative window bits" feature to tell zlib + * that there's no zlib header waiting for it. + */ zerr = inflateInit2(&zstream, -MAX_WBITS); if (zerr != Z_OK) { if (zerr == Z_VERSION_ERROR) { @@ -708,8 +838,8 @@ bail: assert(zerr == Z_STREAM_END); /* other errors should've been caught */ /* paranoia */ - if ((long) zstream.total_out != uncompLen) { - LOGW("Size mismatch on inflated file (%ld vs %ld)\n", + if (zstream.total_out != uncompLen) { + LOGW("Size mismatch on inflated file (%ld vs %zd)\n", zstream.total_out, uncompLen); goto z_bail; } diff --git a/opengl/libagl/Android.mk b/opengl/libagl/Android.mk index 6cb146c060..8abd649968 100644 --- a/opengl/libagl/Android.mk +++ b/opengl/libagl/Android.mk @@ -37,6 +37,10 @@ ifeq ($(TARGET_ARCH),arm) LOCAL_CFLAGS += -fstrict-aliasing endif +ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) + LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER +endif + ifneq ($(TARGET_SIMULATOR),true) # we need to access the private Bionic header <bionic_tls.h> # on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp index 9407bd5530..d67612e681 100644 --- a/opengl/libagl/texture.cpp +++ b/opengl/libagl/texture.cpp @@ -1515,7 +1515,7 @@ void glReadPixels( ogles_error(c, GL_INVALID_VALUE); return; } - if (x<0 || x<0) { + if (x<0 || y<0) { ogles_error(c, GL_INVALID_VALUE); return; } |