diff options
Diffstat (limited to 'libs')
199 files changed, 20898 insertions, 9494 deletions
diff --git a/libs/audioflinger/A2dpAudioInterface.cpp b/libs/audioflinger/A2dpAudioInterface.cpp index 16a4f2d..6f9e934 100644 --- a/libs/audioflinger/A2dpAudioInterface.cpp +++ b/libs/audioflinger/A2dpAudioInterface.cpp @@ -29,25 +29,41 @@ namespace android { // ---------------------------------------------------------------------------- -A2dpAudioInterface::A2dpAudioInterface() : - mOutput(0) +//AudioHardwareInterface* A2dpAudioInterface::createA2dpInterface() +//{ +// AudioHardwareInterface* hw = 0; +// +// hw = AudioHardwareInterface::create(); +// LOGD("new A2dpAudioInterface(hw: %p)", hw); +// hw = new A2dpAudioInterface(hw); +// return hw; +//} + +A2dpAudioInterface::A2dpAudioInterface(AudioHardwareInterface* hw) : + mOutput(0), mHardwareInterface(hw), mBluetoothEnabled(true) { } A2dpAudioInterface::~A2dpAudioInterface() { - delete mOutput; + closeOutputStream((AudioStreamOut *)mOutput); + delete mHardwareInterface; } status_t A2dpAudioInterface::initCheck() { - return 0; + if (mHardwareInterface == 0) return NO_INIT; + return mHardwareInterface->initCheck(); } AudioStreamOut* A2dpAudioInterface::openOutputStream( - int format, int channelCount, uint32_t sampleRate, status_t *status) + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) { - LOGD("A2dpAudioInterface::openOutputStream %d, %d, %d\n", format, channelCount, sampleRate); + if (!AudioSystem::isA2dpDevice((AudioSystem::audio_devices)devices)) { + LOGV("A2dpAudioInterface::openOutputStream() open HW device: %x", devices); + return mHardwareInterface->openOutputStream(devices, format, channels, sampleRate, status); + } + status_t err = 0; // only one output stream allowed @@ -59,71 +75,127 @@ AudioStreamOut* A2dpAudioInterface::openOutputStream( // create new output stream A2dpAudioStreamOut* out = new A2dpAudioStreamOut(); - if ((err = out->set(format, channelCount, sampleRate)) == NO_ERROR) { + if ((err = out->set(devices, format, channels, sampleRate)) == NO_ERROR) { mOutput = out; + mOutput->setBluetoothEnabled(mBluetoothEnabled); } else { delete out; } - + if (status) *status = err; return mOutput; } +void A2dpAudioInterface::closeOutputStream(AudioStreamOut* out) { + if (mOutput == 0 || mOutput != out) { + LOGW("Attempt to close invalid output stream"); + } + else { + delete mOutput; + mOutput = 0; + } +} + + AudioStreamIn* A2dpAudioInterface::openInputStream( - int inputSource, int format, int channelCount, uint32_t sampleRate, - status_t *status, AudioSystem::audio_in_acoustics acoustics) + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, + AudioSystem::audio_in_acoustics acoustics) { - if (status) - *status = -1; - return NULL; + return mHardwareInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); +} + +void A2dpAudioInterface::closeInputStream(AudioStreamIn* in) +{ + return mHardwareInterface->closeInputStream(in); +} + +status_t A2dpAudioInterface::setMode(int mode) +{ + return mHardwareInterface->setMode(mode); } status_t A2dpAudioInterface::setMicMute(bool state) { - return 0; + return mHardwareInterface->setMicMute(state); } status_t A2dpAudioInterface::getMicMute(bool* state) { - return 0; + return mHardwareInterface->getMicMute(state); } -status_t A2dpAudioInterface::setParameter(const char *key, const char *value) +status_t A2dpAudioInterface::setParameters(const String8& keyValuePairs) { - LOGD("setParameter %s,%s\n", key, value); + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + String8 key; + status_t status = NO_ERROR; + + LOGV("setParameters() %s", keyValuePairs.string()); + + key = "bluetooth_enabled"; + if (param.get(key, value) == NO_ERROR) { + mBluetoothEnabled = (value == "true"); + if (mOutput) { + mOutput->setBluetoothEnabled(mBluetoothEnabled); + } + param.remove(key); + } - if (!key || !value) - return -EINVAL; + if (param.size()) { + status_t hwStatus = mHardwareInterface->setParameters(param.toString()); + if (status == NO_ERROR) { + status = hwStatus; + } + } - if (strcmp(key, "a2dp_sink_address") == 0) { - return mOutput->setAddress(value); + return status; +} + +String8 A2dpAudioInterface::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + AudioParameter a2dpParam = AudioParameter(); + String8 value; + String8 key; + + key = "bluetooth_enabled"; + if (param.get(key, value) == NO_ERROR) { + value = mBluetoothEnabled ? "true" : "false"; + a2dpParam.add(key, value); + param.remove(key); } - if (strcmp(key, "bluetooth_enabled") == 0) { - mOutput->setBluetoothEnabled(strcmp(value, "true") == 0); + + String8 keyValuePairs = a2dpParam.toString(); + + if (param.size()) { + keyValuePairs += ";"; + keyValuePairs += mHardwareInterface->getParameters(param.toString()); } - return 0; + LOGV("getParameters() %s", keyValuePairs.string()); + return keyValuePairs; } -status_t A2dpAudioInterface::setVoiceVolume(float v) +size_t A2dpAudioInterface::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) { - return 0; + return mHardwareInterface->getInputBufferSize(sampleRate, format, channelCount); } -status_t A2dpAudioInterface::setMasterVolume(float v) +status_t A2dpAudioInterface::setVoiceVolume(float v) { - return 0; + return mHardwareInterface->setVoiceVolume(v); } -status_t A2dpAudioInterface::doRouting() +status_t A2dpAudioInterface::setMasterVolume(float v) { - return 0; + return mHardwareInterface->setMasterVolume(v); } status_t A2dpAudioInterface::dump(int fd, const Vector<String16>& args) { - return 0; + return mHardwareInterface->dumpState(fd, args); } // ---------------------------------------------------------------------------- @@ -132,7 +204,7 @@ A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() : mFd(-1), mStandby(true), mStartCount(0), mRetryCount(0), mData(NULL), // assume BT enabled to start, this is safe because its only the // enabled->disabled transition we are worried about - mBluetoothEnabled(true) + mBluetoothEnabled(true), mDevice(0) { // use any address by default strcpy(mA2dpAddress, "00:00:00:00:00:00"); @@ -140,27 +212,43 @@ A2dpAudioInterface::A2dpAudioStreamOut::A2dpAudioStreamOut() : } status_t A2dpAudioInterface::A2dpAudioStreamOut::set( - int format, int channels, uint32_t rate) + uint32_t device, int *pFormat, uint32_t *pChannels, uint32_t *pRate) { - LOGD("A2dpAudioStreamOut::set %d, %d, %d\n", format, channels, rate); + int lFormat = pFormat ? *pFormat : 0; + uint32_t lChannels = pChannels ? *pChannels : 0; + uint32_t lRate = pRate ? *pRate : 0; + + LOGD("A2dpAudioStreamOut::set %x, %d, %d, %d\n", device, lFormat, lChannels, lRate); // fix up defaults - if (format == 0) format = AudioSystem::PCM_16_BIT; - if (channels == 0) channels = channelCount(); - if (rate == 0) rate = sampleRate(); + if (lFormat == 0) lFormat = format(); + if (lChannels == 0) lChannels = channels(); + if (lRate == 0) lRate = sampleRate(); // check values - if ((format != AudioSystem::PCM_16_BIT) || - (channels != channelCount()) || - (rate != sampleRate())) + if ((lFormat != format()) || + (lChannels != channels()) || + (lRate != sampleRate())){ + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); return BAD_VALUE; + } + if (pFormat) *pFormat = lFormat; + if (pChannels) *pChannels = lChannels; + if (pRate) *pRate = lRate; + + mDevice = device; return NO_ERROR; } A2dpAudioInterface::A2dpAudioStreamOut::~A2dpAudioStreamOut() { + LOGV("A2dpAudioStreamOut destructor"); + standby(); close(); + LOGV("A2dpAudioStreamOut destructor returning from close()"); } ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes) @@ -230,6 +318,59 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::standby() return result; } +status_t A2dpAudioInterface::A2dpAudioStreamOut::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + String8 key = String8("a2dp_sink_address"); + status_t status = NO_ERROR; + int device; + LOGV("A2dpAudioStreamOut::setParameters() %s", keyValuePairs.string()); + + if (param.get(key, value) == NO_ERROR) { + if (value.length() != strlen("00:00:00:00:00:00")) { + status = BAD_VALUE; + } else { + setAddress(value.string()); + } + param.remove(key); + } + key = AudioParameter::keyRouting; + if (param.getInt(key, device) == NO_ERROR) { + if (AudioSystem::isA2dpDevice((AudioSystem::audio_devices)device)) { + mDevice = device; + status = NO_ERROR; + } else { + status = BAD_VALUE; + } + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 A2dpAudioInterface::A2dpAudioStreamOut::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8("a2dp_sink_address"); + + if (param.get(key, value) == NO_ERROR) { + value = mA2dpAddress; + param.add(key, value); + } + key = AudioParameter::keyRouting; + if (param.get(key, value) == NO_ERROR) { + param.addInt(key, (int)mDevice); + } + + LOGV("A2dpAudioStreamOut::getParameters() %s", param.toString().string()); + return param.toString(); +} + status_t A2dpAudioInterface::A2dpAudioStreamOut::setAddress(const char* address) { Mutex::Autolock lock(mLock); @@ -260,12 +401,14 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::setBluetoothEnabled(bool enable status_t A2dpAudioInterface::A2dpAudioStreamOut::close() { Mutex::Autolock lock(mLock); + LOGV("A2dpAudioStreamOut::close() calling close_l()"); return close_l(); } status_t A2dpAudioInterface::A2dpAudioStreamOut::close_l() { if (mData) { + LOGV("A2dpAudioStreamOut::close_l() calling a2dp_cleanup(mData)"); a2dp_cleanup(mData); mData = NULL; } @@ -277,5 +420,4 @@ status_t A2dpAudioInterface::A2dpAudioStreamOut::dump(int fd, const Vector<Strin return NO_ERROR; } - }; // namespace android diff --git a/libs/audioflinger/A2dpAudioInterface.h b/libs/audioflinger/A2dpAudioInterface.h index 091e775..d6709e2 100644 --- a/libs/audioflinger/A2dpAudioInterface.h +++ b/libs/audioflinger/A2dpAudioInterface.h @@ -32,38 +32,44 @@ class A2dpAudioInterface : public AudioHardwareBase class A2dpAudioStreamOut; public: - A2dpAudioInterface(); + A2dpAudioInterface(AudioHardwareInterface* hw); virtual ~A2dpAudioInterface(); virtual status_t initCheck(); virtual status_t setVoiceVolume(float volume); virtual status_t setMasterVolume(float volume); + virtual status_t setMode(int mode); + // mic mute virtual status_t setMicMute(bool state); virtual status_t getMicMute(bool* state); - // Temporary interface, do not use - // TODO: Replace with a more generic key:value get/set mechanism - virtual status_t setParameter(const char *key, const char *value); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); // create I/O streams virtual AudioStreamOut* openOutputStream( - int format=0, - int channelCount=0, - uint32_t sampleRate=0, + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); virtual AudioStreamIn* openInputStream( - int inputSource, - int format, - int channelCount, - uint32_t sampleRate, + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); +// static AudioHardwareInterface* createA2dpInterface(); protected: - virtual status_t doRouting(); virtual status_t dump(int fd, const Vector<String16>& args); private: @@ -71,19 +77,22 @@ private: public: A2dpAudioStreamOut(); virtual ~A2dpAudioStreamOut(); - status_t set(int format, - int channelCount, - uint32_t sampleRate); + status_t set(uint32_t device, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate); virtual uint32_t sampleRate() const { return 44100; } // SBC codec wants a multiple of 512 virtual size_t bufferSize() const { return 512 * 20; } - virtual int channelCount() const { return 2; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; } virtual int format() const { return AudioSystem::PCM_16_BIT; } virtual uint32_t latency() const { return ((1000*bufferSize())/frameSize())/sampleRate() + 200; } - virtual status_t setVolume(float volume) { return INVALID_OPERATION; } + virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; } virtual ssize_t write(const void* buffer, size_t bytes); status_t standby(); virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); private: friend class A2dpAudioInterface; @@ -102,11 +111,18 @@ private: void* mData; Mutex mLock; bool mBluetoothEnabled; + uint32_t mDevice; }; + friend class A2dpAudioStreamOut; + A2dpAudioStreamOut* mOutput; + AudioHardwareInterface *mHardwareInterface; + char mA2dpAddress[20]; + bool mBluetoothEnabled; }; + // ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk index 50d516b..c10e02b 100644 --- a/libs/audioflinger/Android.mk +++ b/libs/audioflinger/Android.mk @@ -1,16 +1,30 @@ LOCAL_PATH:= $(call my-dir) +#AUDIO_POLICY_TEST := true +#ENABLE_AUDIO_DUMP := true + include $(CLEAR_VARS) + +ifeq ($(AUDIO_POLICY_TEST),true) + ENABLE_AUDIO_DUMP := true +endif + + LOCAL_SRC_FILES:= \ AudioHardwareGeneric.cpp \ AudioHardwareStub.cpp \ - AudioDumpInterface.cpp \ AudioHardwareInterface.cpp +ifeq ($(ENABLE_AUDIO_DUMP),true) + LOCAL_SRC_FILES += AudioDumpInterface.cpp + LOCAL_CFLAGS += -DENABLE_AUDIO_DUMP +endif + LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ + libbinder \ libmedia \ libhardware_legacy @@ -20,8 +34,44 @@ endif LOCAL_MODULE:= libaudiointerface +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_SRC_FILES += A2dpAudioInterface.cpp + LOCAL_SHARED_LIBRARIES += liba2dp + LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP + LOCAL_C_INCLUDES += $(call include-path-for, bluez) +endif + include $(BUILD_STATIC_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + AudioPolicyManagerGeneric.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libmedia + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl +else + LOCAL_SHARED_LIBRARIES += libdl +endif + +LOCAL_MODULE:= libaudiopolicygeneric + +ifeq ($(BOARD_HAVE_BLUETOOTH),true) + LOCAL_CFLAGS += -DWITH_A2DP +endif + +ifeq ($(AUDIO_POLICY_TEST),true) + LOCAL_CFLAGS += -DAUDIO_POLICY_TEST +endif + +include $(BUILD_SHARED_LIBRARY) + include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ @@ -29,28 +79,44 @@ LOCAL_SRC_FILES:= \ AudioMixer.cpp.arm \ AudioResampler.cpp.arm \ AudioResamplerSinc.cpp.arm \ - AudioResamplerCubic.cpp.arm + AudioResamplerCubic.cpp.arm \ + AudioPolicyService.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ + libbinder \ libmedia \ - libhardware_legacy + libhardware_legacy \ + libaudiopolicygeneric ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) LOCAL_STATIC_LIBRARIES += libaudiointerface + LOCAL_CFLAGS += -DGENERIC_AUDIO +else + LOCAL_SHARED_LIBRARIES += libaudio libaudiopolicy +endif + +ifeq ($(TARGET_SIMULATOR),true) + LOCAL_LDLIBS += -ldl else - LOCAL_SHARED_LIBRARIES += libaudio + LOCAL_SHARED_LIBRARIES += libdl endif LOCAL_MODULE:= libaudioflinger ifeq ($(BOARD_HAVE_BLUETOOTH),true) - LOCAL_SRC_FILES += A2dpAudioInterface.cpp - LOCAL_SHARED_LIBRARIES += liba2dp LOCAL_CFLAGS += -DWITH_BLUETOOTH -DWITH_A2DP - LOCAL_C_INCLUDES += $(call include-path-for, bluez-libs) - LOCAL_C_INCLUDES += $(call include-path-for, bluez-utils) +endif + +ifeq ($(AUDIO_POLICY_TEST),true) + LOCAL_CFLAGS += -DAUDIO_POLICY_TEST +endif + +ifeq ($(TARGET_SIMULATOR),true) + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt -lpthread + endif endif include $(BUILD_SHARED_LIBRARY) diff --git a/libs/audioflinger/AudioDumpInterface.cpp b/libs/audioflinger/AudioDumpInterface.cpp index b4940cb..87bb014 100644 --- a/libs/audioflinger/AudioDumpInterface.cpp +++ b/libs/audioflinger/AudioDumpInterface.cpp @@ -16,6 +16,7 @@ */ #define LOG_TAG "AudioFlingerDump" +//#define LOG_NDEBUG 0 #include <stdint.h> #include <sys/types.h> @@ -28,68 +29,209 @@ namespace android { -bool gFirst = true; // true if first write after a standby - // ---------------------------------------------------------------------------- AudioDumpInterface::AudioDumpInterface(AudioHardwareInterface* hw) + : mFirstHwOutput(true), mPolicyCommands(String8("")), mFileName(String8("")) { if(hw == 0) { LOGE("Dump construct hw = 0"); } mFinalInterface = hw; - mStreamOut = 0; + LOGV("Constructor %p, mFinalInterface %p", this, mFinalInterface); } AudioDumpInterface::~AudioDumpInterface() { + for (size_t i = 0; i < mOutputs.size(); i++) { + closeOutputStream((AudioStreamOut *)mOutputs[i]); + } if(mFinalInterface) delete mFinalInterface; - if(mStreamOut) delete mStreamOut; } AudioStreamOut* AudioDumpInterface::openOutputStream( - int format, int channelCount, uint32_t sampleRate, status_t *status) + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) +{ + AudioStreamOut* outFinal = NULL; + int lFormat = AudioSystem::PCM_16_BIT; + uint32_t lChannels = AudioSystem::CHANNEL_OUT_STEREO; + 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; + } + } + } else { + if (format != 0 && *format != 0) lFormat = *format; + if (channels != 0 && *channels != 0) lChannels = *channels; + if (sampleRate != 0 && *sampleRate != 0) lRate = *sampleRate; + if (status) *status = NO_ERROR; + } + LOGV("openOutputStream(), outFinal %p", outFinal); + + AudioStreamOutDump *dumOutput = new AudioStreamOutDump(this, mOutputs.size(), outFinal, + devices, lFormat, lChannels, lRate); + mOutputs.add(dumOutput); + + return dumOutput; +} + +void AudioDumpInterface::closeOutputStream(AudioStreamOut* out) +{ + AudioStreamOutDump *dumpOut = (AudioStreamOutDump *)out; + + if (mOutputs.indexOf(dumpOut) < 0) { + LOGW("Attempt to close invalid output stream"); + return; + } + dumpOut->standby(); + if (dumpOut->finalStream() != NULL) { + mFinalInterface->closeOutputStream(dumpOut->finalStream()); + } + + mOutputs.remove(dumpOut); + delete dumpOut; +} + +AudioStreamIn* AudioDumpInterface::openInputStream(uint32_t devices, int *format, uint32_t *channels, + uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) { - AudioStreamOut* outFinal = mFinalInterface->openOutputStream(format, channelCount, sampleRate, status); + AudioStreamIn* inFinal = NULL; + int lFormat = AudioSystem::PCM_16_BIT; + uint32_t lChannels = AudioSystem::CHANNEL_IN_MONO; + uint32_t lRate = 8000; + - if(outFinal) { - mStreamOut = new AudioStreamOutDump(outFinal); - return mStreamOut; + if (mInputs.size() == 0) { + inFinal = mFinalInterface->openInputStream(devices, format, channels, sampleRate, status, acoustics); + if (inFinal == 0) return 0; + + lFormat = inFinal->format(); + lChannels = inFinal->channels(); + lRate = inFinal->sampleRate(); } else { - LOGE("Dump outFinal=0"); - return 0; + if (format != 0 && *format != 0) lFormat = *format; + if (channels != 0 && *channels != 0) lChannels = *channels; + if (sampleRate != 0 && *sampleRate != 0) lRate = *sampleRate; + if (status) *status = NO_ERROR; + } + LOGV("openInputStream(), inFinal %p", inFinal); + + AudioStreamInDump *dumInput = new AudioStreamInDump(this, mInputs.size(), inFinal, + devices, lFormat, lChannels, lRate); + mInputs.add(dumInput); + + return dumInput; +} +void AudioDumpInterface::closeInputStream(AudioStreamIn* in) +{ + AudioStreamInDump *dumpIn = (AudioStreamInDump *)in; + + if (mInputs.indexOf(dumpIn) < 0) { + LOGW("Attempt to close invalid input stream"); + return; + } + dumpIn->standby(); + if (dumpIn->finalStream() != NULL) { + mFinalInterface->closeInputStream(dumpIn->finalStream()); } + + mInputs.remove(dumpIn); + delete dumpIn; } + +status_t AudioDumpInterface::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 value; + int valueInt; + LOGV("setParameters %s", keyValuePairs.string()); + + if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) { + mFileName = value; + return NO_ERROR; + } + if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) { + Mutex::Autolock _l(mLock); + param.remove(String8("test_cmd_policy")); + mPolicyCommands = param.toString(); + LOGV("test_cmd_policy command %s written", mPolicyCommands.string()); + return NO_ERROR; + } + + if (mFinalInterface != 0 ) return mFinalInterface->setParameters(keyValuePairs); + return NO_ERROR; +} + +String8 AudioDumpInterface::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + +// LOGV("getParameters %s", keys.string()); + + if (param.get(String8("test_cmd_file_name"), value) == NO_ERROR) { + return mFileName; + } + if (param.get(String8("test_cmd_policy"), value) == NO_ERROR) { + Mutex::Autolock _l(mLock); +// LOGV("test_cmd_policy command %s read", mPolicyCommands.string()); + return mPolicyCommands; + } + + if (mFinalInterface != 0 ) return mFinalInterface->getParameters(keys); + return String8(""); +} + + // ---------------------------------------------------------------------------- -AudioStreamOutDump::AudioStreamOutDump( AudioStreamOut* finalStream) +AudioStreamOutDump::AudioStreamOutDump(AudioDumpInterface *interface, + int id, + AudioStreamOut* finalStream, + uint32_t devices, + int format, + uint32_t channels, + 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) { - mFinalStream = finalStream; - mOutFile = 0; + LOGV("AudioStreamOutDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); } AudioStreamOutDump::~AudioStreamOutDump() { Close(); - delete mFinalStream; } ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) { ssize_t ret; - ret = mFinalStream->write(buffer, bytes); - if(!mOutFile && gFirst) { - gFirst = false; - // check if dump file exist - mOutFile = fopen(FLINGER_DUMP_NAME, "r"); - if(mOutFile) { - fclose(mOutFile); - mOutFile = fopen(FLINGER_DUMP_NAME, "ab"); + if (mFinalStream) { + ret = mFinalStream->write(buffer, bytes); + } else { + usleep((bytes * 1000000) / frameSize() / sampleRate()); + ret = bytes; + } + if(!mOutFile) { + 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); } } if (mOutFile) { @@ -100,13 +242,65 @@ ssize_t AudioStreamOutDump::write(const void* buffer, size_t bytes) status_t AudioStreamOutDump::standby() { + LOGV("AudioStreamOutDump standby(), mOutFile %p, mFinalStream %p", mOutFile, mFinalStream); + Close(); - gFirst = true; - return mFinalStream->standby(); + if (mFinalStream != 0 ) return mFinalStream->standby(); + return NO_ERROR; +} + +uint32_t AudioStreamOutDump::sampleRate() const +{ + if (mFinalStream != 0 ) return mFinalStream->sampleRate(); + return mSampleRate; +} + +size_t AudioStreamOutDump::bufferSize() const +{ + if (mFinalStream != 0 ) return mFinalStream->bufferSize(); + return mBufferSize; +} + +uint32_t AudioStreamOutDump::channels() const +{ + if (mFinalStream != 0 ) return mFinalStream->channels(); + return mChannels; +} +int AudioStreamOutDump::format() const +{ + if (mFinalStream != 0 ) return mFinalStream->format(); + return mFormat; +} +uint32_t AudioStreamOutDump::latency() const +{ + if (mFinalStream != 0 ) return mFinalStream->latency(); + return 0; +} +status_t AudioStreamOutDump::setVolume(float left, float right) +{ + if (mFinalStream != 0 ) return mFinalStream->setVolume(left, right); + return NO_ERROR; +} +status_t AudioStreamOutDump::setParameters(const String8& keyValuePairs) +{ + LOGV("AudioStreamOutDump::setParameters()"); + if (mFinalStream != 0 ) return mFinalStream->setParameters(keyValuePairs); + return NO_ERROR; +} +String8 AudioStreamOutDump::getParameters(const String8& keys) +{ + String8 result = String8(""); + if (mFinalStream != 0 ) result = mFinalStream->getParameters(keys); + return result; } +status_t AudioStreamOutDump::dump(int fd, const Vector<String16>& args) +{ + if (mFinalStream != 0 ) return mFinalStream->dump(fd, args); + return NO_ERROR; +} -void AudioStreamOutDump::Close(void) +void AudioStreamOutDump::Close() { if(mOutFile) { fclose(mOutFile); @@ -114,4 +308,140 @@ void AudioStreamOutDump::Close(void) } } +// ---------------------------------------------------------------------------- + +AudioStreamInDump::AudioStreamInDump(AudioDumpInterface *interface, + int id, + AudioStreamIn* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate) + : mInterface(interface), mId(id), + mSampleRate(sampleRate), mFormat(format), mChannels(channels), mDevice(devices), + mBufferSize(1024), mFinalStream(finalStream), mInFile(0) +{ + LOGV("AudioStreamInDump Constructor %p, mInterface %p, mFinalStream %p", this, mInterface, mFinalStream); +} + + +AudioStreamInDump::~AudioStreamInDump() +{ + Close(); +} + +ssize_t AudioStreamInDump::read(void* buffer, ssize_t bytes) +{ + if (mFinalStream) { + return mFinalStream->read(buffer, bytes); + } + + usleep((bytes * 1000000) / frameSize() / sampleRate()); + + if(!mInFile) { + 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"); + mInFile = fopen(name, "rb"); + LOGV("Opening dump file %s, fh %p", name, mInFile); + if (mInFile) { + fseek(mInFile, 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); + } + } + return bytes; +} + +status_t AudioStreamInDump::standby() +{ + LOGV("AudioStreamInDump standby(), mInFile %p, mFinalStream %p", mInFile, mFinalStream); + + Close(); + if (mFinalStream != 0 ) return mFinalStream->standby(); + return NO_ERROR; +} + +status_t AudioStreamInDump::setGain(float gain) +{ + if (mFinalStream != 0 ) return mFinalStream->setGain(gain); + return NO_ERROR; +} + +uint32_t AudioStreamInDump::sampleRate() const +{ + if (mFinalStream != 0 ) return mFinalStream->sampleRate(); + return mSampleRate; +} + +size_t AudioStreamInDump::bufferSize() const +{ + if (mFinalStream != 0 ) return mFinalStream->bufferSize(); + return mBufferSize; +} + +uint32_t AudioStreamInDump::channels() const +{ + if (mFinalStream != 0 ) return mFinalStream->channels(); + return mChannels; +} + +int AudioStreamInDump::format() const +{ + if (mFinalStream != 0 ) return mFinalStream->format(); + return mFormat; +} + +status_t AudioStreamInDump::setParameters(const String8& keyValuePairs) +{ + LOGV("AudioStreamInDump::setParameters()"); + if (mFinalStream != 0 ) return mFinalStream->setParameters(keyValuePairs); + return NO_ERROR; +} + +String8 AudioStreamInDump::getParameters(const String8& keys) +{ + String8 result = String8(""); + if (mFinalStream != 0 ) result = mFinalStream->getParameters(keys); + return result; +} + +status_t AudioStreamInDump::dump(int fd, const Vector<String16>& args) +{ + if (mFinalStream != 0 ) return mFinalStream->dump(fd, args); + return NO_ERROR; +} + +void AudioStreamInDump::Close() +{ + if(mInFile) { + fclose(mInFile); + mInFile = 0; + } +} }; // namespace android diff --git a/libs/audioflinger/AudioDumpInterface.h b/libs/audioflinger/AudioDumpInterface.h index b72c94e..4de4a16 100644 --- a/libs/audioflinger/AudioDumpInterface.h +++ b/libs/audioflinger/AudioDumpInterface.h @@ -20,35 +20,94 @@ #include <stdint.h> #include <sys/types.h> +#include <utils/String8.h> +#include <utils/SortedVector.h> #include <hardware_legacy/AudioHardwareBase.h> namespace android { -#define FLINGER_DUMP_NAME "/data/FlingerOut.pcm" // name of file used for dump +#define AUDIO_DUMP_WAVE_HDR_SIZE 44 + +class AudioDumpInterface; class AudioStreamOutDump : public AudioStreamOut { public: - AudioStreamOutDump( AudioStreamOut* FinalStream); + AudioStreamOutDump(AudioDumpInterface *interface, + int id, + AudioStreamOut* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate); ~AudioStreamOutDump(); - virtual ssize_t write(const void* buffer, size_t bytes); - - virtual uint32_t sampleRate() const { return mFinalStream->sampleRate(); } - virtual size_t bufferSize() const { return mFinalStream->bufferSize(); } - virtual int channelCount() const { return mFinalStream->channelCount(); } - virtual int format() const { return mFinalStream->format(); } - virtual uint32_t latency() const { return mFinalStream->latency(); } - virtual status_t setVolume(float volume) - { return mFinalStream->setVolume(volume); } + + virtual ssize_t write(const void* buffer, size_t bytes); + virtual uint32_t sampleRate() const; + virtual size_t bufferSize() const; + virtual uint32_t channels() const; + virtual int format() const; + virtual uint32_t latency() const; + virtual status_t setVolume(float left, float right); virtual status_t standby(); - virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalStream->dump(fd, args); } + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual status_t dump(int fd, const Vector<String16>& args); void Close(void); + AudioStreamOut* finalStream() { return mFinalStream; } + uint32_t device() { return mDevice; } private: + AudioDumpInterface *mInterface; + int mId; + uint32_t mSampleRate; // + uint32_t mFormat; // + uint32_t mChannels; // output configuration + uint32_t mLatency; // + uint32_t mDevice; // current device this output is routed to + size_t mBufferSize; AudioStreamOut *mFinalStream; - FILE *mOutFile; // output file + FILE *mOutFile; // output file + int mFileCount; }; +class AudioStreamInDump : public AudioStreamIn { +public: + AudioStreamInDump(AudioDumpInterface *interface, + int id, + AudioStreamIn* finalStream, + uint32_t devices, + int format, + uint32_t channels, + uint32_t sampleRate); + ~AudioStreamInDump(); + + virtual uint32_t sampleRate() const; + virtual size_t bufferSize() const; + virtual uint32_t channels() const; + virtual int format() const; + + virtual status_t setGain(float gain); + virtual ssize_t read(void* buffer, ssize_t bytes); + virtual status_t standby(); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); + virtual status_t dump(int fd, const Vector<String16>& args); + void Close(void); + AudioStreamIn* finalStream() { return mFinalStream; } + uint32_t device() { return mDevice; } + +private: + AudioDumpInterface *mInterface; + int mId; + uint32_t mSampleRate; // + uint32_t mFormat; // + uint32_t mChannels; // output configuration + uint32_t mDevice; // current device this output is routed to + size_t mBufferSize; + AudioStreamIn *mFinalStream; + FILE *mInFile; // output file +}; class AudioDumpInterface : public AudioHardwareBase { @@ -56,10 +115,13 @@ class AudioDumpInterface : public AudioHardwareBase public: AudioDumpInterface(AudioHardwareInterface* hw); virtual AudioStreamOut* openOutputStream( - int format=0, - int channelCount=0, - uint32_t sampleRate=0, + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); + virtual ~AudioDumpInterface(); virtual status_t initCheck() @@ -75,21 +137,25 @@ public: virtual status_t getMicMute(bool* state) {return mFinalInterface->getMicMute(state);} - virtual status_t setParameter(const char* key, const char* value) - {return mFinalInterface->setParameter(key, value);} + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); - virtual AudioStreamIn* openInputStream(int inputSource, int format, int channelCount, - uint32_t sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) - { return mFinalInterface->openInputStream(inputSource, format, channelCount, sampleRate, status, acoustics); } + 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); virtual status_t dump(int fd, const Vector<String16>& args) { return mFinalInterface->dumpState(fd, args); } + String8 fileName() const { return mFileName; } protected: - virtual status_t doRouting() {return mFinalInterface->setRouting(mMode, mRoutes[mMode]);} - - AudioHardwareInterface *mFinalInterface; - AudioStreamOutDump *mStreamOut; + AudioHardwareInterface *mFinalInterface; + SortedVector<AudioStreamOutDump *> mOutputs; + bool mFirstHwOutput; + SortedVector<AudioStreamInDump *> mInputs; + Mutex mLock; + String8 mPolicyCommands; + String8 mFileName; }; }; // namespace android diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index f5bdeda..1336131 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -24,10 +24,10 @@ #include <sys/time.h> #include <sys/resource.h> -#include <utils/IServiceManager.h> +#include <binder/IServiceManager.h> #include <utils/Log.h> -#include <utils/Parcel.h> -#include <utils/IPCThreadState.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> #include <utils/String16.h> #include <utils/threads.h> @@ -71,15 +71,9 @@ static const float MAX_GAIN = 4096.0f; static const int8_t kMaxTrackRetries = 50; static const int8_t kMaxTrackStartupRetries = 50; -static const int kStartSleepTime = 30000; -static const int kStopSleepTime = 30000; - static const int kDumpLockRetries = 50; static const int kDumpLockSleep = 20000; -// Maximum number of pending buffers allocated by OutputTrack::write() -static const uint8_t kMaxOutputTrackBuffers = 5; - #define AUDIOFLINGER_SECURITY_ENABLED 1 @@ -121,131 +115,32 @@ static bool settingsAllowed() { AudioFlinger::AudioFlinger() : BnAudioFlinger(), - mAudioHardware(0), mA2dpAudioInterface(0), mA2dpEnabled(false), mNotifyA2dpChange(false), - mForcedSpeakerCount(0), mA2dpDisableCount(0), mA2dpSuppressed(false), mForcedRoute(0), - mRouteRestoreTime(0), mMusicMuteSaved(false) + mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false) { mHardwareStatus = AUDIO_HW_IDLE; + mAudioHardware = AudioHardwareInterface::create(); + mHardwareStatus = AUDIO_HW_INIT; if (mAudioHardware->initCheck() == NO_ERROR) { // open 16-bit output stream for s/w mixer - mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; - status_t status; - AudioStreamOut *hwOutput = mAudioHardware->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); - mHardwareStatus = AUDIO_HW_IDLE; - if (hwOutput) { - mHardwareMixerThread = new MixerThread(this, hwOutput, AudioSystem::AUDIO_OUTPUT_HARDWARE); - } else { - LOGE("Failed to initialize hardware output stream, status: %d", status); - } - -#ifdef WITH_A2DP - // Create A2DP interface - mA2dpAudioInterface = new A2dpAudioInterface(); - AudioStreamOut *a2dpOutput = mA2dpAudioInterface->openOutputStream(AudioSystem::PCM_16_BIT, 0, 0, &status); - if (a2dpOutput) { - mA2dpMixerThread = new MixerThread(this, a2dpOutput, AudioSystem::AUDIO_OUTPUT_A2DP); - if (hwOutput) { - uint32_t frameCount = ((a2dpOutput->bufferSize()/a2dpOutput->frameSize()) * hwOutput->sampleRate()) / a2dpOutput->sampleRate(); - MixerThread::OutputTrack *a2dpOutTrack = new MixerThread::OutputTrack(mA2dpMixerThread, - hwOutput->sampleRate(), - AudioSystem::PCM_16_BIT, - hwOutput->channelCount(), - frameCount); - mHardwareMixerThread->setOuputTrack(a2dpOutTrack); - } - } else { - LOGE("Failed to initialize A2DP output stream, status: %d", status); - } -#endif - - // FIXME - this should come from settings - setRouting(AudioSystem::MODE_NORMAL, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); - setRouting(AudioSystem::MODE_RINGTONE, AudioSystem::ROUTE_SPEAKER, AudioSystem::ROUTE_ALL); - setRouting(AudioSystem::MODE_IN_CALL, AudioSystem::ROUTE_EARPIECE, AudioSystem::ROUTE_ALL); + setMode(AudioSystem::MODE_NORMAL); setMasterVolume(1.0f); setMasterMute(false); - - // Start record thread - mAudioRecordThread = new AudioRecordThread(mAudioHardware, this); - if (mAudioRecordThread != 0) { - mAudioRecordThread->run("AudioRecordThread", PRIORITY_URGENT_AUDIO); - } - } else { + } else { LOGE("Couldn't even initialize the stubbed audio hardware!"); } } AudioFlinger::~AudioFlinger() { - if (mAudioRecordThread != 0) { - mAudioRecordThread->exit(); - mAudioRecordThread.clear(); - } - mHardwareMixerThread.clear(); - delete mAudioHardware; - // deleting mA2dpAudioInterface also deletes mA2dpOutput; -#ifdef WITH_A2DP - mA2dpMixerThread.clear(); - delete mA2dpAudioInterface; -#endif + mRecordThreads.clear(); + mPlaybackThreads.clear(); } -#ifdef WITH_A2DP -// setA2dpEnabled_l() must be called with AudioFlinger::mLock held -void AudioFlinger::setA2dpEnabled_l(bool enable) -{ - SortedVector < sp<MixerThread::Track> > tracks; - SortedVector < wp<MixerThread::Track> > activeTracks; - - LOGV_IF(enable, "set output to A2DP\n"); - LOGV_IF(!enable, "set output to hardware audio\n"); - - // Transfer tracks playing on MUSIC stream from one mixer to the other - if (enable) { - mHardwareMixerThread->getTracks_l(tracks, activeTracks); - mA2dpMixerThread->putTracks_l(tracks, activeTracks); - } else { - mA2dpMixerThread->getTracks_l(tracks, activeTracks); - mHardwareMixerThread->putTracks_l(tracks, activeTracks); - } - mA2dpEnabled = enable; - mNotifyA2dpChange = true; - mWaitWorkCV.broadcast(); -} - -// checkA2dpEnabledChange_l() must be called with AudioFlinger::mLock held -void AudioFlinger::checkA2dpEnabledChange_l() -{ - if (mNotifyA2dpChange) { - // Notify AudioSystem of the A2DP activation/deactivation - size_t size = mNotificationClients.size(); - for (size_t i = 0; i < size; i++) { - sp<IBinder> binder = mNotificationClients.itemAt(i).promote(); - if (binder != NULL) { - LOGV("Notifying output change to client %p", binder.get()); - sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); - client->a2dpEnabledChanged(mA2dpEnabled); - } - } - mNotifyA2dpChange = false; - } -} -#endif // WITH_A2DP - -bool AudioFlinger::streamForcedToSpeaker(int streamType) -{ - // NOTE that streams listed here must not be routed to A2DP by default: - // AudioSystem::routedToA2dpOutput(streamType) == false - return (streamType == AudioSystem::RING || - streamType == AudioSystem::ALARM || - streamType == AudioSystem::NOTIFICATION || - streamType == AudioSystem::ENFORCED_AUDIBLE); -} status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) { @@ -275,10 +170,7 @@ status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) char buffer[SIZE]; String8 result; int hardwareStatus = mHardwareStatus; - - if (hardwareStatus == AUDIO_HW_IDLE && mHardwareMixerThread->mStandby) { - hardwareStatus = AUDIO_HW_STANDBY; - } + snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus); result.append(buffer); write(fd, result.string(), result.size()); @@ -336,13 +228,16 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) dumpClients(fd, args); dumpInternals(fd, args); - mHardwareMixerThread->dump(fd, args); -#ifdef WITH_A2DP - mA2dpMixerThread->dump(fd, args); -#endif - // dump record client - if (mAudioRecordThread != 0) mAudioRecordThread->dump(fd, args); + // dump playback threads + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + mPlaybackThreads[i]->dump(fd, args); + } + + // dump record threads + for (size_t i = 0; i < mRecordThreads.size(); i++) { + mRecordThreads[i]->dump(fd, args); + } if (mAudioHardware) { mAudioHardware->dumpState(fd, args); @@ -352,6 +247,7 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) return NO_ERROR; } + // IAudioFlinger interface @@ -364,9 +260,10 @@ sp<IAudioTrack> AudioFlinger::createTrack( int frameCount, uint32_t flags, const sp<IMemory>& sharedBuffer, + void *output, status_t *status) { - sp<MixerThread::Track> track; + sp<PlaybackThread::Track> track; sp<TrackHandle> trackHandle; sp<Client> client; wp<Client> wclient; @@ -380,6 +277,12 @@ sp<IAudioTrack> AudioFlinger::createTrack( { Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGE("unknown output thread"); + lStatus = BAD_VALUE; + goto Exit; + } wclient = mClients.valueFor(pid); @@ -389,16 +292,8 @@ sp<IAudioTrack> AudioFlinger::createTrack( client = new Client(this, pid); mClients.add(pid, client); } -#ifdef WITH_A2DP - if (isA2dpEnabled() && AudioSystem::routedToA2dpOutput(streamType)) { - track = mA2dpMixerThread->createTrack_l(client, streamType, sampleRate, format, - channelCount, frameCount, sharedBuffer, &lStatus); - } else -#endif - { - track = mHardwareMixerThread->createTrack_l(client, streamType, sampleRate, format, - channelCount, frameCount, sharedBuffer, &lStatus); - } + track = thread->createTrack_l(client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer, &lStatus); } if (lStatus == NO_ERROR) { trackHandle = new TrackHandle(track); @@ -413,54 +308,59 @@ Exit: return trackHandle; } -uint32_t AudioFlinger::sampleRate(int output) const +uint32_t AudioFlinger::sampleRate(void *output) const { -#ifdef WITH_A2DP - if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { - return mA2dpMixerThread->sampleRate(); - } -#endif - return mHardwareMixerThread->sampleRate(); + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGW("sampleRate() unknown thread %p", output); + return 0; + } + return thread->sampleRate(); } -int AudioFlinger::channelCount(int output) const +int AudioFlinger::channelCount(void *output) const { -#ifdef WITH_A2DP - if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { - return mA2dpMixerThread->channelCount(); - } -#endif - return mHardwareMixerThread->channelCount(); + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGW("channelCount() unknown thread %p", output); + return 0; + } + return thread->channelCount(); } -int AudioFlinger::format(int output) const +int AudioFlinger::format(void *output) const { -#ifdef WITH_A2DP - if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { - return mA2dpMixerThread->format(); - } -#endif - return mHardwareMixerThread->format(); + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGW("format() unknown thread %p", output); + return 0; + } + return thread->format(); } -size_t AudioFlinger::frameCount(int output) const +size_t AudioFlinger::frameCount(void *output) const { -#ifdef WITH_A2DP - if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { - return mA2dpMixerThread->frameCount(); - } -#endif - return mHardwareMixerThread->frameCount(); + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGW("frameCount() unknown thread %p", output); + return 0; + } + return thread->frameCount(); } -uint32_t AudioFlinger::latency(int output) const +uint32_t AudioFlinger::latency(void *output) const { -#ifdef WITH_A2DP - if (output == AudioSystem::AUDIO_OUTPUT_A2DP) { - return mA2dpMixerThread->latency(); - } -#endif - return mHardwareMixerThread->latency(); + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGW("latency() unknown thread %p", output); + return 0; + } + return thread->latency(); } status_t AudioFlinger::setMasterVolume(float value) @@ -477,94 +377,12 @@ status_t AudioFlinger::setMasterVolume(float value) value = 1.0f; } mHardwareStatus = AUDIO_HW_IDLE; - mHardwareMixerThread->setMasterVolume(value); -#ifdef WITH_A2DP - mA2dpMixerThread->setMasterVolume(value); -#endif - - return NO_ERROR; -} - -status_t AudioFlinger::setRouting(int mode, uint32_t routes, uint32_t mask) -{ - status_t err = NO_ERROR; - - // check calling permissions - if (!settingsAllowed()) { - return PERMISSION_DENIED; - } - if ((mode < AudioSystem::MODE_CURRENT) || (mode >= AudioSystem::NUM_MODES)) { - LOGW("Illegal value: setRouting(%d, %u, %u)", mode, routes, mask); - return BAD_VALUE; - } - -#ifdef WITH_A2DP - LOGV("setRouting %d %d %d, tid %d, calling tid %d\n", mode, routes, mask, gettid(), - IPCThreadState::self()->getCallingPid()); - if (mode == AudioSystem::MODE_NORMAL && - (mask & AudioSystem::ROUTE_BLUETOOTH_A2DP)) { - AutoMutex lock(&mLock); - - bool enableA2dp = false; - if (routes & AudioSystem::ROUTE_BLUETOOTH_A2DP) { - enableA2dp = true; - } - if (mA2dpDisableCount > 0) { - mA2dpSuppressed = enableA2dp; - } else { - setA2dpEnabled_l(enableA2dp); - } - LOGV("setOutput done\n"); - } - // setRouting() is always called at least for mode == AudioSystem::MODE_IN_CALL when - // SCO is enabled, whatever current mode is so we can safely handle A2DP disabling only - // in this case to avoid doing it several times. - if (mode == AudioSystem::MODE_IN_CALL && - (mask & AudioSystem::ROUTE_BLUETOOTH_SCO)) { - AutoMutex lock(&mLock); - handleRouteDisablesA2dp_l(routes); - } -#endif - // do nothing if only A2DP routing is affected - mask &= ~AudioSystem::ROUTE_BLUETOOTH_A2DP; - if (mask) { - AutoMutex lock(mHardwareLock); - mHardwareStatus = AUDIO_HW_GET_ROUTING; - uint32_t r; - err = mAudioHardware->getRouting(mode, &r); - if (err == NO_ERROR) { - r = (r & ~mask) | (routes & mask); - if (mode == AudioSystem::MODE_NORMAL || - (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { - mSavedRoute = r; - r |= mForcedRoute; - LOGV("setRouting mSavedRoute %08x mForcedRoute %08x\n", mSavedRoute, mForcedRoute); - } - mHardwareStatus = AUDIO_HW_SET_ROUTING; - err = mAudioHardware->setRouting(mode, r); - } - mHardwareStatus = AUDIO_HW_IDLE; - } - return err; -} + mMasterVolume = value; + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) + mPlaybackThreads[i]->setMasterVolume(value); -uint32_t AudioFlinger::getRouting(int mode) const -{ - uint32_t routes = 0; - if ((mode >= AudioSystem::MODE_CURRENT) && (mode < AudioSystem::NUM_MODES)) { - if (mode == AudioSystem::MODE_NORMAL || - (mode == AudioSystem::MODE_CURRENT && getMode() == AudioSystem::MODE_NORMAL)) { - routes = mSavedRoute; - } else { - mHardwareStatus = AUDIO_HW_GET_ROUTING; - mAudioHardware->getRouting(mode, &routes); - mHardwareStatus = AUDIO_HW_IDLE; - } - } else { - LOGW("Illegal value: getRouting(%d)", mode); - } - return routes; + return NO_ERROR; } status_t AudioFlinger::setMode(int mode) @@ -585,15 +403,6 @@ status_t AudioFlinger::setMode(int mode) return ret; } -int AudioFlinger::getMode() const -{ - int mode = AudioSystem::MODE_INVALID; - mHardwareStatus = AUDIO_HW_SET_MODE; - mAudioHardware->getMode(&mode); - mHardwareStatus = AUDIO_HW_IDLE; - return mode; -} - status_t AudioFlinger::setMicMute(bool state) { // check calling permissions @@ -623,36 +432,46 @@ status_t AudioFlinger::setMasterMute(bool muted) if (!settingsAllowed()) { return PERMISSION_DENIED; } - mHardwareMixerThread->setMasterMute(muted); -#ifdef WITH_A2DP - mA2dpMixerThread->setMasterMute(muted); -#endif + + mMasterMute = muted; + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) + mPlaybackThreads[i]->setMasterMute(muted); + return NO_ERROR; } float AudioFlinger::masterVolume() const { - return mHardwareMixerThread->masterVolume(); + return mMasterVolume; } bool AudioFlinger::masterMute() const { - return mHardwareMixerThread->masterMute(); + return mMasterMute; } -status_t AudioFlinger::setStreamVolume(int stream, float value) +status_t AudioFlinger::setStreamVolume(int stream, float value, void *output) { // check calling permissions if (!settingsAllowed()) { return PERMISSION_DENIED; } - if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES || - uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) { + if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { return BAD_VALUE; } + AutoMutex lock(mLock); + PlaybackThread *thread = NULL; + if (output) { + thread = checkPlaybackThread_l(output); + if (thread == NULL) { + return BAD_VALUE; + } + } + status_t ret = NO_ERROR; + if (stream == AudioSystem::VOICE_CALL || stream == AudioSystem::BLUETOOTH_SCO) { float hwValue; @@ -669,12 +488,18 @@ status_t AudioFlinger::setStreamVolume(int stream, float value) mHardwareStatus = AUDIO_SET_VOICE_VOLUME; ret = mAudioHardware->setVoiceVolume(hwValue); mHardwareStatus = AUDIO_HW_IDLE; + } - mHardwareMixerThread->setStreamVolume(stream, value); -#ifdef WITH_A2DP - mA2dpMixerThread->setStreamVolume(stream, value); -#endif + mStreamTypes[stream].volume = value; + + if (thread == NULL) { + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) + mPlaybackThreads[i]->setStreamVolume(stream, value); + + } else { + thread->setStreamVolume(stream, value); + } return ret; } @@ -686,82 +511,116 @@ status_t AudioFlinger::setStreamMute(int stream, bool muted) return PERMISSION_DENIED; } - if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES || + if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES || uint32_t(stream) == AudioSystem::ENFORCED_AUDIBLE) { return BAD_VALUE; } -#ifdef WITH_A2DP - mA2dpMixerThread->setStreamMute(stream, muted); -#endif - if (stream == AudioSystem::MUSIC) - { - AutoMutex lock(&mHardwareLock); - if (mForcedRoute != 0) - mMusicMuteSaved = muted; - else - mHardwareMixerThread->setStreamMute(stream, muted); - } else { - mHardwareMixerThread->setStreamMute(stream, muted); - } + mStreamTypes[stream].mute = muted; + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) + mPlaybackThreads[i]->setStreamMute(stream, muted); return NO_ERROR; } -float AudioFlinger::streamVolume(int stream) const +float AudioFlinger::streamVolume(int stream, void *output) const { - if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + if (stream < 0 || uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { return 0.0f; } - - float volume = mHardwareMixerThread->streamVolume(stream); + + AutoMutex lock(mLock); + float volume; + if (output) { + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + return 0.0f; + } + volume = thread->streamVolume(stream); + } else { + volume = mStreamTypes[stream].volume; + } + // remove correction applied by setStreamVolume() if (stream == AudioSystem::VOICE_CALL) { volume = (volume - 0.01) / 0.99 ; } - + return volume; } bool AudioFlinger::streamMute(int stream) const { - if (uint32_t(stream) >= AudioSystem::NUM_STREAM_TYPES) { + if (stream < 0 || stream >= (int)AudioSystem::NUM_STREAM_TYPES) { return true; } - - if (stream == AudioSystem::MUSIC && mForcedRoute != 0) - { - return mMusicMuteSaved; - } - return mHardwareMixerThread->streamMute(stream); + + return mStreamTypes[stream].mute; } bool AudioFlinger::isMusicActive() const { Mutex::Autolock _l(mLock); - #ifdef WITH_A2DP - if (isA2dpEnabled()) { - return mA2dpMixerThread->isMusicActive_l(); - } - #endif - return mHardwareMixerThread->isMusicActive_l(); + for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads[i]->isMusicActive()) { + return true; + } + } + return false; } -status_t AudioFlinger::setParameter(const char* key, const char* value) +status_t AudioFlinger::setParameters(void *ioHandle, const String8& keyValuePairs) { - status_t result, result2; - AutoMutex lock(mHardwareLock); - mHardwareStatus = AUDIO_SET_PARAMETER; - - LOGV("setParameter() key %s, value %s, tid %d, calling tid %d", key, value, gettid(), IPCThreadState::self()->getCallingPid()); - result = mAudioHardware->setParameter(key, value); - if (mA2dpAudioInterface) { - result2 = mA2dpAudioInterface->setParameter(key, value); - if (result2) - result = result2; + status_t result; + + LOGV("setParameters(): io %p, keyvalue %s, tid %d, calling tid %d", + ioHandle, keyValuePairs.string(), gettid(), IPCThreadState::self()->getCallingPid()); + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; } - mHardwareStatus = AUDIO_HW_IDLE; - return result; + + // ioHandle == 0 means the parameters are global to the audio hardware interface + if (ioHandle == 0) { + AutoMutex lock(mHardwareLock); + mHardwareStatus = AUDIO_SET_PARAMETER; + result = mAudioHardware->setParameters(keyValuePairs); + mHardwareStatus = AUDIO_HW_IDLE; + return result; + } + + // Check if parameters are for an output + PlaybackThread *playbackThread = checkPlaybackThread_l(ioHandle); + if (playbackThread != NULL) { + return playbackThread->setParameters(keyValuePairs); + } + + // Check if parameters are for an input + RecordThread *recordThread = checkRecordThread_l(ioHandle); + if (recordThread != NULL) { + return recordThread->setParameters(keyValuePairs); + } + + return BAD_VALUE; +} + +String8 AudioFlinger::getParameters(void *ioHandle, const String8& keys) +{ +// LOGV("getParameters() io %p, keys %s, tid %d, calling tid %d", +// ioHandle, keys.string(), gettid(), IPCThreadState::self()->getCallingPid()); + + if (ioHandle == 0) { + return mAudioHardware->getParameters(keys); + } + PlaybackThread *playbackThread = checkPlaybackThread_l(ioHandle); + if (playbackThread != NULL) { + return playbackThread->getParameters(keys); + } + RecordThread *recordThread = checkRecordThread_l(ioHandle); + if (recordThread != NULL) { + return recordThread->getParameters(keys); + } + return String8(""); } size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) @@ -771,7 +630,7 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int cha void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) { - + LOGV("registerClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid()); Mutex::Autolock _l(mLock); @@ -780,12 +639,21 @@ void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) LOGV("Adding notification client %p", binder.get()); binder->linkToDeath(this); mNotificationClients.add(binder); - client->a2dpEnabledChanged(isA2dpEnabled()); + } + + // the config change is always sent from playback or record threads to avoid deadlock + // with AudioSystem::gLock + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + mPlaybackThreads[i]->sendConfigEvent(AudioSystem::OUTPUT_OPENED); + } + + for (size_t i = 0; i < mRecordThreads.size(); i++) { + mRecordThreads[i]->sendConfigEvent(AudioSystem::INPUT_OPENED); } } void AudioFlinger::binderDied(const wp<IBinder>& who) { - + LOGV("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid()); Mutex::Autolock _l(mLock); @@ -800,6 +668,17 @@ void AudioFlinger::binderDied(const wp<IBinder>& who) { } } +void AudioFlinger::audioConfigChanged(int event, void *param1, void *param2) { + Mutex::Autolock _l(mLock); + size_t size = mNotificationClients.size(); + for (size_t i = 0; i < size; i++) { + sp<IBinder> binder = mNotificationClients.itemAt(i); + LOGV("audioConfigChanged() Notifying change to client %p", binder.get()); + sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder); + client->ioConfigChanged(event, param1, param2); + } +} + void AudioFlinger::removeClient(pid_t pid) { LOGV("removeClient() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid()); @@ -807,147 +686,139 @@ void AudioFlinger::removeClient(pid_t pid) mClients.removeItem(pid); } -bool AudioFlinger::isA2dpEnabled() const +// ---------------------------------------------------------------------------- + +AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger) + : Thread(false), + mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0), + mFormat(0), mFrameSize(1), mNewParameters(String8("")), mStandby(false) { - return mA2dpEnabled; } -void AudioFlinger::handleForcedSpeakerRoute(int command) +AudioFlinger::ThreadBase::~ThreadBase() { - switch(command) { - case ACTIVE_TRACK_ADDED: - { - AutoMutex lock(mHardwareLock); - if (mForcedSpeakerCount++ == 0) { - if (mForcedRoute == 0) { - mMusicMuteSaved = mHardwareMixerThread->streamMute(AudioSystem::MUSIC); - LOGV("++mForcedSpeakerCount == 0, mMusicMuteSaved = %d, mRouteRestoreTime = %d", mMusicMuteSaved, mRouteRestoreTime); - if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { - LOGV("Route forced to Speaker ON %08x", mSavedRoute | AudioSystem::ROUTE_SPEAKER); - mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, true); - usleep(mHardwareMixerThread->latency()*1000); - mHardwareStatus = AUDIO_HW_SET_ROUTING; - mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute | AudioSystem::ROUTE_SPEAKER); - mHardwareStatus = AUDIO_HW_IDLE; - // delay track start so that audio hardware has time to siwtch routes - usleep(kStartSleepTime); - } - } - mForcedRoute = AudioSystem::ROUTE_SPEAKER; - mRouteRestoreTime = 0; - } - LOGV("mForcedSpeakerCount incremented to %d", mForcedSpeakerCount); - } - break; - case ACTIVE_TRACK_REMOVED: - { - AutoMutex lock(mHardwareLock); - if (mForcedSpeakerCount > 0){ - if (--mForcedSpeakerCount == 0) { - mRouteRestoreTime = systemTime() + milliseconds(kStopSleepTime/1000); - } - LOGV("mForcedSpeakerCount decremented to %d", mForcedSpeakerCount); - } else { - LOGE("mForcedSpeakerCount is already zero"); - } - } - break; - case CHECK_ROUTE_RESTORE_TIME: - case FORCE_ROUTE_RESTORE: - if (mRouteRestoreTime) { - AutoMutex lock(mHardwareLock); - if (mRouteRestoreTime && - (systemTime() > mRouteRestoreTime || command == FORCE_ROUTE_RESTORE)) { - mHardwareMixerThread->setStreamMute(AudioSystem::MUSIC, mMusicMuteSaved); - mForcedRoute = 0; - if (!(mSavedRoute & AudioSystem::ROUTE_SPEAKER)) { - mHardwareStatus = AUDIO_HW_SET_ROUTING; - mAudioHardware->setRouting(AudioSystem::MODE_NORMAL, mSavedRoute); - mHardwareStatus = AUDIO_HW_IDLE; - LOGV("Route forced to Speaker OFF %08x", mSavedRoute); - } - mRouteRestoreTime = 0; - } - } - break; - } } -#ifdef WITH_A2DP -// handleRouteDisablesA2dp_l() must be called with AudioFlinger::mLock held -void AudioFlinger::handleRouteDisablesA2dp_l(int routes) -{ - if (routes & AudioSystem::ROUTE_BLUETOOTH_SCO) { - if (mA2dpDisableCount++ == 0) { - if (mA2dpEnabled) { - setA2dpEnabled_l(false); - mA2dpSuppressed = true; - } - } - LOGV("mA2dpDisableCount incremented to %d", mA2dpDisableCount); - } else { - if (mA2dpDisableCount > 0) { - if (--mA2dpDisableCount == 0) { - if (mA2dpSuppressed) { - setA2dpEnabled_l(true); - mA2dpSuppressed = false; - } - } - LOGV("mA2dpDisableCount decremented to %d", mA2dpDisableCount); - } else { - LOGV("mA2dpDisableCount is already zero"); - } +void AudioFlinger::ThreadBase::exit() +{ + // keep a strong ref on ourself so that we want get + // destroyed in the middle of requestExitAndWait() + sp <ThreadBase> strongMe = this; + + LOGV("ThreadBase::exit"); + { + AutoMutex lock(&mLock); + requestExit(); + mWaitWorkCV.signal(); } + requestExitAndWait(); } -#endif -// ---------------------------------------------------------------------------- +uint32_t AudioFlinger::ThreadBase::sampleRate() const +{ + return mSampleRate; +} -AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType) - : Thread(false), - mAudioFlinger(audioFlinger), mAudioMixer(0), mOutput(output), mOutputType(outputType), - mSampleRate(0), mFrameCount(0), mChannelCount(0), mFormat(0), mMixBuffer(0), - mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mStandby(false), - mInWrite(false) +int AudioFlinger::ThreadBase::channelCount() const { - mSampleRate = output->sampleRate(); - mChannelCount = output->channelCount(); + return mChannelCount; +} - // FIXME - Current mixer implementation only supports stereo output - if (mChannelCount == 1) { - LOGE("Invalid audio hardware channel count"); +int AudioFlinger::ThreadBase::format() const +{ + return mFormat; +} + +size_t AudioFlinger::ThreadBase::frameCount() const +{ + return mFrameCount; +} + +status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs) +{ + status_t result; + + Mutex::Autolock _l(mLock); + mNewParameters = keyValuePairs; + + mWaitWorkCV.signal(); + mParamCond.wait(mLock); + + return mParamStatus; +} + +void AudioFlinger::ThreadBase::sendConfigEvent(int event, int param) +{ + Mutex::Autolock _l(mLock); + ConfigEvent *configEvent = new ConfigEvent(); + configEvent->mEvent = event; + configEvent->mParam = param; + mConfigEvents.add(configEvent); + LOGV("sendConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param); + mWaitWorkCV.signal(); +} + +void AudioFlinger::ThreadBase::processConfigEvents() +{ + mLock.lock(); + while(!mConfigEvents.isEmpty()) { + LOGV("processConfigEvents() remaining events %d", mConfigEvents.size()); + ConfigEvent *configEvent = mConfigEvents[0]; + mConfigEvents.removeAt(0); + // release mLock because audioConfigChanged() will call + // Audioflinger::audioConfigChanged() which locks AudioFlinger mLock thus creating + // potential cross deadlock between AudioFlinger::mLock and mLock + mLock.unlock(); + audioConfigChanged(configEvent->mEvent, configEvent->mParam); + delete configEvent; + mLock.lock(); } + mLock.unlock(); +} - mFormat = output->format(); - mFrameCount = output->bufferSize() / output->channelCount() / sizeof(int16_t); - mAudioMixer = new AudioMixer(mFrameCount, output->sampleRate()); - // FIXME - Current mixer implementation only supports stereo output: Always - // Allocate a stereo buffer even if HW output is mono. - mMixBuffer = new int16_t[mFrameCount * 2]; - memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); +// ---------------------------------------------------------------------------- + +AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output) + : ThreadBase(audioFlinger), + mMixBuffer(0), mSuspended(false), mBytesWritten(0), mOutput(output), + mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false) +{ + readOutputParameters(); + + mMasterVolume = mAudioFlinger->masterVolume(); + mMasterMute = mAudioFlinger->masterMute(); + + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream); + mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream); + } + // notify client processes that a new input has been opened + sendConfigEvent(AudioSystem::OUTPUT_OPENED); } -AudioFlinger::MixerThread::~MixerThread() +AudioFlinger::PlaybackThread::~PlaybackThread() { delete [] mMixBuffer; - delete mAudioMixer; + if (mType != DUPLICATING) { + mAudioFlinger->mAudioHardware->closeOutputStream(mOutput); + } } -status_t AudioFlinger::MixerThread::dump(int fd, const Vector<String16>& args) +status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args) { dumpInternals(fd, args); dumpTracks(fd, args); return NO_ERROR; } -status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& args) +status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; - snprintf(buffer, SIZE, "Output %d mixer thread tracks\n", mOutputType); + snprintf(buffer, SIZE, "Output thread %p tracks\n", this); result.append(buffer); result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); for (size_t i = 0; i < mTracks.size(); ++i) { @@ -958,7 +829,7 @@ status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& a } } - snprintf(buffer, SIZE, "Output %d mixer thread active tracks\n", mOutputType); + snprintf(buffer, SIZE, "Output thread %p active tracks\n", this); result.append(buffer); result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); for (size_t i = 0; i < mActiveTracks.size(); ++i) { @@ -975,15 +846,13 @@ status_t AudioFlinger::MixerThread::dumpTracks(int fd, const Vector<String16>& a return NO_ERROR; } -status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) +status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; - snprintf(buffer, SIZE, "Output %d mixer thread internals\n", mOutputType); - result.append(buffer); - snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames()); + snprintf(buffer, SIZE, "Output thread %p internals\n", this); result.append(buffer); snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); result.append(buffer); @@ -1000,238 +869,354 @@ status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16> } // Thread virtuals +status_t AudioFlinger::PlaybackThread::readyToRun() +{ + if (mSampleRate == 0) { + LOGE("No working audio driver found."); + return NO_INIT; + } + LOGI("AudioFlinger's thread %p ready to run", this); + return NO_ERROR; +} + +void AudioFlinger::PlaybackThread::onFirstRef() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "Playback Thread %p", this); + + run(buffer, ANDROID_PRIORITY_URGENT_AUDIO); +} + +// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held +sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l( + const sp<AudioFlinger::Client>& client, + int streamType, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + const sp<IMemory>& sharedBuffer, + status_t *status) +{ + sp<Track> track; + status_t lStatus; + + if (mType == DIRECT) { + if (sampleRate != mSampleRate || format != mFormat || channelCount != mChannelCount) { + LOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelCount %d for output %p", + sampleRate, format, channelCount, mOutput); + lStatus = BAD_VALUE; + goto Exit; + } + } else { + // Resampler implementation limits input sampling rate to 2 x output sampling rate. + if (sampleRate > mSampleRate*2) { + LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate); + lStatus = BAD_VALUE; + goto Exit; + } + } + + if (mOutput == 0) { + LOGE("Audio driver not initialized."); + lStatus = NO_INIT; + goto Exit; + } + + { // scope for mLock + Mutex::Autolock _l(mLock); + track = new Track(this, client, streamType, sampleRate, format, + channelCount, frameCount, sharedBuffer); + if (track->getCblk() == NULL) { + lStatus = NO_MEMORY; + goto Exit; + } + mTracks.add(track); + } + lStatus = NO_ERROR; + +Exit: + if(status) { + *status = lStatus; + } + return track; +} + +uint32_t AudioFlinger::PlaybackThread::latency() const +{ + if (mOutput) { + return mOutput->latency(); + } + else { + return 0; + } +} + +status_t AudioFlinger::PlaybackThread::setMasterVolume(float value) +{ + mMasterVolume = value; + return NO_ERROR; +} + +status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted) +{ + mMasterMute = muted; + return NO_ERROR; +} + +float AudioFlinger::PlaybackThread::masterVolume() const +{ + return mMasterVolume; +} + +bool AudioFlinger::PlaybackThread::masterMute() const +{ + return mMasterMute; +} + +status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value) +{ + mStreamTypes[stream].volume = value; + return NO_ERROR; +} + +status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted) +{ + mStreamTypes[stream].mute = muted; + return NO_ERROR; +} + +float AudioFlinger::PlaybackThread::streamVolume(int stream) const +{ + return mStreamTypes[stream].volume; +} + +bool AudioFlinger::PlaybackThread::streamMute(int stream) const +{ + return mStreamTypes[stream].mute; +} + +bool AudioFlinger::PlaybackThread::isMusicActive() const +{ + Mutex::Autolock _l(mLock); + size_t count = mActiveTracks.size(); + for (size_t i = 0 ; i < count ; ++i) { + sp<Track> t = mActiveTracks[i].promote(); + if (t == 0) continue; + Track* const track = t.get(); + if (t->type() == AudioSystem::MUSIC) + return true; + } + return false; +} + +// addTrack_l() must be called with ThreadBase::mLock held +status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track) +{ + status_t status = ALREADY_EXISTS; + + // here the track could be either new, or restarted + // in both cases "unstop" the track + if (track->isPaused()) { + track->mState = TrackBase::RESUMING; + LOGV("PAUSED => RESUMING (%d)", track->name()); + } else { + track->mState = TrackBase::ACTIVE; + LOGV("? => ACTIVE (%d)", track->name()); + } + // set retry count for buffer fill + track->mRetryCount = kMaxTrackStartupRetries; + if (mActiveTracks.indexOf(track) < 0) { + // the track is newly added, make sure it fills up all its + // buffers before playing. This is to ensure the client will + // effectively get the latency it requested. + track->mFillingUpStatus = Track::FS_FILLING; + track->mResetDone = false; + mActiveTracks.add(track); + status = NO_ERROR; + } + + LOGV("mWaitWorkCV.broadcast"); + mWaitWorkCV.broadcast(); + + return status; +} + +// destroyTrack_l() must be called with ThreadBase::mLock held +void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track) +{ + track->mState = TrackBase::TERMINATED; + if (mActiveTracks.indexOf(track) < 0) { + LOGV("remove track (%d) and delete from mixer", track->name()); + mTracks.remove(track); + deleteTrackName_l(track->name()); + } +} + +String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys) +{ + return mOutput->getParameters(keys); +} + +void AudioFlinger::PlaybackThread::audioConfigChanged(int event, int param) { + AudioSystem::OutputDescriptor desc; + void *param2 = 0; + + LOGV("PlaybackThread::audioConfigChanged, thread %p, event %d, param %d", this, event, param); + + switch (event) { + case AudioSystem::OUTPUT_OPENED: + case AudioSystem::OUTPUT_CONFIG_CHANGED: + desc.channels = mChannelCount; + desc.samplingRate = mSampleRate; + desc.format = mFormat; + desc.frameCount = mFrameCount; + desc.latency = latency(); + param2 = &desc; + break; + + case AudioSystem::STREAM_CONFIG_CHANGED: + param2 = ¶m; + case AudioSystem::OUTPUT_CLOSED: + default: + break; + } + mAudioFlinger->audioConfigChanged(event, this, param2); +} + +void AudioFlinger::PlaybackThread::readOutputParameters() +{ + mSampleRate = mOutput->sampleRate(); + mChannelCount = AudioSystem::popCount(mOutput->channels()); + + mFormat = mOutput->format(); + mFrameSize = mOutput->frameSize(); + mFrameCount = mOutput->bufferSize() / mFrameSize; + + mMinBytesToWrite = (mOutput->latency() * mSampleRate * mFrameSize) / 1000; + // FIXME - Current mixer implementation only supports stereo output: Always + // Allocate a stereo buffer even if HW output is mono. + if (mMixBuffer != NULL) delete mMixBuffer; + mMixBuffer = new int16_t[mFrameCount * 2]; + memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); +} + +// ---------------------------------------------------------------------------- + +AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output) + : PlaybackThread(audioFlinger, output), + mAudioMixer(0) +{ + mType = PlaybackThread::MIXER; + mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); + + // FIXME - Current mixer implementation only supports stereo output + if (mChannelCount == 1) { + LOGE("Invalid audio hardware channel count"); + } +} + +AudioFlinger::MixerThread::~MixerThread() +{ + delete mAudioMixer; +} + bool AudioFlinger::MixerThread::threadLoop() { unsigned long sleepTime = kBufferRecoveryInUsecs; int16_t* curBuf = mMixBuffer; Vector< sp<Track> > tracksToRemove; size_t enabledTracks = 0; - nsecs_t standbyTime = systemTime(); - size_t mixBufferSize = mFrameCount*mChannelCount*sizeof(int16_t); + nsecs_t standbyTime = systemTime(); + size_t mixBufferSize = mFrameCount * mFrameSize; nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 2; -#ifdef WITH_A2DP - bool outputTrackActive = false; -#endif + while (!exitPending()) + { + processConfigEvents(); - do { enabledTracks = 0; - { // scope for the AudioFlinger::mLock - - Mutex::Autolock _l(mAudioFlinger->mLock); + { // scope for mLock -#ifdef WITH_A2DP - if (mOutputTrack != NULL && !mAudioFlinger->isA2dpEnabled()) { - if (outputTrackActive) { - mAudioFlinger->mLock.unlock(); - mOutputTrack->stop(); - mAudioFlinger->mLock.lock(); - outputTrackActive = false; - } + Mutex::Autolock _l(mLock); + + if (checkForNewParameters_l()) { + mixBufferSize = mFrameCount * mFrameSize; + maxPeriod = seconds(mFrameCount) / mSampleRate * 2; } - mAudioFlinger->checkA2dpEnabledChange_l(); -#endif const SortedVector< wp<Track> >& activeTracks = mActiveTracks; // put audio hardware into standby after short delay - if UNLIKELY(!activeTracks.size() && systemTime() > standbyTime) { - // wait until we have something to do... - LOGV("Audio hardware entering standby, output %d\n", mOutputType); + if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) || + mSuspended) { if (!mStandby) { + LOGV("Audio hardware entering standby, mixer %p, mSuspended %d\n", this, mSuspended); mOutput->standby(); mStandby = true; + mBytesWritten = 0; } - -#ifdef WITH_A2DP - if (outputTrackActive) { - mAudioFlinger->mLock.unlock(); - mOutputTrack->stop(); - mAudioFlinger->mLock.lock(); - outputTrackActive = false; - } -#endif - if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { - mAudioFlinger->handleForcedSpeakerRoute(FORCE_ROUTE_RESTORE); - } - // we're about to wait, flush the binder command buffer - IPCThreadState::self()->flushCommands(); - mAudioFlinger->mWaitWorkCV.wait(mAudioFlinger->mLock); - LOGV("Audio hardware exiting standby, output %d\n", mOutputType); - - if (mMasterMute == false) { - char value[PROPERTY_VALUE_MAX]; - property_get("ro.audio.silent", value, "0"); - if (atoi(value)) { - LOGD("Silence is golden"); - setMasterMute(true); - } - } - - standbyTime = systemTime() + kStandbyTimeInNsecs; - continue; - } - // Forced route to speaker is handled by hardware mixer thread - if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { - mAudioFlinger->handleForcedSpeakerRoute(CHECK_ROUTE_RESTORE_TIME); - } + if (!activeTracks.size() && mConfigEvents.isEmpty()) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); - // find out which tracks need to be processed - size_t count = activeTracks.size(); - for (size_t i=0 ; i<count ; i++) { - sp<Track> t = activeTracks[i].promote(); - if (t == 0) continue; + if (exitPending()) break; - Track* const track = t.get(); - audio_track_cblk_t* cblk = track->cblk(); + // wait until we have something to do... + LOGV("MixerThread %p TID %d going to sleep\n", this, gettid()); + mWaitWorkCV.wait(mLock); + LOGV("MixerThread %p TID %d waking up\n", this, gettid()); - // The first time a track is added we wait - // for all its buffers to be filled before processing it - mAudioMixer->setActiveTrack(track->name()); - if (cblk->framesReady() && (track->isReady() || track->isStopped()) && - !track->isPaused()) - { - //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); - - // compute volume for this track - int16_t left, right; - if (track->isMuted() || mMasterMute || track->isPausing()) { - left = right = 0; - if (track->isPausing()) { - LOGV("paused(%d)", track->name()); - track->setPaused(); + if (mMasterMute == false) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + setMasterMute(true); } - } else { - float typeVolume = mStreamTypes[track->type()].volume; - float v = mMasterVolume * typeVolume; - float v_clamped = v * cblk->volume[0]; - if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; - left = int16_t(v_clamped); - v_clamped = v * cblk->volume[1]; - if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; - right = int16_t(v_clamped); } - // XXX: these things DON'T need to be done each time - mAudioMixer->setBufferProvider(track); - mAudioMixer->enable(AudioMixer::MIXING); - - int param; - if ( track->mFillingUpStatus == Track::FS_FILLED) { - // no ramp for the first volume setting - track->mFillingUpStatus = Track::FS_ACTIVE; - if (track->mState == TrackBase::RESUMING) { - track->mState = TrackBase::ACTIVE; - param = AudioMixer::RAMP_VOLUME; - } else { - param = AudioMixer::VOLUME; - } - } else { - param = AudioMixer::RAMP_VOLUME; - } - mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); - mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); - mAudioMixer->setParameter( - AudioMixer::TRACK, - AudioMixer::FORMAT, track->format()); - mAudioMixer->setParameter( - AudioMixer::TRACK, - AudioMixer::CHANNEL_COUNT, track->channelCount()); - mAudioMixer->setParameter( - AudioMixer::RESAMPLE, - AudioMixer::SAMPLE_RATE, - int(cblk->sampleRate)); - - // reset retry count - track->mRetryCount = kMaxTrackRetries; - enabledTracks++; - } else { - //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); - if (track->isStopped()) { - track->reset(); - } - if (track->isTerminated() || track->isStopped() || track->isPaused()) { - // We have consumed all the buffers of this track. - // Remove it from the list of active tracks. - LOGV("remove(%d) from active list", track->name()); - tracksToRemove.add(track); - } else { - // No buffers for this track. Give it a few chances to - // fill a buffer, then remove it from active list. - if (--(track->mRetryCount) <= 0) { - LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); - tracksToRemove.add(track); - } - } - // LOGV("disable(%d)", track->name()); - mAudioMixer->disable(AudioMixer::MIXING); + standbyTime = systemTime() + kStandbyTimeInNsecs; + continue; } } - // remove all the tracks that need to be... - count = tracksToRemove.size(); - if (UNLIKELY(count)) { - for (size_t i=0 ; i<count ; i++) { - const sp<Track>& track = tracksToRemove[i]; - removeActiveTrack_l(track); - if (track->isTerminated()) { - mTracks.remove(track); - deleteTrackName_l(track->mName); - } - } - } + enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove); } - + if (LIKELY(enabledTracks)) { // mix buffers... mAudioMixer->process(curBuf); -#ifdef WITH_A2DP - if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { - if (!outputTrackActive) { - LOGV("starting output track in mixer for output %d", mOutputType); - mOutputTrack->start(); - outputTrackActive = true; - } - mOutputTrack->write(curBuf, mFrameCount); - } -#endif - // output audio to hardware - mLastWriteTime = systemTime(); - mInWrite = true; - mOutput->write(curBuf, mixBufferSize); - mNumWrites++; - mInWrite = false; - mStandby = false; - nsecs_t temp = systemTime(); - standbyTime = temp + kStandbyTimeInNsecs; - nsecs_t delta = temp - mLastWriteTime; - if (delta > maxPeriod) { - LOGW("write blocked for %llu msecs", ns2ms(delta)); - mNumDelayedWrites++; - } - sleepTime = kBufferRecoveryInUsecs; - } else { -#ifdef WITH_A2DP - if (mOutputTrack != NULL && mAudioFlinger->isA2dpEnabled()) { - if (outputTrackActive) { - mOutputTrack->write(curBuf, 0); - if (mOutputTrack->bufferQueueEmpty()) { - mOutputTrack->stop(); - outputTrackActive = false; - } else { - standbyTime = systemTime() + kStandbyTimeInNsecs; - } + if (mSuspended) { + usleep(kMaxBufferRecoveryInUsecs); + } else { + mLastWriteTime = systemTime(); + mInWrite = true; + int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize); + if (bytesWritten > 0) mBytesWritten += bytesWritten; + mNumWrites++; + mInWrite = false; + mStandby = false; + nsecs_t temp = systemTime(); + standbyTime = temp + kStandbyTimeInNsecs; + nsecs_t delta = temp - mLastWriteTime; + if (delta > maxPeriod) { + LOGW("write blocked for %llu msecs", ns2ms(delta)); + mNumDelayedWrites++; } + sleepTime = kBufferRecoveryInUsecs; } -#endif + } else { // There was nothing to mix this round, which means all // active tracks were late. Sleep a little bit to give // them another chance. If we're too late, the audio // hardware will zero-fill for us. - //LOGV("no buffers - usleep(%lu)", sleepTime); + // LOGV("thread %p no buffers - usleep(%lu)", this, sleepTime); usleep(sleepTime); if (sleepTime < kMaxBufferRecoveryInUsecs) { sleepTime += kBufferRecoveryInUsecs; @@ -1242,101 +1227,165 @@ bool AudioFlinger::MixerThread::threadLoop() // since we can't guarantee the destructors won't acquire that // same lock. tracksToRemove.clear(); - } while (true); - - return false; -} + } -status_t AudioFlinger::MixerThread::readyToRun() -{ - if (mSampleRate == 0) { - LOGE("No working audio driver found."); - return NO_INIT; + if (!mStandby) { + mOutput->standby(); } - LOGI("AudioFlinger's thread ready to run for output %d", mOutputType); - return NO_ERROR; + sendConfigEvent(AudioSystem::OUTPUT_CLOSED); + processConfigEvents(); + + LOGV("MixerThread %p exiting", this); + return false; } -void AudioFlinger::MixerThread::onFirstRef() +// prepareTracks_l() must be called with ThreadBase::mLock held +size_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove) { - const size_t SIZE = 256; - char buffer[SIZE]; - snprintf(buffer, SIZE, "Mixer Thread for output %d", mOutputType); - - run(buffer, ANDROID_PRIORITY_URGENT_AUDIO); -} + size_t enabledTracks = 0; + // find out which tracks need to be processed + size_t count = activeTracks.size(); + for (size_t i=0 ; i<count ; i++) { + sp<Track> t = activeTracks[i].promote(); + if (t == 0) continue; -// MixerThread::createTrack_l() must be called with AudioFlinger::mLock held -sp<AudioFlinger::MixerThread::Track> AudioFlinger::MixerThread::createTrack_l( - const sp<AudioFlinger::Client>& client, - int streamType, - uint32_t sampleRate, - int format, - int channelCount, - int frameCount, - const sp<IMemory>& sharedBuffer, - status_t *status) -{ - sp<Track> track; - status_t lStatus; - - // Resampler implementation limits input sampling rate to 2 x output sampling rate. - if (sampleRate > mSampleRate*2) { - LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate); - lStatus = BAD_VALUE; - goto Exit; - } + Track* const track = t.get(); + audio_track_cblk_t* cblk = track->cblk(); + // The first time a track is added we wait + // for all its buffers to be filled before processing it + mAudioMixer->setActiveTrack(track->name()); + if (cblk->framesReady() && (track->isReady() || track->isStopped()) && + !track->isPaused()) + { + //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + + // compute volume for this track + int16_t left, right; + if (track->isMuted() || mMasterMute || track->isPausing() || + mStreamTypes[track->type()].mute) { + left = right = 0; + if (track->isPausing()) { + track->setPaused(); + } + } else { + float typeVolume = mStreamTypes[track->type()].volume; + float v = mMasterVolume * typeVolume; + float v_clamped = v * cblk->volume[0]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + left = int16_t(v_clamped); + v_clamped = v * cblk->volume[1]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + right = int16_t(v_clamped); + } - if (mSampleRate == 0) { - LOGE("Audio driver not initialized."); - lStatus = NO_INIT; - goto Exit; + // XXX: these things DON'T need to be done each time + mAudioMixer->setBufferProvider(track); + mAudioMixer->enable(AudioMixer::MIXING); + + int param; + if ( track->mFillingUpStatus == Track::FS_FILLED) { + // no ramp for the first volume setting + track->mFillingUpStatus = Track::FS_ACTIVE; + if (track->mState == TrackBase::RESUMING) { + track->mState = TrackBase::ACTIVE; + param = AudioMixer::RAMP_VOLUME; + } else { + param = AudioMixer::VOLUME; + } + } else { + param = AudioMixer::RAMP_VOLUME; + } + mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); + mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::FORMAT, track->format()); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::CHANNEL_COUNT, track->channelCount()); + mAudioMixer->setParameter( + AudioMixer::RESAMPLE, + AudioMixer::SAMPLE_RATE, + int(cblk->sampleRate)); + + // reset retry count + track->mRetryCount = kMaxTrackRetries; + enabledTracks++; + } else { + //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); + if (track->isStopped()) { + track->reset(); + } + if (track->isTerminated() || track->isStopped() || track->isPaused()) { + // We have consumed all the buffers of this track. + // Remove it from the list of active tracks. + tracksToRemove->add(track); + mAudioMixer->disable(AudioMixer::MIXING); + } else { + // No buffers for this track. Give it a few chances to + // fill a buffer, then remove it from active list. + if (--(track->mRetryCount) <= 0) { + LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); + tracksToRemove->add(track); + } + // For tracks using static shared memry buffer, make sure that we have + // written enough data to audio hardware before disabling the track + // NOTE: this condition with arrive before track->mRetryCount <= 0 so we + // don't care about code removing track from active list above. + if ((track->mSharedBuffer == 0) || (mBytesWritten >= mMinBytesToWrite)) { + mAudioMixer->disable(AudioMixer::MIXING); + } else { + enabledTracks++; + } + } + } } - track = new Track(this, client, streamType, sampleRate, format, - channelCount, frameCount, sharedBuffer); - if (track->getCblk() == NULL) { - lStatus = NO_MEMORY; - goto Exit; + // remove all the tracks that need to be... + count = tracksToRemove->size(); + if (UNLIKELY(count)) { + for (size_t i=0 ; i<count ; i++) { + const sp<Track>& track = tracksToRemove->itemAt(i); + mActiveTracks.remove(track); + if (track->isTerminated()) { + mTracks.remove(track); + deleteTrackName_l(track->mName); + } + } } - mTracks.add(track); - lStatus = NO_ERROR; -Exit: - if(status) { - *status = lStatus; - } - return track; + return enabledTracks; } -// getTracks_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::getTracks_l( +void AudioFlinger::MixerThread::getTracks( SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks) + SortedVector < wp<Track> >& activeTracks, + int streamType) { + LOGV ("MixerThread::getTracks() mixer %p, mTracks.size %d, mActiveTracks.size %d", this, mTracks.size(), mActiveTracks.size()); + Mutex::Autolock _l(mLock); size_t size = mTracks.size(); - LOGV ("MixerThread::getTracks_l() for output %d, mTracks.size %d, mActiveTracks.size %d", mOutputType, mTracks.size(), mActiveTracks.size()); for (size_t i = 0; i < size; i++) { sp<Track> t = mTracks[i]; - if (AudioSystem::routedToA2dpOutput(t->mStreamType)) { + if (t->type() == streamType) { tracks.add(t); int j = mActiveTracks.indexOf(t); if (j >= 0) { t = mActiveTracks[j].promote(); if (t != NULL) { - activeTracks.add(t); - } + activeTracks.add(t); + } } } } size = activeTracks.size(); for (size_t i = 0; i < size; i++) { - removeActiveTrack_l(activeTracks[i]); + mActiveTracks.remove(activeTracks[i]); } - + size = tracks.size(); for (size_t i = 0; i < size; i++) { sp<Track> t = tracks[i]; @@ -1345,219 +1394,554 @@ void AudioFlinger::MixerThread::getTracks_l( } } -// putTracks_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::putTracks_l( +void AudioFlinger::MixerThread::putTracks( SortedVector < sp<Track> >& tracks, SortedVector < wp<Track> >& activeTracks) { - - LOGV ("MixerThread::putTracks_l() for output %d, tracks.size %d, activeTracks.size %d", mOutputType, tracks.size(), activeTracks.size()); - + LOGV ("MixerThread::putTracks() mixer %p, tracks.size %d, activeTracks.size %d", this, tracks.size(), activeTracks.size()); + Mutex::Autolock _l(mLock); size_t size = tracks.size(); for (size_t i = 0; i < size ; i++) { sp<Track> t = tracks[i]; int name = getTrackName_l(); if (name < 0) return; - + t->mName = name; - t->mMixerThread = this; + t->mThread = this; mTracks.add(t); int j = activeTracks.indexOf(t); if (j >= 0) { - addActiveTrack_l(t); - } + mActiveTracks.add(t); + } } } -uint32_t AudioFlinger::MixerThread::sampleRate() const -{ - return mSampleRate; -} - -int AudioFlinger::MixerThread::channelCount() const +// getTrackName_l() must be called with ThreadBase::mLock held +int AudioFlinger::MixerThread::getTrackName_l() { - return mChannelCount; + return mAudioMixer->getTrackName(); } -int AudioFlinger::MixerThread::format() const +// deleteTrackName_l() must be called with ThreadBase::mLock held +void AudioFlinger::MixerThread::deleteTrackName_l(int name) { - return mFormat; + mAudioMixer->deleteTrackName(name); } -size_t AudioFlinger::MixerThread::frameCount() const +// checkForNewParameters_l() must be called with ThreadBase::mLock held +bool AudioFlinger::MixerThread::checkForNewParameters_l() { - return mFrameCount; -} + bool reconfig = false; -uint32_t AudioFlinger::MixerThread::latency() const -{ - if (mOutput) { - return mOutput->latency(); - } - else { - return 0; + if (mNewParameters != "") { + status_t status = NO_ERROR; + AudioParameter param = AudioParameter(mNewParameters); + int value; + if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { + if (value != AudioSystem::PCM_16_BIT) { + status = BAD_VALUE; + } else { + reconfig = true; + } + } + if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { + if (value != AudioSystem::CHANNEL_OUT_STEREO) { + status = BAD_VALUE; + } else { + reconfig = true; + } + } + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be garantied + // if frame count is changed after track creation + if (!mTracks.isEmpty()) { + status = INVALID_OPERATION; + } else { + reconfig = true; + } + } + if (status == NO_ERROR) { + status = mOutput->setParameters(mNewParameters); + if (!mStandby && status == INVALID_OPERATION) { + mOutput->standby(); + mStandby = true; + mBytesWritten = 0; + status = mOutput->setParameters(mNewParameters); + } + if (status == NO_ERROR && reconfig) { + delete mAudioMixer; + readOutputParameters(); + mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); + for (size_t i = 0; i < mTracks.size() ; i++) { + int name = getTrackName_l(); + if (name < 0) break; + mTracks[i]->mName = name; + } + sendConfigEvent(AudioSystem::OUTPUT_CONFIG_CHANGED); + } + } + mParamStatus = status; + mNewParameters = ""; + mParamCond.signal(); } + return reconfig; } -status_t AudioFlinger::MixerThread::setMasterVolume(float value) +status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) { - mMasterVolume = value; + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + PlaybackThread::dumpInternals(fd, args); + + snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames()); + result.append(buffer); + write(fd, result.string(), result.size()); return NO_ERROR; } -status_t AudioFlinger::MixerThread::setMasterMute(bool muted) +// ---------------------------------------------------------------------------- +AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output) + : PlaybackThread(audioFlinger, output), + mLeftVolume (1.0), mRightVolume(1.0) { - mMasterMute = muted; - return NO_ERROR; + mType = PlaybackThread::DIRECT; } -float AudioFlinger::MixerThread::masterVolume() const +AudioFlinger::DirectOutputThread::~DirectOutputThread() { - return mMasterVolume; } -bool AudioFlinger::MixerThread::masterMute() const + +bool AudioFlinger::DirectOutputThread::threadLoop() { - return mMasterMute; + unsigned long sleepTime = kBufferRecoveryInUsecs; + sp<Track> trackToRemove; + sp<Track> activeTrack; + nsecs_t standbyTime = systemTime(); + int8_t *curBuf; + size_t mixBufferSize = mFrameCount*mFrameSize; + + while (!exitPending()) + { + processConfigEvents(); + + { // scope for the mLock + + Mutex::Autolock _l(mLock); + + if (checkForNewParameters_l()) { + mixBufferSize = mFrameCount*mFrameSize; + } + + // put audio hardware into standby after short delay + if UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) || + mSuspended) { + // wait until we have something to do... + if (!mStandby) { + LOGV("Audio hardware entering standby, mixer %p\n", this); + mOutput->standby(); + mStandby = true; + mBytesWritten = 0; + } + + if (!mActiveTracks.size() && mConfigEvents.isEmpty()) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + + if (exitPending()) break; + + LOGV("DirectOutputThread %p TID %d going to sleep\n", this, gettid()); + mWaitWorkCV.wait(mLock); + LOGV("DirectOutputThread %p TID %d waking up in active mode\n", this, gettid()); + + if (mMasterMute == false) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + setMasterMute(true); + } + } + + standbyTime = systemTime() + kStandbyTimeInNsecs; + continue; + } + } + + // find out which tracks need to be processed + if (mActiveTracks.size() != 0) { + sp<Track> t = mActiveTracks[0].promote(); + if (t == 0) continue; + + Track* const track = t.get(); + audio_track_cblk_t* cblk = track->cblk(); + + // The first time a track is added we wait + // for all its buffers to be filled before processing it + if (cblk->framesReady() && (track->isReady() || track->isStopped()) && + !track->isPaused()) + { + //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + + // compute volume for this track + float left, right; + if (track->isMuted() || mMasterMute || track->isPausing() || + mStreamTypes[track->type()].mute) { + left = right = 0; + if (track->isPausing()) { + track->setPaused(); + } + } else { + float typeVolume = mStreamTypes[track->type()].volume; + float v = mMasterVolume * typeVolume; + float v_clamped = v * cblk->volume[0]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + left = v_clamped/MAX_GAIN; + v_clamped = v * cblk->volume[1]; + if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + right = v_clamped/MAX_GAIN; + } + + if (left != mLeftVolume || right != mRightVolume) { + mOutput->setVolume(left, right); + left = mLeftVolume; + right = mRightVolume; + } + + if (track->mFillingUpStatus == Track::FS_FILLED) { + track->mFillingUpStatus = Track::FS_ACTIVE; + if (track->mState == TrackBase::RESUMING) { + track->mState = TrackBase::ACTIVE; + } + } + + // reset retry count + track->mRetryCount = kMaxTrackRetries; + activeTrack = t; + } else { + //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); + if (track->isStopped()) { + track->reset(); + } + if (track->isTerminated() || track->isStopped() || track->isPaused()) { + // We have consumed all the buffers of this track. + // Remove it from the list of active tracks. + trackToRemove = track; + } else { + // No buffers for this track. Give it a few chances to + // fill a buffer, then remove it from active list. + if (--(track->mRetryCount) <= 0) { + LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); + trackToRemove = track; + } + + // For tracks using static shared memry buffer, make sure that we have + // written enough data to audio hardware before disabling the track + // NOTE: this condition with arrive before track->mRetryCount <= 0 so we + // don't care about code removing track from active list above. + if ((track->mSharedBuffer != 0) && (mBytesWritten < mMinBytesToWrite)) { + activeTrack = t; + } + } + } + } + + // remove all the tracks that need to be... + if (UNLIKELY(trackToRemove != 0)) { + mActiveTracks.remove(trackToRemove); + if (trackToRemove->isTerminated()) { + mTracks.remove(trackToRemove); + deleteTrackName_l(trackToRemove->mName); + } + } + } + + if (activeTrack != 0) { + AudioBufferProvider::Buffer buffer; + size_t frameCount = mFrameCount; + curBuf = (int8_t *)mMixBuffer; + // output audio to hardware + mLastWriteTime = systemTime(); + mInWrite = true; + while(frameCount) { + buffer.frameCount = frameCount; + activeTrack->getNextBuffer(&buffer); + if (UNLIKELY(buffer.raw == 0)) { + memset(curBuf, 0, frameCount * mFrameSize); + break; + } + memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize); + frameCount -= buffer.frameCount; + curBuf += buffer.frameCount * mFrameSize; + activeTrack->releaseBuffer(&buffer); + } + if (mSuspended) { + usleep(kMaxBufferRecoveryInUsecs); + } else { + int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize); + if (bytesWritten) mBytesWritten += bytesWritten; + mNumWrites++; + mInWrite = false; + mStandby = false; + nsecs_t temp = systemTime(); + standbyTime = temp + kStandbyTimeInNsecs; + sleepTime = kBufferRecoveryInUsecs; + } + } else { + // There was nothing to mix this round, which means all + // active tracks were late. Sleep a little bit to give + // them another chance. If we're too late, the audio + // hardware will zero-fill for us. + //LOGV("no buffers - usleep(%lu)", sleepTime); + usleep(sleepTime); + if (sleepTime < kMaxBufferRecoveryInUsecs) { + sleepTime += kBufferRecoveryInUsecs; + } + } + + // finally let go of removed track, without the lock held + // since we can't guarantee the destructors won't acquire that + // same lock. + trackToRemove.clear(); + activeTrack.clear(); + } + + if (!mStandby) { + mOutput->standby(); + } + sendConfigEvent(AudioSystem::OUTPUT_CLOSED); + processConfigEvents(); + + LOGV("DirectOutputThread %p exiting", this); + return false; } -status_t AudioFlinger::MixerThread::setStreamVolume(int stream, float value) +// getTrackName_l() must be called with ThreadBase::mLock held +int AudioFlinger::DirectOutputThread::getTrackName_l() { - mStreamTypes[stream].volume = value; - return NO_ERROR; + return 0; } -status_t AudioFlinger::MixerThread::setStreamMute(int stream, bool muted) +// deleteTrackName_l() must be called with ThreadBase::mLock held +void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name) { - mStreamTypes[stream].mute = muted; - return NO_ERROR; } -float AudioFlinger::MixerThread::streamVolume(int stream) const +// checkForNewParameters_l() must be called with ThreadBase::mLock held +bool AudioFlinger::DirectOutputThread::checkForNewParameters_l() { - return mStreamTypes[stream].volume; + bool reconfig = false; + + if (mNewParameters != "") { + status_t status = NO_ERROR; + AudioParameter param = AudioParameter(mNewParameters); + int value; + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be garantied + // if frame count is changed after track creation + if (!mTracks.isEmpty()) { + status = INVALID_OPERATION; + } else { + reconfig = true; + } + } + if (status == NO_ERROR) { + status = mOutput->setParameters(mNewParameters); + if (!mStandby && status == INVALID_OPERATION) { + mOutput->standby(); + mStandby = true; + mBytesWritten = 0; + status = mOutput->setParameters(mNewParameters); + } + if (status == NO_ERROR && reconfig) { + readOutputParameters(); + sendConfigEvent(AudioSystem::OUTPUT_CONFIG_CHANGED); + } + } + mParamStatus = status; + mNewParameters = ""; + mParamCond.signal(); + } + return reconfig; } -bool AudioFlinger::MixerThread::streamMute(int stream) const +// ---------------------------------------------------------------------------- + +AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread) + : MixerThread(audioFlinger, mainThread->getOutput()) { - return mStreamTypes[stream].mute; + mType = PlaybackThread::DUPLICATING; + addOutputTrack(mainThread); } -// isMusicActive_l() must be called with AudioFlinger::mLock held -bool AudioFlinger::MixerThread::isMusicActive_l() const +AudioFlinger::DuplicatingThread::~DuplicatingThread() { - size_t count = mActiveTracks.size(); - for (size_t i = 0 ; i < count ; ++i) { - sp<Track> t = mActiveTracks[i].promote(); - if (t == 0) continue; - Track* const track = t.get(); - if (t->mStreamType == AudioSystem::MUSIC) - return true; - } - return false; + mOutputTracks.clear(); } -// addTrack_l() must be called with AudioFlinger::mLock held -status_t AudioFlinger::MixerThread::addTrack_l(const sp<Track>& track) +bool AudioFlinger::DuplicatingThread::threadLoop() { - status_t status = ALREADY_EXISTS; + unsigned long sleepTime = kBufferRecoveryInUsecs; + int16_t* curBuf = mMixBuffer; + Vector< sp<Track> > tracksToRemove; + size_t enabledTracks = 0; + nsecs_t standbyTime = systemTime(); + size_t mixBufferSize = mFrameCount*mFrameSize; + SortedVector< sp<OutputTrack> > outputTracks; - // here the track could be either new, or restarted - // in both cases "unstop" the track - if (track->isPaused()) { - track->mState = TrackBase::RESUMING; - LOGV("PAUSED => RESUMING (%d)", track->name()); - } else { - track->mState = TrackBase::ACTIVE; - LOGV("? => ACTIVE (%d)", track->name()); - } - // set retry count for buffer fill - track->mRetryCount = kMaxTrackStartupRetries; - if (mActiveTracks.indexOf(track) < 0) { - // the track is newly added, make sure it fills up all its - // buffers before playing. This is to ensure the client will - // effectively get the latency it requested. - track->mFillingUpStatus = Track::FS_FILLING; - track->mResetDone = false; - addActiveTrack_l(track); - status = NO_ERROR; - } - - LOGV("mWaitWorkCV.broadcast"); - mAudioFlinger->mWaitWorkCV.broadcast(); + while (!exitPending()) + { + processConfigEvents(); - return status; -} + enabledTracks = 0; + { // scope for the mLock -// destroyTrack_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::destroyTrack_l(const sp<Track>& track) -{ - track->mState = TrackBase::TERMINATED; - if (mActiveTracks.indexOf(track) < 0) { - LOGV("remove track (%d) and delete from mixer", track->name()); - mTracks.remove(track); - deleteTrackName_l(track->name()); - } -} + Mutex::Autolock _l(mLock); -// addActiveTrack_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::addActiveTrack_l(const wp<Track>& t) -{ - mActiveTracks.add(t); + if (checkForNewParameters_l()) { + mixBufferSize = mFrameCount*mFrameSize; + } - // Force routing to speaker for certain stream types - // The forced routing to speaker is managed by hardware mixer - if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { - sp<Track> track = t.promote(); - if (track == NULL) return; - - if (streamForcedToSpeaker(track->type())) { - mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_ADDED); - } - } -} + const SortedVector< wp<Track> >& activeTracks = mActiveTracks; -// removeActiveTrack_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::removeActiveTrack_l(const wp<Track>& t) -{ - mActiveTracks.remove(t); + for (size_t i = 0; i < mOutputTracks.size(); i++) { + outputTracks.add(mOutputTracks[i]); + } + + // put audio hardware into standby after short delay + if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) || + mSuspended) { + if (!mStandby) { + for (size_t i = 0; i < outputTracks.size(); i++) { + mLock.unlock(); + outputTracks[i]->stop(); + mLock.lock(); + } + mStandby = true; + mBytesWritten = 0; + } + + if (!activeTracks.size() && mConfigEvents.isEmpty()) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + outputTracks.clear(); + + if (exitPending()) break; + + LOGV("DuplicatingThread %p TID %d going to sleep\n", this, gettid()); + mWaitWorkCV.wait(mLock); + LOGV("DuplicatingThread %p TID %d waking up\n", this, gettid()); + if (mMasterMute == false) { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.audio.silent", value, "0"); + if (atoi(value)) { + LOGD("Silence is golden"); + setMasterMute(true); + } + } + + standbyTime = systemTime() + kStandbyTimeInNsecs; + sleepTime = kBufferRecoveryInUsecs; + continue; + } + } - // Force routing to speaker for certain stream types - // The forced routing to speaker is managed by hardware mixer - if (mOutputType == AudioSystem::AUDIO_OUTPUT_HARDWARE) { - sp<Track> track = t.promote(); - if (track == NULL) return; + enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove); + } - if (streamForcedToSpeaker(track->type())) { - mAudioFlinger->handleForcedSpeakerRoute(ACTIVE_TRACK_REMOVED); + bool mustSleep = true; + if (LIKELY(enabledTracks)) { + // mix buffers... + mAudioMixer->process(curBuf); + if (!mSuspended) { + for (size_t i = 0; i < outputTracks.size(); i++) { + outputTracks[i]->write(curBuf, mFrameCount); + } + mStandby = false; + mustSleep = false; + mBytesWritten += mixBufferSize; + } + } else { + // flush remaining overflow buffers in output tracks + for (size_t i = 0; i < outputTracks.size(); i++) { + if (outputTracks[i]->isActive()) { + outputTracks[i]->write(curBuf, 0); + standbyTime = systemTime() + kStandbyTimeInNsecs; + mustSleep = false; + } + } } + if (mustSleep) { +// LOGV("threadLoop() sleeping %d", sleepTime); + usleep(sleepTime); + if (sleepTime < kMaxBufferRecoveryInUsecs) { + sleepTime += kBufferRecoveryInUsecs; + } + } else { + sleepTime = kBufferRecoveryInUsecs; + } + + // finally let go of all our tracks, without the lock held + // since we can't guarantee the destructors won't acquire that + // same lock. + tracksToRemove.clear(); + outputTracks.clear(); } -} -// getTrackName_l() must be called with AudioFlinger::mLock held -int AudioFlinger::MixerThread::getTrackName_l() -{ - return mAudioMixer->getTrackName(); + if (!mStandby) { + for (size_t i = 0; i < outputTracks.size(); i++) { + mLock.unlock(); + outputTracks[i]->stop(); + mLock.lock(); + } + } + + sendConfigEvent(AudioSystem::OUTPUT_CLOSED); + processConfigEvents(); + + return false; } -// deleteTrackName_l() must be called with AudioFlinger::mLock held -void AudioFlinger::MixerThread::deleteTrackName_l(int name) +void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) { - mAudioMixer->deleteTrackName(name); + int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate(); + OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread, + mSampleRate, + mFormat, + mChannelCount, + frameCount); + thread->setStreamVolume(AudioSystem::NUM_STREAM_TYPES, 1.0f); + mOutputTracks.add(outputTrack); + LOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread); } -size_t AudioFlinger::MixerThread::getOutputFrameCount() +void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread) { - return mOutput->bufferSize() / mOutput->channelCount() / sizeof(int16_t); + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mOutputTracks.size(); i++) { + if (mOutputTracks[i]->thread() == (ThreadBase *)thread) { + mOutputTracks.removeAt(i); + return; + } + } + LOGV("removeOutputTrack(): unkonwn thread: %p", thread); } + // ---------------------------------------------------------------------------- // TrackBase constructor must be called with AudioFlinger::mLock held -AudioFlinger::MixerThread::TrackBase::TrackBase( - const sp<MixerThread>& mixerThread, +AudioFlinger::ThreadBase::TrackBase::TrackBase( + const wp<ThreadBase>& thread, const sp<Client>& client, uint32_t sampleRate, int format, @@ -1566,7 +1950,7 @@ AudioFlinger::MixerThread::TrackBase::TrackBase( uint32_t flags, const sp<IMemory>& sharedBuffer) : RefBase(), - mMixerThread(mixerThread), + mThread(thread), mClient(client), mFrameCount(0), mState(IDLE), @@ -1574,13 +1958,6 @@ AudioFlinger::MixerThread::TrackBase::TrackBase( mFormat(format), mFlags(flags & ~SYSTEM_FLAGS_MASK) { - mName = mixerThread->getTrackName_l(); - LOGV("TrackBase contructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid()); - if (mName < 0) { - LOGE("no more track names availlable"); - return; - } - LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); // LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); @@ -1634,16 +2011,19 @@ AudioFlinger::MixerThread::TrackBase::TrackBase( } } -AudioFlinger::MixerThread::TrackBase::~TrackBase() +AudioFlinger::PlaybackThread::TrackBase::~TrackBase() { if (mCblk) { - mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + mCblk->~audio_track_cblk_t(); // destroy our shared-structure. + if (mClient == NULL) { + delete mCblk; + } } mCblkMemory.clear(); // and free the shared memory mClient.clear(); } -void AudioFlinger::MixerThread::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) +void AudioFlinger::PlaybackThread::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) { buffer->raw = 0; mFrameCount = buffer->frameCount; @@ -1651,7 +2031,7 @@ void AudioFlinger::MixerThread::TrackBase::releaseBuffer(AudioBufferProvider::Bu buffer->frameCount = 0; } -bool AudioFlinger::MixerThread::TrackBase::step() { +bool AudioFlinger::PlaybackThread::TrackBase::step() { bool result; audio_track_cblk_t* cblk = this->cblk(); @@ -1663,7 +2043,7 @@ bool AudioFlinger::MixerThread::TrackBase::step() { return result; } -void AudioFlinger::MixerThread::TrackBase::reset() { +void AudioFlinger::PlaybackThread::TrackBase::reset() { audio_track_cblk_t* cblk = this->cblk(); cblk->user = 0; @@ -1674,27 +2054,27 @@ void AudioFlinger::MixerThread::TrackBase::reset() { LOGV("TrackBase::reset"); } -sp<IMemory> AudioFlinger::MixerThread::TrackBase::getCblk() const +sp<IMemory> AudioFlinger::PlaybackThread::TrackBase::getCblk() const { return mCblkMemory; } -int AudioFlinger::MixerThread::TrackBase::sampleRate() const { +int AudioFlinger::PlaybackThread::TrackBase::sampleRate() const { return (int)mCblk->sampleRate; } -int AudioFlinger::MixerThread::TrackBase::channelCount() const { +int AudioFlinger::PlaybackThread::TrackBase::channelCount() const { return (int)mCblk->channels; } -void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { +void* AudioFlinger::PlaybackThread::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const { audio_track_cblk_t* cblk = this->cblk(); - int16_t *bufferStart = (int16_t *)mBuffer + (offset-cblk->serverBase)*cblk->channels; - int16_t *bufferEnd = bufferStart + frames * cblk->channels; + int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase)*cblk->frameSize; + int8_t *bufferEnd = bufferStart + frames * cblk->frameSize; // Check validity of returned pointer in case the track control block would have been corrupted. - if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd || - (cblk->channels == 2 && ((unsigned long)bufferStart & 3))) { + if (bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd || + ((unsigned long)bufferStart & (unsigned long)(cblk->frameSize - 1))) { LOGE("TrackBase::getBuffer buffer out of range:\n start: %p, end %p , mBuffer %p mBufferEnd %p\n \ server %d, serverBase %d, user %d, userBase %d, channels %d", bufferStart, bufferEnd, mBuffer, mBufferEnd, @@ -1707,9 +2087,9 @@ void* AudioFlinger::MixerThread::TrackBase::getBuffer(uint32_t offset, uint32_t // ---------------------------------------------------------------------------- -// Track constructor must be called with AudioFlinger::mLock held -AudioFlinger::MixerThread::Track::Track( - const sp<MixerThread>& mixerThread, +// Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held +AudioFlinger::PlaybackThread::Track::Track( + const wp<ThreadBase>& thread, const sp<Client>& client, int streamType, uint32_t sampleRate, @@ -1717,40 +2097,58 @@ AudioFlinger::MixerThread::Track::Track( int channelCount, int frameCount, const sp<IMemory>& sharedBuffer) - : TrackBase(mixerThread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer) + : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer), + mMute(false), mSharedBuffer(sharedBuffer), mName(-1) { + sp<ThreadBase> baseThread = thread.promote(); + if (baseThread != 0) { + PlaybackThread *playbackThread = (PlaybackThread *)baseThread.get(); + mName = playbackThread->getTrackName_l(); + } + LOGV("Track constructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + if (mName < 0) { + LOGE("no more track names available"); + } mVolume[0] = 1.0f; mVolume[1] = 1.0f; - mMute = false; - mSharedBuffer = sharedBuffer; mStreamType = streamType; + // NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of + // 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack + mCblk->frameSize = AudioSystem::isLinearPCM(format) ? channelCount * sizeof(int16_t) : sizeof(int8_t); } -AudioFlinger::MixerThread::Track::~Track() +AudioFlinger::PlaybackThread::Track::~Track() { - wp<Track> weak(this); // never create a strong ref from the dtor - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - mState = TERMINATED; + LOGV("PlaybackThread::Track destructor"); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + mState = TERMINATED; + } } -void AudioFlinger::MixerThread::Track::destroy() +void AudioFlinger::PlaybackThread::Track::destroy() { - // NOTE: destroyTrack_l() can remove a strong reference to this Track + // NOTE: destroyTrack_l() can remove a strong reference to this Track // by removing it from mTracks vector, so there is a risk that this Tracks's - // desctructor is called. As the destructor needs to lock AudioFlinger::mLock, - // we must acquire a strong reference on this Track before locking AudioFlinger::mLock + // desctructor is called. As the destructor needs to lock mLock, + // we must acquire a strong reference on this Track before locking mLock // here so that the destructor is called only when exiting this function. - // On the other hand, as long as Track::destroy() is only called by - // TrackHandle destructor, the TrackHandle still holds a strong ref on + // On the other hand, as long as Track::destroy() is only called by + // TrackHandle destructor, the TrackHandle still holds a strong ref on // this Track with its member mTrack. sp<Track> keep(this); - { // scope for AudioFlinger::mLock - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - mMixerThread->destroyTrack_l(this); + { // scope for mLock + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + playbackThread->destroyTrack_l(this); + } } } -void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size) +void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) { snprintf(buffer, size, " %5d %5d %3u %3u %3u %3u %1d %1d %1d %5u %5u %5u %04x %04x\n", mName - AudioMixer::TRACK0, @@ -1769,7 +2167,7 @@ void AudioFlinger::MixerThread::Track::dump(char* buffer, size_t size) mCblk->user); } -status_t AudioFlinger::MixerThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) { audio_track_cblk_t* cblk = this->cblk(); uint32_t framesReady; @@ -1806,76 +2204,90 @@ status_t AudioFlinger::MixerThread::Track::getNextBuffer(AudioBufferProvider::Bu getNextBuffer_exit: buffer->raw = 0; buffer->frameCount = 0; + LOGV("getNextBuffer() no more data"); return NOT_ENOUGH_DATA; } -bool AudioFlinger::MixerThread::Track::isReady() const { +bool AudioFlinger::PlaybackThread::Track::isReady() const { if (mFillingUpStatus != FS_FILLING) return true; if (mCblk->framesReady() >= mCblk->frameCount || mCblk->forceReady) { mFillingUpStatus = FS_FILLED; mCblk->forceReady = 0; - LOGV("Track::isReady() track %d for output %d", mName, mMixerThread->mOutputType); return true; } return false; } -status_t AudioFlinger::MixerThread::Track::start() +status_t AudioFlinger::PlaybackThread::Track::start() { - LOGV("start(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - mMixerThread->addTrack_l(this); + LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + playbackThread->addTrack_l(this); + } return NO_ERROR; } -void AudioFlinger::MixerThread::Track::stop() +void AudioFlinger::PlaybackThread::Track::stop() { - LOGV("stop(%d), calling thread %d for output %d", mName, IPCThreadState::self()->getCallingPid(), mMixerThread->mOutputType); - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - if (mState > STOPPED) { - mState = STOPPED; - // If the track is not active (PAUSED and buffers full), flush buffers - if (mMixerThread->mActiveTracks.indexOf(this) < 0) { - reset(); + LOGV("stop(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + if (mState > STOPPED) { + mState = STOPPED; + // If the track is not active (PAUSED and buffers full), flush buffers + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + if (playbackThread->mActiveTracks.indexOf(this) < 0) { + reset(); + } + LOGV("(> STOPPED) => STOPPED (%d)", mName); } - LOGV("(> STOPPED) => STOPPED (%d)", mName); } } -void AudioFlinger::MixerThread::Track::pause() +void AudioFlinger::PlaybackThread::Track::pause() { LOGV("pause(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - if (mState == ACTIVE || mState == RESUMING) { - mState = PAUSING; - LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + if (mState == ACTIVE || mState == RESUMING) { + mState = PAUSING; + LOGV("ACTIVE/RESUMING => PAUSING (%d)", mName); + } } } -void AudioFlinger::MixerThread::Track::flush() +void AudioFlinger::PlaybackThread::Track::flush() { LOGV("flush(%d)", mName); - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - if (mState != STOPPED && mState != PAUSED && mState != PAUSING) { - return; - } - // No point remaining in PAUSED state after a flush => go to - // STOPPED state - mState = STOPPED; + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + if (mState != STOPPED && mState != PAUSED && mState != PAUSING) { + return; + } + // No point remaining in PAUSED state after a flush => go to + // STOPPED state + mState = STOPPED; - mCblk->lock.lock(); - // NOTE: reset() will reset cblk->user and cblk->server with - // the risk that at the same time, the AudioMixer is trying to read - // data. In this case, getNextBuffer() would return a NULL pointer - // as audio buffer => the AudioMixer code MUST always test that pointer - // returned by getNextBuffer() is not NULL! - reset(); - mCblk->lock.unlock(); + mCblk->lock.lock(); + // NOTE: reset() will reset cblk->user and cblk->server with + // the risk that at the same time, the AudioMixer is trying to read + // data. In this case, getNextBuffer() would return a NULL pointer + // as audio buffer => the AudioMixer code MUST always test that pointer + // returned by getNextBuffer() is not NULL! + reset(); + mCblk->lock.unlock(); + } } -void AudioFlinger::MixerThread::Track::reset() +void AudioFlinger::PlaybackThread::Track::reset() { // Do not reset twice to avoid discarding data written just after a flush and before // the audioflinger thread detects the track is stopped. @@ -1885,17 +2297,17 @@ void AudioFlinger::MixerThread::Track::reset() // written to buffer mCblk->flowControlFlag = 1; mCblk->forceReady = 0; - mFillingUpStatus = FS_FILLING; + mFillingUpStatus = FS_FILLING; mResetDone = true; } } -void AudioFlinger::MixerThread::Track::mute(bool muted) +void AudioFlinger::PlaybackThread::Track::mute(bool muted) { mMute = muted; } -void AudioFlinger::MixerThread::Track::setVolume(float left, float right) +void AudioFlinger::PlaybackThread::Track::setVolume(float left, float right) { mVolume[0] = left; mVolume[1] = right; @@ -1904,28 +2316,33 @@ void AudioFlinger::MixerThread::Track::setVolume(float left, float right) // ---------------------------------------------------------------------------- // RecordTrack constructor must be called with AudioFlinger::mLock held -AudioFlinger::MixerThread::RecordTrack::RecordTrack( - const sp<MixerThread>& mixerThread, +AudioFlinger::RecordThread::RecordTrack::RecordTrack( + const wp<ThreadBase>& thread, const sp<Client>& client, - int inputSource, uint32_t sampleRate, int format, int channelCount, int frameCount, uint32_t flags) - : TrackBase(mixerThread, client, sampleRate, format, + : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, flags, 0), - mOverflow(false), mInputSource(inputSource) + mOverflow(false) { + LOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer); + if (format == AudioSystem::PCM_16_BIT) { + mCblk->frameSize = channelCount * sizeof(int16_t); + } else if (format == AudioSystem::PCM_8_BIT) { + mCblk->frameSize = channelCount * sizeof(int8_t); + } else { + mCblk->frameSize = sizeof(int8_t); + } } -AudioFlinger::MixerThread::RecordTrack::~RecordTrack() +AudioFlinger::RecordThread::RecordTrack::~RecordTrack() { - Mutex::Autolock _l(mMixerThread->mAudioFlinger->mLock); - mMixerThread->deleteTrackName_l(mName); } -status_t AudioFlinger::MixerThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) { audio_track_cblk_t* cblk = this->cblk(); uint32_t framesAvail; @@ -1964,180 +2381,231 @@ getNextBuffer_exit: return NOT_ENOUGH_DATA; } -status_t AudioFlinger::MixerThread::RecordTrack::start() +status_t AudioFlinger::RecordThread::RecordTrack::start() { - return mMixerThread->mAudioFlinger->startRecord(this); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + RecordThread *recordThread = (RecordThread *)thread.get(); + return recordThread->start(this); + } + return NO_INIT; } -void AudioFlinger::MixerThread::RecordTrack::stop() +void AudioFlinger::RecordThread::RecordTrack::stop() { - mMixerThread->mAudioFlinger->stopRecord(this); - TrackBase::reset(); - // Force overerrun condition to avoid false overrun callback until first data is - // read from buffer - mCblk->flowControlFlag = 1; + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + RecordThread *recordThread = (RecordThread *)thread.get(); + recordThread->stop(this); + TrackBase::reset(); + // Force overerrun condition to avoid false overrun callback until first data is + // read from buffer + mCblk->flowControlFlag = 1; + } } // ---------------------------------------------------------------------------- -AudioFlinger::MixerThread::OutputTrack::OutputTrack( - const sp<MixerThread>& mixerThread, +AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( + const wp<ThreadBase>& thread, uint32_t sampleRate, int format, int channelCount, int frameCount) - : Track(mixerThread, NULL, AudioSystem::SYSTEM, sampleRate, format, channelCount, frameCount, NULL), - mOutputMixerThread(mixerThread) + : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL), + mActive(false) { - + + PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get(); mCblk->out = 1; mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); mCblk->volume[0] = mCblk->volume[1] = 0x1000; mOutBuffer.frameCount = 0; - mCblk->bufferTimeoutMs = 10; - - LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p", - mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd); - + mWaitTimeMs = (playbackThread->frameCount() * 2 * 1000) / playbackThread->sampleRate(); + + LOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, mCblk->frameCount %d, mCblk->sampleRate %d, mCblk->channels %d mBufferEnd %p mWaitTimeMs %d", + mCblk, mBuffer, mCblk->buffers, mCblk->frameCount, mCblk->sampleRate, mCblk->channels, mBufferEnd, mWaitTimeMs); + } -AudioFlinger::MixerThread::OutputTrack::~OutputTrack() +AudioFlinger::PlaybackThread::OutputTrack::~OutputTrack() { stop(); } -status_t AudioFlinger::MixerThread::OutputTrack::start() +status_t AudioFlinger::PlaybackThread::OutputTrack::start() { status_t status = Track::start(); - + if (status != NO_ERROR) { + return status; + } + + mActive = true; mRetryCount = 127; return status; } -void AudioFlinger::MixerThread::OutputTrack::stop() +void AudioFlinger::PlaybackThread::OutputTrack::stop() { Track::stop(); clearBufferQueue(); mOutBuffer.frameCount = 0; + mActive = false; } -void AudioFlinger::MixerThread::OutputTrack::write(int16_t* data, uint32_t frames) +bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames) { Buffer *pInBuffer; Buffer inBuffer; uint32_t channels = mCblk->channels; - + bool outputBufferFull = false; inBuffer.frameCount = frames; inBuffer.i16 = data; - - if (mCblk->user == 0) { - mOutputMixerThread->mAudioFlinger->mLock.lock(); - bool isMusicActive = mOutputMixerThread->isMusicActive_l(); - mOutputMixerThread->mAudioFlinger->mLock.unlock(); - if (isMusicActive) { - mCblk->forceReady = 1; - LOGV("OutputTrack::start() force ready"); - } else if (mCblk->frameCount > frames){ - if (mBufferQueue.size() < kMaxOutputTrackBuffers) { - uint32_t startFrames = (mCblk->frameCount - frames); - LOGV("OutputTrack::start() write %d frames", startFrames); - pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[startFrames * channels]; - pInBuffer->frameCount = startFrames; - pInBuffer->i16 = pInBuffer->mBuffer; - memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t)); - mBufferQueue.add(pInBuffer); - } else { - LOGW ("OutputTrack::write() no more buffers"); + + uint32_t waitTimeLeftMs = mWaitTimeMs; + + if (!mActive) { + start(); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + MixerThread *mixerThread = (MixerThread *)thread.get(); + if (mCblk->frameCount > frames){ + if (mBufferQueue.size() < kMaxOverFlowBuffers) { + uint32_t startFrames = (mCblk->frameCount - frames); + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[startFrames * channels]; + pInBuffer->frameCount = startFrames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, startFrames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + LOGW ("OutputTrack::write() %p no more buffers in queue", this); + } } - } + } } - while (1) { + while (waitTimeLeftMs) { // First write pending buffers, then new data if (mBufferQueue.size()) { pInBuffer = mBufferQueue.itemAt(0); } else { pInBuffer = &inBuffer; } - + if (pInBuffer->frameCount == 0) { break; } - + if (mOutBuffer.frameCount == 0) { mOutBuffer.frameCount = pInBuffer->frameCount; - if (obtainBuffer(&mOutBuffer) == (status_t)AudioTrack::NO_MORE_BUFFERS) { + nsecs_t startTime = systemTime(); + if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)AudioTrack::NO_MORE_BUFFERS) { + LOGV ("OutputTrack::write() %p no more output buffers", this); + outputBufferFull = true; break; } + uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime); +// LOGV("OutputTrack::write() waitTimeMs %d waitTimeLeftMs %d", waitTimeMs, waitTimeLeftMs) + if (waitTimeLeftMs >= waitTimeMs) { + waitTimeLeftMs -= waitTimeMs; + } else { + waitTimeLeftMs = 0; + } } - + uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount; memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channels * sizeof(int16_t)); mCblk->stepUser(outFrames); pInBuffer->frameCount -= outFrames; pInBuffer->i16 += outFrames * channels; mOutBuffer.frameCount -= outFrames; - mOutBuffer.i16 += outFrames * channels; - + mOutBuffer.i16 += outFrames * channels; + if (pInBuffer->frameCount == 0) { if (mBufferQueue.size()) { mBufferQueue.removeAt(0); delete [] pInBuffer->mBuffer; delete pInBuffer; + LOGV("OutputTrack::write() %p released overflow buffer %d", this, mBufferQueue.size()); } else { break; } } } - + // If we could not write all frames, allocate a buffer and queue it for next time. if (inBuffer.frameCount) { - if (mBufferQueue.size() < kMaxOutputTrackBuffers) { + if (mBufferQueue.size() < kMaxOverFlowBuffers) { pInBuffer = new Buffer; pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channels]; pInBuffer->frameCount = inBuffer.frameCount; pInBuffer->i16 = pInBuffer->mBuffer; memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channels * sizeof(int16_t)); mBufferQueue.add(pInBuffer); + LOGV("OutputTrack::write() %p adding overflow buffer %d", this, mBufferQueue.size()); } else { - LOGW("OutputTrack::write() no more buffers"); + LOGW("OutputTrack::write() %p no more overflow buffers", this); } } - + // Calling write() with a 0 length buffer, means that no more data will be written: - // If no more buffers are pending, fill output track buffer to make sure it is started + // If no more buffers are pending, fill output track buffer to make sure it is started // by output mixer. - if (frames == 0 && mBufferQueue.size() == 0 && mCblk->user < mCblk->frameCount) { - frames = mCblk->frameCount - mCblk->user; - pInBuffer = new Buffer; - pInBuffer->mBuffer = new int16_t[frames * channels]; - pInBuffer->frameCount = frames; - pInBuffer->i16 = pInBuffer->mBuffer; - memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t)); - mBufferQueue.add(pInBuffer); + if (frames == 0 && mBufferQueue.size() == 0) { + if (mCblk->user < mCblk->frameCount) { + frames = mCblk->frameCount - mCblk->user; + pInBuffer = new Buffer; + pInBuffer->mBuffer = new int16_t[frames * channels]; + pInBuffer->frameCount = frames; + pInBuffer->i16 = pInBuffer->mBuffer; + memset(pInBuffer->raw, 0, frames * channels * sizeof(int16_t)); + mBufferQueue.add(pInBuffer); + } else { + stop(); + } } + return outputBufferFull; } -status_t AudioFlinger::MixerThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs) { int active; - int timeout = 0; status_t result; audio_track_cblk_t* cblk = mCblk; uint32_t framesReq = buffer->frameCount; - LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server); +// LOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server); buffer->frameCount = 0; - + uint32_t framesAvail = cblk->framesAvailable(); + if (framesAvail == 0) { - return AudioTrack::NO_MORE_BUFFERS; + Mutex::Autolock _l(cblk->lock); + goto start_loop_here; + while (framesAvail == 0) { + active = mActive; + if (UNLIKELY(!active)) { + LOGV("Not active and NO_MORE_BUFFERS"); + return AudioTrack::NO_MORE_BUFFERS; + } + result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs)); + if (result != NO_ERROR) { + return AudioTrack::NO_MORE_BUFFERS; + } + // read the server count again + start_loop_here: + framesAvail = cblk->framesAvailable_l(); + } } +// if (framesAvail < framesReq) { +// return AudioTrack::NO_MORE_BUFFERS; +// } + if (framesReq > framesAvail) { framesReq = framesAvail; } @@ -2155,11 +2623,11 @@ status_t AudioFlinger::MixerThread::OutputTrack::obtainBuffer(AudioBufferProvide } -void AudioFlinger::MixerThread::OutputTrack::clearBufferQueue() +void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue() { size_t size = mBufferQueue.size(); Buffer *pBuffer; - + for (size_t i = 0; i < size; i++) { pBuffer = mBufferQueue.itemAt(i); delete [] pBuffer->mBuffer; @@ -2191,7 +2659,7 @@ const sp<MemoryDealer>& AudioFlinger::Client::heap() const // ---------------------------------------------------------------------------- -AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::MixerThread::Track>& track) +AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track) : BnAudioTrack(), mTrack(track) { @@ -2243,7 +2711,7 @@ status_t AudioFlinger::TrackHandle::onTransact( sp<IAudioRecord> AudioFlinger::openRecord( pid_t pid, - int inputSource, + void *input, uint32_t sampleRate, int format, int channelCount, @@ -2251,14 +2719,13 @@ sp<IAudioRecord> AudioFlinger::openRecord( uint32_t flags, status_t *status) { - sp<MixerThread::RecordTrack> recordTrack; + sp<RecordThread::RecordTrack> recordTrack; sp<RecordHandle> recordHandle; sp<Client> client; wp<Client> wclient; - AudioStreamIn* input = 0; - int inFrameCount; - size_t inputBufferSize; status_t lStatus; + RecordThread *thread; + size_t inFrameCount; // check calling permissions if (!recordingAllowed()) { @@ -2266,30 +2733,15 @@ sp<IAudioRecord> AudioFlinger::openRecord( goto Exit; } - if (uint32_t(inputSource) >= AudioRecord::NUM_INPUT_SOURCES) { - LOGE("invalid stream type"); - lStatus = BAD_VALUE; - goto Exit; - } - - if (mAudioRecordThread == 0) { - LOGE("Audio record thread not started"); - lStatus = NO_INIT; - goto Exit; - } - - - // Check that audio input stream accepts requested audio parameters - inputBufferSize = mAudioHardware->getInputBufferSize(sampleRate, format, channelCount); - if (inputBufferSize == 0) { - lStatus = BAD_VALUE; - LOGE("Bad audio input parameters: sampling rate %u, format %d, channels %d", sampleRate, format, channelCount); - goto Exit; - } - // add client to list { // scope for mLock Mutex::Autolock _l(mLock); + thread = checkRecordThread_l(input); + if (thread == NULL) { + lStatus = BAD_VALUE; + goto Exit; + } + wclient = mClients.valueFor(pid); if (wclient != NULL) { client = wclient.promote(); @@ -2298,12 +2750,8 @@ sp<IAudioRecord> AudioFlinger::openRecord( mClients.add(pid, client); } - // frameCount must be a multiple of input buffer size - inFrameCount = inputBufferSize/channelCount/sizeof(short); - frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount; - // create new record track. The record track uses one track in mHardwareMixerThread by convention. - recordTrack = new MixerThread::RecordTrack(mHardwareMixerThread, client, inputSource, sampleRate, + recordTrack = new RecordThread::RecordTrack(thread, client, sampleRate, format, channelCount, frameCount, flags); } if (recordTrack->getCblk() == NULL) { @@ -2323,22 +2771,9 @@ Exit: return recordHandle; } -status_t AudioFlinger::startRecord(MixerThread::RecordTrack* recordTrack) { - if (mAudioRecordThread != 0) { - return mAudioRecordThread->start(recordTrack); - } - return NO_INIT; -} - -void AudioFlinger::stopRecord(MixerThread::RecordTrack* recordTrack) { - if (mAudioRecordThread != 0) { - mAudioRecordThread->stop(recordTrack); - } -} - // ---------------------------------------------------------------------------- -AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::MixerThread::RecordTrack>& recordTrack) +AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordThread::RecordTrack>& recordTrack) : BnAudioRecord(), mRecordTrack(recordTrack) { @@ -2370,86 +2805,165 @@ status_t AudioFlinger::RecordHandle::onTransact( // ---------------------------------------------------------------------------- -AudioFlinger::AudioRecordThread::AudioRecordThread(AudioHardwareInterface* audioHardware, - const sp<AudioFlinger>& audioFlinger) : - mAudioHardware(audioHardware), - mAudioFlinger(audioFlinger), - mActive(false) +AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, uint32_t channels) : + ThreadBase(audioFlinger), + mInput(input), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0) { + mReqChannelCount = AudioSystem::popCount(channels); + mReqSampleRate = sampleRate; + readInputParameters(); + sendConfigEvent(AudioSystem::INPUT_OPENED); } -AudioFlinger::AudioRecordThread::~AudioRecordThread() + +AudioFlinger::RecordThread::~RecordThread() { + mAudioFlinger->mAudioHardware->closeInputStream(mInput); + delete[] mRsmpInBuffer; + if (mResampler != 0) { + delete mResampler; + delete[] mRsmpOutBuffer; + } } -bool AudioFlinger::AudioRecordThread::threadLoop() +void AudioFlinger::RecordThread::onFirstRef() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "Record Thread %p", this); + + run(buffer, PRIORITY_URGENT_AUDIO); +} +bool AudioFlinger::RecordThread::threadLoop() { - LOGV("AudioRecordThread: start record loop"); AudioBufferProvider::Buffer buffer; - int inBufferSize = 0; - int inFrameCount = 0; - AudioStreamIn* input = 0; + sp<RecordTrack> activeTrack; - mActive = 0; - // start recording while (!exitPending()) { - if (!mActive) { - mLock.lock(); - if (!mActive && !exitPending()) { - LOGV("AudioRecordThread: loop stopping"); - if (input) { - delete input; - input = 0; + + processConfigEvents(); + + { // scope for mLock + Mutex::Autolock _l(mLock); + checkForNewParameters_l(); + if (mActiveTrack == 0 && mConfigEvents.isEmpty()) { + if (!mStandby) { + mInput->standby(); + mStandby = true; } - mRecordTrack.clear(); - mStopped.signal(); + if (exitPending()) break; + + LOGV("RecordThread: loop stopping"); + // go to sleep mWaitWorkCV.wait(mLock); - - LOGV("AudioRecordThread: loop starting"); - if (mRecordTrack != 0) { - input = mAudioHardware->openInputStream( - mRecordTrack->inputSource(), - mRecordTrack->format(), - mRecordTrack->channelCount(), - mRecordTrack->sampleRate(), - &mStartStatus, - (AudioSystem::audio_in_acoustics)(mRecordTrack->mFlags >> 16)); - if (input != 0) { - inBufferSize = input->bufferSize(); - inFrameCount = inBufferSize/input->frameSize(); + LOGV("RecordThread: loop starting"); + continue; + } + if (mActiveTrack != 0) { + if (mActiveTrack->mState == TrackBase::PAUSING) { + mActiveTrack.clear(); + mStartStopCond.broadcast(); + } else if (mActiveTrack->mState == TrackBase::RESUMING) { + mRsmpInIndex = mFrameCount; + if (mReqChannelCount != mActiveTrack->channelCount()) { + mActiveTrack.clear(); + } else { + mActiveTrack->mState == TrackBase::ACTIVE; } - } else { - mStartStatus = NO_INIT; + mStartStopCond.broadcast(); } - if (mStartStatus !=NO_ERROR) { - LOGW("record start failed, status %d", mStartStatus); - mActive = false; - mRecordTrack.clear(); - } - mWaitWorkCV.signal(); + mStandby = false; } - mLock.unlock(); - } else if (mRecordTrack != 0) { - - buffer.frameCount = inFrameCount; - if (LIKELY(mRecordTrack->getNextBuffer(&buffer) == NO_ERROR && - (int)buffer.frameCount == inFrameCount)) { - LOGV("AudioRecordThread read: %d frames", buffer.frameCount); - ssize_t bytesRead = input->read(buffer.raw, inBufferSize); - if (bytesRead < 0) { - LOGE("Error reading audio input"); - sleep(1); + } + + if (mActiveTrack != 0) { + buffer.frameCount = mFrameCount; + if (LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) { + size_t framesOut = buffer.frameCount; + if (mResampler == 0) { + // no resampling + while (framesOut) { + size_t framesIn = mFrameCount - mRsmpInIndex; + if (framesIn) { + int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize; + int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) * mActiveTrack->mCblk->frameSize; + if (framesIn > framesOut) + framesIn = framesOut; + mRsmpInIndex += framesIn; + framesOut -= framesIn; + if (mChannelCount == mReqChannelCount || + mFormat != AudioSystem::PCM_16_BIT) { + memcpy(dst, src, framesIn * mFrameSize); + } else { + int16_t *src16 = (int16_t *)src; + int16_t *dst16 = (int16_t *)dst; + if (mChannelCount == 1) { + while (framesIn--) { + *dst16++ = *src16; + *dst16++ = *src16++; + } + } else { + while (framesIn--) { + *dst16++ = (int16_t)(((int32_t)*src16 + (int32_t)*(src16 + 1)) >> 1); + src16 += 2; + } + } + } + } + if (framesOut && mFrameCount == mRsmpInIndex) { + ssize_t bytesRead; + if (framesOut == mFrameCount && + (mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) { + bytesRead = mInput->read(buffer.raw, mInputBytes); + framesOut = 0; + } else { + bytesRead = mInput->read(mRsmpInBuffer, mInputBytes); + mRsmpInIndex = 0; + } + if (bytesRead < 0) { + LOGE("Error reading audio input"); + sleep(1); + mRsmpInIndex = mFrameCount; + framesOut = 0; + buffer.frameCount = 0; + } + } + } + } else { + // resampling + + memset(mRsmpOutBuffer, 0, framesOut * 2 * sizeof(int32_t)); + // alter output frame count as if we were expecting stereo samples + if (mChannelCount == 1 && mReqChannelCount == 1) { + framesOut >>= 1; + } + mResampler->resample(mRsmpOutBuffer, framesOut, this); + // ditherAndClamp() works as long as all buffers returned by mActiveTrack->getNextBuffer() + // are 32 bit aligned which should be always true. + if (mChannelCount == 2 && mReqChannelCount == 1) { + AudioMixer::ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut); + // the resampler always outputs stereo samples: do post stereo to mono conversion + int16_t *src = (int16_t *)mRsmpOutBuffer; + int16_t *dst = buffer.i16; + while (framesOut--) { + *dst++ = (int16_t)(((int32_t)*src + (int32_t)*(src + 1)) >> 1); + src += 2; + } + } else { + AudioMixer::ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut); + } + } - mRecordTrack->releaseBuffer(&buffer); - mRecordTrack->overflow(); + mActiveTrack->releaseBuffer(&buffer); + mActiveTrack->overflow(); } - // client isn't retrieving buffers fast enough else { - if (!mRecordTrack->setOverflow()) - LOGW("AudioRecordThread: buffer overflow"); + if (!mActiveTrack->setOverflow()) + LOGW("RecordThread: buffer overflow"); // Release the processor for a while before asking for a new buffer. // This will give the application more chance to read from the buffer and // clear the overflow. @@ -2458,65 +2972,64 @@ bool AudioFlinger::AudioRecordThread::threadLoop() } } - - if (input) { - delete input; + if (!mStandby) { + mInput->standby(); } - mRecordTrack.clear(); - + mActiveTrack.clear(); + + sendConfigEvent(AudioSystem::INPUT_CLOSED); + processConfigEvents(); + + LOGV("RecordThread %p exiting", this); return false; } -status_t AudioFlinger::AudioRecordThread::start(MixerThread::RecordTrack* recordTrack) +status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack) { - LOGV("AudioRecordThread::start"); + LOGV("RecordThread::start"); AutoMutex lock(&mLock); - mActive = true; - // If starting the active track, just reset mActive in case a stop - // was pending and exit - if (recordTrack == mRecordTrack.get()) return NO_ERROR; - if (mRecordTrack != 0) return -EBUSY; + if (mActiveTrack != 0) { + if (recordTrack != mActiveTrack.get()) return -EBUSY; - mRecordTrack = recordTrack; + if (mActiveTrack->mState == TrackBase::PAUSING) mActiveTrack->mState = TrackBase::RESUMING; + return NO_ERROR; + } + + mActiveTrack = recordTrack; + mActiveTrack->mState = TrackBase::RESUMING; // signal thread to start LOGV("Signal record thread"); mWaitWorkCV.signal(); - mWaitWorkCV.wait(mLock); - LOGV("Record started, status %d", mStartStatus); - return mStartStatus; -} - -void AudioFlinger::AudioRecordThread::stop(MixerThread::RecordTrack* recordTrack) { - LOGV("AudioRecordThread::stop"); - AutoMutex lock(&mLock); - if (mActive && (recordTrack == mRecordTrack.get())) { - mActive = false; - mStopped.wait(mLock); + mStartStopCond.wait(mLock); + if (mActiveTrack != 0) { + LOGV("Record started OK"); + return NO_ERROR; + } else { + LOGV("Record failed to start"); + return BAD_VALUE; } } -void AudioFlinger::AudioRecordThread::exit() -{ - LOGV("AudioRecordThread::exit"); - { - AutoMutex lock(&mLock); - requestExit(); - mWaitWorkCV.signal(); +void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) { + LOGV("RecordThread::stop"); + AutoMutex lock(&mLock); + if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) { + mActiveTrack->mState = TrackBase::PAUSING; + mStartStopCond.wait(mLock); } - requestExitAndWait(); } -status_t AudioFlinger::AudioRecordThread::dump(int fd, const Vector<String16>& args) +status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; char buffer[SIZE]; String8 result; pid_t pid = 0; - if (mRecordTrack != 0 && mRecordTrack->mClient != 0) { - snprintf(buffer, SIZE, "Record client pid: %d\n", mRecordTrack->mClient->pid()); + if (mActiveTrack != 0 && mActiveTrack->mClient != 0) { + snprintf(buffer, SIZE, "Record client pid: %d\n", mActiveTrack->mClient->pid()); result.append(buffer); } else { result.append("No record client\n"); @@ -2525,6 +3038,463 @@ status_t AudioFlinger::AudioRecordThread::dump(int fd, const Vector<String16>& a return NO_ERROR; } +status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer) +{ + size_t framesReq = buffer->frameCount; + size_t framesReady = mFrameCount - mRsmpInIndex; + int channelCount; + + if (framesReady == 0) { + ssize_t bytesRead = mInput->read(mRsmpInBuffer, mInputBytes); + if (bytesRead < 0) { + LOGE("RecordThread::getNextBuffer() Error reading audio input"); + sleep(1); + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; + } + mRsmpInIndex = 0; + framesReady = mFrameCount; + } + + if (framesReq > framesReady) { + framesReq = framesReady; + } + + if (mChannelCount == 1 && mReqChannelCount == 2) { + channelCount = 1; + } else { + channelCount = 2; + } + buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount; + buffer->frameCount = framesReq; + return NO_ERROR; +} + +void AudioFlinger::RecordThread::releaseBuffer(AudioBufferProvider::Buffer* buffer) +{ + mRsmpInIndex += buffer->frameCount; + buffer->frameCount = 0; +} + +bool AudioFlinger::RecordThread::checkForNewParameters_l() +{ + bool reconfig = false; + + if (mNewParameters != "") { + status_t status = NO_ERROR; + AudioParameter param = AudioParameter(mNewParameters); + int value; + int reqFormat = mFormat; + int reqSamplingRate = mReqSampleRate; + int reqChannelCount = mReqChannelCount; + if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { + reqSamplingRate = value; + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { + reqFormat = value; + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { + reqChannelCount = AudioSystem::popCount(value); + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be garantied + // if frame count is changed after track creation + if (mActiveTrack != 0) { + status = INVALID_OPERATION; + } else { + reconfig = true; + } + } + if (status == NO_ERROR) { + status = mInput->setParameters(mNewParameters); + if (status == INVALID_OPERATION) { + mInput->standby(); + status = mInput->setParameters(mNewParameters); + } + if (reconfig) { + if (status == BAD_VALUE && + reqFormat == mInput->format() && reqFormat == AudioSystem::PCM_16_BIT && + ((int)mInput->sampleRate() <= 2 * reqSamplingRate) && + (AudioSystem::popCount(mInput->channels()) < 3) && (reqChannelCount < 3)) { + status = NO_ERROR; + } + if (status == NO_ERROR) { + readInputParameters(); + sendConfigEvent(AudioSystem::INPUT_CONFIG_CHANGED); + } + } + } + mNewParameters = ""; + mParamStatus = status; + mParamCond.signal(); + } + return reconfig; +} + +String8 AudioFlinger::RecordThread::getParameters(const String8& keys) +{ + return mInput->getParameters(keys); +} + +void AudioFlinger::RecordThread::audioConfigChanged(int event, int param) { + AudioSystem::OutputDescriptor desc; + void *param2 = 0; + + switch (event) { + case AudioSystem::INPUT_OPENED: + case AudioSystem::INPUT_CONFIG_CHANGED: + desc.channels = mChannelCount; + desc.samplingRate = mSampleRate; + desc.format = mFormat; + desc.frameCount = mFrameCount; + desc.latency = 0; + param2 = &desc; + break; + + case AudioSystem::INPUT_CLOSED: + default: + break; + } + mAudioFlinger->audioConfigChanged(event, this, param2); +} + +void AudioFlinger::RecordThread::readInputParameters() +{ + if (mRsmpInBuffer) delete mRsmpInBuffer; + if (mRsmpOutBuffer) delete mRsmpOutBuffer; + if (mResampler) delete mResampler; + mResampler = 0; + + mSampleRate = mInput->sampleRate(); + mChannelCount = AudioSystem::popCount(mInput->channels()); + mFormat = mInput->format(); + mFrameSize = mInput->frameSize(); + mInputBytes = mInput->bufferSize(); + mFrameCount = mInputBytes / mFrameSize; + mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount]; + + if (mSampleRate != mReqSampleRate && mChannelCount < 3 && mReqChannelCount < 3) + { + int channelCount; + // optmization: if mono to mono, use the resampler in stereo to stereo mode to avoid + // stereo to mono post process as the resampler always outputs stereo. + if (mChannelCount == 1 && mReqChannelCount == 2) { + channelCount = 1; + } else { + channelCount = 2; + } + mResampler = AudioResampler::create(16, channelCount, mReqSampleRate); + mResampler->setSampleRate(mSampleRate); + mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN); + mRsmpOutBuffer = new int32_t[mFrameCount * 2]; + + // optmization: if mono to mono, alter input frame count as if we were inputing stereo samples + if (mChannelCount == 1 && mReqChannelCount == 1) { + mFrameCount >>= 1; + } + + } + mRsmpInIndex = mFrameCount; +} + +// ---------------------------------------------------------------------------- + +void *AudioFlinger::openOutput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t *pLatencyMs, + uint32_t flags) +{ + status_t status; + PlaybackThread *thread = NULL; + mHardwareStatus = AUDIO_HW_OUTPUT_OPEN; + uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0; + uint32_t format = pFormat ? *pFormat : 0; + uint32_t channels = pChannels ? *pChannels : 0; + uint32_t latency = pLatencyMs ? *pLatencyMs : 0; + + LOGV("openOutput(), Device %x, SamplingRate %d, Format %d, Channels %x, flags %x", + pDevices ? *pDevices : 0, + samplingRate, + format, + channels, + flags); + + if (pDevices == NULL || *pDevices == 0) { + return NULL; + } + Mutex::Autolock _l(mLock); + + AudioStreamOut *output = mAudioHardware->openOutputStream(*pDevices, + (int *)&format, + &channels, + &samplingRate, + &status); + LOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, Channels %x, status %d", + output, + samplingRate, + format, + channels, + status); + + mHardwareStatus = AUDIO_HW_IDLE; + if (output != 0) { + if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) || + (format != AudioSystem::PCM_16_BIT) || + (channels != AudioSystem::CHANNEL_OUT_STEREO)) { + thread = new DirectOutputThread(this, output); + LOGV("openOutput() created direct output %p", thread); + } else { + thread = new MixerThread(this, output); + LOGV("openOutput() created mixer output %p", thread); + } + mPlaybackThreads.add(thread); + + if (pSamplingRate) *pSamplingRate = samplingRate; + if (pFormat) *pFormat = format; + if (pChannels) *pChannels = channels; + if (pLatencyMs) *pLatencyMs = thread->latency(); + } + + return thread; +} + +void *AudioFlinger::openDuplicateOutput(void *output1, void *output2) +{ + Mutex::Autolock _l(mLock); + + if (checkMixerThread_l(output1) == NULL || + checkMixerThread_l(output2) == NULL) { + LOGW("openDuplicateOutput() wrong output mixer type %p or %p", output1, output2); + return NULL; + } + + DuplicatingThread *thread = new DuplicatingThread(this, (MixerThread *)output1); + thread->addOutputTrack( (MixerThread *)output2); + mPlaybackThreads.add(thread); + return thread; +} + +status_t AudioFlinger::closeOutput(void *output) +{ + PlaybackThread *thread; + { + Mutex::Autolock _l(mLock); + thread = checkPlaybackThread_l(output); + if (thread == NULL) { + return BAD_VALUE; + } + + LOGV("closeOutput() %p", thread); + + if (thread->type() == PlaybackThread::MIXER) { + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads[i]->type() == PlaybackThread::DUPLICATING) { + DuplicatingThread *dupThread = (DuplicatingThread *)mPlaybackThreads[i].get(); + dupThread->removeOutputTrack((MixerThread *)thread); + } + } + } + mPlaybackThreads.remove(thread); + } + thread->exit(); + + return NO_ERROR; +} + +status_t AudioFlinger::suspendOutput(void *output) +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + + if (thread == NULL) { + return BAD_VALUE; + } + + LOGV("suspendOutput() %p", output); + thread->suspend(); + + return NO_ERROR; +} + +status_t AudioFlinger::restoreOutput(void *output) +{ + Mutex::Autolock _l(mLock); + PlaybackThread *thread = checkPlaybackThread_l(output); + + if (thread == NULL) { + return BAD_VALUE; + } + + LOGV("restoreOutput() %p", output); + + thread->restore(); + + return NO_ERROR; +} + +void *AudioFlinger::openInput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t acoustics) +{ + status_t status; + RecordThread *thread = NULL; + uint32_t samplingRate = pSamplingRate ? *pSamplingRate : 0; + uint32_t format = pFormat ? *pFormat : 0; + uint32_t channels = pChannels ? *pChannels : 0; + uint32_t reqSamplingRate = samplingRate; + uint32_t reqFormat = format; + uint32_t reqChannels = channels; + + if (pDevices == NULL || *pDevices == 0) { + return NULL; + } + Mutex::Autolock _l(mLock); + + AudioStreamIn *input = mAudioHardware->openInputStream(*pDevices, + (int *)&format, + &channels, + &samplingRate, + &status, + (AudioSystem::audio_in_acoustics)acoustics); + LOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, acoustics %x, status %d", + input, + samplingRate, + format, + channels, + acoustics, + status); + + // If the input could not be opened with the requested parameters and we can handle the conversion internally, + // try to open again with the proposed parameters. The AudioFlinger can resample the input and do mono to stereo + // or stereo to mono conversions on 16 bit PCM inputs. + if (input == 0 && status == BAD_VALUE && + reqFormat == format && format == AudioSystem::PCM_16_BIT && + (samplingRate <= 2 * reqSamplingRate) && + (AudioSystem::popCount(channels) < 3) && (AudioSystem::popCount(reqChannels) < 3)) { + LOGV("openInput() reopening with proposed sampling rate and channels"); + input = mAudioHardware->openInputStream(*pDevices, + (int *)&format, + &channels, + &samplingRate, + &status, + (AudioSystem::audio_in_acoustics)acoustics); + } + + if (input != 0) { + // Start record thread + thread = new RecordThread(this, input, reqSamplingRate, reqChannels); + mRecordThreads.add(thread); + + if (pSamplingRate) *pSamplingRate = reqSamplingRate; + if (pFormat) *pFormat = format; + if (pChannels) *pChannels = reqChannels; + + input->standby(); + } + + return thread; +} + +status_t AudioFlinger::closeInput(void *input) +{ + RecordThread *thread; + { + Mutex::Autolock _l(mLock); + thread = checkRecordThread_l(input); + if (thread == NULL) { + return BAD_VALUE; + } + + LOGV("closeInput() %p", thread); + mRecordThreads.remove(thread); + } + thread->exit(); + + return NO_ERROR; +} + +status_t AudioFlinger::setStreamOutput(uint32_t stream, void *output) +{ + Mutex::Autolock _l(mLock); + MixerThread *dstThread = checkMixerThread_l(output); + if (dstThread == NULL) { + LOGW("setStreamOutput() bad output thread %p", output); + return BAD_VALUE; + } + + LOGV("setStreamOutput() stream %d to output %p", stream, dstThread); + + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + PlaybackThread *thread = mPlaybackThreads[i].get(); + if (thread != dstThread && + thread->type() != PlaybackThread::DIRECT) { + MixerThread *srcThread = (MixerThread *)thread; + SortedVector < sp<MixerThread::Track> > tracks; + SortedVector < wp<MixerThread::Track> > activeTracks; + srcThread->getTracks(tracks, activeTracks, stream); + if (tracks.size()) { + dstThread->putTracks(tracks, activeTracks); + dstThread->sendConfigEvent(AudioSystem::STREAM_CONFIG_CHANGED, stream); + } + } + } + + return NO_ERROR; +} + +// checkPlaybackThread_l() must be called with AudioFlinger::mLock held +AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(void *output) const +{ + PlaybackThread *thread = NULL; + + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads[i] == output) { + thread = (PlaybackThread *)output; + break; + } + } + + return thread; +} + +// checkMixerThread_l() must be called with AudioFlinger::mLock held +AudioFlinger::MixerThread *AudioFlinger::checkMixerThread_l(void *output) const +{ + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread != NULL) { + if (thread->type() == PlaybackThread::DIRECT) { + thread = NULL; + } + } + return (MixerThread *)thread; +} + +// checkRecordThread_l() must be called with AudioFlinger::mLock held +AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(void *input) const +{ + RecordThread *thread = NULL; + + for (size_t i = 0; i < mRecordThreads.size(); i++) { + if (mRecordThreads[i] == input) { + thread = (RecordThread *)input; + break; + } + } + + return thread; +} + +// ---------------------------------------------------------------------------- + status_t AudioFlinger::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -2532,6 +3502,7 @@ status_t AudioFlinger::onTransact( } // ---------------------------------------------------------------------------- + void AudioFlinger::instantiate() { defaultServiceManager()->addService( String16("media.audio_flinger"), new AudioFlinger()); diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index 634934e..06c5846 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -30,8 +30,7 @@ #include <utils/Atomic.h> #include <utils/Errors.h> #include <utils/threads.h> -#include <utils/MemoryDealer.h> -#include <utils/KeyedVector.h> +#include <binder/MemoryDealer.h> #include <utils/SortedVector.h> #include <utils/Vector.h> @@ -44,6 +43,7 @@ namespace android { class audio_track_cblk_t; class AudioMixer; class AudioBuffer; +class AudioResampler; // ---------------------------------------------------------------------------- @@ -56,7 +56,7 @@ class AudioBuffer; static const nsecs_t kStandbyTimeInNsecs = seconds(3); -class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient +class AudioFlinger : public BnAudioFlinger, public IBinder::DeathRecipient { public: static void instantiate(); @@ -73,13 +73,14 @@ public: int frameCount, uint32_t flags, const sp<IMemory>& sharedBuffer, + void *output, status_t *status); - virtual uint32_t sampleRate(int output) const; - virtual int channelCount(int output) const; - virtual int format(int output) const; - virtual size_t frameCount(int output) const; - virtual uint32_t latency(int output) const; + virtual uint32_t sampleRate(void *output) const; + virtual int channelCount(void *output) const; + virtual int format(void *output) const; + virtual size_t frameCount(void *output) const; + virtual uint32_t latency(void *output) const; virtual status_t setMasterVolume(float value); virtual status_t setMasterMute(bool muted); @@ -87,33 +88,51 @@ public: virtual float masterVolume() const; virtual bool masterMute() const; - virtual status_t setStreamVolume(int stream, float value); + virtual status_t setStreamVolume(int stream, float value, void *output); virtual status_t setStreamMute(int stream, bool muted); - virtual float streamVolume(int stream) const; + virtual float streamVolume(int stream, void *output) const; virtual bool streamMute(int stream) const; - virtual status_t setRouting(int mode, uint32_t routes, uint32_t mask); - virtual uint32_t getRouting(int mode) const; - virtual status_t setMode(int mode); - virtual int getMode() const; virtual status_t setMicMute(bool state); virtual bool getMicMute() const; virtual bool isMusicActive() const; - virtual bool isA2dpEnabled() const; - - virtual status_t setParameter(const char* key, const char* value); + virtual status_t setParameters(void *ioHandle, const String8& keyValuePairs); + virtual String8 getParameters(void *ioHandle, const String8& keys); virtual void registerClient(const sp<IAudioFlingerClient>& client); - + virtual size_t getInputBufferSize(uint32_t sampleRate, int format, int channelCount); - - virtual void wakeUp() { mWaitWorkCV.broadcast(); } - + + virtual void *openOutput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t *pLatencyMs, + uint32_t flags); + + virtual void *openDuplicateOutput(void *output1, void *output2); + + virtual status_t closeOutput(void *output); + + virtual status_t suspendOutput(void *output); + + virtual status_t restoreOutput(void *output); + + virtual void *openInput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t acoustics); + + virtual status_t closeInput(void *input); + + virtual status_t setStreamOutput(uint32_t stream, void *output); + // IBinder::DeathRecipient virtual void binderDied(const wp<IBinder>& who); @@ -139,7 +158,7 @@ public: // record interface virtual sp<IAudioRecord> openRecord( pid_t pid, - int inputSource, + void *input, uint32_t sampleRate, int format, int channelCount, @@ -153,30 +172,12 @@ public: Parcel* reply, uint32_t flags); + void audioConfigChanged(int event, void *param1, void *param2); + private: AudioFlinger(); virtual ~AudioFlinger(); - - void setOutput(int outputType); - void doSetOutput(int outputType); - -#ifdef WITH_A2DP - void setA2dpEnabled_l(bool enable); - void checkA2dpEnabledChange_l(); -#endif - static bool streamForcedToSpeaker(int streamType); - - // Management of forced route to speaker for certain track types. - enum force_speaker_command { - ACTIVE_TRACK_ADDED = 0, - ACTIVE_TRACK_REMOVED, - CHECK_ROUTE_RESTORE_TIME, - FORCE_ROUTE_RESTORE - }; - void handleForcedSpeakerRoute(int command); -#ifdef WITH_A2DP - void handleRouteDisablesA2dp_l(int routes); -#endif + // Internal dump utilites. status_t dumpPermissionDenial(int fd, const Vector<String16>& args); @@ -201,14 +202,17 @@ private: class TrackHandle; class RecordHandle; - class AudioRecordThread; - - - // --- MixerThread --- - class MixerThread : public Thread { + class RecordThread; + class PlaybackThread; + class MixerThread; + class DirectOutputThread; + class Track; + class RecordTrack; + + class ThreadBase : public Thread { public: - - // --- Track --- + ThreadBase (const sp<AudioFlinger>& audioFlinger); + virtual ~ThreadBase(); // base for record and playback class TrackBase : public AudioBufferProvider, public RefBase { @@ -230,7 +234,7 @@ private: // The upper 16 bits are used for track-specific flags. }; - TrackBase(const sp<MixerThread>& mixerThread, + TrackBase(const wp<ThreadBase>& thread, const sp<Client>& client, uint32_t sampleRate, int format, @@ -245,9 +249,12 @@ private: sp<IMemory> getCblk() const; protected: - friend class MixerThread; + friend class ThreadBase; friend class RecordHandle; - friend class AudioRecordThread; + friend class PlaybackThread; + friend class RecordThread; + friend class MixerThread; + friend class DirectOutputThread; TrackBase(const TrackBase&); TrackBase& operator = (const TrackBase&); @@ -269,10 +276,6 @@ private: void* getBuffer(uint32_t offset, uint32_t frames) const; - int name() const { - return mName; - } - bool isStopped() const { return mState == STOPPED; } @@ -284,14 +287,13 @@ private: bool step(); void reset(); - sp<MixerThread> mMixerThread; + wp<ThreadBase> mThread; sp<Client> mClient; sp<IMemory> mCblkMemory; audio_track_cblk_t* mCblk; void* mBuffer; void* mBufferEnd; uint32_t mFrameCount; - int mName; // we don't really need a lock for these int mState; int mClientTid; @@ -299,10 +301,68 @@ private: uint32_t mFlags; }; + class ConfigEvent { + public: + ConfigEvent() : mEvent(0), mParam(0) {} + + int mEvent; + int mParam; + }; + + uint32_t sampleRate() const; + int channelCount() const; + int format() const; + size_t frameCount() const; + void wakeUp() { mWaitWorkCV.broadcast(); } + void exit(); + virtual bool checkForNewParameters_l() = 0; + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys) = 0; + virtual void audioConfigChanged(int event, int param = 0) = 0; + void sendConfigEvent(int event, int param = 0); + void processConfigEvents(); + + mutable Mutex mLock; + + protected: + + friend class Track; + friend class TrackBase; + friend class PlaybackThread; + friend class MixerThread; + friend class DirectOutputThread; + friend class DuplicatingThread; + friend class RecordThread; + friend class RecordTrack; + + Condition mWaitWorkCV; + sp<AudioFlinger> mAudioFlinger; + uint32_t mSampleRate; + size_t mFrameCount; + int mChannelCount; + int mFormat; + uint32_t mFrameSize; + Condition mParamCond; + String8 mNewParameters; + status_t mParamStatus; + Vector<ConfigEvent *> mConfigEvents; + bool mStandby; + }; + + // --- PlaybackThread --- + class PlaybackThread : public ThreadBase { + public: + + enum type { + MIXER, + DIRECT, + DUPLICATING + }; + // playback track class Track : public TrackBase { public: - Track( const sp<MixerThread>& mixerThread, + Track( const wp<ThreadBase>& thread, const sp<Client>& client, int streamType, uint32_t sampleRate, @@ -321,6 +381,9 @@ private: void destroy(); void mute(bool); void setVolume(float left, float right); + int name() const { + return mName; + } int type() const { return mStreamType; @@ -328,29 +391,25 @@ private: protected: - friend class MixerThread; + friend class ThreadBase; friend class AudioFlinger; - friend class AudioFlinger::TrackHandle; + friend class TrackHandle; + friend class PlaybackThread; + friend class MixerThread; + friend class DirectOutputThread; Track(const Track&); Track& operator = (const Track&); virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); - - bool isMuted() const { - return (mMute || mMixerThread->mStreamTypes[mStreamType].mute); - } - + bool isMuted() { return mMute; } bool isPausing() const { return mState == PAUSING; } - bool isPaused() const { return mState == PAUSED; } - bool isReady() const; - void setPaused() { mState = PAUSED; } void reset(); @@ -364,54 +423,20 @@ private: sp<IMemory> mSharedBuffer; bool mResetDone; int mStreamType; + int mName; }; // end of Track - // record track - class RecordTrack : public TrackBase { - public: - RecordTrack(const sp<MixerThread>& mixerThread, - const sp<Client>& client, - int inputSource, - uint32_t sampleRate, - int format, - int channelCount, - int frameCount, - uint32_t flags); - ~RecordTrack(); - - virtual status_t start(); - virtual void stop(); - - bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } - bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; } - - int inputSource() const { return mInputSource; } - - private: - friend class AudioFlinger; - friend class AudioFlinger::RecordHandle; - friend class AudioFlinger::AudioRecordThread; - friend class MixerThread; - - RecordTrack(const Track&); - RecordTrack& operator = (const Track&); - - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); - - bool mOverflow; - int mInputSource; - }; // playback track class OutputTrack : public Track { public: - + class Buffer: public AudioBufferProvider::Buffer { public: int16_t *mBuffer; }; - - OutputTrack( const sp<MixerThread>& mixerThread, + + OutputTrack( const wp<ThreadBase>& thread, uint32_t sampleRate, int format, int channelCount, @@ -420,35 +445,35 @@ private: virtual status_t start(); virtual void stop(); - void write(int16_t* data, uint32_t frames); + bool write(int16_t* data, uint32_t frames); bool bufferQueueEmpty() { return (mBufferQueue.size() == 0) ? true : false; } + bool isActive() { return mActive; } + wp<ThreadBase>& thread() { return mThread; } private: - status_t obtainBuffer(AudioBufferProvider::Buffer* buffer); + status_t obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs); void clearBufferQueue(); - - sp<MixerThread> mOutputMixerThread; + + // Maximum number of pending buffers allocated by OutputTrack::write() + static const uint8_t kMaxOverFlowBuffers = 3; + Vector < Buffer* > mBufferQueue; AudioBufferProvider::Buffer mOutBuffer; - uint32_t mFramesWritten; - - }; // end of OutputTrack + uint32_t mWaitTimeMs; + bool mActive; - MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int outputType); - virtual ~MixerThread(); + }; // end of OutputTrack + + PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output); + virtual ~PlaybackThread(); virtual status_t dump(int fd, const Vector<String16>& args); // Thread virtuals - virtual bool threadLoop(); virtual status_t readyToRun(); virtual void onFirstRef(); - virtual uint32_t sampleRate() const; - virtual int channelCount() const; - virtual int format() const; - virtual size_t frameCount() const; virtual uint32_t latency() const; virtual status_t setMasterVolume(float value); @@ -463,9 +488,8 @@ private: virtual float streamVolume(int stream) const; virtual bool streamMute(int stream) const; - bool isMusicActive_l() const; - - + bool isMusicActive() const; + sp<Track> createTrack_l( const sp<AudioFlinger::Client>& client, int streamType, @@ -475,13 +499,15 @@ private: int frameCount, const sp<IMemory>& sharedBuffer, status_t *status); - - void getTracks_l(SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks); - void putTracks_l(SortedVector < sp<Track> >& tracks, - SortedVector < wp<Track> >& activeTracks); - void setOuputTrack(OutputTrack *track) { mOutputTrack = track; } - + + AudioStreamOut* getOutput() { return mOutput; } + + virtual int type() const { return mType; } + void suspend() { mSuspended = true; } + void restore() { mSuspended = false; } + virtual String8 getParameters(const String8& keys); + virtual void audioConfigChanged(int event, int param = 0); + struct stream_type_t { stream_type_t() : volume(1.0f), @@ -492,56 +518,113 @@ private: bool mute; }; - private: + protected: + int mType; + int16_t* mMixBuffer; + bool mSuspended; + int mBytesWritten; + bool mMasterMute; + SortedVector< wp<Track> > mActiveTracks; + private: friend class AudioFlinger; friend class Track; friend class TrackBase; - friend class RecordTrack; - - MixerThread(const Client&); - MixerThread& operator = (const MixerThread&); - + friend class MixerThread; + friend class DirectOutputThread; + friend class DuplicatingThread; + + PlaybackThread(const Client&); + PlaybackThread& operator = (const PlaybackThread&); + status_t addTrack_l(const sp<Track>& track); void destroyTrack_l(const sp<Track>& track); - int getTrackName_l(); - void deleteTrackName_l(int name); - void addActiveTrack_l(const wp<Track>& t); - void removeActiveTrack_l(const wp<Track>& t); - size_t getOutputFrameCount(); + virtual int getTrackName_l() = 0; + virtual void deleteTrackName_l(int name) = 0; + void readOutputParameters(); - status_t dumpInternals(int fd, const Vector<String16>& args); + virtual status_t dumpInternals(int fd, const Vector<String16>& args); status_t dumpTracks(int fd, const Vector<String16>& args); - - sp<AudioFlinger> mAudioFlinger; - SortedVector< wp<Track> > mActiveTracks; + SortedVector< sp<Track> > mTracks; - stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES]; - AudioMixer* mAudioMixer; + // mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread + stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES + 1]; AudioStreamOut* mOutput; - int mOutputType; - uint32_t mSampleRate; - size_t mFrameCount; - int mChannelCount; - int mFormat; - int16_t* mMixBuffer; float mMasterVolume; - bool mMasterMute; nsecs_t mLastWriteTime; int mNumWrites; int mNumDelayedWrites; - bool mStandby; bool mInWrite; - sp <OutputTrack> mOutputTrack; + int mMinBytesToWrite; + }; + + class MixerThread : public PlaybackThread { + public: + MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output); + virtual ~MixerThread(); + + // Thread virtuals + virtual bool threadLoop(); + + void getTracks(SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks, + int streamType); + void putTracks(SortedVector < sp<Track> >& tracks, + SortedVector < wp<Track> >& activeTracks); + virtual int getTrackName_l(); + virtual void deleteTrackName_l(int name); + virtual bool checkForNewParameters_l(); + virtual status_t dumpInternals(int fd, const Vector<String16>& args); + + protected: + size_t prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove); + + AudioMixer* mAudioMixer; + }; + + class DirectOutputThread : public PlaybackThread { + public: + + DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output); + ~DirectOutputThread(); + + // Thread virtuals + virtual bool threadLoop(); + + virtual int getTrackName_l(); + virtual void deleteTrackName_l(int name); + virtual bool checkForNewParameters_l(); + + private: + float mLeftVolume; + float mRightVolume; }; - + class DuplicatingThread : public MixerThread { + public: + DuplicatingThread (const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread); + ~DuplicatingThread(); + + // Thread virtuals + virtual bool threadLoop(); + void addOutputTrack(MixerThread* thread); + void removeOutputTrack(MixerThread* thread); + + private: + SortedVector < sp<OutputTrack> > mOutputTracks; + }; + + PlaybackThread *checkPlaybackThread_l(void *output) const; + MixerThread *checkMixerThread_l(void *output) const; + RecordThread *checkRecordThread_l(void *input) const; + float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; } + friend class AudioBuffer; class TrackHandle : public android::BnAudioTrack { public: - TrackHandle(const sp<MixerThread::Track>& track); + TrackHandle(const sp<PlaybackThread::Track>& track); virtual ~TrackHandle(); virtual status_t start(); virtual void stop(); @@ -553,20 +636,91 @@ private: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); private: - sp<MixerThread::Track> mTrack; + sp<PlaybackThread::Track> mTrack; }; friend class Client; - friend class MixerThread::Track; + friend class PlaybackThread::Track; void removeClient(pid_t pid); + // record thread + class RecordThread : public ThreadBase, public AudioBufferProvider + { + public: + + // record track + class RecordTrack : public TrackBase { + public: + RecordTrack(const wp<ThreadBase>& thread, + const sp<Client>& client, + uint32_t sampleRate, + int format, + int channelCount, + int frameCount, + uint32_t flags); + ~RecordTrack(); + + virtual status_t start(); + virtual void stop(); + + bool overflow() { bool tmp = mOverflow; mOverflow = false; return tmp; } + bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; } + + private: + friend class AudioFlinger; + friend class RecordThread; + + RecordTrack(const RecordTrack&); + RecordTrack& operator = (const RecordTrack&); + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + + bool mOverflow; + }; + + + RecordThread(const sp<AudioFlinger>& audioFlinger, + AudioStreamIn *input, + uint32_t sampleRate, + uint32_t channels); + ~RecordThread(); + + virtual bool threadLoop(); + virtual status_t readyToRun() { return NO_ERROR; } + virtual void onFirstRef(); + + status_t start(RecordTrack* recordTrack); + void stop(RecordTrack* recordTrack); + status_t dump(int fd, const Vector<String16>& args); + AudioStreamIn* getInput() { return mInput; } + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + virtual bool checkForNewParameters_l(); + virtual String8 getParameters(const String8& keys); + virtual void audioConfigChanged(int event, int param = 0); + void readInputParameters(); + + private: + RecordThread(); + AudioStreamIn *mInput; + sp<RecordTrack> mActiveTrack; + Condition mStartStopCond; + AudioResampler *mResampler; + int32_t *mRsmpOutBuffer; + int16_t *mRsmpInBuffer; + size_t mRsmpInIndex; + size_t mInputBytes; + int mReqChannelCount; + uint32_t mReqSampleRate; + }; class RecordHandle : public android::BnAudioRecord { public: - RecordHandle(const sp<MixerThread::RecordTrack>& recordTrack); + RecordHandle(const sp<RecordThread::RecordTrack>& recordTrack); virtual ~RecordHandle(); virtual status_t start(); virtual void stop(); @@ -574,66 +728,30 @@ private: virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); private: - sp<MixerThread::RecordTrack> mRecordTrack; + sp<RecordThread::RecordTrack> mRecordTrack; }; - // record thread - class AudioRecordThread : public Thread - { - public: - AudioRecordThread(AudioHardwareInterface* audioHardware, const sp<AudioFlinger>& audioFlinger); - virtual ~AudioRecordThread(); - virtual bool threadLoop(); - virtual status_t readyToRun() { return NO_ERROR; } - virtual void onFirstRef() {} - - status_t start(MixerThread::RecordTrack* recordTrack); - void stop(MixerThread::RecordTrack* recordTrack); - void exit(); - status_t dump(int fd, const Vector<String16>& args); - - private: - AudioRecordThread(); - AudioHardwareInterface *mAudioHardware; - sp<AudioFlinger> mAudioFlinger; - sp<MixerThread::RecordTrack> mRecordTrack; - Mutex mLock; - Condition mWaitWorkCV; - Condition mStopped; - volatile bool mActive; - status_t mStartStatus; - }; + friend class RecordThread; + friend class PlaybackThread; - friend class AudioRecordThread; - friend class MixerThread; - status_t startRecord(MixerThread::RecordTrack* recordTrack); - void stopRecord(MixerThread::RecordTrack* recordTrack); - - mutable Mutex mHardwareLock; mutable Mutex mLock; - mutable Condition mWaitWorkCV; DefaultKeyedVector< pid_t, wp<Client> > mClients; - sp<MixerThread> mA2dpMixerThread; - sp<MixerThread> mHardwareMixerThread; + mutable Mutex mHardwareLock; AudioHardwareInterface* mAudioHardware; - AudioHardwareInterface* mA2dpAudioInterface; - sp<AudioRecordThread> mAudioRecordThread; - bool mA2dpEnabled; - bool mNotifyA2dpChange; mutable int mHardwareStatus; - SortedVector< wp<IBinder> > mNotificationClients; - int mForcedSpeakerCount; - int mA2dpDisableCount; - - // true if A2DP should resume when mA2dpDisableCount returns to zero - bool mA2dpSuppressed; - uint32_t mSavedRoute; - uint32_t mForcedRoute; - nsecs_t mRouteRestoreTime; - bool mMusicMuteSaved; + + + SortedVector< sp<PlaybackThread> > mPlaybackThreads; + PlaybackThread::stream_type_t mStreamTypes[AudioSystem::NUM_STREAM_TYPES]; + float mMasterVolume; + bool mMasterMute; + + SortedVector< sp<RecordThread> > mRecordThreads; + + SortedVector< sp<IBinder> > mNotificationClients; }; // ---------------------------------------------------------------------------- diff --git a/libs/audioflinger/AudioHardwareGeneric.cpp b/libs/audioflinger/AudioHardwareGeneric.cpp index 1e159b8..57874f3 100644 --- a/libs/audioflinger/AudioHardwareGeneric.cpp +++ b/libs/audioflinger/AudioHardwareGeneric.cpp @@ -49,8 +49,8 @@ AudioHardwareGeneric::AudioHardwareGeneric() AudioHardwareGeneric::~AudioHardwareGeneric() { if (mFd >= 0) ::close(mFd); - delete mOutput; - delete mInput; + closeOutputStream((AudioStreamOut *)mOutput); + closeInputStream((AudioStreamIn *)mInput); } status_t AudioHardwareGeneric::initCheck() @@ -63,7 +63,7 @@ status_t AudioHardwareGeneric::initCheck() } AudioStreamOut* AudioHardwareGeneric::openOutputStream( - int format, int channelCount, uint32_t sampleRate, status_t *status) + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) { AutoMutex lock(mLock); @@ -77,7 +77,7 @@ AudioStreamOut* AudioHardwareGeneric::openOutputStream( // create new output stream AudioStreamOutGeneric* out = new AudioStreamOutGeneric(); - status_t lStatus = out->set(this, mFd, format, channelCount, sampleRate); + status_t lStatus = out->set(this, mFd, devices, format, channels, sampleRate); if (status) { *status = lStatus; } @@ -89,17 +89,19 @@ AudioStreamOut* AudioHardwareGeneric::openOutputStream( return mOutput; } -void AudioHardwareGeneric::closeOutputStream(AudioStreamOutGeneric* out) { - if (out == mOutput) mOutput = 0; +void AudioHardwareGeneric::closeOutputStream(AudioStreamOut* out) { + if (mOutput && out == mOutput) { + delete mOutput; + mOutput = 0; + } } AudioStreamIn* AudioHardwareGeneric::openInputStream( - int inputSource, int format, int channelCount, uint32_t sampleRate, + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) { // check for valid input source - if ((inputSource < AudioRecord::DEFAULT_INPUT) || - (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) { + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { return 0; } @@ -115,7 +117,7 @@ AudioStreamIn* AudioHardwareGeneric::openInputStream( // create new output stream AudioStreamInGeneric* in = new AudioStreamInGeneric(); - status_t lStatus = in->set(this, mFd, format, channelCount, sampleRate, acoustics); + status_t lStatus = in->set(this, mFd, devices, format, channels, sampleRate, acoustics); if (status) { *status = lStatus; } @@ -127,8 +129,11 @@ AudioStreamIn* AudioHardwareGeneric::openInputStream( return mInput; } -void AudioHardwareGeneric::closeInputStream(AudioStreamInGeneric* in) { - if (in == mInput) mInput = 0; +void AudioHardwareGeneric::closeInputStream(AudioStreamIn* in) { + if (mInput && in == mInput) { + delete mInput; + mInput = 0; + } } status_t AudioHardwareGeneric::setVoiceVolume(float v) @@ -185,30 +190,42 @@ status_t AudioHardwareGeneric::dump(int fd, const Vector<String16>& args) status_t AudioStreamOutGeneric::set( AudioHardwareGeneric *hw, int fd, - int format, - int channels, - uint32_t rate) + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate) { + int lFormat = pFormat ? *pFormat : 0; + uint32_t lChannels = pChannels ? *pChannels : 0; + uint32_t lRate = pRate ? *pRate : 0; + // fix up defaults - if (format == 0) format = AudioSystem::PCM_16_BIT; - if (channels == 0) channels = channelCount(); - if (rate == 0) rate = sampleRate(); + if (lFormat == 0) lFormat = format(); + if (lChannels == 0) lChannels = channels(); + if (lRate == 0) lRate = sampleRate(); // check values - if ((format != AudioSystem::PCM_16_BIT) || - (channels != channelCount()) || - (rate != sampleRate())) + if ((lFormat != format()) || + (lChannels != channels()) || + (lRate != sampleRate())) { + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); return BAD_VALUE; + } + + if (pFormat) *pFormat = lFormat; + if (pChannels) *pChannels = lChannels; + if (pRate) *pRate = lRate; mAudioHardware = hw; mFd = fd; + mDevice = devices; return NO_ERROR; } AudioStreamOutGeneric::~AudioStreamOutGeneric() { - if (mAudioHardware) - mAudioHardware->closeOutputStream(this); } ssize_t AudioStreamOutGeneric::write(const void* buffer, size_t bytes) @@ -234,10 +251,12 @@ status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args) result.append(buffer); snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); result.append(buffer); - snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); result.append(buffer); snprintf(buffer, SIZE, "\tformat: %d\n", format()); result.append(buffer); + snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice); + result.append(buffer); snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware); result.append(buffer); snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); @@ -246,29 +265,68 @@ status_t AudioStreamOutGeneric::dump(int fd, const Vector<String16>& args) return NO_ERROR; } +status_t AudioStreamOutGeneric::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 key = String8(AudioParameter::keyRouting); + status_t status = NO_ERROR; + int device; + LOGV("setParameters() %s", keyValuePairs.string()); + + if (param.getInt(key, device) == NO_ERROR) { + mDevice = device; + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 AudioStreamOutGeneric::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8(AudioParameter::keyRouting); + + if (param.get(key, value) == NO_ERROR) { + param.addInt(key, (int)mDevice); + } + + LOGV("getParameters() %s", param.toString().string()); + return param.toString(); +} + // ---------------------------------------------------------------------------- // record functions status_t AudioStreamInGeneric::set( AudioHardwareGeneric *hw, int fd, - int format, - int channels, - uint32_t rate, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics) { // FIXME: remove logging - LOGD("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, format, channels, rate); + if (pFormat == 0 || pChannels == 0 || pRate == 0) return BAD_VALUE; + LOGD("AudioStreamInGeneric::set(%p, %d, %d, %d, %u)", hw, fd, *pFormat, *pChannels, *pRate); // check values - if ((format != AudioSystem::PCM_16_BIT) || - (channels != channelCount()) || - (rate != sampleRate())) { + if ((*pFormat != format()) || + (*pChannels != channels()) || + (*pRate != sampleRate())) { LOGE("Error opening input channel"); + *pFormat = format(); + *pChannels = channels(); + *pRate = sampleRate(); return BAD_VALUE; } mAudioHardware = hw; mFd = fd; + mDevice = devices; return NO_ERROR; } @@ -276,14 +334,12 @@ AudioStreamInGeneric::~AudioStreamInGeneric() { // FIXME: remove logging LOGD("AudioStreamInGeneric destructor"); - if (mAudioHardware) - mAudioHardware->closeInputStream(this); } ssize_t AudioStreamInGeneric::read(void* buffer, ssize_t bytes) { // FIXME: remove logging - LOGD("AudioStreamInGeneric::read(%p, %d) from fd %d", buffer, bytes, mFd); + LOGD("AudioStreamInGeneric::read(%p, %d) from fd %d", buffer, (int)bytes, mFd); AutoMutex lock(mLock); if (mFd < 0) { LOGE("Attempt to read from unopened device"); @@ -303,10 +359,12 @@ status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args) result.append(buffer); snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); result.append(buffer); - snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); result.append(buffer); snprintf(buffer, SIZE, "\tformat: %d\n", format()); result.append(buffer); + snprintf(buffer, SIZE, "\tdevice: %d\n", mDevice); + result.append(buffer); snprintf(buffer, SIZE, "\tmAudioHardware: %p\n", mAudioHardware); result.append(buffer); snprintf(buffer, SIZE, "\tmFd: %d\n", mFd); @@ -315,6 +373,39 @@ status_t AudioStreamInGeneric::dump(int fd, const Vector<String16>& args) return NO_ERROR; } +status_t AudioStreamInGeneric::setParameters(const String8& keyValuePairs) +{ + AudioParameter param = AudioParameter(keyValuePairs); + String8 key = String8(AudioParameter::keyRouting); + status_t status = NO_ERROR; + int device; + LOGV("setParameters() %s", keyValuePairs.string()); + + if (param.getInt(key, device) == NO_ERROR) { + mDevice = device; + param.remove(key); + } + + if (param.size()) { + status = BAD_VALUE; + } + return status; +} + +String8 AudioStreamInGeneric::getParameters(const String8& keys) +{ + AudioParameter param = AudioParameter(keys); + String8 value; + String8 key = String8(AudioParameter::keyRouting); + + if (param.get(key, value) == NO_ERROR) { + param.addInt(key, (int)mDevice); + } + + LOGV("getParameters() %s", param.toString().string()); + return param.toString(); +} + // ---------------------------------------------------------------------------- }; // namespace android diff --git a/libs/audioflinger/AudioHardwareGeneric.h b/libs/audioflinger/AudioHardwareGeneric.h index c89df87..42da413 100644 --- a/libs/audioflinger/AudioHardwareGeneric.h +++ b/libs/audioflinger/AudioHardwareGeneric.h @@ -39,24 +39,28 @@ public: virtual status_t set( AudioHardwareGeneric *hw, int mFd, - int format, - int channelCount, - uint32_t sampleRate); + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate); virtual uint32_t sampleRate() const { return 44100; } virtual size_t bufferSize() const { return 4096; } - virtual int channelCount() const { return 2; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; } virtual int format() const { return AudioSystem::PCM_16_BIT; } virtual uint32_t latency() const { return 20; } - virtual status_t setVolume(float volume) { return INVALID_OPERATION; } + virtual status_t setVolume(float left, float right) { return INVALID_OPERATION; } virtual ssize_t write(const void* buffer, size_t bytes); virtual status_t standby(); virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); private: AudioHardwareGeneric *mAudioHardware; Mutex mLock; int mFd; + uint32_t mDevice; }; class AudioStreamInGeneric : public AudioStreamIn { @@ -67,24 +71,28 @@ public: virtual status_t set( AudioHardwareGeneric *hw, int mFd, - int format, - int channelCount, - uint32_t sampleRate, + uint32_t devices, + int *pFormat, + uint32_t *pChannels, + uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics); - uint32_t sampleRate() const { return 8000; } + virtual uint32_t sampleRate() const { return 8000; } virtual size_t bufferSize() const { return 320; } - virtual int channelCount() const { return 1; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; } virtual int format() const { return AudioSystem::PCM_16_BIT; } virtual status_t setGain(float gain) { return INVALID_OPERATION; } virtual ssize_t read(void* buffer, ssize_t bytes); virtual status_t dump(int fd, const Vector<String16>& args); virtual status_t standby() { return NO_ERROR; } + virtual status_t setParameters(const String8& keyValuePairs); + virtual String8 getParameters(const String8& keys); private: AudioHardwareGeneric *mAudioHardware; Mutex mLock; int mFd; + uint32_t mDevice; }; @@ -101,28 +109,27 @@ public: virtual status_t setMicMute(bool state); virtual status_t getMicMute(bool* state); - virtual status_t setParameter(const char* key, const char* value) - { return NO_ERROR; } - // create I/O streams virtual AudioStreamOut* openOutputStream( - int format=0, - int channelCount=0, - uint32_t sampleRate=0, + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); virtual AudioStreamIn* openInputStream( - int inputSource, - int format, - int channelCount, - uint32_t sampleRate, + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); void closeOutputStream(AudioStreamOutGeneric* out); void closeInputStream(AudioStreamInGeneric* in); protected: - virtual status_t doRouting() { return NO_ERROR; } virtual status_t dump(int fd, const Vector<String16>& args); private: diff --git a/libs/audioflinger/AudioHardwareInterface.cpp b/libs/audioflinger/AudioHardwareInterface.cpp index cc1bd8f..37be329 100644 --- a/libs/audioflinger/AudioHardwareInterface.cpp +++ b/libs/audioflinger/AudioHardwareInterface.cpp @@ -18,6 +18,7 @@ #include <cutils/properties.h> #include <string.h> #include <unistd.h> +//#define LOG_NDEBUG 0 #define LOG_TAG "AudioHardwareInterface" #include <utils/Log.h> @@ -25,15 +26,17 @@ #include "AudioHardwareStub.h" #include "AudioHardwareGeneric.h" +#ifdef WITH_A2DP +#include "A2dpAudioInterface.h" +#endif -//#define DUMP_FLINGER_OUT // if defined allows recording samples in a file -#ifdef DUMP_FLINGER_OUT +#ifdef ENABLE_AUDIO_DUMP #include "AudioDumpInterface.h" #endif // change to 1 to log routing calls -#define LOG_ROUTING_CALLS 0 +#define LOG_ROUTING_CALLS 1 namespace android { @@ -48,14 +51,6 @@ static const char* routingModeStrings[] = "IN_CALL" }; -static const char* routeStrings[] = -{ - "EARPIECE ", - "SPEAKER ", - "BLUETOOTH ", - "HEADSET ", - "BLUETOOTH_A2DP " -}; static const char* routeNone = "NONE"; static const char* displayMode(int mode) @@ -64,22 +59,6 @@ static const char* displayMode(int mode) return routingModeStrings[0]; return routingModeStrings[mode+3]; } - -static const char* displayRoutes(uint32_t routes) -{ - static char routeStr[80]; - if (routes == 0) - return routeNone; - routeStr[0] = 0; - int bitMask = 1; - for (int i = 0; i < 4; ++i, bitMask <<= 1) { - if (routes & bitMask) { - strcat(routeStr, routeStrings[i]); - } - } - routeStr[strlen(routeStr)-1] = 0; - return routeStr; -} #endif // ---------------------------------------------------------------------------- @@ -112,13 +91,17 @@ AudioHardwareInterface* AudioHardwareInterface::create() hw = new AudioHardwareStub(); } -#ifdef DUMP_FLINGER_OUT +#ifdef WITH_A2DP + hw = new A2dpAudioInterface(hw); +#endif + +#ifdef ENABLE_AUDIO_DUMP // This code adds a record of buffers in a file to write calls made by AudioFlinger. // It replaces the current AudioHardwareInterface object by an intermediate one which // will record buffers in a file (after sending them to hardware) for testing purpose. - // This feature is enabled by defining symbol DUMP_FLINGER_OUT. - // The output file is FLINGER_DUMP_NAME. Pause are not recorded in the file. - + // This feature is enabled by defining symbol ENABLE_AUDIO_DUMP. + // The output file is set with setParameters("test_cmd_file_name=<name>"). Pause are not recorded in the file. + LOGV("opening PCM dump interface"); hw = new AudioDumpInterface(hw); // replace interface #endif return hw; @@ -132,48 +115,9 @@ AudioStreamIn::~AudioStreamIn() {} AudioHardwareBase::AudioHardwareBase() { - // force a routing update on initialization - memset(&mRoutes, 0, sizeof(mRoutes)); mMode = 0; } -// generics for audio routing - the real work is done in doRouting -status_t AudioHardwareBase::setRouting(int mode, uint32_t routes) -{ -#if LOG_ROUTING_CALLS - LOGD("setRouting: mode=%s, routes=[%s]", displayMode(mode), displayRoutes(routes)); -#endif - if (mode == AudioSystem::MODE_CURRENT) - mode = mMode; - if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) - return BAD_VALUE; - uint32_t old = mRoutes[mode]; - mRoutes[mode] = routes; - if ((mode != mMode) || (old == routes)) - return NO_ERROR; -#if LOG_ROUTING_CALLS - const char* oldRouteStr = strdup(displayRoutes(old)); - LOGD("doRouting: mode=%s, old route=[%s], new route=[%s]", - displayMode(mode), oldRouteStr, displayRoutes(routes)); - delete oldRouteStr; -#endif - return doRouting(); -} - -status_t AudioHardwareBase::getRouting(int mode, uint32_t* routes) -{ - if (mode == AudioSystem::MODE_CURRENT) - mode = mMode; - if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) - return BAD_VALUE; - *routes = mRoutes[mode]; -#if LOG_ROUTING_CALLS - LOGD("getRouting: mode=%s, routes=[%s]", - displayMode(mode), displayRoutes(*routes)); -#endif - return NO_ERROR; -} - status_t AudioHardwareBase::setMode(int mode) { #if LOG_ROUTING_CALLS @@ -182,29 +126,24 @@ status_t AudioHardwareBase::setMode(int mode) if ((mode < 0) || (mode >= AudioSystem::NUM_MODES)) return BAD_VALUE; if (mMode == mode) - return NO_ERROR; -#if LOG_ROUTING_CALLS - LOGD("doRouting: old mode=%s, new mode=%s route=[%s]", - displayMode(mMode), displayMode(mode), displayRoutes(mRoutes[mode])); -#endif + return ALREADY_EXISTS; mMode = mode; - return doRouting(); + return NO_ERROR; } -status_t AudioHardwareBase::getMode(int* mode) +// default implementation +status_t AudioHardwareBase::setParameters(const String8& keyValuePairs) { - // Implement: set audio routing - *mode = mMode; return NO_ERROR; } -status_t AudioHardwareBase::setParameter(const char* key, const char* value) +// default implementation +String8 AudioHardwareBase::getParameters(const String8& keys) { - // default implementation is to ignore - return NO_ERROR; + String8 result = String8(""); + return result; } - // default implementation size_t AudioHardwareBase::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) { @@ -233,10 +172,6 @@ status_t AudioHardwareBase::dumpState(int fd, const Vector<String16>& args) result.append(buffer); snprintf(buffer, SIZE, "\tmMode: %d\n", mMode); result.append(buffer); - for (int i = 0, n = AudioSystem::NUM_MODES; i < n; ++i) { - snprintf(buffer, SIZE, "\tmRoutes[%d]: %d\n", i, mRoutes[i]); - result.append(buffer); - } ::write(fd, result.string(), result.size()); dump(fd, args); // Dump the state of the concrete child. return NO_ERROR; diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp index 0ab4c60..1a03059 100644 --- a/libs/audioflinger/AudioHardwareStub.cpp +++ b/libs/audioflinger/AudioHardwareStub.cpp @@ -43,10 +43,10 @@ status_t AudioHardwareStub::initCheck() } AudioStreamOut* AudioHardwareStub::openOutputStream( - int format, int channelCount, uint32_t sampleRate, status_t *status) + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status) { AudioStreamOutStub* out = new AudioStreamOutStub(); - status_t lStatus = out->set(format, channelCount, sampleRate); + status_t lStatus = out->set(format, channels, sampleRate); if (status) { *status = lStatus; } @@ -56,18 +56,22 @@ AudioStreamOut* AudioHardwareStub::openOutputStream( return 0; } +void AudioHardwareStub::closeOutputStream(AudioStreamOut* out) +{ + delete out; +} + AudioStreamIn* AudioHardwareStub::openInputStream( - int inputSource, int format, int channelCount, uint32_t sampleRate, + uint32_t devices, int *format, uint32_t *channels, uint32_t *sampleRate, status_t *status, AudioSystem::audio_in_acoustics acoustics) { // check for valid input source - if ((inputSource < AudioRecord::DEFAULT_INPUT) || - (inputSource >= AudioRecord::NUM_INPUT_SOURCES)) { + if (!AudioSystem::isInputDevice((AudioSystem::audio_devices)devices)) { return 0; } AudioStreamInStub* in = new AudioStreamInStub(); - status_t lStatus = in->set(format, channelCount, sampleRate, acoustics); + status_t lStatus = in->set(format, channels, sampleRate, acoustics); if (status) { *status = lStatus; } @@ -77,6 +81,11 @@ AudioStreamIn* AudioHardwareStub::openInputStream( return 0; } +void AudioHardwareStub::closeInputStream(AudioStreamIn* in) +{ + delete in; +} + status_t AudioHardwareStub::setVoiceVolume(float volume) { return NO_ERROR; @@ -107,24 +116,19 @@ status_t AudioHardwareStub::dump(int fd, const Vector<String16>& args) // ---------------------------------------------------------------------------- -status_t AudioStreamOutStub::set(int format, int channels, uint32_t rate) +status_t AudioStreamOutStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate) { - // fix up defaults - if (format == 0) format = AudioSystem::PCM_16_BIT; - if (channels == 0) channels = channelCount(); - if (rate == 0) rate = sampleRate(); + if (pFormat) *pFormat = format(); + if (pChannels) *pChannels = channels(); + if (pRate) *pRate = sampleRate(); - if ((format == AudioSystem::PCM_16_BIT) && - (channels == channelCount()) && - (rate == sampleRate())) - return NO_ERROR; - return BAD_VALUE; + return NO_ERROR; } ssize_t AudioStreamOutStub::write(const void* buffer, size_t bytes) { // fake timing for audio output - usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate()); + usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate()); return bytes; } @@ -141,7 +145,7 @@ status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args) snprintf(buffer, SIZE, "AudioStreamOutStub::dump\n"); snprintf(buffer, SIZE, "\tsample rate: %d\n", sampleRate()); snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); - snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); snprintf(buffer, SIZE, "\tformat: %d\n", format()); result.append(buffer); ::write(fd, result.string(), result.size()); @@ -150,20 +154,16 @@ status_t AudioStreamOutStub::dump(int fd, const Vector<String16>& args) // ---------------------------------------------------------------------------- -status_t AudioStreamInStub::set(int format, int channels, uint32_t rate, +status_t AudioStreamInStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics) { - if ((format == AudioSystem::PCM_16_BIT) && - (channels == channelCount()) && - (rate == sampleRate())) - return NO_ERROR; - return BAD_VALUE; + return NO_ERROR; } ssize_t AudioStreamInStub::read(void* buffer, ssize_t bytes) { // fake timing for audio input - usleep(bytes * 1000000 / sizeof(int16_t) / channelCount() / sampleRate()); + usleep(bytes * 1000000 / sizeof(int16_t) / AudioSystem::popCount(channels()) / sampleRate()); memset(buffer, 0, bytes); return bytes; } @@ -179,7 +179,7 @@ status_t AudioStreamInStub::dump(int fd, const Vector<String16>& args) result.append(buffer); snprintf(buffer, SIZE, "\tbuffer size: %d\n", bufferSize()); result.append(buffer); - snprintf(buffer, SIZE, "\tchannel count: %d\n", channelCount()); + snprintf(buffer, SIZE, "\tchannels: %d\n", channels()); result.append(buffer); snprintf(buffer, SIZE, "\tformat: %d\n", format()); result.append(buffer); diff --git a/libs/audioflinger/AudioHardwareStub.h b/libs/audioflinger/AudioHardwareStub.h index bf63cc5..8f43259 100644 --- a/libs/audioflinger/AudioHardwareStub.h +++ b/libs/audioflinger/AudioHardwareStub.h @@ -29,29 +29,33 @@ namespace android { class AudioStreamOutStub : public AudioStreamOut { public: - virtual status_t set(int format, int channelCount, uint32_t sampleRate); + virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate); virtual uint32_t sampleRate() const { return 44100; } virtual size_t bufferSize() const { return 4096; } - virtual int channelCount() const { return 2; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_OUT_STEREO; } virtual int format() const { return AudioSystem::PCM_16_BIT; } virtual uint32_t latency() const { return 0; } - virtual status_t setVolume(float volume) { return NO_ERROR; } + virtual status_t setVolume(float left, float right) { return NO_ERROR; } virtual ssize_t write(const void* buffer, size_t bytes); virtual status_t standby(); virtual status_t dump(int fd, const Vector<String16>& args); + virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;} + virtual String8 getParameters(const String8& keys) {String8 result = String8(""); return result;} }; class AudioStreamInStub : public AudioStreamIn { public: - virtual status_t set(int format, int channelCount, uint32_t sampleRate, AudioSystem::audio_in_acoustics acoustics); + virtual status_t set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, AudioSystem::audio_in_acoustics acoustics); virtual uint32_t sampleRate() const { return 8000; } virtual size_t bufferSize() const { return 320; } - virtual int channelCount() const { return 1; } + virtual uint32_t channels() const { return AudioSystem::CHANNEL_IN_MONO; } virtual int format() const { return AudioSystem::PCM_16_BIT; } virtual status_t setGain(float gain) { return NO_ERROR; } virtual ssize_t read(void* buffer, ssize_t bytes); virtual status_t dump(int fd, const Vector<String16>& args); virtual status_t standby() { return NO_ERROR; } + virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR;} + virtual String8 getParameters(const String8& keys) {String8 result = String8(""); return result;} }; class AudioHardwareStub : public AudioHardwareBase @@ -67,26 +71,25 @@ public: virtual status_t setMicMute(bool state) { mMicMute = state; return NO_ERROR; } virtual status_t getMicMute(bool* state) { *state = mMicMute ; return NO_ERROR; } - virtual status_t setParameter(const char* key, const char* value) - { return NO_ERROR; } - // create I/O streams virtual AudioStreamOut* openOutputStream( - int format=0, - int channelCount=0, - uint32_t sampleRate=0, + uint32_t devices, + int *format=0, + uint32_t *channels=0, + uint32_t *sampleRate=0, status_t *status=0); + virtual void closeOutputStream(AudioStreamOut* out); virtual AudioStreamIn* openInputStream( - int inputSource, - int format, - int channelCount, - uint32_t sampleRate, + uint32_t devices, + int *format, + uint32_t *channels, + uint32_t *sampleRate, status_t *status, - AudioSystem::audio_in_acoustics acoustics); + AudioSystem::audio_in_acoustics acoustics); + virtual void closeInputStream(AudioStreamIn* in); protected: - virtual status_t doRouting() { return NO_ERROR; } virtual status_t dump(int fd, const Vector<String16>& args); bool mMicMute; diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp index b02efcc..19a442a 100644 --- a/libs/audioflinger/AudioMixer.cpp +++ b/libs/audioflinger/AudioMixer.cpp @@ -610,7 +610,6 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, t->in = in; } -inline void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c) { for (size_t i=0 ; i<c ; i++) { diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h index 72ca28a..15766cd 100644 --- a/libs/audioflinger/AudioMixer.h +++ b/libs/audioflinger/AudioMixer.h @@ -85,6 +85,8 @@ public: uint32_t trackNames() const { return mTrackNames; } + static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c); + private: enum { @@ -176,7 +178,6 @@ private: static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp); static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); - static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c); static void process__validate(state_t* state, void* output); static void process__nop(state_t* state, void* output); diff --git a/libs/audioflinger/AudioPolicyManagerGeneric.cpp b/libs/audioflinger/AudioPolicyManagerGeneric.cpp new file mode 100644 index 0000000..4b31815 --- /dev/null +++ b/libs/audioflinger/AudioPolicyManagerGeneric.cpp @@ -0,0 +1,753 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioPolicyManagerGeneric" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> +#include "AudioPolicyManagerGeneric.h" +#include <media/mediarecorder.h> + +namespace android { + + +// ---------------------------------------------------------------------------- +// AudioPolicyInterface implementation +// ---------------------------------------------------------------------------- + + +status_t AudioPolicyManagerGeneric::setDeviceConnectionState(AudioSystem::audio_devices device, + AudioSystem::device_connection_state state, + const char *device_address) +{ + + LOGV("setDeviceConnectionState() device: %x, state %d, address %s", device, state, device_address); + + // connect/disconnect only 1 device at a time + if (AudioSystem::popCount(device) != 1) return BAD_VALUE; + + if (strlen(device_address) >= MAX_DEVICE_ADDRESS_LEN) { + LOGE("setDeviceConnectionState() invalid address: %s", device_address); + return BAD_VALUE; + } + + // handle output devices + if (AudioSystem::isOutputDevice(device)) { + switch (state) + { + // handle output device connection + case AudioSystem::DEVICE_STATE_AVAILABLE: + if (mAvailableOutputDevices & device) { + LOGW("setDeviceConnectionState() device already connected: %x", device); + return INVALID_OPERATION; + } + LOGV("setDeviceConnectionState() connecting device %x", device); + + // register new device as available + mAvailableOutputDevices |= device; + break; + // handle output device disconnection + case AudioSystem::DEVICE_STATE_UNAVAILABLE: + if (!(mAvailableOutputDevices & device)) { + LOGW("setDeviceConnectionState() device not connected: %x", device); + return INVALID_OPERATION; + } + LOGV("setDeviceConnectionState() disconnecting device %x", device); + // remove device from available output devices + mAvailableOutputDevices &= ~device; + break; + + default: + LOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + return NO_ERROR; + } + // handle input devices + if (AudioSystem::isInputDevice(device)) { + switch (state) + { + // handle input device connection + case AudioSystem::DEVICE_STATE_AVAILABLE: + if (mAvailableInputDevices & device) { + LOGW("setDeviceConnectionState() device already connected: %d", device); + return INVALID_OPERATION; + } + mAvailableInputDevices |= device; + break; + + // handle input device disconnection + case AudioSystem::DEVICE_STATE_UNAVAILABLE: + if (!(mAvailableInputDevices & device)) { + LOGW("setDeviceConnectionState() device not connected: %d", device); + return INVALID_OPERATION; + } + mAvailableInputDevices &= ~device; + break; + + default: + LOGE("setDeviceConnectionState() invalid state: %x", state); + return BAD_VALUE; + } + return NO_ERROR; + } + + LOGW("setDeviceConnectionState() invalid device: %x", device); + return BAD_VALUE; +} + +AudioSystem::device_connection_state AudioPolicyManagerGeneric::getDeviceConnectionState(AudioSystem::audio_devices device, + const char *device_address) +{ + AudioSystem::device_connection_state state = AudioSystem::DEVICE_STATE_UNAVAILABLE; + String8 address = String8(device_address); + if (AudioSystem::isOutputDevice(device)) { + if (device & mAvailableOutputDevices) { + state = AudioSystem::DEVICE_STATE_AVAILABLE; + } + } else if (AudioSystem::isInputDevice(device)) { + if (device & mAvailableInputDevices) { + state = AudioSystem::DEVICE_STATE_AVAILABLE; + } + } + + return state; +} + +void AudioPolicyManagerGeneric::setPhoneState(int state) +{ + LOGV("setPhoneState() state %d", state); + uint32_t newDevice = 0; + if (state < 0 || state >= AudioSystem::NUM_MODES) { + LOGW("setPhoneState() invalid state %d", state); + return; + } + + if (state == mPhoneState ) { + LOGW("setPhoneState() setting same state %d", state); + return; + } + // store previous phone state for management of sonification strategy below + int oldState = mPhoneState; + mPhoneState = state; + + // if leaving or entering in call state, handle special case of active streams + // pertaining to sonification strategy see handleIncallSonification() + if (state == AudioSystem::MODE_IN_CALL || + oldState == AudioSystem::MODE_IN_CALL) { + bool starting = (state == AudioSystem::MODE_IN_CALL) ? true : false; + LOGV("setPhoneState() in call state management: new state is %d", state); + for (int stream = 0; stream < AudioSystem::NUM_STREAM_TYPES; stream++) { + handleIncallSonification(stream, starting); + } + } +} + +void AudioPolicyManagerGeneric::setRingerMode(uint32_t mode, uint32_t mask) +{ + LOGV("setRingerMode() mode %x, mask %x", mode, mask); + + mRingerMode = mode; +} + +void AudioPolicyManagerGeneric::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) +{ + LOGV("setForceUse) usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); + mForceUse[usage] = config; +} + +AudioSystem::forced_config AudioPolicyManagerGeneric::getForceUse(AudioSystem::force_use usage) +{ + return mForceUse[usage]; +} + +void AudioPolicyManagerGeneric::setSystemProperty(const char* property, const char* value) +{ + LOGV("setSystemProperty() property %s, value %s", property, value); + if (strcmp(property, "ro.camera.sound.forced") == 0) { + if (atoi(value)) { + LOGV("ENFORCED_AUDIBLE cannot be muted"); + mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = false; + } else { + LOGV("ENFORCED_AUDIBLE can be muted"); + mStreams[AudioSystem::ENFORCED_AUDIBLE].mCanBeMuted = true; + } + } +} + +audio_io_handle_t AudioPolicyManagerGeneric::getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::output_flags flags) +{ + LOGV("getOutput() stream %d, samplingRate %d, format %d, channels %x, flags %x", stream, samplingRate, format, channels, flags); + +#ifdef AUDIO_POLICY_TEST + if (mCurOutput != 0) { + LOGV("getOutput() test output mCurOutput %d, samplingRate %d, format %d, channelcount %d, mDirectOutput %d", + mCurOutput, mTestSamplingRate, mTestFormat, mTestChannelcount, mDirectOutput); + + if (mTestOutputs[mCurOutput] == 0) { + LOGV("getOutput() opening test output"); + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); + outputDesc->mDevice = mTestDevice; + outputDesc->mSamplingRate = mTestSamplingRate; + outputDesc->mFormat = mTestFormat; + outputDesc->mChannels = (mTestChannelcount == 1) ? AudioSystem::CHANNEL_OUT_MONO : AudioSystem::CHANNEL_OUT_STEREO; + outputDesc->mLatency = mTestLatencyMs; + outputDesc->mFlags = (AudioSystem::output_flags)(mDirectOutput ? AudioSystem::OUTPUT_FLAG_DIRECT : 0); + outputDesc->mRefCount[stream] = 0; + mTestOutputs[mCurOutput] = mpClientInterface->openOutput(&outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannels, + &outputDesc->mLatency, + outputDesc->mFlags); + mOutputs.add(mTestOutputs[mCurOutput], outputDesc); + } + return mTestOutputs[mCurOutput]; + } +#endif //AUDIO_POLICY_TEST + if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) || + (format != 0 && !AudioSystem::isLinearPCM(format)) || + (channels != 0 && channels != AudioSystem::CHANNEL_OUT_MONO && channels != AudioSystem::CHANNEL_OUT_STEREO)) { + return NULL; + } + + return mHardwareOutput; +} + +status_t AudioPolicyManagerGeneric::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream) +{ + LOGV("startOutput() output %p, stream %d", output, stream); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + LOGW("startOutput() unknow output %p", output); + return BAD_VALUE; + } + + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + + // handle special case for sonification while in call + if (mPhoneState == AudioSystem::MODE_IN_CALL) { + handleIncallSonification(stream, true); + } + + // incremenent usage count for this stream on the requested output: + outputDesc->changeRefCount(stream, 1); + return NO_ERROR; +} + +status_t AudioPolicyManagerGeneric::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream) +{ + LOGV("stopOutput() output %p, stream %d", output, stream); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + LOGW("stopOutput() unknow output %p", output); + return BAD_VALUE; + } + + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + + // handle special case for sonification while in call + if (mPhoneState == AudioSystem::MODE_IN_CALL) { + handleIncallSonification(stream, false); + } + + if (outputDesc->isUsedByStream(stream)) { + // decrement usage count of this stream on the output + outputDesc->changeRefCount(stream, -1); + return NO_ERROR; + } else { + LOGW("stopOutput() refcount is already 0 for output %p", output); + return INVALID_OPERATION; + } +} + +void AudioPolicyManagerGeneric::releaseOutput(audio_io_handle_t output) +{ + LOGV("releaseOutput() %p", output); + ssize_t index = mOutputs.indexOfKey(output); + if (index < 0) { + LOGW("releaseOutput() releasing unknown output %p", output); + return; + } + +#ifdef AUDIO_POLICY_TEST + int testIndex = testOutputIndex(output); + if (testIndex != 0) { + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index); + if (outputDesc->refCount() == 0) { + mpClientInterface->closeOutput(output); + delete mOutputs.valueAt(index); + mOutputs.removeItem(output); + mTestOutputs[testIndex] = 0; + } + } +#endif //AUDIO_POLICY_TEST +} + +audio_io_handle_t AudioPolicyManagerGeneric::getInput(int inputSource, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::audio_in_acoustics acoustics) +{ + audio_io_handle_t input = 0; + uint32_t device; + + LOGV("getInput() inputSource %d, samplingRate %d, format %d, channels %x, acoustics %x", inputSource, samplingRate, format, channels, acoustics); + + AudioInputDescriptor *inputDesc = new AudioInputDescriptor(); + inputDesc->mDevice = AudioSystem::DEVICE_IN_BUILTIN_MIC; + inputDesc->mSamplingRate = samplingRate; + inputDesc->mFormat = format; + inputDesc->mChannels = channels; + inputDesc->mAcoustics = acoustics; + inputDesc->mRefCount = 0; + input = mpClientInterface->openInput(&inputDesc->mDevice, + &inputDesc->mSamplingRate, + &inputDesc->mFormat, + &inputDesc->mChannels, + inputDesc->mAcoustics); + + // only accept input with the exact requested set of parameters + if ((samplingRate != inputDesc->mSamplingRate) || + (format != inputDesc->mFormat) || + (channels != inputDesc->mChannels)) { + LOGV("getOutput() failed opening input: samplingRate %d, format %d, channels %d", + samplingRate, format, channels); + mpClientInterface->closeInput(input); + delete inputDesc; + return NULL; + } + mInputs.add(input, inputDesc); + return input; +} + +status_t AudioPolicyManagerGeneric::startInput(audio_io_handle_t input) +{ + LOGV("startInput() input %p", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + LOGW("startInput() unknow input %p", input); + return BAD_VALUE; + } + AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + +#ifdef AUDIO_POLICY_TEST + if (mTestInput == 0) +#endif //AUDIO_POLICY_TEST + { + // refuse 2 active AudioRecord clients at the same time + for (size_t i = 0; i < mInputs.size(); i++) { + if (mInputs.valueAt(i)->mRefCount > 0) { + LOGW("startInput() input %p, other input %p already started", input, mInputs.keyAt(i)); + return INVALID_OPERATION; + } + } + } + + inputDesc->mRefCount = 1; + return NO_ERROR; +} + +status_t AudioPolicyManagerGeneric::stopInput(audio_io_handle_t input) +{ + LOGV("stopInput() input %p", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + LOGW("stopInput() unknow input %p", input); + return BAD_VALUE; + } + AudioInputDescriptor *inputDesc = mInputs.valueAt(index); + + if (inputDesc->mRefCount == 0) { + LOGW("stopInput() input %p already stopped", input); + return INVALID_OPERATION; + } else { + inputDesc->mRefCount = 0; + return NO_ERROR; + } +} + +void AudioPolicyManagerGeneric::releaseInput(audio_io_handle_t input) +{ + LOGV("releaseInput() %p", input); + ssize_t index = mInputs.indexOfKey(input); + if (index < 0) { + LOGW("releaseInput() releasing unknown input %p", input); + return; + } + mpClientInterface->closeInput(input); + delete mInputs.valueAt(index); + mInputs.removeItem(input); +} + + + +void AudioPolicyManagerGeneric::initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax) +{ + LOGV("initStreamVolume() stream %d, min %d, max %d", stream , indexMin, indexMax); + mStreams[stream].mIndexMin = indexMin; + mStreams[stream].mIndexMax = indexMax; +} + +status_t AudioPolicyManagerGeneric::setStreamVolumeIndex(AudioSystem::stream_type stream, int index) +{ + + if ((index < mStreams[stream].mIndexMin) || (index > mStreams[stream].mIndexMax)) { + return BAD_VALUE; + } + + LOGV("setStreamVolumeIndex() stream %d, index %d", stream, index); + mStreams[stream].mIndexCur = index; + + // do not change actual stream volume if the stream is muted + if (mStreams[stream].mMuteCount != 0) { + return NO_ERROR; + } + + // Do not changed in call volume if bluetooth is connected and vice versa + if ((stream == AudioSystem::VOICE_CALL && mForceUse[AudioSystem::FOR_COMMUNICATION] == AudioSystem::FORCE_BT_SCO) || + (stream == AudioSystem::BLUETOOTH_SCO && mForceUse[AudioSystem::FOR_COMMUNICATION] != AudioSystem::FORCE_BT_SCO)) { + LOGV("setStreamVolumeIndex() cannot set stream %d volume with force use = %d for comm", + stream, mForceUse[AudioSystem::FOR_COMMUNICATION]); + return INVALID_OPERATION; + } + + // compute and apply stream volume on all outputs according to connected device + for (size_t i = 0; i < mOutputs.size(); i++) { + AudioOutputDescriptor *outputDesc = mOutputs.valueAt(i); + uint32_t device = outputDesc->device(); + + float volume = computeVolume((int)stream, index, device); + + LOGV("setStreamVolume() for output %p stream %d, volume %f", mOutputs.keyAt(i), stream, volume); + mpClientInterface->setStreamVolume(stream, volume, mOutputs.keyAt(i)); + } + return NO_ERROR; +} + +status_t AudioPolicyManagerGeneric::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index) +{ + if (index == 0) { + return BAD_VALUE; + } + LOGV("getStreamVolumeIndex() stream %d", stream); + *index = mStreams[stream].mIndexCur; + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- +// AudioPolicyManagerGeneric +// ---------------------------------------------------------------------------- + +// --- class factory + +AudioPolicyManagerGeneric::AudioPolicyManagerGeneric(AudioPolicyClientInterface *clientInterface) + : +#ifdef AUDIO_POLICY_TEST + Thread(false), +#endif //AUDIO_POLICY_TEST + mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0) +{ + mpClientInterface = clientInterface; + + for (int i = 0; i < AudioSystem::NUM_FORCE_USE; i++) { + mForceUse[i] = AudioSystem::FORCE_NONE; + } + + // devices available by default are speaker, ear piece and microphone + mAvailableOutputDevices = AudioSystem::DEVICE_OUT_SPEAKER; + mAvailableInputDevices = AudioSystem::DEVICE_IN_BUILTIN_MIC; + + // open hardware output + AudioOutputDescriptor *outputDesc = new AudioOutputDescriptor(); + outputDesc->mDevice = (uint32_t)AudioSystem::DEVICE_OUT_SPEAKER; + mHardwareOutput = mpClientInterface->openOutput(&outputDesc->mDevice, + &outputDesc->mSamplingRate, + &outputDesc->mFormat, + &outputDesc->mChannels, + &outputDesc->mLatency, + outputDesc->mFlags); + + if (mHardwareOutput == 0) { + LOGE("Failed to initialize hardware output stream, samplingRate: %d, format %d, channels %d", + outputDesc->mSamplingRate, outputDesc->mFormat, outputDesc->mChannels); + } else { + mOutputs.add(mHardwareOutput, outputDesc); + } + +#ifdef AUDIO_POLICY_TEST + mTestDevice = AudioSystem::DEVICE_OUT_SPEAKER; + mTestSamplingRate = 44100; + mTestFormat = AudioSystem::PCM_16_BIT; + mTestChannelcount = 2; + mTestLatencyMs = 0; + mCurOutput = 0; + mDirectOutput = false; + for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { + mTestOutputs[i] = 0; + } + + const size_t SIZE = 256; + char buffer[SIZE]; + snprintf(buffer, SIZE, "AudioPolicyManagerTest"); + run(buffer, ANDROID_PRIORITY_AUDIO); +#endif //AUDIO_POLICY_TEST +} + +AudioPolicyManagerGeneric::~AudioPolicyManagerGeneric() +{ +#ifdef AUDIO_POLICY_TEST + exit(); +#endif //AUDIO_POLICY_TEST + + for (size_t i = 0; i < mOutputs.size(); i++) { + mpClientInterface->closeOutput(mOutputs.keyAt(i)); + delete mOutputs.valueAt(i); + } + mOutputs.clear(); + for (size_t i = 0; i < mInputs.size(); i++) { + mpClientInterface->closeInput(mInputs.keyAt(i)); + delete mInputs.valueAt(i); + } + mInputs.clear(); +} + +#ifdef AUDIO_POLICY_TEST +bool AudioPolicyManagerGeneric::threadLoop() +{ + LOGV("entering threadLoop()"); + while (!exitPending()) + { + Mutex::Autolock _l(mLock); + mWaitWorkCV.waitRelative(mLock, milliseconds(50)); + String8 command; + command = mpClientInterface->getParameters(0, String8("test_cmd_policy")); + if (command != "") { + LOGV("Test command %s received", command.string()); + AudioParameter param = AudioParameter(command); + int valueInt; + String8 value; + if (param.getInt(String8("test_cmd_policy_output"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_output")); + mCurOutput = valueInt; + } + if (param.get(String8("test_cmd_policy_direct"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_direct")); + if (value == "false") { + mDirectOutput = false; + } else if (value == "true") { + mDirectOutput = true; + } + } + if (param.getInt(String8("test_cmd_policy_input"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_input")); + mTestInput = valueInt; + } + + if (param.get(String8("test_cmd_policy_format"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_format")); + if (value == "PCM 16 bits") { + mTestFormat = AudioSystem::PCM_16_BIT; + } else if (value == "PCM 8 bits") { + mTestFormat = AudioSystem::PCM_8_BIT; + } else if (value == "Compressed MP3") { + mTestFormat = AudioSystem::MP3; + } + } + if (param.get(String8("test_cmd_policy_channels"), value) == NO_ERROR) { + param.remove(String8("test_cmd_policy_channels")); + if (value == "Channels Stereo") { + mTestChannelcount = 2; + } else if (value == "Channels Mono") { + mTestChannelcount = 1; + } + } + if (param.getInt(String8("test_cmd_policy_sampleRate"), valueInt) == NO_ERROR) { + param.remove(String8("test_cmd_policy_sampleRate")); + if (valueInt >= 0 && valueInt <= 96000) { + mTestSamplingRate = valueInt; + } + } + mpClientInterface->setParameters(0, String8("test_cmd_policy=")); + } + } + return false; +} + +void AudioPolicyManagerGeneric::exit() +{ + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +int AudioPolicyManagerGeneric::testOutputIndex(audio_io_handle_t output) +{ + for (int i = 0; i < NUM_TEST_OUTPUTS; i++) { + if (output == mTestOutputs[i]) return i; + } + return 0; +} +#endif //AUDIO_POLICY_TEST + +// --- + +AudioPolicyManagerGeneric::routing_strategy AudioPolicyManagerGeneric::getStrategy(AudioSystem::stream_type stream) +{ + // stream to strategy mapping + switch (stream) { + case AudioSystem::VOICE_CALL: + case AudioSystem::BLUETOOTH_SCO: + return STRATEGY_PHONE; + case AudioSystem::RING: + case AudioSystem::NOTIFICATION: + case AudioSystem::ALARM: + case AudioSystem::ENFORCED_AUDIBLE: + return STRATEGY_SONIFICATION; + case AudioSystem::DTMF: + return STRATEGY_DTMF; + default: + LOGE("unknown stream type"); + case AudioSystem::SYSTEM: + // NOTE: SYSTEM stream uses MEDIA strategy because muting music and switching outputs + // while key clicks are played produces a poor result + case AudioSystem::TTS: + case AudioSystem::MUSIC: + return STRATEGY_MEDIA; + } +} + + +float AudioPolicyManagerGeneric::computeVolume(int stream, int index, uint32_t device) +{ + float volume = 1.0; + + StreamDescriptor &streamDesc = mStreams[stream]; + + // Force max volume if stream cannot be muted + if (!streamDesc.mCanBeMuted) index = streamDesc.mIndexMax; + + int volInt = (100 * (index - streamDesc.mIndexMin)) / (streamDesc.mIndexMax - streamDesc.mIndexMin); + volume = AudioSystem::linearToLog(volInt); + + return volume; +} + +void AudioPolicyManagerGeneric::setStreamMute(int stream, bool on, audio_io_handle_t output) +{ + LOGV("setStreamMute() stream %d, mute %d, output %p", stream, on, output); + + StreamDescriptor &streamDesc = mStreams[stream]; + + if (on) { + if (streamDesc.mMuteCount++ == 0) { + if (streamDesc.mCanBeMuted) { + mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, 0, output); + } + } + } else { + if (streamDesc.mMuteCount == 0) { + LOGW("setStreamMute() unmuting non muted stream!"); + return; + } + if (--streamDesc.mMuteCount == 0) { + uint32_t device = mOutputs.valueFor(output)->mDevice; + float volume = computeVolume(stream, streamDesc.mIndexCur, device); + mpClientInterface->setStreamVolume((AudioSystem::stream_type)stream, volume, output); + } + } +} + +void AudioPolicyManagerGeneric::handleIncallSonification(int stream, bool starting) +{ + // if the stream pertains to sonification strategy and we are in call we must + // mute the stream if it is low visibility. If it is high visibility, we must play a tone + // in the device used for phone strategy and play the tone if the selected device does not + // interfere with the device used for phone strategy + if (getStrategy((AudioSystem::stream_type)stream) == STRATEGY_SONIFICATION) { + AudioOutputDescriptor *outputDesc = mOutputs.valueFor(mHardwareOutput); + LOGV("handleIncallSonification() stream %d starting %d device %x", stream, starting, outputDesc->mDevice); + if (outputDesc->isUsedByStream((AudioSystem::stream_type)stream)) { + if (AudioSystem::isLowVisibility((AudioSystem::stream_type)stream)) { + LOGV("handleIncallSonification() low visibility"); + setStreamMute(stream, starting, mHardwareOutput); + } else { + if (starting) { + mpClientInterface->startTone(ToneGenerator::TONE_SUP_CALL_WAITING, AudioSystem::VOICE_CALL); + } else { + mpClientInterface->stopTone(); + } + } + } + } +} + + +// --- AudioOutputDescriptor class implementation + +AudioPolicyManagerGeneric::AudioOutputDescriptor::AudioOutputDescriptor() + : mSamplingRate(0), mFormat(0), mChannels(0), mLatency(0), + mFlags((AudioSystem::output_flags)0), mDevice(0) +{ + // clear usage count for all stream types + for (int i = 0; i < AudioSystem::NUM_STREAM_TYPES; i++) { + mRefCount[i] = 0; + } +} + +uint32_t AudioPolicyManagerGeneric::AudioOutputDescriptor::device() +{ + return mDevice; +} + +void AudioPolicyManagerGeneric::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta) +{ + if ((delta + (int)mRefCount[stream]) < 0) { + LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]); + mRefCount[stream] = 0; + return; + } + mRefCount[stream] += delta; + LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]); +} + +uint32_t AudioPolicyManagerGeneric::AudioOutputDescriptor::refCount() +{ + uint32_t refcount = 0; + for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) { + refcount += mRefCount[i]; + } + return refcount; +} + +// --- AudioInputDescriptor class implementation + +AudioPolicyManagerGeneric::AudioInputDescriptor::AudioInputDescriptor() + : mSamplingRate(0), mFormat(0), mChannels(0), + mAcoustics((AudioSystem::audio_in_acoustics)0), mDevice(0), mRefCount(0) +{ +} + +}; // namespace android diff --git a/libs/audioflinger/AudioPolicyManagerGeneric.h b/libs/audioflinger/AudioPolicyManagerGeneric.h new file mode 100644 index 0000000..ddcb306 --- /dev/null +++ b/libs/audioflinger/AudioPolicyManagerGeneric.h @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <hardware_legacy/AudioPolicyInterface.h> +#include <utils/threads.h> + + +namespace android { + +// ---------------------------------------------------------------------------- + +#define MAX_DEVICE_ADDRESS_LEN 20 +#define NUM_TEST_OUTPUTS 5 + +class AudioPolicyManagerGeneric: public AudioPolicyInterface +#ifdef AUDIO_POLICY_TEST + , public Thread +#endif //AUDIO_POLICY_TEST +{ + +public: + AudioPolicyManagerGeneric(AudioPolicyClientInterface *clientInterface); + virtual ~AudioPolicyManagerGeneric(); + + // AudioPolicyInterface + virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device, + AudioSystem::device_connection_state state, + const char *device_address); + virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device, + const char *device_address); + virtual void setPhoneState(int state); + virtual void setRingerMode(uint32_t mode, uint32_t mask); + virtual void setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config); + virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage); + virtual void setSystemProperty(const char* property, const char* value); + virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::output_flags flags); + virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream); + virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream); + virtual void releaseOutput(audio_io_handle_t output); + virtual audio_io_handle_t getInput(int inputSource, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::audio_in_acoustics acoustics); + // indicates to the audio policy manager that the input starts being used. + virtual status_t startInput(audio_io_handle_t input); + // indicates to the audio policy manager that the input stops being used. + virtual status_t stopInput(audio_io_handle_t input); + virtual void releaseInput(audio_io_handle_t input); + virtual void initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax); + virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index); + virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index); + +private: + + enum routing_strategy { + STRATEGY_MEDIA, + STRATEGY_PHONE, + STRATEGY_SONIFICATION, + STRATEGY_DTMF, + NUM_STRATEGIES + }; + + // descriptor for audio outputs. Used to maintain current configuration of each opened audio output + // and keep track of the usage of this output by each audio stream type. + class AudioOutputDescriptor + { + public: + AudioOutputDescriptor(); + + + uint32_t device(); + void changeRefCount(AudioSystem::stream_type, int delta); + bool isUsedByStream(AudioSystem::stream_type stream) { return mRefCount[stream] > 0 ? true : false; } + uint32_t refCount(); + + uint32_t mSamplingRate; // + uint32_t mFormat; // + uint32_t mChannels; // output configuration + uint32_t mLatency; // + AudioSystem::output_flags mFlags; // + uint32_t mDevice; // current device this output is routed to + uint32_t mRefCount[AudioSystem::NUM_STREAM_TYPES]; // number of streams of each type using this output + }; + + // descriptor for audio inputs. Used to maintain current configuration of each opened audio input + // and keep track of the usage of this input. + class AudioInputDescriptor + { + public: + AudioInputDescriptor(); + + uint32_t mSamplingRate; // + uint32_t mFormat; // input configuration + uint32_t mChannels; // + AudioSystem::audio_in_acoustics mAcoustics; // + uint32_t mDevice; // current device this input is routed to + uint32_t mRefCount; // number of AudioRecord clients using this output + }; + + // stream descriptor used for volume control + class StreamDescriptor + { + public: + StreamDescriptor() + : mIndexMin(0), mIndexMax(1), mIndexCur(1), mMuteCount(0), mCanBeMuted(true) {} + + int mIndexMin; // min volume index + int mIndexMax; // max volume index + int mIndexCur; // current volume index + int mMuteCount; // mute request counter + bool mCanBeMuted; // true is the stream can be muted + }; + + // return the strategy corresponding to a given stream type + static routing_strategy getStrategy(AudioSystem::stream_type stream); + // return the output handle of an output routed to the specified device, 0 if no output + // is routed to the device + float computeVolume(int stream, int index, uint32_t device); + // Mute or unmute the stream on the specified output + void setStreamMute(int stream, bool on, audio_io_handle_t output); + // handle special cases for sonification strategy while in call: mute streams or replace by + // a special tone in the device used for communication + void handleIncallSonification(int stream, bool starting); + + +#ifdef AUDIO_POLICY_TEST + virtual bool threadLoop(); + void exit(); + int testOutputIndex(audio_io_handle_t output); +#endif //AUDIO_POLICY_TEST + + + AudioPolicyClientInterface *mpClientInterface; // audio policy client interface + audio_io_handle_t mHardwareOutput; // hardware output handler + + KeyedVector<audio_io_handle_t, AudioOutputDescriptor *> mOutputs; // list ot output descritors + KeyedVector<audio_io_handle_t, AudioInputDescriptor *> mInputs; // list of input descriptors + uint32_t mAvailableOutputDevices; // bit field of all available output devices + uint32_t mAvailableInputDevices; // bit field of all available input devices + int mPhoneState; // current phone state + uint32_t mRingerMode; // current ringer mode + AudioSystem::forced_config mForceUse[AudioSystem::NUM_FORCE_USE]; // current forced use configuration + + StreamDescriptor mStreams[AudioSystem::NUM_STREAM_TYPES]; // stream descriptors for volume control + +#ifdef AUDIO_POLICY_TEST + Mutex mLock; + Condition mWaitWorkCV; + + int mCurOutput; + bool mDirectOutput; + audio_io_handle_t mTestOutputs[NUM_TEST_OUTPUTS]; + int mTestInput; + uint32_t mTestDevice; + uint32_t mTestSamplingRate; + uint32_t mTestFormat; + uint32_t mTestChannelcount; + uint32_t mTestLatencyMs; +#endif //AUDIO_POLICY_TEST + +}; + +}; diff --git a/libs/audioflinger/AudioPolicyService.cpp b/libs/audioflinger/AudioPolicyService.cpp new file mode 100644 index 0000000..7f6c4ed --- /dev/null +++ b/libs/audioflinger/AudioPolicyService.cpp @@ -0,0 +1,650 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "AudioPolicyService" +//#define LOG_NDEBUG 0 +#include <binder/IServiceManager.h> +#include <utils/Log.h> +#include <cutils/properties.h> +#include <binder/IPCThreadState.h> +#include <utils/String16.h> +#include <utils/threads.h> +#include "AudioPolicyService.h" +#include "AudioPolicyManagerGeneric.h" +#include <cutils/properties.h> +#include <dlfcn.h> + +// ---------------------------------------------------------------------------- +// the sim build doesn't have gettid + +#ifndef HAVE_GETTID +# define gettid getpid +#endif + +namespace android { + +static bool checkPermission() { +#ifndef HAVE_ANDROID_OS + return true; +#endif + if (getpid() == IPCThreadState::self()->getCallingPid()) return true; + bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")); + if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS"); + return ok; +} + +// ---------------------------------------------------------------------------- + +AudioPolicyService::AudioPolicyService() + : BnAudioPolicyService() , mpPolicyManager(NULL) +{ + char value[PROPERTY_VALUE_MAX]; + + // start tone playback thread + mTonePlaybacThread = new AudioCommandThread(); + // start audio commands thread + mAudioCommandThread = new AudioCommandThread(); + +#if (defined GENERIC_AUDIO) || (defined AUDIO_POLICY_TEST) + mpPolicyManager = new AudioPolicyManagerGeneric(this); + LOGV("build for GENERIC_AUDIO - using generic audio policy"); +#else + // if running in emulation - use the emulator driver + if (property_get("ro.kernel.qemu", value, 0)) { + LOGV("Running in emulation - using generic audio policy"); + mpPolicyManager = new AudioPolicyManagerGeneric(this); + } + else { + LOGV("Using hardware specific audio policy"); + mpPolicyManager = createAudioPolicyManager(this); + } +#endif + + // load properties + property_get("ro.camera.sound.forced", value, "0"); + mpPolicyManager->setSystemProperty("ro.camera.sound.forced", value); +} + +AudioPolicyService::~AudioPolicyService() +{ + mTonePlaybacThread->exit(); + mTonePlaybacThread.clear(); + mAudioCommandThread->exit(); + mAudioCommandThread.clear(); + + if (mpPolicyManager) { + delete mpPolicyManager; + } +} + + +status_t AudioPolicyService::setDeviceConnectionState(AudioSystem::audio_devices device, + AudioSystem::device_connection_state state, + const char *device_address) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (!AudioSystem::isOutputDevice(device) && !AudioSystem::isInputDevice(device)) { + return BAD_VALUE; + } + if (state != AudioSystem::DEVICE_STATE_AVAILABLE && state != AudioSystem::DEVICE_STATE_UNAVAILABLE) { + return BAD_VALUE; + } + + LOGV("setDeviceConnectionState() tid %d", gettid()); + Mutex::Autolock _l(mLock); + return mpPolicyManager->setDeviceConnectionState(device, state, device_address); +} + +AudioSystem::device_connection_state AudioPolicyService::getDeviceConnectionState(AudioSystem::audio_devices device, + const char *device_address) +{ + if (mpPolicyManager == NULL) { + return AudioSystem::DEVICE_STATE_UNAVAILABLE; + } + if (!checkPermission()) { + return AudioSystem::DEVICE_STATE_UNAVAILABLE; + } + return mpPolicyManager->getDeviceConnectionState(device, device_address); +} + +status_t AudioPolicyService::setPhoneState(int state) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (state < 0 || state >= AudioSystem::NUM_MODES) { + return BAD_VALUE; + } + + LOGV("setPhoneState() tid %d", gettid()); + + // TODO: check if it is more appropriate to do it in platform specific policy manager + AudioSystem::setMode(state); + + Mutex::Autolock _l(mLock); + mpPolicyManager->setPhoneState(state); + return NO_ERROR; +} + +status_t AudioPolicyService::setRingerMode(uint32_t mode, uint32_t mask) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + + mpPolicyManager->setRingerMode(mode, mask); + return NO_ERROR; +} + +status_t AudioPolicyService::setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) { + return BAD_VALUE; + } + if (config < 0 || config >= AudioSystem::NUM_FORCE_CONFIG) { + return BAD_VALUE; + } + LOGV("setForceUse() tid %d", gettid()); + Mutex::Autolock _l(mLock); + mpPolicyManager->setForceUse(usage, config); + return NO_ERROR; +} + +AudioSystem::forced_config AudioPolicyService::getForceUse(AudioSystem::force_use usage) +{ + if (mpPolicyManager == NULL) { + return AudioSystem::FORCE_NONE; + } + if (!checkPermission()) { + return AudioSystem::FORCE_NONE; + } + if (usage < 0 || usage >= AudioSystem::NUM_FORCE_USE) { + return AudioSystem::FORCE_NONE; + } + return mpPolicyManager->getForceUse(usage); +} + +audio_io_handle_t AudioPolicyService::getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::output_flags flags) +{ + if (mpPolicyManager == NULL) { + return NULL; + } + LOGV("getOutput() tid %d", gettid()); + Mutex::Autolock _l(mLock); + return mpPolicyManager->getOutput(stream, samplingRate, format, channels, flags); +} + +status_t AudioPolicyService::startOutput(audio_io_handle_t output, AudioSystem::stream_type stream) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + LOGV("startOutput() tid %d", gettid()); + Mutex::Autolock _l(mLock); + return mpPolicyManager->startOutput(output, stream); +} + +status_t AudioPolicyService::stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + LOGV("stopOutput() tid %d", gettid()); + Mutex::Autolock _l(mLock); + return mpPolicyManager->stopOutput(output, stream); +} + +void AudioPolicyService::releaseOutput(audio_io_handle_t output) +{ + if (mpPolicyManager == NULL) { + return; + } + LOGV("releaseOutput() tid %d", gettid()); + Mutex::Autolock _l(mLock); + mpPolicyManager->releaseOutput(output); +} + +audio_io_handle_t AudioPolicyService::getInput(int inputSource, + uint32_t samplingRate, + uint32_t format, + uint32_t channels, + AudioSystem::audio_in_acoustics acoustics) +{ + if (mpPolicyManager == NULL) { + return NULL; + } + Mutex::Autolock _l(mLock); + return mpPolicyManager->getInput(inputSource, samplingRate, format, channels, acoustics); +} + +status_t AudioPolicyService::startInput(audio_io_handle_t input) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + return mpPolicyManager->startInput(input); +} + +status_t AudioPolicyService::stopInput(audio_io_handle_t input) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + Mutex::Autolock _l(mLock); + return mpPolicyManager->stopInput(input); +} + +void AudioPolicyService::releaseInput(audio_io_handle_t input) +{ + if (mpPolicyManager == NULL) { + return; + } + Mutex::Autolock _l(mLock); + mpPolicyManager->releaseInput(input); +} + +status_t AudioPolicyService::initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + mpPolicyManager->initStreamVolume(stream, indexMin, indexMax); + return NO_ERROR; +} + +status_t AudioPolicyService::setStreamVolumeIndex(AudioSystem::stream_type stream, int index) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + + return mpPolicyManager->setStreamVolumeIndex(stream, index); +} + +status_t AudioPolicyService::getStreamVolumeIndex(AudioSystem::stream_type stream, int *index) +{ + if (mpPolicyManager == NULL) { + return NO_INIT; + } + if (!checkPermission()) { + return PERMISSION_DENIED; + } + if (stream < 0 || stream >= AudioSystem::NUM_STREAM_TYPES) { + return BAD_VALUE; + } + return mpPolicyManager->getStreamVolumeIndex(stream, index); +} + +void AudioPolicyService::binderDied(const wp<IBinder>& who) { + LOGW("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(), IPCThreadState::self()->getCallingPid()); +} + +status_t AudioPolicyService::dump(int fd, const Vector<String16>& args) +{ + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + dumpPermissionDenial(fd, args); + } else { + + } + return NO_ERROR; +} + +status_t AudioPolicyService::dumpPermissionDenial(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump AudioPolicyService from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + result.append(buffer); + write(fd, result.string(), result.size()); + return NO_ERROR; +} + +status_t AudioPolicyService::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnAudioPolicyService::onTransact(code, data, reply, flags); +} + + +// ---------------------------------------------------------------------------- +void AudioPolicyService::instantiate() { + defaultServiceManager()->addService( + String16("media.audio_policy"), new AudioPolicyService()); +} + + +// ---------------------------------------------------------------------------- +// AudioPolicyClientInterface implementation +// ---------------------------------------------------------------------------- + + +audio_io_handle_t AudioPolicyService::openOutput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t *pLatencyMs, + AudioSystem::output_flags flags) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + LOGW("openOutput() could not get AudioFlinger"); + return NULL; + } + + return af->openOutput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, pLatencyMs, flags); +} + +audio_io_handle_t AudioPolicyService::openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + LOGW("openDuplicateOutput() could not get AudioFlinger"); + return NULL; + } + return af->openDuplicateOutput(output1, output2); +} + +status_t AudioPolicyService::closeOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + + return af->closeOutput(output); +} + + +status_t AudioPolicyService::suspendOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + LOGW("suspendOutput() could not get AudioFlinger"); + return PERMISSION_DENIED; + } + + return af->suspendOutput(output); +} + +status_t AudioPolicyService::restoreOutput(audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + LOGW("restoreOutput() could not get AudioFlinger"); + return PERMISSION_DENIED; + } + + return af->restoreOutput(output); +} + +audio_io_handle_t AudioPolicyService::openInput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t acoustics) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) { + LOGW("openInput() could not get AudioFlinger"); + return NULL; + } + + return af->openInput(pDevices, pSamplingRate, (uint32_t *)pFormat, pChannels, acoustics); +} + +status_t AudioPolicyService::closeInput(audio_io_handle_t input) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + + return af->closeInput(input); +} + +status_t AudioPolicyService::setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output) +{ + return mAudioCommandThread->volumeCommand((int)stream, volume, (void *)output); +} + +status_t AudioPolicyService::setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output) +{ + sp<IAudioFlinger> af = AudioSystem::get_audio_flinger(); + if (af == 0) return PERMISSION_DENIED; + + return af->setStreamOutput(stream, output); +} + + +void AudioPolicyService::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs) +{ + mAudioCommandThread->parametersCommand((void *)ioHandle, keyValuePairs); +} + +String8 AudioPolicyService::getParameters(audio_io_handle_t ioHandle, const String8& keys) +{ + String8 result = AudioSystem::getParameters(ioHandle, keys); + return result; +} + +status_t AudioPolicyService::startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream) +{ + mTonePlaybacThread->startToneCommand(tone, stream); + return NO_ERROR; +} + +status_t AudioPolicyService::stopTone() +{ + mTonePlaybacThread->stopToneCommand(); + return NO_ERROR; +} + + +// ----------- AudioPolicyService::AudioCommandThread implementation ---------- + +AudioPolicyService::AudioCommandThread::AudioCommandThread() + : Thread(false) +{ + mpToneGenerator = NULL; +} + + +AudioPolicyService::AudioCommandThread::~AudioCommandThread() +{ + mAudioCommands.clear(); + if (mpToneGenerator != NULL) delete mpToneGenerator; +} + +void AudioPolicyService::AudioCommandThread::onFirstRef() +{ + const size_t SIZE = 256; + char buffer[SIZE]; + + snprintf(buffer, SIZE, "AudioCommandThread"); + + run(buffer, ANDROID_PRIORITY_AUDIO); +} + +bool AudioPolicyService::AudioCommandThread::threadLoop() +{ + mLock.lock(); + while (!exitPending()) + { + while(!mAudioCommands.isEmpty()) { + AudioCommand *command = mAudioCommands[0]; + mAudioCommands.removeAt(0); + switch (command->mCommand) { + case START_TONE: { + mLock.unlock(); + ToneData *data = (ToneData *)command->mParam; + LOGV("AudioCommandThread() processing start tone %d on stream %d", + data->mType, data->mStream); + if (mpToneGenerator != NULL) + delete mpToneGenerator; + mpToneGenerator = new ToneGenerator(data->mStream, 1.0); + mpToneGenerator->startTone(data->mType); + delete data; + mLock.lock(); + }break; + case STOP_TONE: { + mLock.unlock(); + LOGV("AudioCommandThread() processing stop tone"); + if (mpToneGenerator != NULL) { + mpToneGenerator->stopTone(); + delete mpToneGenerator; + mpToneGenerator = NULL; + } + mLock.lock(); + }break; + case SET_VOLUME: { + VolumeData *data = (VolumeData *)command->mParam; + LOGV("AudioCommandThread() processing set volume stream %d, volume %f, output %p", data->mStream, data->mVolume, data->mIO); + mCommandStatus = AudioSystem::setStreamVolume(data->mStream, data->mVolume, data->mIO); + mCommandCond.signal(); + mWaitWorkCV.wait(mLock); + delete data; + }break; + case SET_PARAMETERS: { + ParametersData *data = (ParametersData *)command->mParam; + LOGV("AudioCommandThread() processing set parameters string %s, io %p", data->mKeyValuePairs.string(), data->mIO); + mCommandStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs); + mCommandCond.signal(); + mWaitWorkCV.wait(mLock); + delete data; + }break; + default: + LOGW("AudioCommandThread() unknown command %d", command->mCommand); + } + delete command; + } + LOGV("AudioCommandThread() going to sleep"); + mWaitWorkCV.wait(mLock); + LOGV("AudioCommandThread() waking up"); + } + mLock.unlock(); + return false; +} + +void AudioPolicyService::AudioCommandThread::startToneCommand(int type, int stream) +{ + Mutex::Autolock _l(mLock); + AudioCommand *command = new AudioCommand(); + command->mCommand = START_TONE; + ToneData *data = new ToneData(); + data->mType = type; + data->mStream = stream; + command->mParam = (void *)data; + mAudioCommands.add(command); + LOGV("AudioCommandThread() adding tone start type %d, stream %d", type, stream); + mWaitWorkCV.signal(); +} + +void AudioPolicyService::AudioCommandThread::stopToneCommand() +{ + Mutex::Autolock _l(mLock); + AudioCommand *command = new AudioCommand(); + command->mCommand = STOP_TONE; + command->mParam = NULL; + mAudioCommands.add(command); + LOGV("AudioCommandThread() adding tone stop"); + mWaitWorkCV.signal(); +} + +status_t AudioPolicyService::AudioCommandThread::volumeCommand(int stream, float volume, void *output) +{ + Mutex::Autolock _l(mLock); + AudioCommand *command = new AudioCommand(); + command->mCommand = SET_VOLUME; + VolumeData *data = new VolumeData(); + data->mStream = stream; + data->mVolume = volume; + data->mIO = output; + command->mParam = data; + mAudioCommands.add(command); + LOGV("AudioCommandThread() adding set volume stream %d, volume %f, output %p", stream, volume, output); + mWaitWorkCV.signal(); + mCommandCond.wait(mLock); + status_t status = mCommandStatus; + mWaitWorkCV.signal(); + return status; +} + +status_t AudioPolicyService::AudioCommandThread::parametersCommand(void *ioHandle, const String8& keyValuePairs) +{ + Mutex::Autolock _l(mLock); + AudioCommand *command = new AudioCommand(); + command->mCommand = SET_PARAMETERS; + ParametersData *data = new ParametersData(); + data->mIO = ioHandle; + data->mKeyValuePairs = keyValuePairs; + command->mParam = data; + mAudioCommands.add(command); + LOGV("AudioCommandThread() adding set parameter string %s, io %p", keyValuePairs.string(), ioHandle); + mWaitWorkCV.signal(); + mCommandCond.wait(mLock); + status_t status = mCommandStatus; + mWaitWorkCV.signal(); + return status; +} + +void AudioPolicyService::AudioCommandThread::exit() +{ + LOGV("AudioCommandThread::exit"); + { + AutoMutex _l(mLock); + requestExit(); + mWaitWorkCV.signal(); + } + requestExitAndWait(); +} + +}; // namespace android diff --git a/libs/audioflinger/AudioPolicyService.h b/libs/audioflinger/AudioPolicyService.h new file mode 100644 index 0000000..1c46975 --- /dev/null +++ b/libs/audioflinger/AudioPolicyService.h @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_AUDIOPOLICYSERVICE_H +#define ANDROID_AUDIOPOLICYSERVICE_H + +#include <media/IAudioPolicyService.h> +#include <hardware_legacy/AudioPolicyInterface.h> +#include <media/ToneGenerator.h> + +namespace android { + +class String8; + +// ---------------------------------------------------------------------------- + +class AudioPolicyService: public BnAudioPolicyService, public AudioPolicyClientInterface, public IBinder::DeathRecipient +{ + +public: + static void instantiate(); + + virtual status_t dump(int fd, const Vector<String16>& args); + + // + // BnAudioPolicyService (see AudioPolicyInterface for method descriptions) + // + + virtual status_t setDeviceConnectionState(AudioSystem::audio_devices device, + AudioSystem::device_connection_state state, + const char *device_address); + virtual AudioSystem::device_connection_state getDeviceConnectionState(AudioSystem::audio_devices device, + const char *device_address); + virtual status_t setPhoneState(int state); + virtual status_t setRingerMode(uint32_t mode, uint32_t mask); + virtual status_t setForceUse(AudioSystem::force_use usage, AudioSystem::forced_config config); + virtual AudioSystem::forced_config getForceUse(AudioSystem::force_use usage); + virtual audio_io_handle_t getOutput(AudioSystem::stream_type stream, + uint32_t samplingRate = 0, + uint32_t format = AudioSystem::FORMAT_DEFAULT, + uint32_t channels = 0, + AudioSystem::output_flags flags = AudioSystem::OUTPUT_FLAG_INDIRECT); + virtual status_t startOutput(audio_io_handle_t output, AudioSystem::stream_type stream); + virtual status_t stopOutput(audio_io_handle_t output, AudioSystem::stream_type stream); + virtual void releaseOutput(audio_io_handle_t output); + virtual audio_io_handle_t getInput(int inputSource, + uint32_t samplingRate = 0, + uint32_t format = AudioSystem::FORMAT_DEFAULT, + uint32_t channels = 0, + AudioSystem::audio_in_acoustics acoustics = (AudioSystem::audio_in_acoustics)0); + virtual status_t startInput(audio_io_handle_t input); + virtual status_t stopInput(audio_io_handle_t input); + virtual void releaseInput(audio_io_handle_t input); + virtual status_t initStreamVolume(AudioSystem::stream_type stream, + int indexMin, + int indexMax); + virtual status_t setStreamVolumeIndex(AudioSystem::stream_type stream, int index); + virtual status_t getStreamVolumeIndex(AudioSystem::stream_type stream, int *index); + + virtual status_t onTransact( + uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags); + + // IBinder::DeathRecipient + virtual void binderDied(const wp<IBinder>& who); + + // + // AudioPolicyClientInterface + // + virtual audio_io_handle_t openOutput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t *pLatencyMs, + AudioSystem::output_flags flags); + virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1, audio_io_handle_t output2); + virtual status_t closeOutput(audio_io_handle_t output); + virtual status_t suspendOutput(audio_io_handle_t output); + virtual status_t restoreOutput(audio_io_handle_t output); + virtual audio_io_handle_t openInput(uint32_t *pDevices, + uint32_t *pSamplingRate, + uint32_t *pFormat, + uint32_t *pChannels, + uint32_t acoustics); + virtual status_t closeInput(audio_io_handle_t input); + virtual status_t setStreamVolume(AudioSystem::stream_type stream, float volume, audio_io_handle_t output); + virtual status_t setStreamOutput(AudioSystem::stream_type stream, audio_io_handle_t output); + virtual void setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs); + virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys); + virtual status_t startTone(ToneGenerator::tone_type tone, AudioSystem::stream_type stream); + virtual status_t stopTone(); + +private: + AudioPolicyService(); + virtual ~AudioPolicyService(); + + // Thread used for tone playback and to send audio config commands to audio flinger + // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because startTone() + // and stopTone() are normally called with mLock locked and requesting a tone start or stop will cause + // calls to AudioPolicyService and an attempt to lock mLock. + // For audio config commands, it is necessary because audio flinger requires that the calling process (user) + // has permission to modify audio settings. + class AudioCommandThread : public Thread { + public: + + // commands for tone AudioCommand + enum { + START_TONE, + STOP_TONE, + SET_VOLUME, + SET_PARAMETERS + }; + + AudioCommandThread (); + virtual ~AudioCommandThread(); + + // Thread virtuals + virtual void onFirstRef(); + virtual bool threadLoop(); + + void exit(); + void startToneCommand(int type = 0, int stream = 0); + void stopToneCommand(); + status_t volumeCommand(int stream, float volume, void *output); + status_t parametersCommand(void *ioHandle, const String8& keyValuePairs); + + private: + // descriptor for requested tone playback event + class AudioCommand { + public: + int mCommand; // START_TONE, STOP_TONE ... + void *mParam; + }; + + class ToneData { + public: + int mType; // tone type (START_TONE only) + int mStream; // stream type (START_TONE only) + }; + + class VolumeData { + public: + int mStream; + float mVolume; + void *mIO; + }; + class ParametersData { + public: + void *mIO; + String8 mKeyValuePairs; + }; + + + Mutex mLock; + Condition mWaitWorkCV; + Vector<AudioCommand *> mAudioCommands; // list of pending tone events + Condition mCommandCond; + status_t mCommandStatus; + ToneGenerator *mpToneGenerator; // the tone generator + }; + + // Internal dump utilities. + status_t dumpPermissionDenial(int fd, const Vector<String16>& args); + + + Mutex mLock; // prevents concurrent access to AudioPolicy manager functions changing device + // connection stated our routing + AudioPolicyInterface* mpPolicyManager; // the platform specific policy manager + sp <AudioCommandThread> mAudioCommandThread; // audio commands thread + sp <AudioCommandThread> mTonePlaybacThread; // tone playback thread +}; + +}; // namespace android + +#endif // ANDROID_AUDIOPOLICYSERVICE_H + + + + + + + + diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk new file mode 100644 index 0000000..2df6775 --- /dev/null +++ b/libs/binder/Android.mk @@ -0,0 +1,45 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# we have the common sources, plus some device-specific stuff +LOCAL_SRC_FILES:= \ + Binder.cpp \ + BpBinder.cpp \ + IInterface.cpp \ + IMemory.cpp \ + IPCThreadState.cpp \ + IPermissionController.cpp \ + IServiceManager.cpp \ + MemoryDealer.cpp \ + MemoryBase.cpp \ + MemoryHeapBase.cpp \ + MemoryHeapPmem.cpp \ + Parcel.cpp \ + Permission.cpp \ + ProcessState.cpp \ + Static.cpp + +LOCAL_LDLIBS += -lpthread + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libcutils \ + libutils + +LOCAL_MODULE:= libbinder + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/utils/Binder.cpp b/libs/binder/Binder.cpp index 37e4685..0dd7622 100644 --- a/libs/utils/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include <utils/Binder.h> +#include <binder/Binder.h> #include <utils/Atomic.h> -#include <utils/BpBinder.h> -#include <utils/IInterface.h> -#include <utils/Parcel.h> +#include <binder/BpBinder.h> +#include <binder/IInterface.h> +#include <binder/Parcel.h> #include <stdio.h> @@ -27,6 +27,17 @@ namespace android { // --------------------------------------------------------------------------- +IBinder::IBinder() + : RefBase() +{ +} + +IBinder::~IBinder() +{ +} + +// --------------------------------------------------------------------------- + sp<IInterface> IBinder::queryLocalInterface(const String16& descriptor) { return NULL; @@ -58,6 +69,8 @@ public: // --------------------------------------------------------------------------- +String16 BBinder::sEmptyDescriptor; + BBinder::BBinder() : mExtras(NULL) { @@ -73,10 +86,10 @@ status_t BBinder::pingBinder() return NO_ERROR; } -String16 BBinder::getInterfaceDescriptor() const +const String16& BBinder::getInterfaceDescriptor() const { LOGW("reached BBinder::getInterfaceDescriptor (this=%p)", this); - return String16(); + return sEmptyDescriptor; } status_t BBinder::transact( diff --git a/libs/utils/BpBinder.cpp b/libs/binder/BpBinder.cpp index 69ab195..5de87ec 100644 --- a/libs/utils/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -17,9 +17,9 @@ #define LOG_TAG "BpBinder" //#define LOG_NDEBUG 0 -#include <utils/BpBinder.h> +#include <binder/BpBinder.h> -#include <utils/IPCThreadState.h> +#include <binder/IPCThreadState.h> #include <utils/Log.h> #include <stdio.h> @@ -98,16 +98,33 @@ BpBinder::BpBinder(int32_t handle) IPCThreadState::self()->incWeakHandle(handle); } -String16 BpBinder::getInterfaceDescriptor() const +bool BpBinder::isDescriptorCached() const { + Mutex::Autolock _l(mLock); + return mDescriptorCache.size() ? true : false; +} + +const String16& BpBinder::getInterfaceDescriptor() const { - String16 res; - Parcel send, reply; - status_t err = const_cast<BpBinder*>(this)->transact( - INTERFACE_TRANSACTION, send, &reply); - if (err == NO_ERROR) { - res = reply.readString16(); + if (isDescriptorCached() == false) { + Parcel send, reply; + // do the IPC without a lock held. + status_t err = const_cast<BpBinder*>(this)->transact( + INTERFACE_TRANSACTION, send, &reply); + if (err == NO_ERROR) { + String16 res(reply.readString16()); + Mutex::Autolock _l(mLock); + // mDescriptorCache could have been assigned while the lock was + // released. + if (mDescriptorCache.size() == 0) + mDescriptorCache = res; + } } - return res; + + // we're returning a reference to a non-static object here. Usually this + // is not something smart to do, however, with binder objects it is + // (usually) safe because they are reference-counted. + + return mDescriptorCache; } bool BpBinder::isBinderAlive() const diff --git a/libs/utils/IInterface.cpp b/libs/binder/IInterface.cpp index 6ea8178..29acf5d 100644 --- a/libs/utils/IInterface.cpp +++ b/libs/binder/IInterface.cpp @@ -14,12 +14,19 @@ * limitations under the License. */ -#include <utils/IInterface.h> +#include <binder/IInterface.h> namespace android { // --------------------------------------------------------------------------- +IInterface::IInterface() + : RefBase() { +} + +IInterface::~IInterface() { +} + sp<IBinder> IInterface::asBinder() { return this ? onAsBinder() : NULL; diff --git a/libs/utils/IMemory.cpp b/libs/binder/IMemory.cpp index 429bc2b..6c1d225 100644 --- a/libs/utils/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -25,11 +25,11 @@ #include <sys/types.h> #include <sys/mman.h> -#include <utils/IMemory.h> +#include <binder/IMemory.h> #include <utils/KeyedVector.h> #include <utils/threads.h> #include <utils/Atomic.h> -#include <utils/Parcel.h> +#include <binder/Parcel.h> #include <utils/CallStack.h> #define VERBOSE 0 @@ -205,11 +205,11 @@ sp<IMemoryHeap> BpMemory::getMemory(ssize_t* offset, size_t* size) const IMPLEMENT_META_INTERFACE(Memory, "android.utils.IMemory"); -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) +BnMemory::BnMemory() { +} + +BnMemory::~BnMemory() { +} status_t BnMemory::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) @@ -299,11 +299,11 @@ void BpMemoryHeap::assertReallyMapped() const ssize_t size = reply.readInt32(); uint32_t flags = reply.readInt32(); - LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%d, err=%d (%s)", + LOGE_IF(err, "binder=%p transaction failed fd=%d, size=%ld, err=%d (%s)", asBinder().get(), parcel_fd, size, err, strerror(-err)); int fd = dup( parcel_fd ); - LOGE_IF(fd==-1, "cannot dup fd=%d, size=%d, err=%d (%s)", + LOGE_IF(fd==-1, "cannot dup fd=%d, size=%ld, err=%d (%s)", parcel_fd, size, err, strerror(errno)); int access = PROT_READ; @@ -316,7 +316,7 @@ void BpMemoryHeap::assertReallyMapped() const mRealHeap = true; mBase = mmap(0, size, access, MAP_SHARED, fd, 0); if (mBase == MAP_FAILED) { - LOGE("cannot map BpMemoryHeap (binder=%p), size=%d, fd=%d (%s)", + LOGE("cannot map BpMemoryHeap (binder=%p), size=%ld, fd=%d (%s)", asBinder().get(), size, fd, strerror(errno)); close(fd); } else { @@ -357,8 +357,14 @@ uint32_t BpMemoryHeap::getFlags() const { IMPLEMENT_META_INTERFACE(MemoryHeap, "android.utils.IMemoryHeap"); +BnMemoryHeap::BnMemoryHeap() { +} + +BnMemoryHeap::~BnMemoryHeap() { +} + status_t BnMemoryHeap::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { case HEAP_ID: { diff --git a/libs/utils/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 04ae142..c371a23 100644 --- a/libs/utils/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -14,17 +14,17 @@ * limitations under the License. */ -#include <utils/IPCThreadState.h> +#include <binder/IPCThreadState.h> -#include <utils/Binder.h> -#include <utils/BpBinder.h> +#include <binder/Binder.h> +#include <binder/BpBinder.h> #include <utils/Debug.h> #include <utils/Log.h> #include <utils/TextOutput.h> #include <utils/threads.h> -#include <private/utils/binder_module.h> -#include <private/utils/Static.h> +#include <private/binder/binder_module.h> +#include <private/binder/Static.h> #include <sys/ioctl.h> #include <signal.h> @@ -366,13 +366,8 @@ void IPCThreadState::restoreCallingIdentity(int64_t token) void IPCThreadState::clearCaller() { - if (mProcess->supportsProcesses()) { - mCallingPid = getpid(); - mCallingUid = getuid(); - } else { - mCallingPid = -1; - mCallingUid = -1; - } + mCallingPid = getpid(); + mCallingUid = getuid(); } void IPCThreadState::flushCommands() diff --git a/libs/utils/IPermissionController.cpp b/libs/binder/IPermissionController.cpp index f01d38f..bff4c9b 100644 --- a/libs/utils/IPermissionController.cpp +++ b/libs/binder/IPermissionController.cpp @@ -16,14 +16,14 @@ #define LOG_TAG "PermissionController" -#include <utils/IPermissionController.h> +#include <binder/IPermissionController.h> #include <utils/Debug.h> #include <utils/Log.h> -#include <utils/Parcel.h> +#include <binder/Parcel.h> #include <utils/String8.h> -#include <private/utils/Static.h> +#include <private/binder/Static.h> namespace android { @@ -55,12 +55,6 @@ IMPLEMENT_META_INTERFACE(PermissionController, "android.os.IPermissionController // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnPermissionController::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/utils/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 9beeadd..0cf4158 100644 --- a/libs/utils/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -16,16 +16,16 @@ #define LOG_TAG "ServiceManager" -#include <utils/IServiceManager.h> +#include <binder/IServiceManager.h> #include <utils/Debug.h> -#include <utils/IPCThreadState.h> #include <utils/Log.h> -#include <utils/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/Parcel.h> #include <utils/String8.h> #include <utils/SystemClock.h> -#include <private/utils/Static.h> +#include <private/binder/Static.h> #include <unistd.h> @@ -53,14 +53,19 @@ bool checkCallingPermission(const String16& permission) static String16 _permission("permission"); + bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid) { IPCThreadState* ipcState = IPCThreadState::self(); - int32_t pid = ipcState->getCallingPid(); - int32_t uid = ipcState->getCallingUid(); + pid_t pid = ipcState->getCallingPid(); + uid_t uid = ipcState->getCallingUid(); if (outPid) *outPid = pid; - if (outUid) *outUid= uid; - + if (outUid) *outUid = uid; + return checkPermission(permission, pid, uid); +} + +bool checkPermission(const String16& permission, pid_t pid, uid_t uid) +{ sp<IPermissionController> pc; gDefaultServiceManagerLock.lock(); pc = gPermissionController; @@ -178,12 +183,6 @@ IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnServiceManager::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/utils/MemoryBase.cpp b/libs/binder/MemoryBase.cpp index f25e11c..033066b 100644 --- a/libs/utils/MemoryBase.cpp +++ b/libs/binder/MemoryBase.cpp @@ -18,7 +18,7 @@ #include <stdlib.h> #include <stdint.h> -#include <utils/MemoryBase.h> +#include <binder/MemoryBase.h> namespace android { diff --git a/libs/utils/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index cf8201b..d5ffe7f 100644 --- a/libs/utils/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp @@ -16,13 +16,13 @@ #define LOG_TAG "MemoryDealer" -#include <utils/MemoryDealer.h> +#include <binder/MemoryDealer.h> #include <utils/Log.h> -#include <utils/IPCThreadState.h> +#include <binder/IPCThreadState.h> #include <utils/SortedVector.h> #include <utils/String8.h> -#include <utils/MemoryBase.h> +#include <binder/MemoryBase.h> #include <stdint.h> #include <stdio.h> @@ -38,7 +38,15 @@ #include <sys/file.h> namespace android { +// ---------------------------------------------------------------------------- +HeapInterface::HeapInterface() { } +HeapInterface::~HeapInterface() { } + +// ---------------------------------------------------------------------------- + +AllocatorInterface::AllocatorInterface() { } +AllocatorInterface::~AllocatorInterface() { } // ---------------------------------------------------------------------------- @@ -107,7 +115,7 @@ sp<IMemory> MemoryDealer::allocate(size_t size, uint32_t flags) if (new_memory != 0) { memory = new Allocation(this, offset, size, new_memory); } else { - LOGE("couldn't map [%8x, %d]", offset, size); + LOGE("couldn't map [%8lx, %u]", offset, size); if (size) { /* NOTE: it's VERY important to not free allocations of size 0 * because they're special as they don't have any record in the @@ -339,6 +347,10 @@ void SimpleBestFitAllocator::dump_l(String8& result, // ---------------------------------------------------------------------------- +SharedHeap::SharedHeap() + : HeapInterface(), MemoryHeapBase() +{ +} SharedHeap::SharedHeap(size_t size, uint32_t flags, char const * name) : MemoryHeapBase(size, flags, name) diff --git a/libs/utils/MemoryHeapBase.cpp b/libs/binder/MemoryHeapBase.cpp index 8251728..ac38f51 100644 --- a/libs/utils/MemoryHeapBase.cpp +++ b/libs/binder/MemoryHeapBase.cpp @@ -29,7 +29,7 @@ #include <cutils/ashmem.h> #include <cutils/atomic.h> -#include <utils/MemoryHeapBase.h> +#include <binder/MemoryHeapBase.h> #if HAVE_ANDROID_OS #include <linux/android_pmem.h> diff --git a/libs/utils/MemoryHeapPmem.cpp b/libs/binder/MemoryHeapPmem.cpp index eba2b30..3806a42 100644 --- a/libs/utils/MemoryHeapPmem.cpp +++ b/libs/binder/MemoryHeapPmem.cpp @@ -27,8 +27,8 @@ #include <cutils/log.h> -#include <utils/MemoryHeapPmem.h> -#include <utils/MemoryHeapBase.h> +#include <binder/MemoryHeapPmem.h> +#include <binder/MemoryHeapBase.h> #if HAVE_ANDROID_OS #include <linux/android_pmem.h> @@ -108,7 +108,7 @@ void SubRegionMemory::revoke() // promote() it. #if HAVE_ANDROID_OS - if (mSize != NULL) { + if (mSize != 0) { const sp<MemoryHeapPmem>& heap(getHeap()); int our_fd = heap->heapID(); struct pmem_region sub; diff --git a/libs/utils/Parcel.cpp b/libs/binder/Parcel.cpp index b0e3750..f40e4bd 100644 --- a/libs/utils/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -17,19 +17,19 @@ #define LOG_TAG "Parcel" //#define LOG_NDEBUG 0 -#include <utils/Parcel.h> +#include <binder/Parcel.h> -#include <utils/Binder.h> -#include <utils/BpBinder.h> +#include <binder/Binder.h> +#include <binder/BpBinder.h> #include <utils/Debug.h> -#include <utils/ProcessState.h> +#include <binder/ProcessState.h> #include <utils/Log.h> #include <utils/String8.h> #include <utils/String16.h> #include <utils/TextOutput.h> #include <utils/misc.h> -#include <private/utils/binder_module.h> +#include <private/binder/binder_module.h> #include <stdio.h> #include <stdlib.h> @@ -441,9 +441,14 @@ status_t Parcel::writeInterfaceToken(const String16& interface) return writeString16(interface); } +bool Parcel::checkInterface(IBinder* binder) const +{ + return enforceInterface(binder->getInterfaceDescriptor()); +} + bool Parcel::enforceInterface(const String16& interface) const { - String16 str = readString16(); + const String16 str(readString16()); if (str == interface) { return true; } else { diff --git a/libs/binder/Permission.cpp b/libs/binder/Permission.cpp new file mode 100644 index 0000000..fd8fe69 --- /dev/null +++ b/libs/binder/Permission.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <utils/Log.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/Permission.h> + +namespace android { +// --------------------------------------------------------------------------- + +Permission::Permission(char const* name) + : mPermissionName(name), mPid(getpid()) +{ +} + +Permission::Permission(const String16& name) + : mPermissionName(name), mPid(getpid()) +{ +} + +Permission::Permission(const Permission& rhs) + : mPermissionName(rhs.mPermissionName), + mGranted(rhs.mGranted), + mPid(rhs.mPid) +{ +} + +Permission::~Permission() +{ +} + +bool Permission::operator < (const Permission& rhs) const +{ + return mPermissionName < rhs.mPermissionName; +} + +bool Permission::checkCalling() const +{ + IPCThreadState* ipcState = IPCThreadState::self(); + pid_t pid = ipcState->getCallingPid(); + uid_t uid = ipcState->getCallingUid(); + return doCheckPermission(pid, uid); +} + +bool Permission::check(pid_t pid, uid_t uid) const +{ + return doCheckPermission(pid, uid); +} + +bool Permission::doCheckPermission(pid_t pid, uid_t uid) const +{ + if ((uid == 0) || (pid == mPid)) { + // root and ourselves is always okay + return true; + } else { + // see if we already granted this permission for this uid + Mutex::Autolock _l(mLock); + if (mGranted.indexOf(uid) >= 0) + return true; + } + + bool granted = checkPermission(mPermissionName, pid, uid); + if (granted) { + Mutex::Autolock _l(mLock); + // no need to check again, the old item will be replaced if it is + // already there. + mGranted.add(uid); + } + return granted; +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/utils/ProcessState.cpp b/libs/binder/ProcessState.cpp index 4567df6..d7daf73 100644 --- a/libs/utils/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -18,19 +18,19 @@ #include <cutils/process_name.h> -#include <utils/ProcessState.h> +#include <binder/ProcessState.h> #include <utils/Atomic.h> -#include <utils/BpBinder.h> -#include <utils/IPCThreadState.h> +#include <binder/BpBinder.h> +#include <binder/IPCThreadState.h> #include <utils/Log.h> #include <utils/String8.h> -#include <utils/IServiceManager.h> +#include <binder/IServiceManager.h> #include <utils/String8.h> #include <utils/threads.h> -#include <private/utils/binder_module.h> -#include <private/utils/Static.h> +#include <private/binder/binder_module.h> +#include <private/binder/Static.h> #include <errno.h> #include <fcntl.h> diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp new file mode 100644 index 0000000..12b0308 --- /dev/null +++ b/libs/binder/Static.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// All static variables go here, to control initialization and +// destruction order in the library. + +#include <private/binder/Static.h> + +#include <binder/IPCThreadState.h> +#include <utils/Log.h> + +namespace android { + +// ------------ ProcessState.cpp + +Mutex gProcessMutex; +sp<ProcessState> gProcess; + +class LibUtilsIPCtStatics +{ +public: + LibUtilsIPCtStatics() + { + } + + ~LibUtilsIPCtStatics() + { + IPCThreadState::shutdown(); + } +}; + +static LibUtilsIPCtStatics gIPCStatics; + +// ------------ ServiceManager.cpp + +Mutex gDefaultServiceManagerLock; +sp<IServiceManager> gDefaultServiceManager; +sp<IPermissionController> gPermissionController; + +} // namespace android diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk new file mode 100644 index 0000000..6b11a25 --- /dev/null +++ b/libs/rs/Android.mk @@ -0,0 +1,117 @@ +ifeq ($(BUILD_RENDERSCRIPT),true) + +LOCAL_PATH:=$(call my-dir) + + +# Build rsg-generator ==================== +include $(CLEAR_VARS) + +LOCAL_MODULE := rsg-generator + +# These symbols are normally defined by BUILD_XXX, but we need to define them +# here so that local-intermediates-dir works. + +LOCAL_IS_HOST_MODULE := true +LOCAL_MODULE_CLASS := EXECUTABLES +intermediates := $(local-intermediates-dir) + +GEN := $(addprefix $(intermediates)/, \ + lex.yy.c \ + ) +$(GEN): PRIVATE_CUSTOM_TOOL = flex -o $@ $< + +$(intermediates)/lex.yy.c : $(LOCAL_PATH)/spec.lex + $(transform-generated-source) + +$(LOCAL_PATH)/rsg_generator.c : $(intermediates)/lex.yy.c + +LOCAL_SRC_FILES:= \ + rsg_generator.c + +include $(BUILD_HOST_EXECUTABLE) + +# TODO: This should go into build/core/config.mk +RSG_GENERATOR:=$(LOCAL_BUILT_MODULE) + + + +# Build render script lib ==================== +include $(CLEAR_VARS) +LOCAL_MODULE := libRS + +LOCAL_MODULE_CLASS := SHARED_LIBRARIES +intermediates:= $(local-intermediates-dir) + +# Generate custom headers + +GEN := $(addprefix $(intermediates)/, \ + rsgApiStructs.h \ + rsgApiFuncDecl.h \ + ) + +$(GEN) : PRIVATE_PATH := $(LOCAL_PATH) +$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR) $< $@ <$(PRIVATE_PATH)/rs.spec +$(GEN) : $(RSG_GENERATOR) $(LOCAL_PATH)/rs.spec +$(GEN): $(intermediates)/%.h : $(LOCAL_PATH)/%.h.rsg + $(transform-generated-source) + +# used in jni/Android.mk +rs_generated_source += $(GEN) +LOCAL_GENERATED_SOURCES += $(GEN) + +# Generate custom source files + +GEN := $(addprefix $(intermediates)/, \ + rsgApi.cpp \ + rsgApiReplay.cpp \ + ) + +$(GEN) : PRIVATE_PATH := $(LOCAL_PATH) +$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR) $< $@ <$(PRIVATE_PATH)/rs.spec +$(GEN) : $(RSG_GENERATOR) $(LOCAL_PATH)/rs.spec +$(GEN): $(intermediates)/%.cpp : $(LOCAL_PATH)/%.cpp.rsg + $(transform-generated-source) + +# used in jni/Android.mk +rs_generated_source += $(GEN) + +LOCAL_GENERATED_SOURCES += $(GEN) + +LOCAL_SRC_FILES:= \ + rsAdapter.cpp \ + rsAllocation.cpp \ + rsComponent.cpp \ + rsContext.cpp \ + rsDevice.cpp \ + rsElement.cpp \ + rsFileA3D.cpp \ + rsLight.cpp \ + rsLocklessFifo.cpp \ + rsObjectBase.cpp \ + rsMatrix.cpp \ + rsMesh.cpp \ + rsProgram.cpp \ + rsProgramFragment.cpp \ + rsProgramFragmentStore.cpp \ + rsProgramVertex.cpp \ + rsSampler.cpp \ + rsScript.cpp \ + rsScriptC.cpp \ + rsScriptC_Lib.cpp \ + rsThreadIO.cpp \ + rsType.cpp \ + rsTriangleMesh.cpp + +LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libui libacc +LOCAL_LDLIBS := -lpthread -ldl +LOCAL_MODULE:= libRS +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + +# Include the subdirectories ==================== +include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk,\ + java \ + )) + +endif
\ No newline at end of file diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h new file mode 100644 index 0000000..855ea63 --- /dev/null +++ b/libs/rs/RenderScript.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RENDER_SCRIPT_H +#define RENDER_SCRIPT_H + +#include <stdint.h> +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////// +// + +typedef void * RsAdapter1D; +typedef void * RsAdapter2D; +typedef void * RsAllocation; +typedef void * RsContext; +typedef void * RsDevice; +typedef void * RsElement; +typedef void * RsFile; +typedef void * RsSampler; +typedef void * RsScript; +typedef void * RsScriptBasicTemp; +typedef void * RsTriangleMesh; +typedef void * RsType; +typedef void * RsLight; + +typedef void * RsProgramVertex; +typedef void * RsProgramFragment; +typedef void * RsProgramFragmentStore; + +RsDevice rsDeviceCreate(); +void rsDeviceDestroy(RsDevice); + +RsContext rsContextCreate(RsDevice, void *, uint32_t version); +void rsContextDestroy(RsContext); + +enum RsDataType { + RS_TYPE_FLOAT, + RS_TYPE_UNSIGNED, + RS_TYPE_SIGNED +}; + +enum RsDataKind { + RS_KIND_USER, + RS_KIND_RED, + RS_KIND_GREEN, + RS_KIND_BLUE, + RS_KIND_ALPHA, + RS_KIND_LUMINANCE, + RS_KIND_INTENSITY, + RS_KIND_X, + RS_KIND_Y, + RS_KIND_Z, + RS_KIND_W, + RS_KIND_S, + RS_KIND_T, + RS_KIND_Q, + RS_KIND_R, + RS_KIND_NX, + RS_KIND_NY, + RS_KIND_NZ, + RS_KIND_INDEX +}; + +enum RsElementPredefined { + RS_ELEMENT_USER_U8, + RS_ELEMENT_USER_I8, + RS_ELEMENT_USER_U16, + RS_ELEMENT_USER_I16, + RS_ELEMENT_USER_U32, + RS_ELEMENT_USER_I32, + RS_ELEMENT_USER_FLOAT, + + RS_ELEMENT_A_8, // 7 + RS_ELEMENT_RGB_565, // 8 + RS_ELEMENT_RGBA_5551, // 9 + RS_ELEMENT_RGBA_4444, // 10 + RS_ELEMENT_RGB_888, // 11 + RS_ELEMENT_RGBA_8888, // 12 + + RS_ELEMENT_INDEX_16, //13 + RS_ELEMENT_INDEX_32, + RS_ELEMENT_XY_F32, + RS_ELEMENT_XYZ_F32, + RS_ELEMENT_ST_XY_F32, + RS_ELEMENT_ST_XYZ_F32, + RS_ELEMENT_NORM_XYZ_F32, + RS_ELEMENT_NORM_ST_XYZ_F32, +}; + +enum RsSamplerParam { + RS_SAMPLER_MIN_FILTER, + RS_SAMPLER_MAG_FILTER, + RS_SAMPLER_WRAP_S, + RS_SAMPLER_WRAP_T, + RS_SAMPLER_WRAP_R +}; + +enum RsSamplerValue { + RS_SAMPLER_NEAREST, + RS_SAMPLER_LINEAR, + RS_SAMPLER_LINEAR_MIP_LINEAR, + RS_SAMPLER_WRAP, + RS_SAMPLER_CLAMP +}; + +enum RsDimension { + RS_DIMENSION_X, + RS_DIMENSION_Y, + RS_DIMENSION_Z, + RS_DIMENSION_LOD, + RS_DIMENSION_FACE, + + RS_DIMENSION_ARRAY_0 = 100, + RS_DIMENSION_ARRAY_1, + RS_DIMENSION_ARRAY_2, + RS_DIMENSION_ARRAY_3, + RS_DIMENSION_MAX = RS_DIMENSION_ARRAY_3 +}; + +enum RsDepthFunc { + RS_DEPTH_FUNC_ALWAYS, + RS_DEPTH_FUNC_LESS, + RS_DEPTH_FUNC_LEQUAL, + RS_DEPTH_FUNC_GREATER, + RS_DEPTH_FUNC_GEQUAL, + RS_DEPTH_FUNC_EQUAL, + RS_DEPTH_FUNC_NOTEQUAL +}; + +enum RsBlendSrcFunc { + RS_BLEND_SRC_ZERO, + RS_BLEND_SRC_ONE, + RS_BLEND_SRC_DST_COLOR, + RS_BLEND_SRC_ONE_MINUS_DST_COLOR, + RS_BLEND_SRC_SRC_ALPHA, + RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA, + RS_BLEND_SRC_DST_ALPHA, + RS_BLEND_SRC_ONE_MINUS_DST_ALPHA, + RS_BLEND_SRC_SRC_ALPHA_SATURATE +}; + +enum RsBlendDstFunc { + RS_BLEND_DST_ZERO, + RS_BLEND_DST_ONE, + RS_BLEND_DST_SRC_COLOR, + RS_BLEND_DST_ONE_MINUS_SRC_COLOR, + RS_BLEND_DST_SRC_ALPHA, + RS_BLEND_DST_ONE_MINUS_SRC_ALPHA, + RS_BLEND_DST_DST_ALPHA, + RS_BLEND_DST_ONE_MINUS_DST_ALPHA +}; + +enum RsTexEnvMode { + RS_TEX_ENV_MODE_REPLACE, + RS_TEX_ENV_MODE_MODULATE, + RS_TEX_ENV_MODE_DECAL +}; + +enum RsPrimitive { + RS_PRIMITIVE_POINT, + RS_PRIMITIVE_LINE, + RS_PRIMITIVE_LINE_STRIP, + RS_PRIMITIVE_TRIANGLE, + RS_PRIMITIVE_TRIANGLE_STRIP, + RS_PRIMITIVE_TRIANGLE_FAN +}; + + +#include "rsgApiFuncDecl.h" + +#ifdef __cplusplus +}; +#endif + +#endif // RENDER_SCRIPT_H + + + diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h new file mode 100644 index 0000000..0789301 --- /dev/null +++ b/libs/rs/RenderScriptEnv.h @@ -0,0 +1,40 @@ +#include <stdint.h> + + +typedef void * RsAdapter1D; +typedef void * RsAdapter2D; +typedef void * RsAllocation; +typedef void * RsContext; +typedef void * RsDevice; +typedef void * RsElement; +typedef void * RsSampler; +typedef void * RsScript; +typedef void * RsScriptBasicTemp; +typedef void * RsTriangleMesh; +typedef void * RsType; +typedef void * RsProgramFragment; +typedef void * RsProgramFragmentStore; +typedef void * RsLight; + + +typedef struct { + float m[16]; +} rsc_Matrix; + + +typedef struct { + float v[4]; +} rsc_Vector4; + +#define RS_PROGRAM_VERTEX_MODELVIEW_OFFSET 0 +#define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16 +#define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32 + +//typedef int (*rsc_RunScript)(uint32_t launchIndex, const rsc_FunctionTable *); + + +/* EnableCap */ +#define GL_LIGHTING 0x0B50 + +/* LightName */ +#define GL_LIGHT0 0x4000 diff --git a/libs/rs/java/Android.mk b/libs/rs/java/Android.mk new file mode 100644 index 0000000..5053e7d --- /dev/null +++ b/libs/rs/java/Android.mk @@ -0,0 +1 @@ +include $(call all-subdir-makefiles) diff --git a/libs/rs/java/Film/Android.mk b/libs/rs/java/Film/Android.mk new file mode 100644 index 0000000..b7f98fc --- /dev/null +++ b/libs/rs/java/Film/Android.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) +#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript + +LOCAL_PACKAGE_NAME := Film + +include $(BUILD_PACKAGE) diff --git a/libs/rs/java/Film/AndroidManifest.xml b/libs/rs/java/Film/AndroidManifest.xml new file mode 100644 index 0000000..a5ce8a1 --- /dev/null +++ b/libs/rs/java/Film/AndroidManifest.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.film"> + <application android:label="Film"> + <activity android:name="Film" + android:screenOrientation="portrait" + android:theme="@android:style/Theme.Black.NoTitleBar"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/libs/rs/java/Film/res/raw/filmimage.c b/libs/rs/java/Film/res/raw/filmimage.c new file mode 100644 index 0000000..3bd9496 --- /dev/null +++ b/libs/rs/java/Film/res/raw/filmimage.c @@ -0,0 +1,110 @@ +// Fountain test script + +#pragma version(1) +#pragma stateVertex(orthoWindow) +#pragma stateRaster(flat) +#pragma stateFragment(PgmFragBackground) +#pragma stateFragmentStore(MyBlend) + + +int main(void* con, int ft, int launchID) { + int count, touch, x, y, rate, maxLife, lifeShift; + int life; + int ct, ct2; + int newPart; + int drawCount; + int dx, dy, idx; + int posx,posy; + int c; + int srcIdx; + int dstIdx; + + count = loadI32(con, 0, 1); + touch = loadI32(con, 0, 2); + x = loadI32(con, 0, 3); + y = loadI32(con, 0, 4); + + rate = 4; + maxLife = (count / rate) - 1; + lifeShift = 0; + { + life = maxLife; + while (life > 255) { + life = life >> 1; + lifeShift ++; + } + } + + drawRect(con, 0, 256, 0, 512); + contextBindProgramFragment(con, NAMED_PgmFragParts); + + if (touch) { + newPart = loadI32(con, 2, 0); + for (ct2=0; ct2<rate; ct2++) { + dx = scriptRand(con, 0x10000) - 0x8000; + dy = scriptRand(con, 0x10000) - 0x8000; + + idx = newPart * 5 + 1; + storeI32(con, 2, idx, dx); + storeI32(con, 2, idx + 1, dy); + storeI32(con, 2, idx + 2, maxLife); + storeI32(con, 2, idx + 3, x << 16); + storeI32(con, 2, idx + 4, y << 16); + + newPart++; + if (newPart >= count) { + newPart = 0; + } + } + storeI32(con, 2, 0, newPart); + } + + drawCount = 0; + for (ct=0; ct < count; ct++) { + srcIdx = ct * 5 + 1; + + dx = loadI32(con, 2, srcIdx); + dy = loadI32(con, 2, srcIdx + 1); + life = loadI32(con, 2, srcIdx + 2); + posx = loadI32(con, 2, srcIdx + 3); + posy = loadI32(con, 2, srcIdx + 4); + + if (life) { + if (posy < (480 << 16)) { + dstIdx = drawCount * 9; + c = 0xffafcf | ((life >> lifeShift) << 24); + + storeU32(con, 1, dstIdx, c); + storeI32(con, 1, dstIdx + 1, posx); + storeI32(con, 1, dstIdx + 2, posy); + + storeU32(con, 1, dstIdx + 3, c); + storeI32(con, 1, dstIdx + 4, posx + 0x10000); + storeI32(con, 1, dstIdx + 5, posy + dy * 4); + + storeU32(con, 1, dstIdx + 6, c); + storeI32(con, 1, dstIdx + 7, posx - 0x10000); + storeI32(con, 1, dstIdx + 8, posy + dy * 4); + drawCount ++; + } else { + if (dy > 0) { + dy = (-dy) >> 1; + } + } + + posx = posx + dx; + posy = posy + dy; + dy = dy + 0x400; + life --; + + //storeI32(con, 2, srcIdx, dx); + storeI32(con, 2, srcIdx + 1, dy); + storeI32(con, 2, srcIdx + 2, life); + storeI32(con, 2, srcIdx + 3, posx); + storeI32(con, 2, srcIdx + 4, posy); + } + } + + drawTriangleArray(con, NAMED_PartBuffer, drawCount); + return 1; +} diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c new file mode 100644 index 0000000..6885251 --- /dev/null +++ b/libs/rs/java/Film/res/raw/filmstrip.c @@ -0,0 +1,114 @@ +// Fountain test script + +#pragma version(1) +#pragma stateVertex(PVBackground) +#pragma stateFragment(PFBackground) +#pragma stateFragmentStore(PFSBackground) + +/* +typedef struct FilmScriptUserEnvRec { + RsAllocation tex[13]; + int32_t triangleOffsets[64]; + float triangleOffsetsTex[64]; + int32_t triangleOffsetsCount; +} FilmScriptUserEnv; +*/ + +// The script enviroment has 3 env allocations. +// bank0: (r) The enviroment structure +// bank1: (r) The position information +// bank2: (rw) The temporary texture state + +int main(int index) +{ + int f1,f2,f3,f4, f5,f6,f7,f8, f9,f10,f11,f12, f13,f14,f15,f16; + int g1,g2,g3,g4, g5,g6,g7,g8, g9,g10,g11,g12, g13,g14,g15,g16; + float trans; + float rot; + int x; + float focusPos; // float + int focusID; + int lastFocusID; + int imgCount; + + trans = loadF(1, 0); + rot = loadF(1, 1); + + matrixLoadScale(&f16, 2.f, 2.f, 2.f); + matrixTranslate(&f16, 0.f, 0.f, trans); + matrixRotate(&f16, 90.f, 0.f, 0.f, 1.f); + matrixRotate(&f16, rot, 1.f, 0.f, 0.f); + storeMatrix(3, 0, &f16); + + //materialDiffuse(con, 0.0f, 0.0f, 0.0f, 1.0f); + //materialSpecular(con, 0.5f, 0.5f, 0.5f, 0.5f); + //materialShininess(intToFloat(20)); + drawTriangleMesh(NAMED_mesh); + + + + //int imgId = 0; + + bindProgramFragmentStore(NAMED_PFImages); + bindProgramFragment(NAMED_PFSImages); + bindProgramVertex(NAMED_PVImages); + + //focusPos = loadF(1, 2); + //focusID = 0; + //lastFocusID = loadI32(2, 0); + //imgCount = 13; + + /* + disable(GL_LIGHTING); + + + if (trans > (-.3)) { + focusID = -1.0 - focusPos; + if (focusID >= imgCount) { + focusID = -1; + } + } else { + focusID = -1; + } + + if (focusID != lastFocusID) { + if (lastFocusID >= 0) { + uploadToTexture(con, env->tex[lastFocusID], 1); + } + if (focusID >= 0) { + uploadToTexture(con, env->tex[focusID], 0); + } + } + storeEnvI32(con, 2, 0, focusID); + + + for (imgId=1; imgId <= imgCount; imgId++) { + float pos = focusPos + imgId + .4f; + int offset = (int)floor(pos*2); + pos -= 0.75; + + offset += env->triangleOffsetsCount / 2; + + if ((offset < 0) || (offset >= env->triangleOffsetsCount)) { + continue; + } + + int start = offset -2; + int end = offset + 2; + + if (start < 0) { + start = 0; + } + if (end > env->triangleOffsetsCount) { + end = env->triangleOffsetsCount; + } + + programFragmentBindTexture(con, env->fpImages, 0, env->tex[imgId - 1]); + matrixLoadTranslate(con, &m, -pos - env->triangleOffsetsTex[env->triangleOffsetsCount / 2], 0, 0); + storeEnvMatrix(con, 3, RS_PROGRAM_VERTEX_TEXTURE_OFFSET, &m); + renderTriangleMeshRange(con, env->mesh, env->triangleOffsets[start], env->triangleOffsets[end] - env->triangleOffsets[start]); + } +*/ + return 0; +} + diff --git a/libs/rs/java/Film/src/com/android/film/Film.java b/libs/rs/java/Film/src/com/android/film/Film.java new file mode 100644 index 0000000..6e99816 --- /dev/null +++ b/libs/rs/java/Film/src/com/android/film/Film.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.film; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings.System; +import android.util.Config; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.ListView; + +import java.lang.Runtime; + +public class Film extends Activity { + //EventListener mListener = new EventListener(); + + private static final String LOG_TAG = "libRS_jni"; + private static final boolean DEBUG = false; + private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; + + private FilmView mView; + + // get the current looper (from your Activity UI thread for instance + + + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Create our Preview view and set it as the content of our + // Activity + mView = new FilmView(this); + setContentView(mView); + } + + @Override + protected void onResume() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onResume(); + mView.onResume(); + } + + @Override + protected void onPause() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onPause(); + mView.onPause(); + + Runtime.getRuntime().exit(0); + } + + + static void log(String message) { + if (LOG_ENABLED) { + Log.v(LOG_TAG, message); + } + } + + +} + diff --git a/libs/rs/java/Film/src/com/android/film/FilmRS.java b/libs/rs/java/Film/src/com/android/film/FilmRS.java new file mode 100644 index 0000000..fca0818 --- /dev/null +++ b/libs/rs/java/Film/src/com/android/film/FilmRS.java @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.film; + +import java.io.Writer; + +import android.renderscript.RenderScript; +import android.renderscript.ProgramVertexAlloc; +import android.renderscript.Matrix; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.KeyEvent; +import android.view.MotionEvent; + +public class FilmRS { + + public FilmRS() { + } + + public void init(RenderScript rs, Resources res, int width, int height) { + mRS = rs; + mRes = res; + initRS(); + } + + public void setFilmStripPosition(int x, int y) + { + if (x < 50) { + x = 50; + } + if (x > 270) { + x = 270; + } + + float anim = ((float)x-50) / 270.f; + mBufferPos[0] = 2f * anim + 0.5f; // translation + mBufferPos[1] = (anim * 40); // rotation + mBufferPos[2] = ((float)y) / 16.f - 8; // focusPos + mAllocPos.data(mBufferPos); + } + + + private Resources mRes; + private RenderScript mRS; + private RenderScript.Script mScriptStrip; + private RenderScript.Script mScriptImage; + private RenderScript.Element mElementVertex; + private RenderScript.Element mElementIndex; + private RenderScript.Sampler mSampler; + private RenderScript.ProgramFragmentStore mPFSBackground; + private RenderScript.ProgramFragmentStore mPFSImages; + private RenderScript.ProgramFragment mPFBackground; + private RenderScript.ProgramFragment mPFImages; + private RenderScript.ProgramVertex mPVBackground; + private RenderScript.ProgramVertex mPVImages; + private ProgramVertexAlloc mPVA; + + private RenderScript.Allocation mAllocEnv; + private RenderScript.Allocation mAllocPos; + private RenderScript.Allocation mAllocState; + private RenderScript.Allocation mAllocPV; + private RenderScript.TriangleMesh mMesh; + private RenderScript.Light mLight; + + private float[] mBufferPos; + private float[] mBufferPV; + + private void initSamplers() { + mRS.samplerBegin(); + mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN, + RenderScript.SamplerValue.LINEAR_MIP_LINEAR); + mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_S, + RenderScript.SamplerValue.CLAMP); + mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_T, + RenderScript.SamplerValue.CLAMP); + mSampler = mRS.samplerCreate(); + } + + private void initPFS() { + mRS.programFragmentStoreBegin(null, null); + mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.LESS); + mRS.programFragmentStoreDitherEnable(true); + mPFSBackground = mRS.programFragmentStoreCreate(); + mPFSBackground.setName("PFSBackground"); + + mRS.programFragmentStoreBegin(null, null); + mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.EQUAL); + mRS.programFragmentStoreDitherEnable(false); + mRS.programFragmentStoreDepthMask(false); + mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE, + RenderScript.BlendDstFunc.ONE); + mPFSImages = mRS.programFragmentStoreCreate(); + mPFSImages.setName("PFSImages"); + } + + private void initPF() { + mRS.programFragmentBegin(null, null); + mPFBackground = mRS.programFragmentCreate(); + mPFBackground.setName("PFBackground"); + + mRS.programFragmentBegin(null, null); + mRS.programFragmentSetTexEnable(0, true); + //mRS.programFragmentSetEnvMode(0, RS_TEX_ENV_MODE_REPLACE); + //rsProgramFragmentSetType(0, gEnv.tex[0]->getType()); + mPFImages = mRS.programFragmentCreate(); + mPFImages.setName("PFImages"); + } + + private void initPV() { + mRS.lightBegin(); + mLight = mRS.lightCreate(); + mLight.setPosition(0, -0.5f, -1.0f); + + mRS.programVertexBegin(null, null); + mRS.programVertexSetTextureMatrixEnable(true); + mRS.programVertexAddLight(mLight); + mPVBackground = mRS.programVertexCreate(); + mPVBackground.setName("PVBackground"); + + mRS.programVertexBegin(null, null); + mPVImages = mRS.programVertexCreate(); + mPVImages.setName("PVImages"); + } + + + int mParams[] = new int[10]; + + private void initRS() { + mElementVertex = mRS.elementGetPredefined( + RenderScript.ElementPredefined.NORM_ST_XYZ_F32); + mElementIndex = mRS.elementGetPredefined( + RenderScript.ElementPredefined.INDEX_16); + + mRS.triangleMeshBegin(mElementVertex, mElementIndex); + FilmStripMesh fsm = new FilmStripMesh(); + fsm.init(mRS); + mMesh = mRS.triangleMeshCreate(); + mMesh.setName("mesh"); + + initPFS(); + initSamplers(); + initPF(); + initPV(); + mPFImages.bindSampler(mSampler, 0); + + Log.e("rs", "Done loading named"); + + mRS.scriptCBegin(); + mRS.scriptCSetClearColor(0.0f, 0.0f, 0.0f, 1.0f); + mRS.scriptCSetScript(mRes, R.raw.filmstrip); + mRS.scriptCSetRoot(true); + mScriptStrip = mRS.scriptCCreate(); + + mBufferPos = new float[3]; + mAllocPos = mRS.allocationCreatePredefSized( + RenderScript.ElementPredefined.USER_FLOAT, + mBufferPos.length); + + mPVA = new ProgramVertexAlloc(mRS); + mPVBackground.bindAllocation(0, mPVA.mAlloc); + mPVImages.bindAllocation(0, mPVA.mAlloc); + mPVA.setupProjectionNormalized(320, 480); + + + mScriptStrip.bindAllocation(mAllocPos, 1); + //mScriptStrip.bindAllocation(gStateAlloc, 2); + mScriptStrip.bindAllocation(mPVA.mAlloc, 3); + + +/* + { + Resources res = getResources(); + Drawable d = res.getDrawable(R.drawable.gadgets_clock_mp3); + BitmapDrawable bd = (BitmapDrawable)d; + Bitmap b = bd.getBitmap(); + mTexture = mRS.allocationCreateFromBitmap(b, + RenderScript.ElementPredefined.RGB_565, + true); + mTexture.uploadToTexture(0); + } + + mRS.programFragmentStoreBegin(null, null); + mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.SRC_ALPHA, RenderScript.BlendDstFunc.ONE); + mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS); + mPFS = mRS.programFragmentStoreCreate(); + mPFS.setName("MyBlend"); + mRS.contextBindProgramFragmentStore(mPFS); + */ + + setFilmStripPosition(0, 0); + + mRS.contextBindRootScript(mScriptStrip); + } +} + + + diff --git a/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java b/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java new file mode 100644 index 0000000..02bffd8 --- /dev/null +++ b/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.film; + +import java.io.Writer; +import java.lang.Math; +import android.util.Log; + +import android.renderscript.RenderScript; + + +class FilmStripMesh { + + class Vertex { + float nx; + float ny; + float nz; + float s; + float t; + float x; + float y; + float z; + + Vertex() { + nx = 0; + ny = 0; + nz = 0; + s = 0; + t = 0; + x = 0; + y = 0; + z = 0; + } + + void xyz(float _x, float _y, float _z) { + x = _x; + y = _y; + z = _z; + } + + void nxyz(float _x, float _y, float _z) { + nx = _x; + ny = _y; + nz = _z; + } + + void st(float _s, float _t) { + s = _s; + t = _t; + } + + void computeNorm(Vertex v1, Vertex v2) { + float dx = v1.x - v2.x; + float dy = v1.y - v2.y; + float dz = v1.z - v2.z; + float len = (float)java.lang.Math.sqrt(dx*dx + dy*dy + dz*dz); + dx /= len; + dy /= len; + dz /= len; + + nx = dx * dz; + ny = dy * dz; + nz = (float)java.lang.Math.sqrt(dx*dx + dy*dy); + + len = (float)java.lang.Math.sqrt(nx*nx + ny*ny + nz*nz); + nx /= len; + ny /= len; + nz /= len; + } + + void addToRS(RenderScript rs) { + rs.triangleMeshAddVertex_XYZ_ST_NORM(x, y, z, s, t, nx, ny, nz); + } + } + + int[] mTriangleOffsets; + float[] mTriangleOffsetsTex; + int mTriangleOffsetsCount; + + void init(RenderScript rs) + { + float vtx[] = new float[] { + 60.431003f, 124.482050f, + 60.862074f, 120.872604f, + 61.705303f, 117.336662f, + 62.949505f, 113.921127f, + 64.578177f, 110.671304f, + 66.569716f, 107.630302f, + 68.897703f, 104.838457f, + 71.531259f, 102.332803f, + 74.435452f, 100.146577f, + 77.571757f, 98.308777f, + 80.898574f, 96.843781f, + 84.371773f, 95.771023f, + 87.945283f, 95.104731f, + 98.958994f, 95.267098f, + 109.489523f, 98.497596f, + 118.699582f, 104.539366f, + 125.856872f, 112.912022f, + 130.392311f, 122.949849f, + 131.945283f, 133.854731f, + 130.392311f, 144.759613f, + 125.856872f, 154.797439f, + 118.699582f, 163.170096f, + 109.489523f, 169.211866f, + 98.958994f, 172.442364f, + 87.945283f, 172.604731f, + 72.507313f, 172.672927f, + 57.678920f, 168.377071f, + 44.668135f, 160.067134f, + 34.534908f, 148.420104f, + 28.104767f, 134.384831f, + 25.901557f, 119.104731f, + 28.104767f, 103.824631f, + 34.534908f, 89.789358f, + 44.668135f, 78.142327f, + 57.678920f, 69.832390f, + 72.507313f, 65.536534f, + 87.945283f, 65.604731f, + 106.918117f, 65.688542f, + 125.141795f, 60.409056f, + 141.131686f, 50.196376f, + 153.585137f, 35.882502f, + 161.487600f, 18.633545f, + 164.195283f, -0.145269f, + 161.487600f, -18.924084f, + 153.585137f, -36.173040f, + 141.131686f, -50.486914f, + 125.141795f, -60.699594f, + 106.918117f, -65.979081f, + 87.945283f, -65.895269f, + 80f, -65.895269f, + 60f, -65.895269f, + 40f, -65.895269f, + 20f, -65.895269f, + 0f, -65.895269f, + -20f, -65.895269f, + -40f, -65.895269f, + -60f, -65.895269f, + -80f, -65.895269f, + -87.945283f, -65.895269f, + -106.918117f, -65.979081f, + -125.141795f, -60.699594f, + -141.131686f, -50.486914f, + -153.585137f, -36.173040f, + -161.487600f, -18.924084f, + -164.195283f, -0.145269f, + -161.487600f, 18.633545f, + -153.585137f, 35.882502f, + -141.131686f, 50.196376f, + -125.141795f, 60.409056f, + -106.918117f, 65.688542f, + -87.945283f, 65.604731f, + -72.507313f, 65.536534f, + -57.678920f, 69.832390f, + -44.668135f, 78.142327f, + -34.534908f, 89.789358f, + -28.104767f, 103.824631f, + -25.901557f, 119.104731f, + -28.104767f, 134.384831f, + -34.534908f, 148.420104f, + -44.668135f, 160.067134f, + -57.678920f, 168.377071f, + -72.507313f, 172.672927f, + -87.945283f, 172.604731f, + -98.958994f, 172.442364f, + -109.489523f, 169.211866f, + -118.699582f, 163.170096f, + -125.856872f, 154.797439f, + -130.392311f, 144.759613f, + -131.945283f, 133.854731f, + -130.392311f, 122.949849f, + -125.856872f, 112.912022f, + -118.699582f, 104.539366f, + -109.489523f, 98.497596f, + -98.958994f, 95.267098f, + -87.945283f, 95.104731f, + -84.371773f, 95.771023f, + -80.898574f, 96.843781f, + -77.571757f, 98.308777f, + -74.435452f, 100.146577f, + -71.531259f, 102.332803f, + -68.897703f, 104.838457f, + -66.569716f, 107.630302f, + -64.578177f, 110.671304f, + -62.949505f, 113.921127f, + -61.705303f, 117.336662f, + -60.862074f, 120.872604f, + -60.431003f, 124.482050f + }; + + + mTriangleOffsets = new int[64]; + mTriangleOffsetsTex = new float[64]; + + mTriangleOffsets[0] = 0; + mTriangleOffsetsCount = 1; + + Vertex t = new Vertex(); + t.nxyz(1, 0, 0); + int count = vtx.length / 2; + + float runningS = 0; + for (int ct=0; ct < (count-1); ct++) { + t.x = -vtx[ct*2] / 100.f; + t.z = vtx[ct*2+1] / 100.f; + t.s = runningS; + t.nx = (vtx[ct*2+3] - vtx[ct*2 +1]); + t.ny = (vtx[ct*2+2] - vtx[ct*2 ]); + float len = (float)java.lang.Math.sqrt(t.nx * t.nx + t.ny * t.ny); + runningS += len / 100; + t.nx /= len; + t.ny /= len; + t.y = -0.5f; + t.t = 0; + //Log.e("xx", "vtx " + t.x + " " + t.y + " " + t.z); + t.addToRS(rs); + t.y = .5f; + t.t = 1; + t.addToRS(rs); + + //LOGE(" %f", runningS); + if((runningS*2) > mTriangleOffsetsCount) { + //LOGE("**** img %i %i", gTriangleOffsetsCount, ct*2); + mTriangleOffsets[mTriangleOffsetsCount] = ct*2; + mTriangleOffsetsTex[mTriangleOffsetsCount] = t.s; + mTriangleOffsetsCount ++; + } + } + + count = (count * 2 - 2); + for (int ct=0; ct < (count-2); ct+= 2) { + rs.triangleMeshAddTriangle(ct, ct+1, ct+2); + rs.triangleMeshAddTriangle(ct+1, ct+3, ct+2); + } + } + + +} + diff --git a/libs/rs/java/Film/src/com/android/film/FilmView.java b/libs/rs/java/Film/src/com/android/film/FilmView.java new file mode 100644 index 0000000..a743b1b --- /dev/null +++ b/libs/rs/java/Film/src/com/android/film/FilmView.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.film; + +import java.io.Writer; +import java.util.ArrayList; +import java.util.concurrent.Semaphore; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.KeyEvent; +import android.view.MotionEvent; + +public class FilmView extends RSSurfaceView { + + public FilmView(Context context) { + super(context); + + //setFocusable(true); + } + + private RenderScript mRS; + private FilmRS mRender; + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + + mRS = createRenderScript(); + mRender = new FilmRS(); + mRender.init(mRS, getResources(), w, h); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + // break point at here + // this method doesn't work when 'extends View' include 'extends ScrollView'. + return super.onKeyDown(keyCode, event); + } + + + @Override + public boolean onTouchEvent(MotionEvent ev) + { + boolean ret = true; + int act = ev.getAction(); + if (act == ev.ACTION_UP) { + ret = false; + } + mRender.setFilmStripPosition((int)ev.getX(), (int)ev.getY()); + return ret; + } +} + + diff --git a/libs/rs/java/Fountain/Android.mk b/libs/rs/java/Fountain/Android.mk new file mode 100644 index 0000000..b6a9f10 --- /dev/null +++ b/libs/rs/java/Fountain/Android.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) +#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript + +LOCAL_PACKAGE_NAME := Fountain + +include $(BUILD_PACKAGE) diff --git a/libs/rs/java/Fountain/AndroidManifest.xml b/libs/rs/java/Fountain/AndroidManifest.xml new file mode 100644 index 0000000..dd0e428 --- /dev/null +++ b/libs/rs/java/Fountain/AndroidManifest.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.fountain"> + <application android:label="Fountain"> + <activity android:name="Fountain" + android:theme="@android:style/Theme.Translucent"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png b/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png Binary files differnew file mode 100755 index 0000000..e91bfb4 --- /dev/null +++ b/libs/rs/java/Fountain/res/drawable/gadgets_clock_mp3.png diff --git a/libs/rs/java/Fountain/res/raw/fountain.c b/libs/rs/java/Fountain/res/raw/fountain.c new file mode 100644 index 0000000..99551fc --- /dev/null +++ b/libs/rs/java/Fountain/res/raw/fountain.c @@ -0,0 +1,106 @@ +// Fountain test script + +#pragma version(1) +#pragma stateVertex(default) +#pragma stateFragment(PgmFragParts) +#pragma stateFragmentStore(PFSBlend) + + +int main(int launchID) { + int count, touch, x, y, rate, maxLife, lifeShift; + int life; + int ct, ct2; + int newPart; + int drawCount; + int dx, dy, idx; + int posx,posy; + int c; + int srcIdx; + int dstIdx; + + count = loadI32(0, 1); + touch = loadI32(0, 2); + x = loadI32(0, 3); + y = loadI32(0, 4); + + rate = 4; + maxLife = (count / rate) - 1; + lifeShift = 0; + { + life = maxLife; + while (life > 255) { + life = life >> 1; + lifeShift ++; + } + } + + if (touch) { + newPart = loadI32(2, 0); + for (ct2=0; ct2<rate; ct2++) { + dx = (int)((randf(1.f) - 0.5f) * 0x10000); + dy = (int)((randf(1.f) - 0.5f) * 0x10000); + + idx = newPart * 5 + 1; + storeI32(2, idx, dx); + storeI32(2, idx + 1, dy); + storeI32(2, idx + 2, maxLife); + storeI32(2, idx + 3, x << 16); + storeI32(2, idx + 4, y << 16); + + newPart++; + if (newPart >= count) { + newPart = 0; + } + } + storeI32(2, 0, newPart); + } + + drawCount = 0; + for (ct=0; ct < count; ct++) { + srcIdx = ct * 5 + 1; + + dx = loadI32(2, srcIdx); + dy = loadI32(2, srcIdx + 1); + life = loadI32(2, srcIdx + 2); + posx = loadI32(2, srcIdx + 3); + posy = loadI32(2, srcIdx + 4); + + if (life) { + if (posy < (480 << 16)) { + dstIdx = drawCount * 9; + c = 0xffafcf | ((life >> lifeShift) << 24); + + storeI32(1, dstIdx, c); + storeI32(1, dstIdx + 1, posx); + storeI32(1, dstIdx + 2, posy); + + storeI32(1, dstIdx + 3, c); + storeI32(1, dstIdx + 4, posx + 0x10000); + storeI32(1, dstIdx + 5, posy + dy * 4); + + storeI32(1, dstIdx + 6, c); + storeI32(1, dstIdx + 7, posx - 0x10000); + storeI32(1, dstIdx + 8, posy + dy * 4); + drawCount ++; + } else { + if (dy > 0) { + dy = (-dy) >> 1; + } + } + + posx = posx + dx; + posy = posy + dy; + dy = dy + 0x400; + life --; + + //storeI32(2, srcIdx, dx); + storeI32(2, srcIdx + 1, dy); + storeI32(2, srcIdx + 2, life); + storeI32(2, srcIdx + 3, posx); + storeI32(2, srcIdx + 4, posy); + } + } + + drawTriangleArray(NAMED_PartBuffer, drawCount); + return 1; +} diff --git a/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java b/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java new file mode 100644 index 0000000..58c78fa --- /dev/null +++ b/libs/rs/java/Fountain/src/com/android/fountain/Fountain.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.fountain; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings.System; +import android.util.Config; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.ListView; + +import java.lang.Runtime; + +public class Fountain extends Activity { + //EventListener mListener = new EventListener(); + + private static final String LOG_TAG = "libRS_jni"; + private static final boolean DEBUG = false; + private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; + + private FountainView mView; + + // get the current looper (from your Activity UI thread for instance + + + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Create our Preview view and set it as the content of our + // Activity + mView = new FountainView(this); + setContentView(mView); + } + + @Override + protected void onResume() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onResume(); + mView.onResume(); + } + + @Override + protected void onPause() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onPause(); + mView.onPause(); + + Runtime.getRuntime().exit(0); + } + + + static void log(String message) { + if (LOG_ENABLED) { + Log.v(LOG_TAG, message); + } + } + + +} + diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java new file mode 100644 index 0000000..c8e9a1e --- /dev/null +++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.fountain; + +import java.io.Writer; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.renderscript.RenderScript; +import android.renderscript.ProgramVertexAlloc; +import android.util.Log; + +public class FountainRS { + + public FountainRS() { + } + + public void init(RenderScript rs, Resources res, int width, int height) { + mRS = rs; + mRes = res; + initRS(); + } + + public void newTouchPosition(int x, int y) { + mParams[0] = 1; + mParams[1] = x; + mParams[2] = y; + mIntAlloc.subData1D(2, 3, mParams); + } + + + ///////////////////////////////////////// + + private Resources mRes; + + private RenderScript mRS; + private RenderScript.Allocation mIntAlloc; + private RenderScript.Allocation mPartAlloc; + private RenderScript.Allocation mVertAlloc; + private RenderScript.Script mScript; + private RenderScript.ProgramFragmentStore mPFS; + private RenderScript.ProgramFragment mPF; + + private Bitmap mBackground; + + int mParams[] = new int[10]; + + private void initRS() { + int partCount = 1024; + + mIntAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, 10); + mPartAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 3 * 3); + mPartAlloc.setName("PartBuffer"); + mVertAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 5 + 1); + + mRS.programFragmentStoreBegin(null, null); + mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.SRC_ALPHA, RenderScript.BlendDstFunc.ONE); + mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS); + mRS.programFragmentStoreDepthMask(false); + mRS.programFragmentStoreDitherEnable(false); + mPFS = mRS.programFragmentStoreCreate(); + mPFS.setName("PFSBlend"); + + mRS.programFragmentBegin(null, null); + mPF = mRS.programFragmentCreate(); + mPF.setName("PgmFragParts"); + + mParams[0] = 0; + mParams[1] = partCount; + mParams[2] = 0; + mParams[3] = 0; + mParams[4] = 0; + mIntAlloc.data(mParams); + + int t2[] = new int[partCount * 4*3]; + for (int ct=0; ct < t2.length; ct++) { + t2[ct] = 0; + } + mPartAlloc.data(t2); + + mRS.scriptCBegin(); + mRS.scriptCSetClearColor(0.0f, 0.0f, 0.0f, 1.0f); + mRS.scriptCSetScript(mRes, R.raw.fountain); + mRS.scriptCSetRoot(true); + mScript = mRS.scriptCCreate(); + + mScript.bindAllocation(mIntAlloc, 0); + mScript.bindAllocation(mPartAlloc, 1); + mScript.bindAllocation(mVertAlloc, 2); + mRS.contextBindRootScript(mScript); + } + +} + + diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java new file mode 100644 index 0000000..be8b24e --- /dev/null +++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainView.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.fountain; + +import java.io.Writer; +import java.util.ArrayList; +import java.util.concurrent.Semaphore; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.KeyEvent; +import android.view.MotionEvent; + +public class FountainView extends RSSurfaceView { + + public FountainView(Context context) { + super(context); + + //setFocusable(true); + } + + private RenderScript mRS; + private FountainRS mRender; + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + + mRS = createRenderScript(); + mRender = new FountainRS(); + mRender.init(mRS, getResources(), w, h); + } + + + @Override + public boolean onTouchEvent(MotionEvent ev) + { + boolean ret = true; + int act = ev.getAction(); + if (act == ev.ACTION_UP) { + ret = false; + } + mRender.newTouchPosition((int)ev.getX(), (int)ev.getY()); + return ret; + } +} + + diff --git a/libs/rs/java/Rollo/Android.mk b/libs/rs/java/Rollo/Android.mk new file mode 100644 index 0000000..5a4957c --- /dev/null +++ b/libs/rs/java/Rollo/Android.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2008 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) +#LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript + +LOCAL_PACKAGE_NAME := Rollo + +include $(BUILD_PACKAGE) diff --git a/libs/rs/java/Rollo/AndroidManifest.xml b/libs/rs/java/Rollo/AndroidManifest.xml new file mode 100644 index 0000000..127a140 --- /dev/null +++ b/libs/rs/java/Rollo/AndroidManifest.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.rollo"> + <application android:label="Rollo"> + <activity android:name="Rollo" + android:theme="@android:style/Theme.Translucent"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/libs/rs/java/Rollo/res/raw/browser.png b/libs/rs/java/Rollo/res/raw/browser.png Binary files differnew file mode 100644 index 0000000..513f0be --- /dev/null +++ b/libs/rs/java/Rollo/res/raw/browser.png diff --git a/libs/rs/java/Rollo/res/raw/market.png b/libs/rs/java/Rollo/res/raw/market.png Binary files differnew file mode 100644 index 0000000..83b6910 --- /dev/null +++ b/libs/rs/java/Rollo/res/raw/market.png diff --git a/libs/rs/java/Rollo/res/raw/photos.png b/libs/rs/java/Rollo/res/raw/photos.png Binary files differnew file mode 100644 index 0000000..1ed8f1e --- /dev/null +++ b/libs/rs/java/Rollo/res/raw/photos.png diff --git a/libs/rs/java/Rollo/res/raw/rollo.c b/libs/rs/java/Rollo/res/raw/rollo.c new file mode 100644 index 0000000..d338d0d --- /dev/null +++ b/libs/rs/java/Rollo/res/raw/rollo.c @@ -0,0 +1,105 @@ +#pragma version(1) +#pragma stateVertex(PV) +#pragma stateFragment(PF) +#pragma stateFragmentStore(PFS) + +// Scratch buffer layout +#define SCRATCH_FADE 0 +#define SCRATCH_ZOOM 1 +#define SCRATCH_ROT 2 + +//#define STATE_POS_X 0 +#define STATE_DONE 1 +//#define STATE_PRESSURE 2 +#define STATE_ZOOM 3 +//#define STATE_WARP 4 +#define STATE_ORIENTATION 5 +#define STATE_SELECTION 6 +#define STATE_FIRST_VISIBLE 7 +#define STATE_COUNT 8 +#define STATE_TOUCH 9 + +float filter(float val, float target, float str) +{ + float delta = (target - val); + return val + delta * str; +} + +int main(void* con, int ft, int launchID) +{ + int rowCount; + int row; + int col; + int imageID; + int done = loadI32(0, STATE_DONE); + int selectedID = loadI32(0, STATE_SELECTION); + + float f = loadF(2, 0); + + pfClearColor(0.0f, 0.0f, 0.0f, f); + if (done) { + if (f > 0.02f) { + //f = f - 0.02f; + //storeF(2, 0, f); + } + } else { + if (f < 0.8f) { + f = f + 0.02f; + storeF(2, 0, f); + } + } + + float touchCut = 1.f; + if (loadI32(0, STATE_TOUCH)) { + touchCut = 5.f; + } + + + float targetZoom = ((float)loadI32(0, STATE_ZOOM)) / 1000.f; + float zoom = filter(loadF(2, SCRATCH_ZOOM), targetZoom, 0.15 * touchCut); + storeF(2, SCRATCH_ZOOM, zoom); + + float targetRot = loadI32(0, STATE_FIRST_VISIBLE) / 180.0f * 3.14f; + float rot = filter(loadF(2, SCRATCH_ROT), targetRot, 0.1f * touchCut); + storeF(2, SCRATCH_ROT, rot); + + float diam = 8.f;// + curve * 2.f; + float scale = 1.0f / zoom; + + rot = rot * scale; + float rotStep = 20.0f / 180.0f * 3.14f * scale; + rowCount = 4; + int index = 0; + int iconCount = loadI32(0, STATE_COUNT); + while (iconCount) { + float tmpSin = sinf(rot); + float tmpCos = cosf(rot); + + float tx1 = tmpSin * diam - (tmpCos * scale); + float tx2 = tx1 + (tmpCos * scale * 2.f); + float tz1 = tmpCos * diam + (tmpSin * scale); + float tz2 = tz1 - (tmpSin * scale * 2.f); + + int y; + for (y = rowCount -1; (y >= 0) && iconCount; y--) { + float ty1 = ((y * 3.0f) - 4.5f) * scale; + float ty2 = ty1 + scale * 2.f; + bindTexture(NAMED_PF, 0, loadI32(1, y)); + color(1.0f, 1.0f, 1.0f, 1.0f); + if (done && (index != selectedID)) { + color(0.4f, 0.4f, 0.4f, 1.0f); + } + drawQuad(tx1, ty1, tz1, + tx2, ty1, tz2, + tx2, ty2, tz2, + tx1, ty2, tz1); + iconCount--; + index++; + } + rot = rot + rotStep; + } + + return 0; +} + + diff --git a/libs/rs/java/Rollo/res/raw/rollo2.c b/libs/rs/java/Rollo/res/raw/rollo2.c new file mode 100644 index 0000000..b04ea73 --- /dev/null +++ b/libs/rs/java/Rollo/res/raw/rollo2.c @@ -0,0 +1,67 @@ +#pragma version(1) +#pragma stateVertex(PV) +#pragma stateFragment(PF) +#pragma stateFragmentStore(PFS) + +void drawLoop(int x, int y, int z, int rot) +{ + int ct; + int tx; + int ty; + int tmpSin; + int tmpCos; + int sz; + + for (ct = 0; ct < 10; ct ++) { + tmpSin = sinx((ct * 36 + rot) * 0x10000); + tmpCos = cosx((ct * 36 + rot) * 0x10000); + + ty = y + tmpCos * 4; + tx = x + tmpSin * 4; + pfBindTexture(NAMED_PF, 0, loadI32(1, ct & 3)); + + sz = 0xc000; + drawQuad(tx - sz, ty - sz, z, + tx + sz, ty - sz, z, + tx + sz, ty + sz, z, + tx - sz, ty + sz, z); + } +} + +int main(void* con, int ft, int launchID) +{ + int rowCount; + int x; + int y; + int row; + int col; + int imageID; + int tx1; + int ty1; + int tz1; + int tx2; + int ty2; + int tz2; + int tmpSin; + int tmpCos; + int iconCount; + int pressure; + + int ringCount; + + + + rotStep = 16 * 0x10000; + pressure = loadI32(0, 2); + rowCount = 4; + + iconCount = loadI32(0, 1); + rot = (-20 + loadI32(0, 0)) * 0x10000; + + for (ringCount = 0; ringCount < 5; ringCount++) { + drawLoop(0, 0, 0x90000 + (ringCount * 0x80000)); + } + + return 0; +} + diff --git a/libs/rs/java/Rollo/res/raw/settings.png b/libs/rs/java/Rollo/res/raw/settings.png Binary files differnew file mode 100644 index 0000000..dd2cd95 --- /dev/null +++ b/libs/rs/java/Rollo/res/raw/settings.png diff --git a/libs/rs/java/Rollo/src/com/android/rollo/Rollo.java b/libs/rs/java/Rollo/src/com/android/rollo/Rollo.java new file mode 100644 index 0000000..400d801 --- /dev/null +++ b/libs/rs/java/Rollo/src/com/android/rollo/Rollo.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rollo; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings.System; +import android.util.Config; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.ListView; + +import java.lang.Runtime; + +public class Rollo extends Activity { + //EventListener mListener = new EventListener(); + + private static final String LOG_TAG = "libRS_jni"; + private static final boolean DEBUG = false; + private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; + + private RolloView mView; + + // get the current looper (from your Activity UI thread for instance + + + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Create our Preview view and set it as the content of our + // Activity + mView = new RolloView(this); + setContentView(mView); + } + + @Override + protected void onResume() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onResume(); + mView.onResume(); + } + + @Override + protected void onPause() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onPause(); + mView.onPause(); + + Runtime.getRuntime().exit(0); + } + + + static void log(String message) { + if (LOG_ENABLED) { + Log.v(LOG_TAG, message); + } + } + + +} + diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloMesh.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloMesh.java new file mode 100644 index 0000000..d7252fb --- /dev/null +++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloMesh.java @@ -0,0 +1,90 @@ + /* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.rollo; + +import java.io.Writer; +import java.lang.Math; + +import android.renderscript.RenderScript; + + +class RolloMesh { + static public final float mCardHeight = 1.2f; + static public final float mCardWidth = 1.8f; + static public final float mTabHeight = 0.2f; + static public final float mTabs = 3; + static public final float mTabGap = 0.1f; + + static RenderScript.TriangleMesh createCard(RenderScript rs) { + RenderScript.Element vtx = rs.elementGetPredefined( + RenderScript.ElementPredefined.ST_XYZ_F32); + RenderScript.Element idx = rs.elementGetPredefined( + RenderScript.ElementPredefined.INDEX_16); + + float w = mCardWidth / 2; + float h = mCardHeight; + float z = 0; + + rs.triangleMeshBegin(vtx, idx); + rs.triangleMeshAddVertex_XYZ_ST(-w, 0, z, 0, 0); + rs.triangleMeshAddVertex_XYZ_ST(-w, h, z, 0, 1); + rs.triangleMeshAddVertex_XYZ_ST( w, h, z, 1, 1); + rs.triangleMeshAddVertex_XYZ_ST( w, 0, z, 1, 0); + rs.triangleMeshAddTriangle(0,1,2); + rs.triangleMeshAddTriangle(0,2,3); + return rs.triangleMeshCreate(); + } + + static RenderScript.TriangleMesh createTab(RenderScript rs) { + RenderScript.Element vtx = rs.elementGetPredefined( + RenderScript.ElementPredefined.ST_XYZ_F32); + RenderScript.Element idx = rs.elementGetPredefined( + RenderScript.ElementPredefined.INDEX_16); + + + float tabSlope = 0.1f; + float num = 0; + + float w = (mCardWidth - ((mTabs - 1) * mTabGap)) / mTabs; + float w1 = -(mCardWidth / 2) + ((w + mTabGap) * num); + float w2 = w1 + (w * tabSlope); + float w3 = w1 + w - (w * tabSlope); + float w4 = w1 + w; + float h1 = mCardHeight; + float h2 = h1 + mTabHeight; + float z = 0; + + float stScale = w / mTabHeight / 2; + float stScale2 = stScale * (tabSlope / w); + + + rs.triangleMeshBegin(vtx, idx); + rs.triangleMeshAddVertex_XYZ_ST(w1, h1, z, -stScale, 0); + rs.triangleMeshAddVertex_XYZ_ST(w2, h2, z, -stScale2, 1); + rs.triangleMeshAddVertex_XYZ_ST(w3, h2, z, stScale2, 1); + rs.triangleMeshAddVertex_XYZ_ST(w4, h1, z, stScale, 0); + rs.triangleMeshAddTriangle(0,1,2); + rs.triangleMeshAddTriangle(0,2,3); + return rs.triangleMeshCreate(); + } + + + +} + + diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java new file mode 100644 index 0000000..8f48335 --- /dev/null +++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rollo; + +import java.io.Writer; + +import android.renderscript.RenderScript; +import android.renderscript.ProgramVertexAlloc; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.KeyEvent; +import android.view.MotionEvent; + +public class RolloRS { + //public static final int STATE_SELECTED_ID = 0; + public static final int STATE_DONE = 1; + //public static final int STATE_PRESSURE = 2; + public static final int STATE_ZOOM = 3; + //public static final int STATE_WARP = 4; + public static final int STATE_ORIENTATION = 5; + public static final int STATE_SELECTION = 6; + public static final int STATE_FIRST_VISIBLE = 7; + public static final int STATE_COUNT = 8; + public static final int STATE_TOUCH = 9; + + + public RolloRS() { + } + + public void init(RenderScript rs, Resources res, int width, int height) { + mRS = rs; + mRes = res; + initNamed(); + initRS(); + } + + public void setPosition(float column) { + mAllocStateBuf[STATE_FIRST_VISIBLE] = (int)(column * (-20)); + mAllocState.data(mAllocStateBuf); + } + + public void setTouch(boolean touch) { + mAllocStateBuf[STATE_TOUCH] = touch ? 1 : 0; + mAllocState.data(mAllocStateBuf); + } + + public void setZoom(float z) { + //Log.e("rs", "zoom " + Float.toString(z)); + + mAllocStateBuf[STATE_ZOOM] = (int)(z * 1000.f); + mAllocState.data(mAllocStateBuf); + } + + public void setSelected(int index) { + Log.e("rs", "setSelected " + Integer.toString(index)); + + mAllocStateBuf[STATE_SELECTION] = index; + mAllocStateBuf[STATE_DONE] = 1; + mAllocState.data(mAllocStateBuf); + } + + + private Resources mRes; + private RenderScript mRS; + private RenderScript.Script mScript; + private RenderScript.Sampler mSampler; + private RenderScript.ProgramFragmentStore mPFSBackground; + private RenderScript.ProgramFragment mPFBackground; + private RenderScript.ProgramFragment mPFImages; + private RenderScript.ProgramVertex mPV; + private ProgramVertexAlloc mPVAlloc; + private RenderScript.Allocation[] mIcons; + private RenderScript.Allocation mIconPlate; + + private int[] mAllocStateBuf; + private RenderScript.Allocation mAllocState; + + private int[] mAllocIconIDBuf; + private RenderScript.Allocation mAllocIconID; + + private int[] mAllocScratchBuf; + private RenderScript.Allocation mAllocScratch; + + private void initNamed() { + mRS.samplerBegin(); + mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN, + RenderScript.SamplerValue.LINEAR);//_MIP_LINEAR); + mRS.samplerSet(RenderScript.SamplerParam.FILTER_MAG, + RenderScript.SamplerValue.LINEAR); + mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_S, + RenderScript.SamplerValue.CLAMP); + mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_T, + RenderScript.SamplerValue.CLAMP); + mSampler = mRS.samplerCreate(); + + + mRS.programFragmentBegin(null, null); + mRS.programFragmentSetTexEnable(0, true); + mRS.programFragmentSetTexEnvMode(0, RenderScript.EnvMode.MODULATE); + mPFImages = mRS.programFragmentCreate(); + mPFImages.setName("PF"); + mPFImages.bindSampler(mSampler, 0); + + mRS.programFragmentStoreBegin(null, null); + mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.LESS); + mRS.programFragmentStoreDitherEnable(false); + mRS.programFragmentStoreDepthMask(false); + mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE, + RenderScript.BlendDstFunc.ONE); + mPFSBackground = mRS.programFragmentStoreCreate(); + mPFSBackground.setName("PFS"); + + mPVAlloc = new ProgramVertexAlloc(mRS); + mRS.programVertexBegin(null, null); + mRS.programVertexSetTextureMatrixEnable(true); + mPV = mRS.programVertexCreate(); + mPV.setName("PV"); + mPV.bindAllocation(0, mPVAlloc.mAlloc); + + + + mPVAlloc.setupProjectionNormalized(320, 480); + //mPVAlloc.setupOrthoNormalized(320, 480); + mRS.contextBindProgramVertex(mPV); + + mAllocScratchBuf = new int[32]; + for(int ct=0; ct < mAllocScratchBuf.length; ct++) { + mAllocScratchBuf[ct] = 0; + } + mAllocScratch = mRS.allocationCreatePredefSized( + RenderScript.ElementPredefined.USER_I32, mAllocScratchBuf.length); + mAllocScratch.data(mAllocScratchBuf); + + Log.e("rs", "Done loading named"); + + + + { + mIcons = new RenderScript.Allocation[4]; + mAllocIconIDBuf = new int[mIcons.length]; + mAllocIconID = mRS.allocationCreatePredefSized( + RenderScript.ElementPredefined.USER_I32, mAllocIconIDBuf.length); + + + Bitmap b; + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inScaled = false; + + b = BitmapFactory.decodeResource(mRes, R.raw.browser, opts); + mIcons[0] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true); + + b = BitmapFactory.decodeResource(mRes, R.raw.market, opts); + mIcons[1] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true); + + b = BitmapFactory.decodeResource(mRes, R.raw.photos, opts); + mIcons[2] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true); + + b = BitmapFactory.decodeResource(mRes, R.raw.settings, opts); + mIcons[3] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true); + + for(int ct=0; ct < mIcons.length; ct++) { + mIcons[ct].uploadToTexture(0); + mAllocIconIDBuf[ct] = mIcons[ct].getID(); + } + mAllocIconID.data(mAllocIconIDBuf); + + RenderScript.Element e = mRS.elementGetPredefined(RenderScript.ElementPredefined.RGB_565); + mRS.typeBegin(e); + mRS.typeAdd(RenderScript.Dimension.X, 64); + mRS.typeAdd(RenderScript.Dimension.Y, 64); + RenderScript.Type t = mRS.typeCreate(); + mIconPlate = mRS.allocationCreateTyped(t); + //t.destroy(); + //e.destroy(); + + int tmp[] = new int[64 * 32]; + for(int ct = 0; ct < (64*32); ct++) { + tmp[ct] = 7 | (13 << 5) | (7 << 11); + tmp[ct] = tmp[ct] | (tmp[ct] << 16); + } + for(int ct = 0; ct < 32; ct++) { + tmp[ct] = 0; + tmp[ct + (63*32)] = 0; + } + for(int ct = 0; ct < 64; ct++) { + tmp[ct * 32] = 0; + tmp[ct * 32 + 31] = 0; + } + mIconPlate.data(tmp); + Log.e("xx", "plate"); + mIconPlate.uploadToTexture(0); + mIconPlate.setName("Plate"); + mPFImages.bindTexture(mIconPlate, 0); + } + + } + + + + private void initRS() { + mRS.scriptCBegin(); + mRS.scriptCSetClearColor(0.0f, 0.0f, 0.0f, 0.0f); + mRS.scriptCSetScript(mRes, R.raw.rollo); + //mRS.scriptCSetScript(mRes, R.raw.rollo2); + mRS.scriptCSetRoot(true); + //mRS.scriptCSetClearDepth(0); + mScript = mRS.scriptCCreate(); + + mAllocStateBuf = new int[] {0, 0, 0, 8, 0, 0, 0, 0, 38, 0, 0}; + mAllocState = mRS.allocationCreatePredefSized( + RenderScript.ElementPredefined.USER_I32, mAllocStateBuf.length); + mScript.bindAllocation(mAllocState, 0); + mScript.bindAllocation(mAllocIconID, 1); + mScript.bindAllocation(mAllocScratch, 2); + setPosition(0); + setZoom(1); + + //RenderScript.File f = mRS.fileOpen("/sdcard/test.a3d"); + + mRS.contextBindRootScript(mScript); + } +} + + + diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java new file mode 100644 index 0000000..b5e02af --- /dev/null +++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rollo; + +import java.io.Writer; +import java.util.ArrayList; +import java.util.concurrent.Semaphore; +import java.lang.Float; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.graphics.PixelFormat; + + +public class RolloView extends RSSurfaceView { + public RolloView(Context context) { + super(context); + setFocusable(true); + getHolder().setFormat(PixelFormat.TRANSLUCENT); + } + + private RenderScript mRS; + private RolloRS mRender; + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + + mRS = createRenderScript(); + mRender = new RolloRS(); + mRender.init(mRS, getResources(), w, h); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + // break point at here + // this method doesn't work when 'extends View' include 'extends ScrollView'. + return super.onKeyDown(keyCode, event); + } + + boolean mControlMode = false; + boolean mZoomMode = false; + boolean mFlingMode = false; + float mFlingX = 0; + float mFlingY = 0; + float mColumn = -1; + float mOldColumn; + float mZoom = 1; + + int mIconCount = 38; + int mRows = 4; + int mColumns = (mIconCount + mRows - 1) / mRows; + + float mMaxZoom = ((float)mColumns) / 3.f; + + + void setColumn(boolean clamp) + { + //Log.e("rs", " col = " + Float.toString(mColumn)); + float c = mColumn; + if(c > (mColumns -2)) { + c = (mColumns -2); + } + if(c < 1) { + c = 1; + } + mRender.setPosition(c); + if(clamp) { + mColumn = c; + } + } + + void computeSelection(float x, float y) + { + float col = mColumn + (x - 0.5f) * 3; + int iCol = (int)(col + 0.25f); + + float row = (y / 0.8f) * mRows; + int iRow = (int)(row - 0.25f); + + mRender.setSelected(iCol * mRows + iRow); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) + { + boolean ret = true; + int act = ev.getAction(); + if (act == ev.ACTION_UP) { + ret = false; + } + + float nx = ev.getX() / getWidth(); + float ny = ev.getY() / getHeight(); + + mRender.setTouch(ret); + + if((ny > 0.85f) || mControlMode) { + mFlingMode = false; + + // Projector control + if((nx > 0.2f) && (nx < 0.8f) || mControlMode) { + if(act != ev.ACTION_UP) { + float zoom = mMaxZoom; + if(mControlMode) { + if(!mZoomMode) { + zoom = 1.f; + } + float dx = nx - mFlingX; + + if((ny < 0.9) && mZoomMode) { + zoom = mMaxZoom - ((0.9f - ny) * 10.f); + if(zoom < 1) { + zoom = 1; + mZoomMode = false; + } + mOldColumn = mColumn; + } + mColumn += dx * 4;// * zoom; + if(zoom > 1.01f) { + mColumn += (mZoom - zoom) * (nx - 0.5f) * 4 * zoom; + } + } else { + mOldColumn = mColumn; + mColumn = ((float)mColumns) / 2; + mControlMode = true; + mZoomMode = true; + } + mZoom = zoom; + mFlingX = nx; + mRender.setZoom(zoom); + } else { + if(mControlMode && (mZoom < 1.01f)) { + computeSelection(nx, ny); + } + mControlMode = false; + mColumn = mOldColumn; + mRender.setZoom(1.f); + } + } else { + // Do something with corners here.... + } + setColumn(true); + + } else { + // icon control + if(act != ev.ACTION_UP) { + if(mFlingMode) { + mColumn += (mFlingX - nx) * 4; + setColumn(true); + } + mFlingMode = true; + mFlingX = nx; + mFlingY = ny; + } else { + mFlingMode = false; + } + } + + + return ret; + } + + @Override + public boolean onTrackballEvent(MotionEvent ev) + { + float x = ev.getX(); + float y = ev.getY(); + //Float tx = new Float(x); + //Float ty = new Float(y); + //Log.e("rs", "tbe " + tx.toString() + ", " + ty.toString()); + + + return true; + } + +} + + diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec new file mode 100644 index 0000000..2f99808 --- /dev/null +++ b/libs/rs/rs.spec @@ -0,0 +1,449 @@ + + +ContextBindRootScript { + param RsScript sampler + } + +ContextBindProgramFragmentStore { + param RsProgramFragmentStore pgm + } + +ContextBindProgramFragment { + param RsProgramFragment pgm + } + +ContextBindProgramVertex { + param RsProgramVertex pgm + } + +AssignName { + param void *obj + param const char *name + param size_t len + } + +ElementBegin { +} + +ElementAddPredefined { + param RsElementPredefined predef + } + +ElementAdd { + param RsDataKind dataKind + param RsDataType dataType + param bool isNormalized + param size_t bits + } + +ElementCreate { + ret RsElement + } + +ElementGetPredefined { + param RsElementPredefined predef + ret RsElement + } + +ElementDestroy { + param RsElement ve + } + +TypeBegin { + param RsElement type + } + +TypeAdd { + param RsDimension dim + param size_t value + } + +TypeCreate { + ret RsType + } + +TypeDestroy { + param RsType p + } + +AllocationCreateTyped { + param RsType type + ret RsAllocation + } + +AllocationCreatePredefSized { + param RsElementPredefined predef + param size_t count + ret RsAllocation + } + +AllocationCreateSized { + param RsElement e + param size_t count + ret RsAllocation + } + +AllocationCreateFromFile { + param const char *file + param bool genMips + ret RsAllocation + } + +AllocationCreateFromBitmap { + param uint32_t width + param uint32_t height + param RsElementPredefined dstFmt + param RsElementPredefined srcFmt + param bool genMips + param const void * data + ret RsAllocation + } + + +AllocationUploadToTexture { + param RsAllocation alloc + param uint32_t baseMipLevel + } + +AllocationUploadToBufferObject { + param RsAllocation alloc + } + +AllocationDestroy { + param RsAllocation alloc + } + + +AllocationData { + param RsAllocation va + param const void * data + } + +Allocation1DSubData { + param RsAllocation va + param uint32_t xoff + param uint32_t count + param const void *data + } + +Allocation2DSubData { + param RsAllocation va + param uint32_t xoff + param uint32_t yoff + param uint32_t w + param uint32_t h + param const void *data + } + + +Adapter1DCreate { + ret RsAdapter1D + } + +Adapter1DBindAllocation { + param RsAdapter1D adapt + param RsAllocation alloc + } + +Adapter1DDestroy { + param RsAdapter1D adapter + } + +Adapter1DSetConstraint { + param RsAdapter1D adapter + param RsDimension dim + param uint32_t value + } + +Adapter1DData { + param RsAdapter1D adapter + param const void * data + } + +Adapter1DSubData { + param RsAdapter1D adapter + param uint32_t xoff + param uint32_t count + param const void *data + } + +Adapter2DCreate { + ret RsAdapter2D + } + +Adapter2DBindAllocation { + param RsAdapter2D adapt + param RsAllocation alloc + } + +Adapter2DDestroy { + param RsAdapter2D adapter + } + +Adapter2DSetConstraint { + param RsAdapter2D adapter + param RsDimension dim + param uint32_t value + } + +Adapter2DData { + param RsAdapter2D adapter + param const void *data + } + +Adapter2DSubData { + param RsAdapter2D adapter + param uint32_t xoff + param uint32_t yoff + param uint32_t w + param uint32_t h + param const void *data + } + +SamplerBegin { + } + +SamplerSet { + param RsSamplerParam p + param RsSamplerValue value + } + +SamplerCreate { + ret RsSampler + } + +SamplerDestroy { + param RsSampler s + } + +TriangleMeshBegin { + param RsElement vertex + param RsElement index + } + +TriangleMeshAddVertex { + param const void *vtx + } + +TriangleMeshAddTriangle { + param uint32_t idx1 + param uint32_t idx2 + param uint32_t idx3 + } + +TriangleMeshCreate { + ret RsTriangleMesh + } + +TriangleMeshDestroy { + param RsTriangleMesh mesh + } + +TriangleMeshRender { + param RsTriangleMesh vtm + } + +TriangleMeshRenderRange { + param RsTriangleMesh vtm + param uint32_t start + param uint32_t count + } + +ScriptDestroy { + param RsScript script + } + +ScriptBindAllocation { + param RsScript vtm + param RsAllocation va + param uint32_t slot + } + + +ScriptCBegin { + } + +ScriptCSetClearColor { + param float r + param float g + param float b + param float a + } + +ScriptCSetClearDepth { + param float depth + } + +ScriptCSetClearStencil { + param uint32_t stencil + } + +ScriptCAddType { + param RsType type + } + +ScriptCSetRoot { + param bool isRoot + } + +ScriptCSetScript { + param void * codePtr + } + +ScriptCSetText { + param const char * text + param uint32_t length + } + +ScriptCCreate { + ret RsScript + } + + +ProgramFragmentStoreBegin { + param RsElement in + param RsElement out + } + +ProgramFragmentStoreColorMask { + param bool r + param bool g + param bool b + param bool a + } + +ProgramFragmentStoreBlendFunc { + param RsBlendSrcFunc srcFunc + param RsBlendDstFunc destFunc + } + +ProgramFragmentStoreDepthMask { + param bool enable +} + +ProgramFragmentStoreDither { + param bool enable +} + +ProgramFragmentStoreDepthFunc { + param RsDepthFunc func +} + +ProgramFragmentStoreCreate { + ret RsProgramFragmentStore + } + +ProgramFragmentStoreDestroy { + param RsProgramFragmentStore pfs + } + + +ProgramFragmentBegin { + param RsElement in + param RsElement out + } + +ProgramFragmentBindTexture { + param RsProgramFragment pf + param uint32_t slot + param RsAllocation a + } + +ProgramFragmentBindSampler { + param RsProgramFragment pf + param uint32_t slot + param RsSampler s + } + +ProgramFragmentSetType { + param uint32_t slot + param RsType t + } + +ProgramFragmentSetEnvMode { + param uint32_t slot + param RsTexEnvMode env + } + +ProgramFragmentSetTexEnable { + param uint32_t slot + param bool enable + } + +ProgramFragmentCreate { + ret RsProgramFragment + } + +ProgramFragmentDestroy { + param RsProgramFragment pf + } + + +ProgramVertexBegin { + param RsElement in + param RsElement out + } + +ProgramVertexCreate { + ret RsProgramVertex + } + +ProgramVertexBindAllocation { + param RsProgramVertex vpgm + param uint32_t slot + param RsAllocation constants + } + +ProgramVertexSetType { + param uint32_t slot + param RsType constants + } + +ProgramVertexSetTextureMatrixEnable { + param bool enable + } + +ProgramVertexAddLight { + param RsLight light + } + +LightBegin { + } + +LightSetLocal { + param bool isLocal + } + +LightSetMonochromatic { + param bool isMono + } + +LightCreate { + ret RsLight light + } + +LightDestroy { + param RsLight light + } + +LightSetPosition { + param RsLight light + param float x + param float y + param float z + } + +LightSetColor { + param RsLight light + param float r + param float g + param float b + } + +FileOpen { + ret RsFile + param const char *name + param size_t len + } + + diff --git a/libs/rs/rsAdapter.cpp b/libs/rs/rsAdapter.cpp new file mode 100644 index 0000000..7ac2aed --- /dev/null +++ b/libs/rs/rsAdapter.cpp @@ -0,0 +1,245 @@ + +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" + +using namespace android; +using namespace android::renderscript; + + +Adapter1D::Adapter1D() +{ + reset(); +} + +Adapter1D::Adapter1D(Allocation *a) +{ + reset(); + setAllocation(a); +} + +void Adapter1D::reset() +{ + mY = 0; + mZ = 0; + mLOD = 0; + mFace = 0; +} + +void * Adapter1D::getElement(uint32_t x) +{ + rsAssert(mAllocation.get()); + rsAssert(mAllocation->getPtr()); + rsAssert(mAllocation->getType()); + uint8_t * ptr = static_cast<uint8_t *>(mAllocation->getPtr()); + ptr += mAllocation->getType()->getLODOffset(mLOD, x, mY); + return ptr; +} + +void Adapter1D::subData(uint32_t xoff, uint32_t count, const void *data) +{ + if (mAllocation.get() && mAllocation.get()->getType()) { + void *ptr = getElement(xoff); + count *= mAllocation.get()->getType()->getElementSizeBytes(); + memcpy(ptr, data, count); + } +} + +void Adapter1D::data(const void *data) +{ + memcpy(getElement(0), + data, + mAllocation.get()->getType()->getSizeBytes()); +} + +namespace android { +namespace renderscript { + +RsAdapter1D rsi_Adapter1DCreate(Context *rsc) +{ + return new Adapter1D(); +} + +void rsi_Adapter1DDestroy(Context *rsc, RsAdapter1D va) +{ + Adapter1D * a = static_cast<Adapter1D *>(va); + a->decRef(); +} + +void rsi_Adapter1DBindAllocation(Context *rsc, RsAdapter1D va, RsAllocation valloc) +{ + Adapter1D * a = static_cast<Adapter1D *>(va); + Allocation * alloc = static_cast<Allocation *>(valloc); + a->setAllocation(alloc); +} + +void rsi_Adapter1DSetConstraint(Context *rsc, RsAdapter1D va, RsDimension dim, uint32_t value) +{ + Adapter1D * a = static_cast<Adapter1D *>(va); + switch(dim) { + case RS_DIMENSION_X: + rsAssert(!"Cannot contrain X in an 1D adapter"); + return; + case RS_DIMENSION_Y: + a->setY(value); + break; + case RS_DIMENSION_Z: + a->setZ(value); + break; + case RS_DIMENSION_LOD: + a->setLOD(value); + break; + case RS_DIMENSION_FACE: + a->setFace(value); + break; + default: + rsAssert(!"Unimplemented constraint"); + return; + } +} + +void rsi_Adapter1DSubData(Context *rsc, RsAdapter1D va, uint32_t xoff, uint32_t count, const void *data) +{ + Adapter1D * a = static_cast<Adapter1D *>(va); + a->subData(xoff, count, data); +} + +void rsi_Adapter1DData(Context *rsc, RsAdapter1D va, const void *data) +{ + Adapter1D * a = static_cast<Adapter1D *>(va); + a->data(data); +} + +} +} + +////////////////////////// + +Adapter2D::Adapter2D() +{ + reset(); +} + +Adapter2D::Adapter2D(Allocation *a) +{ + reset(); + setAllocation(a); +} + +void Adapter2D::reset() +{ + mZ = 0; + mLOD = 0; + mFace = 0; +} + +void * Adapter2D::getElement(uint32_t x, uint32_t y) const +{ + rsAssert(mAllocation.get()); + rsAssert(mAllocation->getPtr()); + rsAssert(mAllocation->getType()); + uint8_t * ptr = static_cast<uint8_t *>(mAllocation->getPtr()); + ptr += mAllocation->getType()->getLODOffset(mLOD, x, y); + return ptr; +} + +void Adapter2D::subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data) +{ + rsAssert(mAllocation.get()); + rsAssert(mAllocation->getPtr()); + rsAssert(mAllocation->getType()); + + uint32_t eSize = mAllocation.get()->getType()->getElementSizeBytes(); + uint32_t lineSize = eSize * w; + uint32_t destW = getDimX(); + + const uint8_t *src = static_cast<const uint8_t *>(data); + for (uint32_t line=yoff; line < (yoff+h); line++) { + memcpy(getElement(xoff, line), src, lineSize); + src += lineSize; + } +} + +void Adapter2D::data(const void *data) +{ + memcpy(getElement(0,0), + data, + mAllocation.get()->getType()->getSizeBytes()); +} + + + +namespace android { +namespace renderscript { + +RsAdapter2D rsi_Adapter2DCreate(Context *rsc) +{ + return new Adapter2D(); +} + +void rsi_Adapter2DDestroy(Context *rsc, RsAdapter2D va) +{ + Adapter2D * a = static_cast<Adapter2D *>(va); + a->decRef(); +} + +void rsi_Adapter2DBindAllocation(Context *rsc, RsAdapter2D va, RsAllocation valloc) +{ + Adapter2D * a = static_cast<Adapter2D *>(va); + Allocation * alloc = static_cast<Allocation *>(valloc); + a->setAllocation(alloc); +} + +void rsi_Adapter2DSetConstraint(Context *rsc, RsAdapter2D va, RsDimension dim, uint32_t value) +{ + Adapter2D * a = static_cast<Adapter2D *>(va); + switch(dim) { + case RS_DIMENSION_X: + rsAssert(!"Cannot contrain X in an 2D adapter"); + return; + case RS_DIMENSION_Y: + rsAssert(!"Cannot contrain Y in an 2D adapter"); + break; + case RS_DIMENSION_Z: + a->setZ(value); + break; + case RS_DIMENSION_LOD: + a->setLOD(value); + break; + case RS_DIMENSION_FACE: + a->setFace(value); + break; + default: + rsAssert(!"Unimplemented constraint"); + return; + } +} + +void rsi_Adapter2DData(Context *rsc, RsAdapter2D va, const void *data) +{ + Adapter2D * a = static_cast<Adapter2D *>(va); + a->data(data); +} + +void rsi_Adapter2DSubData(Context *rsc, RsAdapter2D va, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data) +{ + Adapter2D * a = static_cast<Adapter2D *>(va); + a->subData(xoff, yoff, w, h, data); +} + +} +} diff --git a/libs/rs/rsAdapter.h b/libs/rs/rsAdapter.h new file mode 100644 index 0000000..865535e --- /dev/null +++ b/libs/rs/rsAdapter.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_ADAPTER_H +#define ANDROID_RS_ADAPTER_H + +#include "rsAllocation.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + +class Adapter1D : public ObjectBase +{ + +public: + // By policy this allocation will hold a pointer to the type + // but will not destroy it on destruction. + Adapter1D(); + Adapter1D(Allocation *); + void reset(); + void * getElement(uint32_t x); + + void setAllocation(Allocation *a) {mAllocation.set(a);} + + uint32_t getDimX() const {return mAllocation->getType()->getLODDimX(mLOD);} + + const Type * getBaseType() const {return mAllocation->getType();} + + inline void setY(uint32_t y) {mY = y;} + inline void setZ(uint32_t z) {mZ = z;} + inline void setLOD(uint32_t lod) {mLOD = lod;} + inline void setFace(uint32_t face) {mFace = face;} + //void setArray(uint32_t num, uint32_t value); + + void subData(uint32_t xoff, uint32_t count, const void *data); + void data(const void *data); + +protected: + ObjectBaseRef<Allocation> mAllocation; + uint32_t mY; + uint32_t mZ; + uint32_t mLOD; + uint32_t mFace; +}; + +class Adapter2D : public ObjectBase +{ + +public: + // By policy this allocation will hold a pointer to the type + // but will not destroy it on destruction. + Adapter2D(); + Adapter2D(Allocation *); + void reset(); + void * getElement(uint32_t x, uint32_t y) const; + + uint32_t getDimX() const {return mAllocation->getType()->getLODDimX(mLOD);} + uint32_t getDimY() const {return mAllocation->getType()->getLODDimY(mLOD);} + const Type * getBaseType() const {return mAllocation->getType();} + + void setAllocation(Allocation *a) {mAllocation.set(a);} + inline void setZ(uint32_t z) {mZ = z;} + inline void setLOD(uint32_t lod) {mLOD = lod;} + inline void setFace(uint32_t face) {mFace = face;} + //void setArray(uint32_t num, uint32_t value); + + void data(const void *data); + void subData(uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data); + +protected: + ObjectBaseRef<Allocation> mAllocation; + uint32_t mZ; + uint32_t mLOD; + uint32_t mFace; +}; + + +} +} +#endif + diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp new file mode 100644 index 0000000..c143307 --- /dev/null +++ b/libs/rs/rsAllocation.cpp @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> + +using namespace android; +using namespace android::renderscript; + +Allocation::Allocation(const Type *type) +{ + mPtr = NULL; + + mCpuWrite = false; + mCpuRead = false; + mGpuWrite = false; + mGpuRead = false; + + mReadWriteRatio = 0; + mUpdateSize = 0; + + mIsTexture = false; + mTextureID = 0; + + mIsVertexBuffer = false; + mBufferID = 0; + + mType.set(type); + mPtr = malloc(mType->getSizeBytes()); + if (!mPtr) { + LOGE("Allocation::Allocation, alloc failure"); + } + +} + +Allocation::~Allocation() +{ +} + +void Allocation::setCpuWritable(bool) +{ +} + +void Allocation::setGpuWritable(bool) +{ +} + +void Allocation::setCpuReadable(bool) +{ +} + +void Allocation::setGpuReadable(bool) +{ +} + +bool Allocation::fixAllocation() +{ + return false; +} + +void Allocation::uploadToTexture(uint32_t lodOffset) +{ + //rsAssert(!mTextureId); + rsAssert(lodOffset < mType->getLODCount()); + + GLenum type = mType->getElement()->getGLType(); + GLenum format = mType->getElement()->getGLFormat(); + + if (!type || !format) { + return; + } + + if (!mTextureID) { + glGenTextures(1, &mTextureID); + } + glBindTexture(GL_TEXTURE_2D, mTextureID); + + Adapter2D adapt(this); + for(uint32_t lod = 0; (lod + lodOffset) < mType->getLODCount(); lod++) { + adapt.setLOD(lod+lodOffset); + + uint16_t * ptr = static_cast<uint16_t *>(adapt.getElement(0,0)); + glTexImage2D(GL_TEXTURE_2D, lod, format, + adapt.getDimX(), adapt.getDimY(), + 0, format, type, ptr); + } +} + +void Allocation::uploadToBufferObject() +{ + rsAssert(!mType->getDimY()); + rsAssert(!mType->getDimZ()); + + if (!mBufferID) { + glGenBuffers(1, &mBufferID); + } + glBindBuffer(GL_ARRAY_BUFFER, mBufferID); + glBufferData(GL_ARRAY_BUFFER, mType->getSizeBytes(), getPtr(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void Allocation::data(const void *data) +{ + memcpy(mPtr, data, mType->getSizeBytes()); +} + +void Allocation::subData(uint32_t xoff, uint32_t count, const void *data) +{ + uint32_t eSize = mType->getElementSizeBytes(); + uint8_t * ptr = static_cast<uint8_t *>(mPtr); + ptr += eSize * xoff; + memcpy(ptr, data, count * eSize); +} + +void Allocation::subData(uint32_t xoff, uint32_t yoff, + uint32_t w, uint32_t h, const void *data) +{ + uint32_t eSize = mType->getElementSizeBytes(); + uint32_t lineSize = eSize * w; + uint32_t destW = mType->getDimX(); + + const uint8_t *src = static_cast<const uint8_t *>(data); + uint8_t *dst = static_cast<uint8_t *>(mPtr); + dst += eSize * (xoff + yoff * destW); + for (uint32_t line=yoff; line < (yoff+h); line++) { + uint8_t * ptr = static_cast<uint8_t *>(mPtr); + memcpy(dst, src, lineSize); + src += lineSize; + dst += destW * eSize; + } +} + +void Allocation::subData(uint32_t xoff, uint32_t yoff, uint32_t zoff, + uint32_t w, uint32_t h, uint32_t d, const void *data) +{ +} + + + +///////////////// +// + + +namespace android { +namespace renderscript { + +RsAllocation rsi_AllocationCreateTyped(Context *rsc, RsType vtype) +{ + const Type * type = static_cast<const Type *>(vtype); + + Allocation * alloc = new Allocation(type); + alloc->incRef(); + return alloc; +} + +RsAllocation rsi_AllocationCreatePredefSized(Context *rsc, RsElementPredefined t, size_t count) +{ + RsElement e = rsi_ElementGetPredefined(rsc, t); + return rsi_AllocationCreateSized(rsc, e, count); +} + +RsAllocation rsi_AllocationCreateSized(Context *rsc, RsElement e, size_t count) +{ + Type * type = new Type(); + type->setDimX(count); + type->setElement(static_cast<Element *>(e)); + type->compute(); + return rsi_AllocationCreateTyped(rsc, type); +} + +void rsi_AllocationUploadToTexture(Context *rsc, RsAllocation va, uint32_t baseMipLevel) +{ + Allocation *alloc = static_cast<Allocation *>(va); + alloc->uploadToTexture(baseMipLevel); +} + +void rsi_AllocationUploadToBufferObject(Context *rsc, RsAllocation va) +{ + Allocation *alloc = static_cast<Allocation *>(va); + alloc->uploadToBufferObject(); +} + +void rsi_AllocationDestroy(Context *rsc, RsAllocation) +{ +} + +static void mip565(const Adapter2D &out, const Adapter2D &in) +{ + uint32_t w = out.getDimX(); + uint32_t h = out.getDimY(); + + for (uint32_t y=0; y < w; y++) { + uint16_t *oPtr = static_cast<uint16_t *>(out.getElement(0, y)); + const uint16_t *i1 = static_cast<uint16_t *>(in.getElement(0, y*2)); + const uint16_t *i2 = static_cast<uint16_t *>(in.getElement(0, y*2+1)); + + for (uint32_t x=0; x < h; x++) { + *oPtr = rsBoxFilter565(i1[0], i1[1], i2[0], i2[1]); + oPtr ++; + i1 += 2; + i2 += 2; + } + } +} + +static void mip8888(const Adapter2D &out, const Adapter2D &in) +{ + uint32_t w = out.getDimX(); + uint32_t h = out.getDimY(); + + for (uint32_t y=0; y < w; y++) { + uint32_t *oPtr = static_cast<uint32_t *>(out.getElement(0, y)); + const uint32_t *i1 = static_cast<uint32_t *>(in.getElement(0, y*2)); + const uint32_t *i2 = static_cast<uint32_t *>(in.getElement(0, y*2+1)); + + for (uint32_t x=0; x < h; x++) { + *oPtr = rsBoxFilter8888(i1[0], i1[1], i2[0], i2[1]); + oPtr ++; + i1 += 2; + i2 += 2; + } + } + +} + + +typedef void (*ElementConverter_t)(void *dst, const void *src, uint32_t count); + +static void elementConverter_cpy_16(void *dst, const void *src, uint32_t count) +{ + memcpy(dst, src, count * 2); +} +static void elementConverter_cpy_8(void *dst, const void *src, uint32_t count) +{ + memcpy(dst, src, count); +} +static void elementConverter_cpy_32(void *dst, const void *src, uint32_t count) +{ + memcpy(dst, src, count * 4); +} + + +static void elementConverter_888_to_565(void *dst, const void *src, uint32_t count) +{ + uint16_t *d = static_cast<uint16_t *>(dst); + const uint8_t *s = static_cast<const uint8_t *>(src); + + while(count--) { + *d = rs888to565(s[0], s[1], s[2]); + d++; + s+= 3; + } +} + +static void elementConverter_8888_to_565(void *dst, const void *src, uint32_t count) +{ + uint16_t *d = static_cast<uint16_t *>(dst); + const uint8_t *s = static_cast<const uint8_t *>(src); + + while(count--) { + *d = rs888to565(s[0], s[1], s[2]); + d++; + s+= 4; + } +} + +static ElementConverter_t pickConverter(RsElementPredefined dstFmt, RsElementPredefined srcFmt) +{ + if ((dstFmt == RS_ELEMENT_RGB_565) && + (srcFmt == RS_ELEMENT_RGB_565)) { + return elementConverter_cpy_16; + } + + if ((dstFmt == RS_ELEMENT_RGB_565) && + (srcFmt == RS_ELEMENT_RGB_888)) { + return elementConverter_888_to_565; + } + + if ((dstFmt == RS_ELEMENT_RGB_565) && + (srcFmt == RS_ELEMENT_RGBA_8888)) { + return elementConverter_8888_to_565; + } + + if ((dstFmt == RS_ELEMENT_RGBA_8888) && + (srcFmt == RS_ELEMENT_RGBA_8888)) { + return elementConverter_cpy_32; + } + + LOGE("pickConverter, unsuported combo"); + return 0; +} + + +RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, uint32_t w, uint32_t h, RsElementPredefined dstFmt, RsElementPredefined srcFmt, bool genMips, const void *data) +{ + rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565)); + rsi_TypeAdd(rsc, RS_DIMENSION_X, w); + rsi_TypeAdd(rsc, RS_DIMENSION_Y, h); + if (genMips) { + rsi_TypeAdd(rsc, RS_DIMENSION_LOD, 1); + } + RsType type = rsi_TypeCreate(rsc); + + RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, type); + Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc); + if (texAlloc == NULL) { + LOGE("Memory allocation failure"); + return NULL; + } + texAlloc->incRef(); + + ElementConverter_t cvt = pickConverter(dstFmt, srcFmt); + cvt(texAlloc->getPtr(), data, w * h); + + if (genMips) { + Adapter2D adapt(texAlloc); + Adapter2D adapt2(texAlloc); + for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) { + adapt.setLOD(lod); + adapt2.setLOD(lod + 1); + mip565(adapt2, adapt); + } + } + + return texAlloc; +} + +RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool genMips) +{ + bool use32bpp = false; + + typedef struct _Win3xBitmapHeader + { + uint16_t type; + uint32_t totalSize; + uint32_t reserved; + uint32_t offset; + int32_t hdrSize; /* Size of this header in bytes */ + int32_t width; /* Image width in pixels */ + int32_t height; /* Image height in pixels */ + int16_t planes; /* Number of color planes */ + int16_t bpp; /* Number of bits per pixel */ + /* Fields added for Windows 3.x follow this line */ + int32_t compression; /* Compression methods used */ + int32_t sizeOfBitmap; /* Size of bitmap in bytes */ + int32_t horzResolution; /* Horizontal resolution in pixels per meter */ + int32_t vertResolution; /* Vertical resolution in pixels per meter */ + int32_t colorsUsed; /* Number of colors in the image */ + int32_t colorsImportant; /* Minimum number of important colors */ + } __attribute__((__packed__)) WIN3XBITMAPHEADER; + + _Win3xBitmapHeader hdr; + + FILE *f = fopen(file, "rb"); + if (f == NULL) { + LOGE("rsAllocationCreateFromBitmap failed to open file %s", file); + return NULL; + } + memset(&hdr, 0, sizeof(hdr)); + fread(&hdr, sizeof(hdr), 1, f); + + if (hdr.bpp != 24) { + LOGE("Unsuported BMP type"); + fclose(f); + return NULL; + } + + int32_t texWidth = rsHigherPow2(hdr.width); + int32_t texHeight = rsHigherPow2(hdr.height); + + if (use32bpp) { + rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGBA_8888)); + } else { + rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565)); + } + rsi_TypeAdd(rsc, RS_DIMENSION_X, texWidth); + rsi_TypeAdd(rsc, RS_DIMENSION_Y, texHeight); + if (genMips) { + rsi_TypeAdd(rsc, RS_DIMENSION_LOD, 1); + } + RsType type = rsi_TypeCreate(rsc); + + RsAllocation vTexAlloc = rsi_AllocationCreateTyped(rsc, type); + Allocation *texAlloc = static_cast<Allocation *>(vTexAlloc); + texAlloc->incRef(); + if (texAlloc == NULL) { + LOGE("Memory allocation failure"); + fclose(f); + return NULL; + } + + // offset to letterbox if height is not pow2 + Adapter2D adapt(texAlloc); + uint8_t * fileInBuf = new uint8_t[texWidth * 3]; + uint32_t yOffset = (hdr.width - hdr.height) / 2; + + if (use32bpp) { + uint8_t *tmp = static_cast<uint8_t *>(adapt.getElement(0, yOffset)); + for (int y=0; y < hdr.height; y++) { + fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET); + fread(fileInBuf, 1, hdr.width * 3, f); + for(int x=0; x < hdr.width; x++) { + tmp[0] = fileInBuf[x*3 + 2]; + tmp[1] = fileInBuf[x*3 + 1]; + tmp[2] = fileInBuf[x*3]; + tmp[3] = 0xff; + tmp += 4; + } + } + } else { + uint16_t *tmp = static_cast<uint16_t *>(adapt.getElement(0, yOffset)); + for (int y=0; y < hdr.height; y++) { + fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET); + fread(fileInBuf, 1, hdr.width * 3, f); + for(int x=0; x < hdr.width; x++) { + *tmp = rs888to565(fileInBuf[x*3 + 2], fileInBuf[x*3 + 1], fileInBuf[x*3]); + tmp++; + } + } + } + + fclose(f); + delete [] fileInBuf; + + if (genMips) { + Adapter2D adapt2(texAlloc); + for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) { + adapt.setLOD(lod); + adapt2.setLOD(lod + 1); + if (use32bpp) { + mip8888(adapt2, adapt); + } else { + mip565(adapt2, adapt); + } + } + } + + return texAlloc; +} + + +void rsi_AllocationData(Context *rsc, RsAllocation va, const void *data) +{ + Allocation *a = static_cast<Allocation *>(va); + a->data(data); +} + +void rsi_Allocation1DSubData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t count, const void *data) +{ + Allocation *a = static_cast<Allocation *>(va); + a->subData(xoff, count, data); +} + +void rsi_Allocation2DSubData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t yoff, uint32_t w, uint32_t h, const void *data) +{ + Allocation *a = static_cast<Allocation *>(va); + a->subData(xoff, yoff, w, h, data); +} + + +} +} diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h new file mode 100644 index 0000000..d0b91fd --- /dev/null +++ b/libs/rs/rsAllocation.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_STRUCTURED_ALLOCATION_H +#define ANDROID_STRUCTURED_ALLOCATION_H + +#include "rsType.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + + +class Allocation : public ObjectBase +{ + // The graphics equilivent of malloc. The allocation contains a structure of elements. + + +public: + // By policy this allocation will hold a pointer to the type + // but will not destroy it on destruction. + Allocation(const Type *); + virtual ~Allocation(); + + void setCpuWritable(bool); + void setGpuWritable(bool); + void setCpuReadable(bool); + void setGpuReadable(bool); + + bool fixAllocation(); + + void * getPtr() const {return mPtr;} + const Type * getType() const {return mType.get();} + + void uploadToTexture(uint32_t lodOffset = 0); + uint32_t getTextureID() const {return mTextureID;} + + void uploadToBufferObject(); + uint32_t getBufferObjectID() const {return mBufferID;} + + + void data(const void *data); + void subData(uint32_t xoff, uint32_t count, const void *data); + void subData(uint32_t xoff, uint32_t yoff, + uint32_t w, uint32_t h, const void *data); + void subData(uint32_t xoff, uint32_t yoff, uint32_t zoff, + uint32_t w, uint32_t h, uint32_t d, const void *data); + +protected: + ObjectBaseRef<const Type> mType; + void * mPtr; + + // Usage restrictions + bool mCpuWrite; + bool mCpuRead; + bool mGpuWrite; + bool mGpuRead; + + // more usage hint data from the application + // which can be used by a driver to pick the best memory type. + // Likely ignored for now + float mReadWriteRatio; + float mUpdateSize; + + + // Is this a legal structure to be used as a texture source. + // Initially this will require 1D or 2D and color data + bool mIsTexture; + uint32_t mTextureID; + + // Is this a legal structure to be used as a vertex source. + // Initially this will require 1D and x(yzw). Additional per element data + // is allowed. + bool mIsVertexBuffer; + uint32_t mBufferID; + +}; + +} +} +#endif + diff --git a/libs/utils/executablepath_darwin.cpp b/libs/rs/rsComponent.cpp index 2e3c3a0..a931811 100644 --- a/libs/utils/executablepath_darwin.cpp +++ b/libs/rs/rsComponent.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,18 +14,30 @@ * limitations under the License. */ -#include <utils/executablepath.h> -#import <Carbon/Carbon.h> -#include <unistd.h> +#include "rsComponent.h" -void executablepath(char s[PATH_MAX]) +using namespace android; +using namespace android::renderscript; + + +Component::Component() { - ProcessSerialNumber psn; - GetCurrentProcess(&psn); - CFDictionaryRef dict; - dict = ProcessInformationCopyDictionary(&psn, 0xffffffff); - CFStringRef value = (CFStringRef)CFDictionaryGetValue(dict, - CFSTR("CFBundleExecutable")); - CFStringGetCString(value, s, PATH_MAX+1, kCFStringEncodingUTF8); + mType = FLOAT; + mKind = NONE; + mIsNormalized = false; + mBits = 0; } +Component::Component( + DataKind dk, DataType dt, + bool isNormalized, uint32_t bits) +{ + mType = dt; + mKind = dk; + mIsNormalized = isNormalized; + mBits = bits; +} + +Component::~Component() +{ +} diff --git a/libs/rs/rsComponent.h b/libs/rs/rsComponent.h new file mode 100644 index 0000000..e1b0585 --- /dev/null +++ b/libs/rs/rsComponent.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_STRUCTURED_COMPONENT_H +#define ANDROID_RS_STRUCTURED_COMPONENT_H + +#include "rsUtils.h" +#include "rsObjectBase.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + +class Component : public ObjectBase +{ +public: + enum DataType { + FLOAT, + UNSIGNED, + SIGNED + }; + + enum DataKind { + NONE, + RED, GREEN, BLUE, ALPHA, LUMINANCE, INTENSITY, + X, Y, Z, W, + S, T, Q, R, + NX, NY, NZ, + INDEX, + USER + }; + + + Component(DataKind dk, DataType dt, bool isNormalized, uint32_t bits); + virtual ~Component(); + + DataType getType() const {return mType;} + bool getIsNormalized() const {return mIsNormalized;} + DataKind getKind() const {return mKind;} + uint32_t getBits() const {return mBits;} + +protected: + + DataType mType; + bool mIsNormalized; + DataKind mKind; + uint32_t mBits; + +private: + Component(); +}; + + +} +} + +#endif //ANDROID_RS_STRUCTURED_COMPONENT_H + diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp new file mode 100644 index 0000000..78b8bf8 --- /dev/null +++ b/libs/rs/rsContext.cpp @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsDevice.h" +#include "rsContext.h" +#include "rsThreadIO.h" +#include "utils/String8.h" +#include <ui/FramebufferNativeWindow.h> + +#include <GLES/gl.h> +#include <GLES/glext.h> + +using namespace android; +using namespace android::renderscript; + +Context * Context::gCon = NULL; +pthread_key_t Context::gThreadTLSKey = 0; + +void Context::initEGL() +{ + mNumConfigs = -1; + + EGLint s_configAttribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, +#if 1 + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, +#else + EGL_RED_SIZE, 5, + EGL_GREEN_SIZE, 6, + EGL_BLUE_SIZE, 5, +#endif + EGL_DEPTH_SIZE, 16, + EGL_NONE + }; + + mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + eglInitialize(mDisplay, &mMajorVersion, &mMinorVersion); + eglChooseConfig(mDisplay, s_configAttribs, &mConfig, 1, &mNumConfigs); + + if (mWndSurface) { + mSurface = eglCreateWindowSurface(mDisplay, mConfig, mWndSurface, + NULL); + } else { + mSurface = eglCreateWindowSurface(mDisplay, mConfig, + android_createDisplaySurface(), + NULL); + } + + mContext = eglCreateContext(mDisplay, mConfig, NULL, NULL); + eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); + eglQuerySurface(mDisplay, mSurface, EGL_WIDTH, &mWidth); + eglQuerySurface(mDisplay, mSurface, EGL_HEIGHT, &mHeight); +} + +bool Context::runScript(Script *s, uint32_t launchID) +{ + ObjectBaseRef<ProgramFragment> frag(mFragment); + ObjectBaseRef<ProgramVertex> vtx(mVertex); + ObjectBaseRef<ProgramFragmentStore> store(mFragmentStore); + + bool ret = s->run(this, launchID); + + mFragment.set(frag); + mVertex.set(vtx); + mFragmentStore.set(store); + return true; + +} + + +bool Context::runRootScript() +{ + rsAssert(mRootScript->mEnviroment.mIsRoot); + + glColor4f(1,1,1,1); + glEnable(GL_LIGHT0); + glViewport(0, 0, mWidth, mHeight); + + glDepthMask(GL_TRUE); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + glClearColor(mRootScript->mEnviroment.mClearColor[0], + mRootScript->mEnviroment.mClearColor[1], + mRootScript->mEnviroment.mClearColor[2], + mRootScript->mEnviroment.mClearColor[3]); + glClearDepthf(mRootScript->mEnviroment.mClearDepth); + glClear(GL_COLOR_BUFFER_BIT); + glClear(GL_DEPTH_BUFFER_BIT); + + return runScript(mRootScript.get(), 0); +} + +void Context::setupCheck() +{ + if (mFragmentStore.get()) { + mFragmentStore->setupGL(); + } + if (mFragment.get()) { + mFragment->setupGL(); + } + if (mVertex.get()) { + mVertex->setupGL(); + } + +} + + +void * Context::threadProc(void *vrsc) +{ + Context *rsc = static_cast<Context *>(vrsc); + + gIO = new ThreadIO(); + rsc->initEGL(); + + ScriptTLSStruct *tlsStruct = new ScriptTLSStruct; + if (!tlsStruct) { + LOGE("Error allocating tls storage"); + return NULL; + } + tlsStruct->mContext = rsc; + tlsStruct->mScript = NULL; + int status = pthread_setspecific(rsc->gThreadTLSKey, tlsStruct); + if (status) { + LOGE("pthread_setspecific %i", status); + } + + rsc->mStateVertex.init(rsc, rsc->mWidth, rsc->mHeight); + rsc->setVertex(NULL); + rsc->mStateFragment.init(rsc, rsc->mWidth, rsc->mHeight); + rsc->setFragment(NULL); + rsc->mStateFragmentStore.init(rsc, rsc->mWidth, rsc->mHeight); + rsc->setFragmentStore(NULL); + + rsc->mRunning = true; + bool mDraw = true; + while (!rsc->mExit) { + mDraw |= gIO->playCoreCommands(rsc, !mDraw); + mDraw &= (rsc->mRootScript.get() != NULL); + + if (mDraw) { + mDraw = rsc->runRootScript(); + eglSwapBuffers(rsc->mDisplay, rsc->mSurface); + } + } + + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(rsc->mDisplay, rsc->mSurface); + eglTerminate(rsc->mDisplay); + return NULL; +} + +Context::Context(Device *dev, Surface *sur) +{ + dev->addContext(this); + mDev = dev; + mRunning = false; + mExit = false; + + // see comment in header + gCon = this; + + int status; + pthread_attr_t threadAttr; + + status = pthread_key_create(&gThreadTLSKey, NULL); + if (status) { + LOGE("Failed to init thread tls key."); + return; + } + + status = pthread_attr_init(&threadAttr); + if (status) { + LOGE("Failed to init thread attribute."); + return; + } + + sched_param sparam; + sparam.sched_priority = ANDROID_PRIORITY_DISPLAY; + pthread_attr_setschedparam(&threadAttr, &sparam); + + mWndSurface = sur; + + LOGV("RS Launching thread"); + status = pthread_create(&mThreadId, &threadAttr, threadProc, this); + if (status) { + LOGE("Failed to start rs context thread."); + } + + while(!mRunning) { + sleep(1); + } + + pthread_attr_destroy(&threadAttr); +} + +Context::~Context() +{ + mExit = true; + void *res; + + int status = pthread_join(mThreadId, &res); + + if (mDev) { + mDev->removeContext(this); + pthread_key_delete(gThreadTLSKey); + } +} + +void Context::swapBuffers() +{ + eglSwapBuffers(mDisplay, mSurface); +} + +void rsContextSwap(RsContext vrsc) +{ + Context *rsc = static_cast<Context *>(vrsc); + rsc->swapBuffers(); +} + +void Context::setRootScript(Script *s) +{ + mRootScript.set(s); +} + +void Context::setFragmentStore(ProgramFragmentStore *pfs) +{ + if (pfs == NULL) { + mFragmentStore.set(mStateFragmentStore.mDefault); + } else { + mFragmentStore.set(pfs); + } + mFragmentStore->setupGL(); +} + +void Context::setFragment(ProgramFragment *pf) +{ + if (pf == NULL) { + mFragment.set(mStateFragment.mDefault); + } else { + mFragment.set(pf); + } + mFragment->setupGL(); +} + +void Context::setVertex(ProgramVertex *pv) +{ + if (pv == NULL) { + mVertex.set(mStateVertex.mDefault); + } else { + mVertex.set(pv); + } + mVertex->setupGL(); +} + +void Context::assignName(ObjectBase *obj, const char *name, uint32_t len) +{ + rsAssert(!obj->getName()); + obj->setName(name, len); + mNames.add(obj); +} + +void Context::removeName(ObjectBase *obj) +{ + for(size_t ct=0; ct < mNames.size(); ct++) { + if (obj == mNames[ct]) { + mNames.removeAt(ct); + return; + } + } +} + +ObjectBase * Context::lookupName(const char *name) const +{ + for(size_t ct=0; ct < mNames.size(); ct++) { + if (!strcmp(name, mNames[ct]->getName())) { + return mNames[ct]; + } + } + return NULL; +} + +void Context::appendNameDefines(String8 *str) const +{ + char buf[256]; + for (size_t ct=0; ct < mNames.size(); ct++) { + str->append("#define NAMED_"); + str->append(mNames[ct]->getName()); + str->append(" "); + sprintf(buf, "%i\n", (int)mNames[ct]); + str->append(buf); + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////// +// + +namespace android { +namespace renderscript { + + +void rsi_ContextBindRootScript(Context *rsc, RsScript vs) +{ + Script *s = static_cast<Script *>(vs); + rsc->setRootScript(s); +} + +void rsi_ContextBindSampler(Context *rsc, uint32_t slot, RsSampler vs) +{ + Sampler *s = static_cast<Sampler *>(vs); + + if (slot > RS_MAX_SAMPLER_SLOT) { + LOGE("Invalid sampler slot"); + return; + } + + s->bindToContext(&rsc->mStateSampler, slot); +} + +void rsi_ContextBindProgramFragmentStore(Context *rsc, RsProgramFragmentStore vpfs) +{ + ProgramFragmentStore *pfs = static_cast<ProgramFragmentStore *>(vpfs); + rsc->setFragmentStore(pfs); +} + +void rsi_ContextBindProgramFragment(Context *rsc, RsProgramFragment vpf) +{ + ProgramFragment *pf = static_cast<ProgramFragment *>(vpf); + rsc->setFragment(pf); +} + +void rsi_ContextBindProgramVertex(Context *rsc, RsProgramVertex vpv) +{ + ProgramVertex *pv = static_cast<ProgramVertex *>(vpv); + rsc->setVertex(pv); +} + +void rsi_AssignName(Context *rsc, void * obj, const char *name, uint32_t len) +{ + ObjectBase *ob = static_cast<ObjectBase *>(obj); + rsc->assignName(ob, name, len); +} + + +} +} + + +RsContext rsContextCreate(RsDevice vdev, void *sur, uint32_t version) +{ + Device * dev = static_cast<Device *>(vdev); + Context *rsc = new Context(dev, (Surface *)sur); + return rsc; +} + +void rsContextDestroy(RsContext vrsc) +{ + Context * rsc = static_cast<Context *>(vrsc); + delete rsc; +} + diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h new file mode 100644 index 0000000..497dbcf --- /dev/null +++ b/libs/rs/rsContext.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_CONTEXT_H +#define ANDROID_RS_CONTEXT_H + +#include "rsUtils.h" + +#include <utils/Vector.h> +#include <ui/Surface.h> + +#include "rsType.h" +#include "rsMatrix.h" +#include "rsAllocation.h" +#include "rsTriangleMesh.h" +#include "rsMesh.h" +#include "rsDevice.h" +#include "rsScriptC.h" +#include "rsAllocation.h" +#include "rsAdapter.h" +#include "rsSampler.h" +#include "rsLight.h" +#include "rsProgramFragment.h" +#include "rsProgramFragmentStore.h" +#include "rsProgramVertex.h" + +#include "rsgApiStructs.h" +#include "rsLocklessFifo.h" + + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + +class Context +{ +public: + Context(Device *, Surface *); + ~Context(); + + static pthread_key_t gThreadTLSKey; + struct ScriptTLSStruct { + Context * mContext; + Script * mScript; + }; + + + //StructuredAllocationContext mStateAllocation; + ElementState mStateElement; + TypeState mStateType; + SamplerState mStateSampler; + ProgramFragmentState mStateFragment; + ProgramFragmentStoreState mStateFragmentStore; + ProgramVertexState mStateVertex; + LightState mStateLight; + + TriangleMeshContext mStateTriangleMesh; + + ScriptCState mScriptC; + + static Context * getContext() {return gCon;} + + void swapBuffers(); + void setRootScript(Script *); + void setVertex(ProgramVertex *); + void setFragment(ProgramFragment *); + void setFragmentStore(ProgramFragmentStore *); + + void updateSurface(void *sur); + + const ProgramFragment * getFragment() {return mFragment.get();} + const ProgramFragmentStore * getFragmentStore() {return mFragmentStore.get();} + + void setupCheck(); + + void assignName(ObjectBase *obj, const char *name, uint32_t len); + void removeName(ObjectBase *obj); + ObjectBase * lookupName(const char *name) const; + void appendNameDefines(String8 *str) const; + + + ProgramFragment * getDefaultProgramFragment() const { + return mStateFragment.mDefault.get(); + } + ProgramVertex * getDefaultProgramVertex() const { + return mStateVertex.mDefault.get(); + } + ProgramFragmentStore * getDefaultProgramFragmentStore() const { + return mStateFragmentStore.mDefault.get(); + } + +protected: + Device *mDev; + + EGLint mNumConfigs; + EGLint mMajorVersion; + EGLint mMinorVersion; + EGLConfig mConfig; + EGLContext mContext; + EGLSurface mSurface; + EGLint mWidth; + EGLint mHeight; + EGLDisplay mDisplay; + + bool mRunning; + bool mExit; + + pthread_t mThreadId; + + ObjectBaseRef<Script> mRootScript; + ObjectBaseRef<ProgramFragment> mFragment; + ObjectBaseRef<ProgramVertex> mVertex; + ObjectBaseRef<ProgramFragmentStore> mFragmentStore; + +private: + Context(); + + void initEGL(); + + bool runScript(Script *s, uint32_t launchID); + bool runRootScript(); + + static void * threadProc(void *); + + // todo: put in TLS + static Context *gCon; + Surface *mWndSurface; + + Vector<ObjectBase *> mNames; +}; + + +} +} +#endif diff --git a/libs/rs/rsDevice.cpp b/libs/rs/rsDevice.cpp new file mode 100644 index 0000000..1b3c41b --- /dev/null +++ b/libs/rs/rsDevice.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsDevice.h" +#include "rsContext.h" + +using namespace android; +using namespace android::renderscript; + +Device::Device() +{ + +} + +Device::~Device() +{ + +} + +void Device::addContext(Context *rsc) +{ + mContexts.add(rsc); +} + +void Device::removeContext(Context *rsc) +{ + for (size_t idx=0; idx < mContexts.size(); idx++) { + if (mContexts[idx] == rsc) { + mContexts.removeAt(idx); + break; + } + } +} + + + +RsDevice rsDeviceCreate() +{ + Device * d = new Device(); + return d; +} + +void rsDeviceDestroy(RsDevice dev) +{ + Device * d = static_cast<Device *>(dev); + delete d; + +} + diff --git a/libs/rs/rsDevice.h b/libs/rs/rsDevice.h new file mode 100644 index 0000000..156315f --- /dev/null +++ b/libs/rs/rsDevice.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_DEVICE_H +#define ANDROID_RS_DEVICE_H + +#include "rsUtils.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + +class Context; + +class Device { +public: + Device(); + ~Device(); + + void addContext(Context *); + void removeContext(Context *); + +protected: + Vector<Context *> mContexts; + + +}; + + + + + +} +} +#endif diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp new file mode 100644 index 0000000..069a128 --- /dev/null +++ b/libs/rs/rsElement.cpp @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" + +#include <GLES/gl.h> + +using namespace android; +using namespace android::renderscript; + +void ElementState::initPredefined() +{ + Component * u_8 = new Component(Component::USER, Component::UNSIGNED, true, 8); + Component * i_8 = new Component(Component::USER, Component::SIGNED, true, 8); + Component * u_16 = new Component(Component::USER, Component::UNSIGNED, true, 16); + Component * i_16 = new Component(Component::USER, Component::SIGNED, true, 16); + Component * u_32 = new Component(Component::USER, Component::UNSIGNED, true, 32); + Component * i_32 = new Component(Component::USER, Component::SIGNED, true, 32); + Component * f_32 = new Component(Component::USER, Component::FLOAT, true, 32); + + + Component * r_4 = new Component(Component::RED, Component::UNSIGNED, true, 4); + Component * r_5 = new Component(Component::RED, Component::UNSIGNED, true, 5); + Component * r_8 = new Component(Component::RED, Component::UNSIGNED, true, 8); + + Component * g_4 = new Component(Component::GREEN, Component::UNSIGNED, true, 4); + Component * g_5 = new Component(Component::GREEN, Component::UNSIGNED, true, 5); + Component * g_6 = new Component(Component::GREEN, Component::UNSIGNED, true, 6); + Component * g_8 = new Component(Component::GREEN, Component::UNSIGNED, true, 8); + + Component * b_4 = new Component(Component::BLUE, Component::UNSIGNED, true, 4); + Component * b_5 = new Component(Component::BLUE, Component::UNSIGNED, true, 5); + Component * b_8 = new Component(Component::BLUE, Component::UNSIGNED, true, 8); + + Component * a_1 = new Component(Component::ALPHA, Component::UNSIGNED, true, 1); + Component * a_4 = new Component(Component::ALPHA, Component::UNSIGNED, true, 4); + Component * a_8 = new Component(Component::ALPHA, Component::UNSIGNED, true, 8); + + Component * idx_16 = new Component(Component::INDEX, Component::UNSIGNED, false, 16); + Component * idx_32 = new Component(Component::INDEX, Component::UNSIGNED, false, 32); + + Component * x = new Component(Component::X, Component::FLOAT, false, 32); + Component * y = new Component(Component::Y, Component::FLOAT, false, 32); + Component * z = new Component(Component::Z, Component::FLOAT, false, 32); + + Component * nx = new Component(Component::NX, Component::FLOAT, false, 32); + Component * ny = new Component(Component::NY, Component::FLOAT, false, 32); + Component * nz = new Component(Component::NZ, Component::FLOAT, false, 32); + + Component * s = new Component(Component::S, Component::FLOAT, false, 32); + Component * t = new Component(Component::T, Component::FLOAT, false, 32); + + Element * e; + + e = new Element(1); + e->setComponent(0, u_8); + mPredefinedList.add(Predefined(RS_ELEMENT_USER_U8, e)); + + e = new Element(1); + e->setComponent(0, i_8); + mPredefinedList.add(Predefined(RS_ELEMENT_USER_I8, e)); + + e = new Element(1); + e->setComponent(0, u_16); + mPredefinedList.add(Predefined(RS_ELEMENT_USER_U16, e)); + + e = new Element(1); + e->setComponent(0, i_16); + mPredefinedList.add(Predefined(RS_ELEMENT_USER_I16, e)); + + e = new Element(1); + e->setComponent(0, u_32); + mPredefinedList.add(Predefined(RS_ELEMENT_USER_U32, e)); + + e = new Element(1); + e->setComponent(0, i_32); + mPredefinedList.add(Predefined(RS_ELEMENT_USER_I32, e)); + + e = new Element(1); + e->setComponent(0, f_32); + mPredefinedList.add(Predefined(RS_ELEMENT_USER_FLOAT, e)); + + e = new Element(1); + e->setComponent(0, a_8); + mPredefinedList.add(Predefined(RS_ELEMENT_A_8, e)); + + e = new Element(3); + e->setComponent(0, r_5); + e->setComponent(1, g_6); + e->setComponent(2, b_5); + mPredefinedList.add(Predefined(RS_ELEMENT_RGB_565, e)); + + e = new Element(4); + e->setComponent(0, r_5); + e->setComponent(1, g_5); + e->setComponent(2, b_5); + e->setComponent(3, a_1); + mPredefinedList.add(Predefined(RS_ELEMENT_RGBA_5551, e)); + + e = new Element(4); + e->setComponent(0, r_4); + e->setComponent(1, g_4); + e->setComponent(2, b_4); + e->setComponent(3, a_4); + mPredefinedList.add(Predefined(RS_ELEMENT_RGBA_4444, e)); + + e = new Element(3); + e->setComponent(0, r_8); + e->setComponent(1, g_8); + e->setComponent(2, b_8); + mPredefinedList.add(Predefined(RS_ELEMENT_RGB_888, e)); + + e = new Element(4); + e->setComponent(0, r_8); + e->setComponent(1, g_8); + e->setComponent(2, b_8); + e->setComponent(3, a_8); + mPredefinedList.add(Predefined(RS_ELEMENT_RGBA_8888, e)); + + e = new Element(1); + e->setComponent(0, idx_16); + mPredefinedList.add(Predefined(RS_ELEMENT_INDEX_16, e)); + + e = new Element(1); + e->setComponent(0, idx_32); + mPredefinedList.add(Predefined(RS_ELEMENT_INDEX_32, e)); + + e = new Element(2); + e->setComponent(0, x); + e->setComponent(1, y); + mPredefinedList.add(Predefined(RS_ELEMENT_XY_F32, e)); + + e = new Element(3); + e->setComponent(0, x); + e->setComponent(1, y); + e->setComponent(2, z); + mPredefinedList.add(Predefined(RS_ELEMENT_XYZ_F32, e)); + + e = new Element(4); + e->setComponent(0, s); + e->setComponent(1, t); + e->setComponent(2, x); + e->setComponent(3, y); + mPredefinedList.add(Predefined(RS_ELEMENT_ST_XY_F32, e)); + + e = new Element(5); + e->setComponent(0, s); + e->setComponent(1, t); + e->setComponent(2, x); + e->setComponent(3, y); + e->setComponent(4, z); + mPredefinedList.add(Predefined(RS_ELEMENT_ST_XYZ_F32, e)); + + e = new Element(6); + e->setComponent(0, nx); + e->setComponent(1, ny); + e->setComponent(2, nz); + e->setComponent(3, x); + e->setComponent(4, y); + e->setComponent(5, z); + mPredefinedList.add(Predefined(RS_ELEMENT_NORM_XYZ_F32, e)); + + e = new Element(8); + e->setComponent(0, nx); + e->setComponent(1, ny); + e->setComponent(2, nz); + e->setComponent(3, s); + e->setComponent(4, t); + e->setComponent(5, x); + e->setComponent(6, y); + e->setComponent(7, z); + mPredefinedList.add(Predefined(RS_ELEMENT_NORM_ST_XYZ_F32, e)); +} + + +Element::Element() +{ + mComponents = NULL; + mComponentCount = 0; +} + +Element::Element(uint32_t count) +{ + mComponents = new ObjectBaseRef<Component> [count]; + mComponentCount = count; +} + +Element::~Element() +{ + clear(); +} + +void Element::clear() +{ + delete [] mComponents; + mComponents = NULL; + mComponentCount = 0; +} + +void Element::setComponent(uint32_t idx, Component *c) +{ + rsAssert(!mComponents[idx].get()); + rsAssert(idx < mComponentCount); + mComponents[idx].set(c); + c->incRef(); +} + + +size_t Element::getSizeBits() const +{ + size_t total = 0; + for (size_t ct=0; ct < mComponentCount; ct++) { + total += mComponents[ct]->getBits(); + } + return total; +} + +size_t Element::getComponentOffsetBits(uint32_t componentNumber) const +{ + size_t offset = 0; + for (uint32_t ct = 0; ct < componentNumber; ct++) { + offset += mComponents[ct]->getBits(); + } + return offset; +} + +uint32_t Element::getGLType() const +{ + int bits[4]; + + if (mComponentCount > 4) { + return 0; + } + + for (uint32_t ct=0; ct < mComponentCount; ct++) { + bits[ct] = mComponents[ct]->getBits(); + if (mComponents[ct]->getType() != Component::UNSIGNED) { + return 0; + } + if (!mComponents[ct]->getIsNormalized()) { + return 0; + } + } + + switch(mComponentCount) { + case 1: + if (bits[0] == 8) { + return GL_UNSIGNED_BYTE; + } + return 0; + case 2: + if ((bits[0] == 8) && + (bits[1] == 8)) { + return GL_UNSIGNED_BYTE; + } + return 0; + case 3: + if ((bits[0] == 8) && + (bits[1] == 8) && + (bits[2] == 8)) { + return GL_UNSIGNED_BYTE; + } + if ((bits[0] == 5) && + (bits[1] == 6) && + (bits[2] == 5)) { + return GL_UNSIGNED_SHORT_5_6_5; + } + return 0; + case 4: + if ((bits[0] == 8) && + (bits[1] == 8) && + (bits[2] == 8) && + (bits[3] == 8)) { + return GL_UNSIGNED_BYTE; + } + if ((bits[0] == 4) && + (bits[1] == 4) && + (bits[2] == 4) && + (bits[3] == 4)) { + return GL_UNSIGNED_SHORT_4_4_4_4; + } + if ((bits[0] == 5) && + (bits[1] == 5) && + (bits[2] == 5) && + (bits[3] == 1)) { + return GL_UNSIGNED_SHORT_5_5_5_1; + } + } + return 0; +} + +uint32_t Element::getGLFormat() const +{ + switch(mComponentCount) { + case 1: + if (mComponents[0]->getKind() == Component::ALPHA) { + return GL_ALPHA; + } + if (mComponents[0]->getKind() == Component::LUMINANCE) { + return GL_LUMINANCE; + } + break; + case 2: + if ((mComponents[0]->getKind() == Component::LUMINANCE) && + (mComponents[1]->getKind() == Component::ALPHA)) { + return GL_LUMINANCE_ALPHA; + } + break; + case 3: + if ((mComponents[0]->getKind() == Component::RED) && + (mComponents[1]->getKind() == Component::GREEN) && + (mComponents[2]->getKind() == Component::BLUE)) { + return GL_RGB; + } + break; + case 4: + if ((mComponents[0]->getKind() == Component::RED) && + (mComponents[1]->getKind() == Component::GREEN) && + (mComponents[2]->getKind() == Component::BLUE) && + (mComponents[3]->getKind() == Component::ALPHA)) { + return GL_RGBA; + } + break; + } + return 0; +} + + +ElementState::ElementState() +{ +} + +ElementState::~ElementState() +{ +} + +///////////////////////////////////////// +// + +namespace android { +namespace renderscript { + +void rsi_ElementBegin(Context *rsc) +{ + rsc->mStateElement.mComponentBuildList.clear(); +} + +void rsi_ElementAddPredefined(Context *rsc, RsElementPredefined predef) +{ + ElementState * sec = &rsc->mStateElement; + + RsElement ve = rsi_ElementGetPredefined(rsc, predef); + const Element *e = static_cast<const Element *>(ve); + + for(size_t ct = 0; ct < sec->mPredefinedList[predef].mElement->getComponentCount(); ct++) { + sec->mComponentBuildList.add(sec->mPredefinedList[predef].mElement->getComponent(ct)); + } +} + +RsElement rsi_ElementGetPredefined(Context *rsc, RsElementPredefined predef) +{ + ElementState * sec = &rsc->mStateElement; + + if (!sec->mPredefinedList.size()) { + sec->initPredefined(); + } + + if ((predef < 0) || + (static_cast<uint32_t>(predef) >= sec->mPredefinedList.size())) { + LOGE("rsElementGetPredefined: Request for bad predefined type"); + // error + return NULL; + } + + rsAssert(sec->mPredefinedList[predef].mEnum == predef); + Element * e = sec->mPredefinedList[predef].mElement; + e->incRef(); + return e; +} + +void rsi_ElementAdd(Context *rsc, RsDataKind dk, RsDataType dt, bool isNormalized, size_t bits) +{ + ElementState * sec = &rsc->mStateElement; + +} + +RsElement rsi_ElementCreate(Context *rsc) +{ + ElementState * sec = &rsc->mStateElement; + + Element *se = new Element(sec->mComponentBuildList.size()); + sec->mAllElements.add(se); + + for (size_t ct = 0; ct < se->getComponentCount(); ct++) { + se->setComponent(ct, sec->mComponentBuildList[ct]); + } + + rsc->mStateElement.mComponentBuildList.clear(); + se->incRef(); + return se; +} + +void rsi_ElementDestroy(Context *rsc, RsElement vse) +{ + ElementState * sec = &rsc->mStateElement; + Element * se = static_cast<Element *>(vse); + + for (size_t ct = 0; ct < sec->mAllElements.size(); ct++) { + if (sec->mAllElements[ct] == se) { + sec->mAllElements.removeAt(ct); + break; + } + } + se->decRef(); +} + + +} +} diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h new file mode 100644 index 0000000..ea6fa8f --- /dev/null +++ b/libs/rs/rsElement.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_STRUCTURED_ELEMENT_H +#define ANDROID_STRUCTURED_ELEMENT_H + +#include "rsComponent.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + +// An element is a group of Components that occupies one cell in a structure. +class Element : public ObjectBase +{ +public: + Element(uint32_t count); + ~Element(); + + + void setComponent(uint32_t idx, Component *c); + + uint32_t getGLType() const; + uint32_t getGLFormat() const; + + + size_t getSizeBits() const; + size_t getSizeBytes() const { + return (getSizeBits() + 7) >> 3; + } + + size_t getComponentOffsetBits(uint32_t componentNumber) const; + size_t getComponentOffsetBytes(uint32_t componentNumber) const { + return (getComponentOffsetBits(componentNumber) + 7) >> 3; + } + + uint32_t getComponentCount() const {return mComponentCount;} + Component * getComponent(uint32_t idx) const {return mComponents[idx].get();} + +protected: + // deallocate any components that are part of this element. + void clear(); + + size_t mComponentCount; + ObjectBaseRef<Component> * mComponents; + //uint32_t *mOffsetTable; + + Element(); +}; + + +class ElementState { +public: + ElementState(); + ~ElementState(); + + Vector<Element *> mAllElements; + Vector<Component *> mComponentBuildList; + + + + struct Predefined { + Predefined() { + mElement = NULL; + } + Predefined(RsElementPredefined en, Element *e) { + mEnum = en; + mElement = e; + } + RsElementPredefined mEnum; + Element * mElement; + }; + Vector<Predefined> mPredefinedList; + + void initPredefined(); + +}; + + +} +} +#endif //ANDROID_STRUCTURED_ELEMENT_H diff --git a/libs/rs/rsFileA3D.cpp b/libs/rs/rsFileA3D.cpp new file mode 100644 index 0000000..86d294b --- /dev/null +++ b/libs/rs/rsFileA3D.cpp @@ -0,0 +1,384 @@ + +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" + + +#include <utils/String8.h> +#include "rsFileA3D.h" + +#include "rsMesh.h" + +using namespace android; +using namespace android::renderscript; + + + +FileA3D::FileA3D() +{ + mRsc = NULL; +} + +FileA3D::~FileA3D() +{ +} + +bool FileA3D::load(Context *rsc, FILE *f) +{ + char magicString[12]; + size_t len; + + LOGE("file open 1"); + len = fread(magicString, 1, 12, f); + if ((len != 12) || + memcmp(magicString, "Android3D_ff", 12)) { + return false; + } + + LOGE("file open 2"); + len = fread(&mMajorVersion, 1, sizeof(mMajorVersion), f); + if (len != sizeof(mMajorVersion)) { + return false; + } + + LOGE("file open 3"); + len = fread(&mMinorVersion, 1, sizeof(mMinorVersion), f); + if (len != sizeof(mMinorVersion)) { + return false; + } + + LOGE("file open 4"); + uint32_t flags; + len = fread(&flags, 1, sizeof(flags), f); + if (len != sizeof(flags)) { + return false; + } + mUse64BitOffsets = (flags & 1) != 0; + + LOGE("file open 64bit = %i", mUse64BitOffsets); + + if (mUse64BitOffsets) { + len = fread(&mDataSize, 1, sizeof(mDataSize), f); + if (len != sizeof(mDataSize)) { + return false; + } + } else { + uint32_t tmp; + len = fread(&tmp, 1, sizeof(tmp), f); + if (len != sizeof(tmp)) { + return false; + } + mDataSize = tmp; + } + + LOGE("file open size = %lli", mDataSize); + + // We should know enough to read the file in at this point. + fseek(f, SEEK_SET, 0); + mAlloc= malloc(mDataSize); + if (!mAlloc) { + return false; + } + mData = (uint8_t *)mAlloc; + len = fread(mAlloc, 1, mDataSize, f); + if (len != mDataSize) { + return false; + } + + LOGE("file start processing"); + return process(rsc); +} + +bool FileA3D::processIndex(Context *rsc, A3DIndexEntry *ie) +{ + bool ret = false; + IO io(mData + ie->mOffset, mUse64BitOffsets); + + LOGE("process index, type %i", ie->mType); + + switch(ie->mType) { + case CHUNK_ELEMENT: + processChunk_Element(rsc, &io, ie); + break; + case CHUNK_ELEMENT_SOURCE: + processChunk_ElementSource(rsc, &io, ie); + break; + case CHUNK_VERTICIES: + processChunk_Verticies(rsc, &io, ie); + break; + case CHUNK_MESH: + processChunk_Mesh(rsc, &io, ie); + break; + case CHUNK_PRIMITIVE: + processChunk_Primitive(rsc, &io, ie); + break; + default: + LOGE("FileA3D Unknown chunk type"); + break; + } + return (ie->mRsObj != NULL); +} + +bool FileA3D::process(Context *rsc) +{ + LOGE("process"); + IO io(mData + 12, mUse64BitOffsets); + bool ret = true; + + // Build the index first + LOGE("process 1"); + io.loadU32(); // major version, already loaded + io.loadU32(); // minor version, already loaded + LOGE("process 2"); + + io.loadU32(); // flags + io.loadOffset(); // filesize, already loaded. + LOGE("process 4"); + uint64_t mIndexOffset = io.loadOffset(); + uint64_t mStringOffset = io.loadOffset(); + + LOGE("process mIndexOffset= 0x%016llx", mIndexOffset); + LOGE("process mStringOffset= 0x%016llx", mStringOffset); + + IO index(mData + mIndexOffset, mUse64BitOffsets); + IO stringTable(mData + mStringOffset, mUse64BitOffsets); + + uint32_t stringEntryCount = stringTable.loadU32(); + LOGE("stringEntryCount %i", stringEntryCount); + mStrings.setCapacity(stringEntryCount); + mStringIndexValues.setCapacity(stringEntryCount); + if (stringEntryCount) { + uint32_t stringType = stringTable.loadU32(); + LOGE("stringType %i", stringType); + rsAssert(stringType==0); + for (uint32_t ct = 0; ct < stringEntryCount; ct++) { + uint64_t offset = stringTable.loadOffset(); + LOGE("string offset 0x%016llx", offset); + IO tmp(mData + offset, mUse64BitOffsets); + String8 s; + tmp.loadString(&s); + LOGE("string %s", s.string()); + mStrings.push(s); + } + } + + LOGE("strings done"); + uint32_t indexEntryCount = index.loadU32(); + LOGE("index count %i", indexEntryCount); + mIndex.setCapacity(indexEntryCount); + for (uint32_t ct = 0; ct < indexEntryCount; ct++) { + A3DIndexEntry e; + uint32_t stringIndex = index.loadU32(); + LOGE("index %i", ct); + LOGE(" string index %i", stringIndex); + e.mType = (A3DChunkType)index.loadU32(); + LOGE(" type %i", e.mType); + e.mOffset = index.loadOffset(); + LOGE(" offset 0x%016llx", e.mOffset); + + if (stringIndex && (stringIndex < mStrings.size())) { + e.mID = mStrings[stringIndex]; + mStringIndexValues.editItemAt(stringIndex) = ct; + LOGE(" id %s", e.mID.string()); + } + + mIndex.push(e); + } + LOGE("index done"); + + // At this point the index should be fully populated. + // We can now walk though it and load all the objects. + for (uint32_t ct = 0; ct < indexEntryCount; ct++) { + LOGE("processing index entry %i", ct); + processIndex(rsc, &mIndex.editItemAt(ct)); + } + + return ret; +} + + +FileA3D::IO::IO(const uint8_t *buf, bool use64) +{ + mData = buf; + mPos = 0; + mUse64 = use64; +} + +uint64_t FileA3D::IO::loadOffset() +{ + uint64_t tmp; + if (mUse64) { + mPos = (mPos + 7) & (~7); + tmp = reinterpret_cast<const uint64_t *>(&mData[mPos])[0]; + mPos += sizeof(uint64_t); + return tmp; + } + return loadU32(); +} + +void FileA3D::IO::loadString(String8 *s) +{ + LOGE("loadString"); + uint32_t len = loadU32(); + LOGE("loadString len %i", len); + s->setTo((const char *)&mData[mPos], len); + mPos += len; +} + + +void FileA3D::processChunk_Mesh(Context *rsc, IO *io, A3DIndexEntry *ie) +{ + Mesh * m = new Mesh; + + m->mPrimitivesCount = io->loadU32(); + m->mPrimitives = new Mesh::Primitive_t *[m->mPrimitivesCount]; + + for (uint32_t ct = 0; ct < m->mPrimitivesCount; ct++) { + uint32_t index = io->loadU32(); + + m->mPrimitives[ct] = (Mesh::Primitive_t *)mIndex[index].mRsObj; + } + ie->mRsObj = m; +} + +void FileA3D::processChunk_Primitive(Context *rsc, IO *io, A3DIndexEntry *ie) +{ + Mesh::Primitive_t * p = new Mesh::Primitive_t; + + p->mIndexCount = io->loadU32(); + uint32_t vertIdx = io->loadU32(); + p->mRestartCounts = io->loadU16(); + uint32_t bits = io->loadU8(); + p->mType = (RsPrimitive)io->loadU8(); + + LOGE("processChunk_Primitive count %i, bits %i", p->mIndexCount, bits); + + p->mVerticies = (Mesh::Verticies_t *)mIndex[vertIdx].mRsObj; + + p->mIndicies = new uint16_t[p->mIndexCount]; + for (uint32_t ct = 0; ct < p->mIndexCount; ct++) { + switch(bits) { + case 8: + p->mIndicies[ct] = io->loadU8(); + break; + case 16: + p->mIndicies[ct] = io->loadU16(); + break; + case 32: + p->mIndicies[ct] = io->loadU32(); + break; + } + LOGE(" idx %i", p->mIndicies[ct]); + } + + if (p->mRestartCounts) { + p->mRestarts = new uint16_t[p->mRestartCounts]; + for (uint32_t ct = 0; ct < p->mRestartCounts; ct++) { + switch(bits) { + case 8: + p->mRestarts[ct] = io->loadU8(); + break; + case 16: + p->mRestarts[ct] = io->loadU16(); + break; + case 32: + p->mRestarts[ct] = io->loadU32(); + break; + } + LOGE(" idx %i", p->mRestarts[ct]); + } + } else { + p->mRestarts = NULL; + } + + ie->mRsObj = p; +} + +void FileA3D::processChunk_Verticies(Context *rsc, IO *io, A3DIndexEntry *ie) +{ + Mesh::Verticies_t *cv = new Mesh::Verticies_t; + cv->mAllocationCount = io->loadU32(); + cv->mAllocations = new Allocation *[cv->mAllocationCount]; + LOGE("processChunk_Verticies count %i", cv->mAllocationCount); + for (uint32_t ct = 0; ct < cv->mAllocationCount; ct++) { + uint32_t i = io->loadU32(); + cv->mAllocations[ct] = (Allocation *)mIndex[i].mRsObj; + LOGE(" idx %i", i); + } + ie->mRsObj = cv; +} + +void FileA3D::processChunk_Element(Context *rsc, IO *io, A3DIndexEntry *ie) +{ + rsi_ElementBegin(rsc); + + uint32_t count = io->loadU32(); + LOGE("processChunk_Element count %i", count); + while (count--) { + RsDataKind dk = (RsDataKind)io->loadU8(); + RsDataType dt = (RsDataType)io->loadU8(); + uint32_t bits = io->loadU8(); + bool isNorm = io->loadU8() != 0; + LOGE(" %i %i %i %i", dk, dt, bits, isNorm); + rsi_ElementAdd(rsc, dk, dt, isNorm, bits); + } + LOGE("processChunk_Element create"); + ie->mRsObj = rsi_ElementCreate(rsc); +} + +void FileA3D::processChunk_ElementSource(Context *rsc, IO *io, A3DIndexEntry *ie) +{ + uint32_t index = io->loadU32(); + uint32_t count = io->loadU32(); + + LOGE("processChunk_ElementSource count %i, index %i", count, index); + + RsElement e = (RsElement)mIndex[index].mRsObj; + + RsAllocation a = rsi_AllocationCreateSized(rsc, e, count); + Allocation * alloc = static_cast<Allocation *>(a); + + float * data = (float *)alloc->getPtr(); + while(count--) { + *data = io->loadF(); + LOGE(" %f", *data); + data++; + } + ie->mRsObj = alloc; +} + +namespace android { +namespace renderscript { + + +RsFile rsi_FileOpen(Context *rsc, char const *path, unsigned int len) +{ + FileA3D *fa3d = new FileA3D; + + FILE *f = fopen("/sdcard/test.a3d", "rb"); + if (f) { + fa3d->load(rsc, f); + fclose(f); + return fa3d; + } + delete fa3d; + return NULL; +} + + +} +} diff --git a/libs/rs/rsFileA3D.h b/libs/rs/rsFileA3D.h new file mode 100644 index 0000000..9ee08ec --- /dev/null +++ b/libs/rs/rsFileA3D.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_FILE_A3D_H +#define ANDROID_RS_FILE_A3D_H + +#include "RenderScript.h" +#include "rsFileA3DDecls.h" +#include "rsMesh.h" + +#include <utils/String8.h> +#include <stdio.h> + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + +class FileA3D +{ +public: + FileA3D(); + ~FileA3D(); + + uint32_t mMajorVersion; + uint32_t mMinorVersion; + uint64_t mIndexOffset; + uint64_t mStringTableOffset; + bool mUse64BitOffsets; + + struct A3DIndexEntry { + String8 mID; + A3DChunkType mType; + uint64_t mOffset; + void * mRsObj; + }; + + bool load(Context *rsc, FILE *f); + +protected: + class IO + { + public: + IO(const uint8_t *, bool use64); + + float loadF() { + mPos = (mPos + 3) & (~3); + float tmp = reinterpret_cast<const float *>(&mData[mPos])[0]; + mPos += sizeof(float); + return tmp; + } + int32_t loadI32() { + mPos = (mPos + 3) & (~3); + int32_t tmp = reinterpret_cast<const int32_t *>(&mData[mPos])[0]; + mPos += sizeof(int32_t); + return tmp; + } + uint32_t loadU32() { + mPos = (mPos + 3) & (~3); + uint32_t tmp = reinterpret_cast<const uint32_t *>(&mData[mPos])[0]; + mPos += sizeof(uint32_t); + return tmp; + } + uint16_t loadU16() { + mPos = (mPos + 1) & (~1); + uint16_t tmp = reinterpret_cast<const uint16_t *>(&mData[mPos])[0]; + mPos += sizeof(uint16_t); + return tmp; + } + uint8_t loadU8() { + uint8_t tmp = reinterpret_cast<const uint8_t *>(&mData[mPos])[0]; + mPos += sizeof(uint8_t); + return tmp; + } + uint64_t loadOffset(); + void loadString(String8 *s); + uint64_t getPos() const {return mPos;} + const uint8_t * getPtr() const; + protected: + const uint8_t * mData; + uint64_t mPos; + bool mUse64; + }; + + + bool process(Context *rsc); + bool processIndex(Context *rsc, A3DIndexEntry *); + void processChunk_Mesh(Context *rsc, IO *io, A3DIndexEntry *ie); + void processChunk_Primitive(Context *rsc, IO *io, A3DIndexEntry *ie); + void processChunk_Verticies(Context *rsc, IO *io, A3DIndexEntry *ie); + void processChunk_Element(Context *rsc, IO *io, A3DIndexEntry *ie); + void processChunk_ElementSource(Context *rsc, IO *io, A3DIndexEntry *ie); + + const uint8_t * mData; + void * mAlloc; + uint64_t mDataSize; + Context * mRsc; + + Vector<A3DIndexEntry> mIndex; + Vector<String8> mStrings; + Vector<uint32_t> mStringIndexValues; + +}; + + +} +} +#endif //ANDROID_RS_FILE_A3D_H + + diff --git a/libs/rs/rsFileA3DDecls.h b/libs/rs/rsFileA3DDecls.h new file mode 100644 index 0000000..2a08bd3 --- /dev/null +++ b/libs/rs/rsFileA3DDecls.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_FILE_A3D_DECLS_H +#define ANDROID_RS_FILE_A3D_DECLS_H + + +#define A3D_MAGIC_KEY "Android3D_ff" + +namespace android { +namespace renderscript { + + enum A3DChunkType { + CHUNK_EMPTY, + + CHUNK_ELEMENT, + CHUNK_ELEMENT_SOURCE, + CHUNK_VERTICIES, + CHUNK_MESH, + CHUNK_PRIMITIVE, + + CHUNK_LAST + }; + + +} +} +#endif //ANDROID_RS_FILE_A3D_H + + + diff --git a/libs/rs/rsLight.cpp b/libs/rs/rsLight.cpp new file mode 100644 index 0000000..24b58b6 --- /dev/null +++ b/libs/rs/rsLight.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" + +#include <GLES/gl.h> + +using namespace android; +using namespace android::renderscript; + + +Light::Light(bool isLocal, bool isMono) +{ + mIsLocal = isLocal; + mIsMono = isMono; + + mPosition[0] = 0; + mPosition[1] = 0; + mPosition[2] = 1; + mPosition[3] = 0; + + mColor[0] = 1.f; + mColor[1] = 1.f; + mColor[2] = 1.f; + mColor[3] = 1.f; +} + +Light::~Light() +{ +} + +void Light::setPosition(float x, float y, float z) +{ + mPosition[0] = x; + mPosition[1] = y; + mPosition[2] = z; +} + +void Light::setColor(float r, float g, float b) +{ + mColor[0] = r; + mColor[1] = g; + mColor[2] = b; +} + +void Light::setupGL(uint32_t num) const +{ + glLightfv(GL_LIGHT0 + num, GL_DIFFUSE, mColor); + glLightfv(GL_LIGHT0 + num, GL_SPECULAR, mColor); + glLightfv(GL_LIGHT0 + num, GL_POSITION, mPosition); +} + +//////////////////////////////////////////// + +LightState::LightState() +{ + clear(); +} + +LightState::~LightState() +{ +} + +void LightState::clear() +{ + mIsLocal = false; + mIsMono = false; +} + + +//////////////////////////////////////////////////// +// + +namespace android { +namespace renderscript { + +void rsi_LightBegin(Context *rsc) +{ + rsc->mStateLight.clear(); +} + +void rsi_LightSetLocal(Context *rsc, bool isLocal) +{ + rsc->mStateLight.mIsLocal = isLocal; +} + +void rsi_LightSetMonochromatic(Context *rsc, bool isMono) +{ + rsc->mStateLight.mIsMono = isMono; +} + +RsLight rsi_LightCreate(Context *rsc) +{ + Light *l = new Light(rsc->mStateLight.mIsLocal, + rsc->mStateLight.mIsMono); + l->incRef(); + return l; +} + +void rsi_LightDestroy(Context *rsc, RsLight vl) +{ + Light *l = static_cast<Light *>(vl); + l->decRef(); +} + +void rsi_LightSetColor(Context *rsc, RsLight vl, float r, float g, float b) +{ + Light *l = static_cast<Light *>(vl); + l->setColor(r, g, b); +} + +void rsi_LightSetPosition(Context *rsc, RsLight vl, float x, float y, float z) +{ + Light *l = static_cast<Light *>(vl); + l->setPosition(x, y, z); +} + + + +} +} diff --git a/libs/rs/rsLight.h b/libs/rs/rsLight.h new file mode 100644 index 0000000..b0c3386 --- /dev/null +++ b/libs/rs/rsLight.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_LIGHT_H +#define ANDROID_LIGHT_H + + +#include "rsObjectBase.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + +// An element is a group of Components that occupies one cell in a structure. +class Light : public ObjectBase +{ +public: + Light(bool isLocal, bool isMono); + virtual ~Light(); + + // Values, mutable after creation. + void setPosition(float x, float y, float z); + void setColor(float r, float g, float b); + + void setupGL(uint32_t num) const; + +protected: + float mColor[4]; + float mPosition[4]; + bool mIsLocal; + bool mIsMono; +}; + + +class LightState { +public: + LightState(); + ~LightState(); + + void clear(); + + bool mIsMono; + bool mIsLocal; +}; + + +} +} +#endif //ANDROID_LIGHT_H + diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp new file mode 100644 index 0000000..c3fee54 --- /dev/null +++ b/libs/rs/rsLocklessFifo.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsLocklessFifo.h" + +using namespace android; + + +LocklessCommandFifo::LocklessCommandFifo() +{ +} + +LocklessCommandFifo::~LocklessCommandFifo() +{ +} + +bool LocklessCommandFifo::init(uint32_t sizeInBytes) +{ + // Add room for a buffer reset command + mBuffer = static_cast<uint8_t *>(malloc(sizeInBytes + 4)); + if (!mBuffer) { + LOGE("LocklessFifo allocation failure"); + return false; + } + + if (!mSignalToControl.init() || !mSignalToWorker.init()) { + LOGE("Signal setup failed"); + free(mBuffer); + return false; + } + + mSize = sizeInBytes; + mPut = mBuffer; + mGet = mBuffer; + mEnd = mBuffer + (sizeInBytes) - 1; + dumpState("init"); + return true; +} + +uint32_t LocklessCommandFifo::getFreeSpace() const +{ + int32_t freeSpace = 0; + //dumpState("getFreeSpace"); + + if (mPut >= mGet) { + freeSpace = mEnd - mPut; + } else { + freeSpace = mGet - mPut; + } + + if (freeSpace < 0) { + freeSpace = 0; + } + return freeSpace; +} + +bool LocklessCommandFifo::isEmpty() const +{ + return mPut == mGet; +} + + +void * LocklessCommandFifo::reserve(uint32_t sizeInBytes) +{ + // Add space for command header and loop token; + sizeInBytes += 8; + + //dumpState("reserve"); + if (getFreeSpace() < sizeInBytes) { + makeSpace(sizeInBytes); + } + + return mPut + 4; +} + +void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes) +{ + //dumpState("commit 1"); + reinterpret_cast<uint16_t *>(mPut)[0] = command; + reinterpret_cast<uint16_t *>(mPut)[1] = sizeInBytes; + mPut += ((sizeInBytes + 3) & ~3) + 4; + //dumpState("commit 2"); + mSignalToWorker.set(); +} + +void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes) +{ + commit(command, sizeInBytes); + flush(); +} + +void LocklessCommandFifo::flush() +{ + //dumpState("flush 1"); + while(mPut != mGet) { + mSignalToControl.wait(); + } + //dumpState("flush 2"); +} + +const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData) +{ + while(1) { + //dumpState("get"); + while(isEmpty()) { + mSignalToControl.set(); + mSignalToWorker.wait(); + } + + *command = reinterpret_cast<const uint16_t *>(mGet)[0]; + *bytesData = reinterpret_cast<const uint16_t *>(mGet)[1]; + if (*command) { + // non-zero command is valid + return mGet+4; + } + + // zero command means reset to beginning. + mGet = mBuffer; + } +} + +void LocklessCommandFifo::next() +{ + uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1]; + mGet += ((bytes + 3) & ~3) + 4; + if (isEmpty()) { + mSignalToControl.set(); + } + //dumpState("next"); +} + +void LocklessCommandFifo::makeSpace(uint32_t bytes) +{ + //dumpState("make space"); + if ((mPut+bytes) > mEnd) { + // Need to loop regardless of where get is. + while((mGet > mPut) && (mBuffer+4 >= mGet)) { + sleep(1); + } + + // Toss in a reset then the normal wait for space will do the rest. + reinterpret_cast<uint16_t *>(mPut)[0] = 0; + reinterpret_cast<uint16_t *>(mPut)[1] = 0; + mPut = mBuffer; + } + + // it will fit here so we just need to wait for space. + while(getFreeSpace() < bytes) { + sleep(1); + } + +} + +void LocklessCommandFifo::dumpState(const char *s) const +{ + LOGV("%s put %p, get %p, buf %p, end %p", s, mPut, mGet, mBuffer, mEnd); +} + +LocklessCommandFifo::Signal::Signal() +{ + mSet = true; +} + +LocklessCommandFifo::Signal::~Signal() +{ + pthread_mutex_destroy(&mMutex); + pthread_cond_destroy(&mCondition); +} + +bool LocklessCommandFifo::Signal::init() +{ + int status = pthread_mutex_init(&mMutex, NULL); + if (status) { + LOGE("LocklessFifo mutex init failure"); + return false; + } + + status = pthread_cond_init(&mCondition, NULL); + if (status) { + LOGE("LocklessFifo condition init failure"); + pthread_mutex_destroy(&mMutex); + return false; + } + + return true; +} + +void LocklessCommandFifo::Signal::set() +{ + int status; + + status = pthread_mutex_lock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i locking for set condition.", status); + return; + } + + mSet = true; + + status = pthread_cond_signal(&mCondition); + if (status) { + LOGE("LocklessCommandFifo: error %i on set condition.", status); + } + + status = pthread_mutex_unlock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status); + } +} + +void LocklessCommandFifo::Signal::wait() +{ + int status; + + status = pthread_mutex_lock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i locking for condition.", status); + return; + } + + if (!mSet) { + status = pthread_cond_wait(&mCondition, &mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i waiting on condition.", status); + } + } + mSet = false; + + status = pthread_mutex_unlock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i unlocking for condition.", status); + } +} + diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h new file mode 100644 index 0000000..abeddf7 --- /dev/null +++ b/libs/rs/rsLocklessFifo.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_LOCKLESS_FIFO_H +#define ANDROID_RS_LOCKLESS_FIFO_H + + +#include "rsUtils.h" + +namespace android { + + +// A simple FIFO to be used as a producer / consumer between two +// threads. One is writer and one is reader. The common cases +// will not require locking. It is not threadsafe for multiple +// readers or writers by design. + +class LocklessCommandFifo +{ +public: + bool init(uint32_t size); + + LocklessCommandFifo(); + ~LocklessCommandFifo(); + + +protected: + class Signal { + public: + Signal(); + ~Signal(); + + bool init(); + + void set(); + void wait(); + + protected: + bool mSet; + pthread_mutex_t mMutex; + pthread_cond_t mCondition; + }; + + uint8_t * volatile mPut; + uint8_t * volatile mGet; + uint8_t * mBuffer; + uint8_t * mEnd; + uint8_t mSize; + + Signal mSignalToWorker; + Signal mSignalToControl; + + + +public: + void * reserve(uint32_t bytes); + void commit(uint32_t command, uint32_t bytes); + void commitSync(uint32_t command, uint32_t bytes); + + void flush(); + const void * get(uint32_t *command, uint32_t *bytesData); + void next(); + + void makeSpace(uint32_t bytes); + + bool isEmpty() const; + uint32_t getFreeSpace() const; + + +private: + void dumpState(const char *) const; +}; + + +} +#endif diff --git a/libs/rs/rsMatrix.cpp b/libs/rs/rsMatrix.cpp new file mode 100644 index 0000000..5f68197 --- /dev/null +++ b/libs/rs/rsMatrix.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsMatrix.h" + +#include "stdlib.h" +#include "string.h" +#include "math.h" + +using namespace android; +using namespace android::renderscript; + + + +void Matrix::loadIdentity() +{ + set(0, 0, 1); + set(1, 0, 0); + set(2, 0, 0); + set(3, 0, 0); + + set(0, 1, 0); + set(1, 1, 1); + set(2, 1, 0); + set(3, 1, 0); + + set(0, 2, 0); + set(1, 2, 0); + set(2, 2, 1); + set(3, 2, 0); + + set(0, 3, 0); + set(1, 3, 0); + set(2, 3, 0); + set(3, 3, 1); +} + +void Matrix::load(const float *v) +{ + memcpy(m, v, sizeof(m)); +} + +void Matrix::load(const Matrix *v) +{ + memcpy(m, v->m, sizeof(m)); +} + +void Matrix::loadRotate(float rot, float x, float y, float z) +{ + float c, s; + m[3] = 0; + m[7] = 0; + m[11]= 0; + m[12]= 0; + m[13]= 0; + m[14]= 0; + m[15]= 1; + rot *= float(M_PI / 180.0f); + c = cosf(rot); + s = sinf(rot); + + const float len = sqrtf(x*x + y*y + z*z); + if (!(len != 1)) { + const float recipLen = 1.f / len; + x *= recipLen; + y *= recipLen; + z *= recipLen; + } + const float nc = 1.0f - c; + const float xy = x * y; + const float yz = y * z; + const float zx = z * x; + const float xs = x * s; + const float ys = y * s; + const float zs = z * s; + m[ 0] = x*x*nc + c; + m[ 4] = xy*nc - zs; + m[ 8] = zx*nc + ys; + m[ 1] = xy*nc + zs; + m[ 5] = y*y*nc + c; + m[ 9] = yz*nc - xs; + m[ 2] = zx*nc - ys; + m[ 6] = yz*nc + xs; + m[10] = z*z*nc + c; +} + +void Matrix::loadScale(float x, float y, float z) +{ + loadIdentity(); + m[0] = x; + m[5] = y; + m[10] = z; +} + +void Matrix::loadTranslate(float x, float y, float z) +{ + loadIdentity(); + m[12] = x; + m[13] = y; + m[14] = z; +} + +void Matrix::loadMultiply(const Matrix *lhs, const Matrix *rhs) +{ + for (int i=0 ; i<4 ; i++) { + float ri0 = 0; + float ri1 = 0; + float ri2 = 0; + float ri3 = 0; + for (int j=0 ; j<4 ; j++) { + const float rhs_ij = rhs->get(i,j); + ri0 += lhs->get(j,0) * rhs_ij; + ri1 += lhs->get(j,1) * rhs_ij; + ri2 += lhs->get(j,2) * rhs_ij; + ri3 += lhs->get(j,3) * rhs_ij; + } + set(i,0, ri0); + set(i,1, ri1); + set(i,2, ri2); + set(i,3, ri3); + } +} + +void Matrix::loadOrtho(float l, float r, float b, float t, float n, float f) { + loadIdentity(); + m[0] = 2 / (r - l); + m[5] = 2 / (t - b); + m[10]= -2 / (f - n); + m[12]= -(r + l) / (r - l); + m[13]= -(t + b) / (t - b); + m[14]= -(f + n) / (f - n); +} + +void Matrix::loadFrustum(float l, float r, float b, float t, float n, float f) { + loadIdentity(); + m[0] = 2 * n / (r - l); + m[5] = 2 * n / (t - b); + m[8] = (r + l) / (r - l); + m[9] = (t + b) / (t - b); + m[10]= -(f + n) / (f - n); + m[11]= -1; + m[14]= -2*f*n / (f - n); + m[15]= 0; +} + + diff --git a/libs/rs/rsMatrix.h b/libs/rs/rsMatrix.h new file mode 100644 index 0000000..7dc4165 --- /dev/null +++ b/libs/rs/rsMatrix.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_MATRIX_H +#define ANDROID_RS_MATRIX_H + + + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + +struct Matrix +{ + float m[16]; + + inline float get(int i, int j) const { + return m[i*4 + j]; + } + + inline void set(int i, int j, float v) { + m[i*4 + j] = v; + } + + void loadIdentity(); + void load(const float *); + void load(const Matrix *); + + void loadRotate(float rot, float x, float y, float z); + void loadScale(float x, float y, float z); + void loadTranslate(float x, float y, float z); + void loadMultiply(const Matrix *lhs, const Matrix *rhs); + + void loadOrtho(float l, float r, float b, float t, float n, float f); + void loadFrustum(float l, float r, float b, float t, float n, float f); + + void multiply(const Matrix *rhs) { + Matrix tmp; + tmp.loadMultiply(this, rhs); + load(&tmp); + } + void rotate(float rot, float x, float y, float z) { + Matrix tmp; + tmp.loadRotate(rot, x, y, z); + multiply(&tmp); + } + void scale(float x, float y, float z) { + Matrix tmp; + tmp.loadScale(x, y, z); + multiply(&tmp); + } + void translate(float x, float y, float z) { + Matrix tmp; + tmp.loadTranslate(x, y, z); + multiply(&tmp); + } + + + +}; + + + +} +} + + + + +#endif + + + + diff --git a/libs/utils/executablepath_linux.cpp b/libs/rs/rsMesh.cpp index b8d2a3d..aeb52ed 100644 --- a/libs/utils/executablepath_linux.cpp +++ b/libs/rs/rsMesh.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,33 @@ * limitations under the License. */ -#include <utils/executablepath.h> -#include <sys/types.h> -#include <unistd.h> -#include <limits.h> -#include <stdio.h> +#include "rsContext.h" -void executablepath(char exe[PATH_MAX]) +using namespace android; +using namespace android::renderscript; + +#include <GLES/gl.h> +#include <GLES/glext.h> + +Mesh::Mesh() +{ + mVerticies = NULL; + mVerticiesCount = 0; + mPrimitives = NULL; + mPrimitivesCount = 0; +} + +Mesh::~Mesh() +{ +} + + + +MeshContext::MeshContext() +{ +} + +MeshContext::~MeshContext() { - char proc[100]; - sprintf(proc, "/proc/%d/exe", getpid()); - - int err = readlink(proc, exe, PATH_MAX); } diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h new file mode 100644 index 0000000..be207a3 --- /dev/null +++ b/libs/rs/rsMesh.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_MESH_H +#define ANDROID_RS_MESH_H + + +#include "RenderScript.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + +// An element is a group of Components that occupies one cell in a structure. +class Mesh : public ObjectBase +{ +public: + Mesh(); + ~Mesh(); + + struct Verticies_t + { + Allocation ** mAllocations; + uint32_t mAllocationCount; + + size_t mVertexDataSize; + + size_t mOffsetCoord; + size_t mOffsetTex; + size_t mOffsetNorm; + + size_t mSizeCoord; + size_t mSizeTex; + size_t mSizeNorm; + + uint32_t mBufferObject; + }; + + struct Primitive_t + { + RsPrimitive mType; + Verticies_t *mVerticies; + + uint32_t mIndexCount; + uint16_t *mIndicies; + + uint32_t mRestartCounts; + uint16_t *mRestarts; + }; + + Verticies_t * mVerticies; + uint32_t mVerticiesCount; + + Primitive_t ** mPrimitives; + uint32_t mPrimitivesCount; + + + + void analyzeElement(); +protected: +}; + +class MeshContext +{ +public: + MeshContext(); + ~MeshContext(); + +}; + + +} +} +#endif //ANDROID_RS_TRIANGLE_MESH_H + + diff --git a/libs/rs/rsObjectBase.cpp b/libs/rs/rsObjectBase.cpp new file mode 100644 index 0000000..3219c39 --- /dev/null +++ b/libs/rs/rsObjectBase.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsObjectBase.h" + +using namespace android; +using namespace android::renderscript; + +ObjectBase::ObjectBase() +{ + mRefCount = 0; + mName = NULL; +} + +ObjectBase::~ObjectBase() +{ + rsAssert(!mRefCount); +} + +void ObjectBase::incRef() const +{ + mRefCount ++; + //LOGV("ObjectBase %p inc ref %i", this, mRefCount); +} + +void ObjectBase::decRef() const +{ + rsAssert(mRefCount > 0); + mRefCount --; + //LOGV("ObjectBase %p dec ref %i", this, mRefCount); + if (!mRefCount) { + delete this; + } +} + +void ObjectBase::setName(const char *name) +{ + delete mName; + mName = NULL; + if (name) { + mName = new char[strlen(name) +1]; + strcpy(mName, name); + } +} + +void ObjectBase::setName(const char *name, uint32_t len) +{ + delete mName; + mName = NULL; + if (name) { + mName = new char[len + 1]; + memcpy(mName, name, len); + mName[len] = 0; + } +} + diff --git a/libs/rs/rsObjectBase.h b/libs/rs/rsObjectBase.h new file mode 100644 index 0000000..b2c3338 --- /dev/null +++ b/libs/rs/rsObjectBase.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_OBJECT_BASE_H +#define ANDROID_RS_OBJECT_BASE_H + +#include "rsUtils.h" + + +namespace android { +namespace renderscript { + +// An element is a group of Components that occupies one cell in a structure. +class ObjectBase +{ +public: + ObjectBase(); + virtual ~ObjectBase(); + + void incRef() const; + void decRef() const; + + const char * getName() const { + return mName; + } + void setName(const char *); + void setName(const char *, uint32_t len); + +private: + char * mName; + mutable int32_t mRefCount; + + +}; + +template<class T> +class ObjectBaseRef +{ +public: + ObjectBaseRef() { + mRef = NULL; + } + + ObjectBaseRef(const ObjectBaseRef &ref) { + mRef = ref.get(); + if (mRef) { + mRef->incRef(); + } + } + + ObjectBaseRef(T *ref) { + mRef = ref; + if (mRef) { + ref->incRef(); + } + } + + ~ObjectBaseRef() { + clear(); + } + + void set(T *ref) { + if (mRef != ref) { + clear(); + mRef = ref; + if (mRef) { + ref->incRef(); + } + } + } + + void set(const ObjectBaseRef &ref) { + set(ref.mRef); + } + + void clear() { + if (mRef) { + mRef->decRef(); + } + mRef = NULL; + } + + inline T * get() const { + return mRef; + } + + inline T * operator-> () const { + return mRef; + } + +protected: + T * mRef; + +}; + + +} +} + +#endif //ANDROID_RS_OBJECT_BASE_H + diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp new file mode 100644 index 0000000..5a83fb7 --- /dev/null +++ b/libs/rs/rsProgram.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" +#include "rsProgram.h" + +using namespace android; +using namespace android::renderscript; + + +Program::Program(Element *in, Element *out) +{ + mElementIn.set(in); + mElementOut.set(out); + + +} + +Program::~Program() +{ +} + + +void Program::setAllocation(Allocation *alloc) +{ + mConstants.set(alloc); + mDirty = true; +} + +void Program::setupGL() +{ + +} + + diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h new file mode 100644 index 0000000..913fdd2 --- /dev/null +++ b/libs/rs/rsProgram.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_PROGRAM_H +#define ANDROID_RS_PROGRAM_H + +#include "rsObjectBase.h" +#include "rsElement.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + + +class Program : public ObjectBase +{ +public: + Program(Element *in, Element *out); + virtual ~Program(); + + + void setAllocation(Allocation *); + + virtual void setupGL(); + +protected: + // Components not listed in "in" will be passed though + // unless overwritten by components in out. + ObjectBaseRef<Element> mElementIn; + ObjectBaseRef<Element> mElementOut; + + ObjectBaseRef<Allocation> mConstants; + + bool mDirty; + +}; + + + +} +} +#endif + + + diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp new file mode 100644 index 0000000..628f93e --- /dev/null +++ b/libs/rs/rsProgramFragment.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" +#include "rsProgramFragment.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> + +using namespace android; +using namespace android::renderscript; + + +ProgramFragment::ProgramFragment(Element *in, Element *out) : + Program(in, out) +{ + for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) { + mEnvModes[ct] = RS_TEX_ENV_MODE_REPLACE; + mTextureDimensions[ct] = 2; + } + mTextureEnableMask = 0; + mEnvModes[1] = RS_TEX_ENV_MODE_DECAL; +} + +ProgramFragment::~ProgramFragment() +{ +} + +void ProgramFragment::setupGL() +{ + for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) { + glActiveTexture(GL_TEXTURE0 + ct); + if (!(mTextureEnableMask & (1 << ct)) || !mTextures[ct].get()) { + glDisable(GL_TEXTURE_2D); + continue; + } + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID()); + + switch(mEnvModes[ct]) { + case RS_TEX_ENV_MODE_REPLACE: + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + break; + case RS_TEX_ENV_MODE_MODULATE: + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + break; + case RS_TEX_ENV_MODE_DECAL: + glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); + break; + } + + if (mSamplers[ct].get()) { + mSamplers[ct]->setupGL(); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + + // Gross hack. + if (ct == 2) { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_ADD); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR); + + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_ADD); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, GL_SRC_ALPHA); + } + } + + + glActiveTexture(GL_TEXTURE0); +} + + +void ProgramFragment::bindTexture(uint32_t slot, Allocation *a) +{ + if (slot >= MAX_TEXTURE) { + LOGE("Attempt to bind a texture to a slot > MAX_TEXTURE"); + return; + } + + //LOGE("bindtex %i %p", slot, a); + mTextures[slot].set(a); +} + +void ProgramFragment::bindSampler(uint32_t slot, Sampler *s) +{ + if (slot >= MAX_TEXTURE) { + LOGE("Attempt to bind a Sampler to a slot > MAX_TEXTURE"); + return; + } + + mSamplers[slot].set(s); +} + +void ProgramFragment::setType(uint32_t slot, const Element *e, uint32_t dim) +{ + if (slot >= MAX_TEXTURE) { + LOGE("Attempt to setType to a slot > MAX_TEXTURE"); + return; + } + + if (dim >= 4) { + LOGE("Attempt to setType to a dimension > 3"); + return; + } + + mTextureFormats[slot].set(e); + mTextureDimensions[slot] = dim; +} + +void ProgramFragment::setEnvMode(uint32_t slot, RsTexEnvMode env) +{ + if (slot >= MAX_TEXTURE) { + LOGE("Attempt to setEnvMode to a slot > MAX_TEXTURE"); + return; + } + + mEnvModes[slot] = env; +} + +void ProgramFragment::setTexEnable(uint32_t slot, bool enable) +{ + if (slot >= MAX_TEXTURE) { + LOGE("Attempt to setEnvMode to a slot > MAX_TEXTURE"); + return; + } + + uint32_t bit = 1 << slot; + mTextureEnableMask &= ~bit; + if (enable) { + mTextureEnableMask |= bit; + } +} + + + +ProgramFragmentState::ProgramFragmentState() +{ + mPF = NULL; +} + +ProgramFragmentState::~ProgramFragmentState() +{ + delete mPF; + +} + +void ProgramFragmentState::init(Context *rsc, int32_t w, int32_t h) +{ + ProgramFragment *pf = new ProgramFragment(NULL, NULL); + mDefault.set(pf); +} + + +namespace android { +namespace renderscript { + +void rsi_ProgramFragmentBegin(Context * rsc, RsElement in, RsElement out) +{ + delete rsc->mStateFragment.mPF; + rsc->mStateFragment.mPF = new ProgramFragment((Element *)in, (Element *)out); +} + +void rsi_ProgramFragmentBindTexture(Context *rsc, RsProgramFragment vpf, uint32_t slot, RsAllocation a) +{ + ProgramFragment *pf = static_cast<ProgramFragment *>(vpf); + pf->bindTexture(slot, static_cast<Allocation *>(a)); + if (pf == rsc->getFragment()) { + pf->setupGL(); + } +} + +void rsi_ProgramFragmentBindSampler(Context *rsc, RsProgramFragment vpf, uint32_t slot, RsSampler s) +{ + ProgramFragment *pf = static_cast<ProgramFragment *>(vpf); + pf->bindSampler(slot, static_cast<Sampler *>(s)); + + if (pf == rsc->getFragment()) { + pf->setupGL(); + } +} + +void rsi_ProgramFragmentSetType(Context *rsc, uint32_t slot, RsType vt) +{ + const Type *t = static_cast<const Type *>(vt); + uint32_t dim = 1; + if (t->getDimY()) { + dim ++; + if (t->getDimZ()) { + dim ++; + } + } + + rsc->mStateFragment.mPF->setType(slot, t->getElement(), dim); +} + +void rsi_ProgramFragmentSetEnvMode(Context *rsc, uint32_t slot, RsTexEnvMode env) +{ + rsc->mStateFragment.mPF->setEnvMode(slot, env); +} + +void rsi_ProgramFragmentSetTexEnable(Context *rsc, uint32_t slot, bool enable) +{ + rsc->mStateFragment.mPF->setTexEnable(slot, enable); +} + +RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc) +{ + ProgramFragment *pf = rsc->mStateFragment.mPF; + pf->incRef(); + rsc->mStateFragment.mPF = 0; + return pf; +} + +void rsi_ProgramFragmentDestroy(Context *rsc, RsProgramFragment vpf) +{ + ProgramFragment *pf = (ProgramFragment *)vpf; + if (pf->getName()) { + rsc->removeName(pf); + } + pf->decRef(); +} + + +} +} + diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h new file mode 100644 index 0000000..896d8dd --- /dev/null +++ b/libs/rs/rsProgramFragment.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_PROGRAM_FRAGMENT_H +#define ANDROID_RS_PROGRAM_FRAGMENT_H + +#include "rsProgram.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + +class ProgramFragment : public Program +{ +public: + const static uint32_t MAX_TEXTURE = 2; + const static uint32_t MAX_CONSTANTS = 2; + + + + ProgramFragment(Element *in, Element *out); + virtual ~ProgramFragment(); + + virtual void setupGL(); + + + + void bindTexture(uint32_t slot, Allocation *); + void bindSampler(uint32_t slot, Sampler *); + void setType(uint32_t slot, const Element *, uint32_t dim); + + void setEnvMode(uint32_t slot, RsTexEnvMode); + void setTexEnable(uint32_t slot, bool); + + + +protected: + // The difference between Textures and Constants is how they are accessed + // Texture lookups go though a sampler which in effect converts normalized + // coordinates into type specific. Multiple samples may also be taken + // and filtered. + // + // Constants are strictly accessed by programetic loads. + ObjectBaseRef<Allocation> mTextures[MAX_TEXTURE]; + ObjectBaseRef<Sampler> mSamplers[MAX_TEXTURE]; + ObjectBaseRef<const Element> mTextureFormats[MAX_TEXTURE]; + uint32_t mTextureDimensions[MAX_TEXTURE]; + + + ObjectBaseRef<Allocation> mConstants[MAX_CONSTANTS]; + ObjectBaseRef<Type> mConstantTypes[MAX_CONSTANTS]; + + + // Hacks to create a program for now + RsTexEnvMode mEnvModes[MAX_TEXTURE]; + uint32_t mTextureEnableMask; + + + + + +}; + +class ProgramFragmentState +{ +public: + ProgramFragmentState(); + ~ProgramFragmentState(); + + ProgramFragment *mPF; + void init(Context *rsc, int32_t w, int32_t h); + + ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE]; + ObjectBaseRef<ProgramFragment> mDefault; + Vector<ProgramFragment *> mPrograms; +}; + + +} +} +#endif + + + + diff --git a/libs/rs/rsProgramFragmentStore.cpp b/libs/rs/rsProgramFragmentStore.cpp new file mode 100644 index 0000000..9ee270f --- /dev/null +++ b/libs/rs/rsProgramFragmentStore.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" +#include "rsProgramFragmentStore.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> + +using namespace android; +using namespace android::renderscript; + + +ProgramFragmentStore::ProgramFragmentStore(Element *in, Element *out) : + Program(in, out) +{ + mDitherEnable = true; + mBlendEnable = false; + mColorRWriteEnable = true; + mColorGWriteEnable = true; + mColorBWriteEnable = true; + mColorAWriteEnable = true; + mBlendSrc = GL_ONE; + mBlendDst = GL_ZERO; + + + mDepthTestEnable = false; + mDepthWriteEnable = true; + mDepthFunc = GL_LESS; + + +} + +ProgramFragmentStore::~ProgramFragmentStore() +{ +} + +void ProgramFragmentStore::setupGL() +{ + glColorMask(mColorRWriteEnable, + mColorGWriteEnable, + mColorBWriteEnable, + mColorAWriteEnable); + if (mBlendEnable) { + glEnable(GL_BLEND); + glBlendFunc(mBlendSrc, mBlendDst); + } else { + glDisable(GL_BLEND); + } + + //LOGE("pfs %i, %i, %x", mDepthWriteEnable, mDepthTestEnable, mDepthFunc); + + glDepthMask(mDepthWriteEnable); + if(mDepthTestEnable || mDepthWriteEnable) { + glEnable(GL_DEPTH_TEST); + glDepthFunc(mDepthFunc); + } else { + glDisable(GL_DEPTH_TEST); + } + + if (mDitherEnable) { + glEnable(GL_DITHER); + } else { + glDisable(GL_DITHER); + } + + +} + +void ProgramFragmentStore::setDitherEnable(bool enable) +{ + mDitherEnable = enable; +} + +void ProgramFragmentStore::setDepthFunc(RsDepthFunc func) +{ + mDepthTestEnable = true; + + switch(func) { + case RS_DEPTH_FUNC_ALWAYS: + mDepthTestEnable = false; + mDepthFunc = GL_ALWAYS; + break; + case RS_DEPTH_FUNC_LESS: + mDepthFunc = GL_LESS; + break; + case RS_DEPTH_FUNC_LEQUAL: + mDepthFunc = GL_LEQUAL; + break; + case RS_DEPTH_FUNC_GREATER: + mDepthFunc = GL_GREATER; + break; + case RS_DEPTH_FUNC_GEQUAL: + mDepthFunc = GL_GEQUAL; + break; + case RS_DEPTH_FUNC_EQUAL: + mDepthFunc = GL_EQUAL; + break; + case RS_DEPTH_FUNC_NOTEQUAL: + mDepthFunc = GL_NOTEQUAL; + break; + } +} + +void ProgramFragmentStore::setDepthMask(bool mask) +{ + mDepthWriteEnable = mask; +} + +void ProgramFragmentStore::setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst) +{ + mBlendEnable = true; + if ((src == RS_BLEND_SRC_ONE) && + (dst == RS_BLEND_DST_ZERO)) { + mBlendEnable = false; + } + + switch(src) { + case RS_BLEND_SRC_ZERO: + mBlendSrc = GL_ZERO; + break; + case RS_BLEND_SRC_ONE: + mBlendSrc = GL_ONE; + break; + case RS_BLEND_SRC_DST_COLOR: + mBlendSrc = GL_DST_COLOR; + break; + case RS_BLEND_SRC_ONE_MINUS_DST_COLOR: + mBlendSrc = GL_ONE_MINUS_DST_COLOR; + break; + case RS_BLEND_SRC_SRC_ALPHA: + mBlendSrc = GL_SRC_ALPHA; + break; + case RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA: + mBlendSrc = GL_ONE_MINUS_SRC_ALPHA; + break; + case RS_BLEND_SRC_DST_ALPHA: + mBlendSrc = GL_DST_ALPHA; + break; + case RS_BLEND_SRC_ONE_MINUS_DST_ALPHA: + mBlendSrc = GL_ONE_MINUS_DST_ALPHA; + break; + case RS_BLEND_SRC_SRC_ALPHA_SATURATE: + mBlendSrc = GL_SRC_ALPHA_SATURATE; + break; + } + + switch(dst) { + case RS_BLEND_DST_ZERO: + mBlendDst = GL_ZERO; + break; + case RS_BLEND_DST_ONE: + mBlendDst = GL_ONE; + break; + case RS_BLEND_DST_SRC_COLOR: + mBlendDst = GL_SRC_COLOR; + break; + case RS_BLEND_DST_ONE_MINUS_SRC_COLOR: + mBlendDst = GL_ONE_MINUS_SRC_COLOR; + break; + case RS_BLEND_DST_SRC_ALPHA: + mBlendDst = GL_SRC_ALPHA; + break; + case RS_BLEND_DST_ONE_MINUS_SRC_ALPHA: + mBlendDst = GL_ONE_MINUS_SRC_ALPHA; + break; + case RS_BLEND_DST_DST_ALPHA: + mBlendDst = GL_DST_ALPHA; + break; + case RS_BLEND_DST_ONE_MINUS_DST_ALPHA: + mBlendDst = GL_ONE_MINUS_DST_ALPHA; + break; + } +} + +void ProgramFragmentStore::setColorMask(bool r, bool g, bool b, bool a) +{ + mColorRWriteEnable = r; + mColorGWriteEnable = g; + mColorBWriteEnable = b; + mColorAWriteEnable = a; +} + + +ProgramFragmentStoreState::ProgramFragmentStoreState() +{ + mPFS = NULL; +} + +ProgramFragmentStoreState::~ProgramFragmentStoreState() +{ + delete mPFS; + +} + +void ProgramFragmentStoreState::init(Context *rsc, int32_t w, int32_t h) +{ + ProgramFragmentStore *pfs = new ProgramFragmentStore(NULL, NULL); + mDefault.set(pfs); +} + + +namespace android { +namespace renderscript { + +void rsi_ProgramFragmentStoreBegin(Context * rsc, RsElement in, RsElement out) +{ + delete rsc->mStateFragmentStore.mPFS; + rsc->mStateFragmentStore.mPFS = new ProgramFragmentStore((Element *)in, (Element *)out); + +} + +void rsi_ProgramFragmentStoreDepthFunc(Context *rsc, RsDepthFunc func) +{ + rsc->mStateFragmentStore.mPFS->setDepthFunc(func); +} + +void rsi_ProgramFragmentStoreDepthMask(Context *rsc, bool mask) +{ + rsc->mStateFragmentStore.mPFS->setDepthMask(mask); +} + +void rsi_ProgramFragmentStoreColorMask(Context *rsc, bool r, bool g, bool b, bool a) +{ + rsc->mStateFragmentStore.mPFS->setColorMask(r, g, b, a); +} + +void rsi_ProgramFragmentStoreBlendFunc(Context *rsc, RsBlendSrcFunc src, RsBlendDstFunc dst) +{ + rsc->mStateFragmentStore.mPFS->setBlendFunc(src, dst); +} + +RsProgramFragmentStore rsi_ProgramFragmentStoreCreate(Context *rsc) +{ + ProgramFragmentStore *pfs = rsc->mStateFragmentStore.mPFS; + pfs->incRef(); + rsc->mStateFragmentStore.mPFS = 0; + return pfs; +} + +void rsi_ProgramFragmentStoreDither(Context *rsc, bool enable) +{ + rsc->mStateFragmentStore.mPFS->setDitherEnable(enable); +} + +void rsi_ProgramFragmentStoreDestroy(Context *rsc, RsProgramFragmentStore vpfs) +{ + ProgramFragmentStore *pfs = (ProgramFragmentStore *)vpfs; + if (pfs->getName()) { + rsc->removeName(pfs); + } + pfs->decRef(); +} + + + + +} +} diff --git a/libs/rs/rsProgramFragmentStore.h b/libs/rs/rsProgramFragmentStore.h new file mode 100644 index 0000000..bd3a9f4 --- /dev/null +++ b/libs/rs/rsProgramFragmentStore.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_PROGRAM_FRAGMENT_STORE_H +#define ANDROID_RS_PROGRAM_FRAGMENT_STORE_H + +#include "rsProgram.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + +class ProgramFragmentStore : public Program +{ +public: + + + + ProgramFragmentStore(Element *in, Element *out); + virtual ~ProgramFragmentStore(); + + virtual void setupGL(); + + + void setDepthFunc(RsDepthFunc); + void setDepthMask(bool); + + void setBlendFunc(RsBlendSrcFunc src, RsBlendDstFunc dst); + void setColorMask(bool, bool, bool, bool); + + void setDitherEnable(bool); + +protected: + bool mDitherEnable; + + bool mBlendEnable; + bool mColorRWriteEnable; + bool mColorGWriteEnable; + bool mColorBWriteEnable; + bool mColorAWriteEnable; + int32_t mBlendSrc; + int32_t mBlendDst; + + + + bool mDepthTestEnable; + bool mDepthWriteEnable; + int32_t mDepthFunc; + + + + bool mStencilTestEnable; + + + +}; + +class ProgramFragmentStoreState +{ +public: + ProgramFragmentStoreState(); + ~ProgramFragmentStoreState(); + void init(Context *rsc, int32_t w, int32_t h); + + ObjectBaseRef<ProgramFragmentStore> mDefault; + ProgramFragmentStore *mPFS; +}; + + +} +} +#endif + + + diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp new file mode 100644 index 0000000..417ba6a --- /dev/null +++ b/libs/rs/rsProgramVertex.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" +#include "rsProgramVertex.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> + +using namespace android; +using namespace android::renderscript; + + +ProgramVertex::ProgramVertex(Element *in, Element *out) : + Program(in, out) +{ + mTextureMatrixEnable = false; + mLightCount = 0; +} + +ProgramVertex::~ProgramVertex() +{ +} + +static void logMatrix(const char *txt, const float *f) +{ + LOGV("Matrix %s, %p", txt, f); + LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[0], f[4], f[8], f[12]); + LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[1], f[5], f[9], f[13]); + LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[2], f[6], f[10], f[14]); + LOGV("%6.2f, %6.2f, %6.2f, %6.2f", f[3], f[7], f[11], f[15]); +} + +void ProgramVertex::setupGL() +{ + const float *f = static_cast<const float *>(mConstants[0]->getPtr()); + + glMatrixMode(GL_TEXTURE); + if (mTextureMatrixEnable) { + glLoadMatrixf(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]); + } else { + glLoadIdentity(); + } + + + LOGE("lights %i ", mLightCount); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + if (mLightCount) { + int v = 1; + glEnable(GL_LIGHTING); + glLightModelxv(GL_LIGHT_MODEL_TWO_SIDE, &v); + for (uint32_t ct = 0; ct < mLightCount; ct++) { + const Light *l = mLights[ct].get(); + glEnable(GL_LIGHT0 + ct); + l->setupGL(ct); + } + for (uint32_t ct = mLightCount; ct < MAX_LIGHTS; ct++) { + glDisable(GL_LIGHT0 + ct); + } + } else { + glDisable(GL_LIGHTING); + } + + if (!f) { + LOGE("Must bind constants to vertex program"); + } + + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]); + glMatrixMode(GL_MODELVIEW); + glLoadMatrixf(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]); +} + +void ProgramVertex::setConstantType(uint32_t slot, const Type *t) +{ + mConstantTypes[slot].set(t); +} + +void ProgramVertex::bindAllocation(uint32_t slot, Allocation *a) +{ + mConstants[slot].set(a); +} + +void ProgramVertex::addLight(const Light *l) +{ + if (mLightCount < MAX_LIGHTS) { + mLights[mLightCount].set(l); + mLightCount++; + } +} + + +ProgramVertexState::ProgramVertexState() +{ + mPV = NULL; +} + +ProgramVertexState::~ProgramVertexState() +{ + delete mPV; +} + +void ProgramVertexState::init(Context *rsc, int32_t w, int32_t h) +{ + ProgramVertex *pv = new ProgramVertex(NULL, NULL); + Allocation *alloc = (Allocation *) + rsi_AllocationCreatePredefSized(rsc, RS_ELEMENT_USER_FLOAT, 48); + mDefaultAlloc.set(alloc); + mDefault.set(pv); + + pv->bindAllocation(0, alloc); + + Matrix m; + m.loadOrtho(0,w, h,0, -1,1); + alloc->subData(RS_PROGRAM_VERTEX_PROJECTION_OFFSET, 16, &m.m[0]); + + m.loadIdentity(); + alloc->subData(RS_PROGRAM_VERTEX_MODELVIEW_OFFSET, 16, &m.m[0]); +} + + +namespace android { +namespace renderscript { + +void rsi_ProgramVertexBegin(Context *rsc, RsElement in, RsElement out) +{ + delete rsc->mStateVertex.mPV; + rsc->mStateVertex.mPV = new ProgramVertex((Element *)in, (Element *)out); +} + +RsProgramVertex rsi_ProgramVertexCreate(Context *rsc) +{ + ProgramVertex *pv = rsc->mStateVertex.mPV; + pv->incRef(); + rsc->mStateVertex.mPV = 0; + return pv; +} + +void rsi_ProgramVertexBindAllocation(Context *rsc, RsProgramVertex vpgm, uint32_t slot, RsAllocation constants) +{ + ProgramVertex *pv = static_cast<ProgramVertex *>(vpgm); + pv->bindAllocation(slot, static_cast<Allocation *>(constants)); +} + +void rsi_ProgramVertexSetType(Context *rsc, uint32_t slot, RsType constants) +{ + rsc->mStateVertex.mPV->setConstantType(slot, static_cast<const Type *>(constants)); +} + +void rsi_ProgramVertexSetTextureMatrixEnable(Context *rsc, bool enable) +{ + rsc->mStateVertex.mPV->setTextureMatrixEnable(enable); +} + +void rsi_ProgramVertexAddLight(Context *rsc, RsLight light) +{ + rsc->mStateVertex.mPV->addLight(static_cast<const Light *>(light)); +} + + +} +} diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h new file mode 100644 index 0000000..ac15b70 --- /dev/null +++ b/libs/rs/rsProgramVertex.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_PROGRAM_VERTEX_H +#define ANDROID_RS_PROGRAM_VERTEX_H + +#include "rsProgram.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + +class ProgramVertex : public Program +{ +public: + const static uint32_t MAX_CONSTANTS = 2; + const static uint32_t MAX_LIGHTS = 8; + + ProgramVertex(Element *in, Element *out); + virtual ~ProgramVertex(); + + virtual void setupGL(); + + + void setConstantType(uint32_t slot, const Type *); + void bindAllocation(uint32_t slot, Allocation *); + void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;} + void addLight(const Light *); + +protected: + bool mDirty; + uint32_t mLightCount; + + ObjectBaseRef<Allocation> mConstants[MAX_CONSTANTS]; + ObjectBaseRef<const Type> mConstantTypes[MAX_CONSTANTS]; + ObjectBaseRef<const Light> mLights[MAX_LIGHTS]; + + + // Hacks to create a program for now + bool mTextureMatrixEnable; + +}; + + +class ProgramVertexState +{ +public: + ProgramVertexState(); + ~ProgramVertexState(); + + void init(Context *rsc, int32_t w, int32_t h); + + ObjectBaseRef<ProgramVertex> mDefault; + ObjectBaseRef<Allocation> mDefaultAlloc; + + + + ProgramVertex *mPV; + + //ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE]; + + +}; + + +} +} +#endif + + diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp new file mode 100644 index 0000000..418f127 --- /dev/null +++ b/libs/rs/rsSampler.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <GLES/gl.h> +#include <GLES/glext.h> + +#include "rsContext.h" +#include "rsSampler.h" + + +using namespace android; +using namespace android::renderscript; + + +Sampler::Sampler() +{ + // Should not get called. + rsAssert(0); +} + +Sampler::Sampler(RsSamplerValue magFilter, + RsSamplerValue minFilter, + RsSamplerValue wrapS, + RsSamplerValue wrapT, + RsSamplerValue wrapR) +{ + mMagFilter = magFilter; + mMinFilter = minFilter; + mWrapS = wrapS; + mWrapT = wrapT; + mWrapR = wrapR; +} + +Sampler::~Sampler() +{ +} + +void Sampler::setupGL() +{ + GLenum trans[] = { + GL_NEAREST, //RS_SAMPLER_NEAREST, + GL_LINEAR, //RS_SAMPLER_LINEAR, + GL_LINEAR_MIPMAP_LINEAR, //RS_SAMPLER_LINEAR_MIP_LINEAR, + GL_REPEAT, //RS_SAMPLER_WRAP, + GL_CLAMP_TO_EDGE, //RS_SAMPLER_CLAMP + + }; + + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]); + +} + +void Sampler::bindToContext(SamplerState *ss, uint32_t slot) +{ + ss->mSamplers[slot].set(this); + mBoundSlot = slot; +} + +void Sampler::unbindFromContext(SamplerState *ss) +{ + int32_t slot = mBoundSlot; + mBoundSlot = -1; + ss->mSamplers[slot].clear(); +} + +void SamplerState::setupGL() +{ + for (uint32_t ct=0; ct < RS_MAX_SAMPLER_SLOT; ct++) { + Sampler *s = mSamplers[ct].get(); + if (s) { + s->setupGL(); + } else { + glBindTexture(GL_TEXTURE_2D, 0); + } + } +} + +//////////////////////////////// + +namespace android { +namespace renderscript { + + +void rsi_SamplerBegin(Context *rsc) +{ + SamplerState * ss = &rsc->mStateSampler; + + ss->mMagFilter = RS_SAMPLER_LINEAR; + ss->mMinFilter = RS_SAMPLER_LINEAR; + ss->mWrapS = RS_SAMPLER_WRAP; + ss->mWrapT = RS_SAMPLER_WRAP; + ss->mWrapR = RS_SAMPLER_WRAP; +} + +void rsi_SamplerSet(Context *rsc, RsSamplerParam param, RsSamplerValue value) +{ + SamplerState * ss = &rsc->mStateSampler; + + switch(param) { + case RS_SAMPLER_MAG_FILTER: + ss->mMagFilter = value; + break; + case RS_SAMPLER_MIN_FILTER: + ss->mMinFilter = value; + break; + case RS_SAMPLER_WRAP_S: + ss->mWrapS = value; + break; + case RS_SAMPLER_WRAP_T: + ss->mWrapT = value; + break; + case RS_SAMPLER_WRAP_R: + ss->mWrapR = value; + break; + } + +} + +RsSampler rsi_SamplerCreate(Context *rsc) +{ + SamplerState * ss = &rsc->mStateSampler; + + + Sampler * s = new Sampler(ss->mMagFilter, + ss->mMinFilter, + ss->mWrapS, + ss->mWrapT, + ss->mWrapR); + return s; +} + +void rsi_SamplerDestroy(Context *rsc, RsSampler vs) +{ + Sampler * s = static_cast<Sampler *>(vs); + s->decRef(); + +} + + +}} diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h new file mode 100644 index 0000000..4b504f6 --- /dev/null +++ b/libs/rs/rsSampler.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_SAMPLER_H +#define ANDROID_RS_SAMPLER_H + +#include "rsAllocation.h" +#include "RenderScript.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + +const static uint32_t RS_MAX_SAMPLER_SLOT = 16; + +class SamplerState; + +class Sampler : public ObjectBase +{ +public: + Sampler(RsSamplerValue magFilter, + RsSamplerValue minFilter, + RsSamplerValue wrapS, + RsSamplerValue wrapT, + RsSamplerValue wrapR); + + virtual ~Sampler(); + + void bind(Allocation *); + void setupGL(); + + void bindToContext(SamplerState *, uint32_t slot); + void unbindFromContext(SamplerState *); + +protected: + RsSamplerValue mMagFilter; + RsSamplerValue mMinFilter; + RsSamplerValue mWrapS; + RsSamplerValue mWrapT; + RsSamplerValue mWrapR; + + int32_t mBoundSlot; + +private: + Sampler(); + +}; + + +class SamplerState +{ +public: + + RsSamplerValue mMagFilter; + RsSamplerValue mMinFilter; + RsSamplerValue mWrapS; + RsSamplerValue mWrapT; + RsSamplerValue mWrapR; + + + ObjectBaseRef<Sampler> mSamplers[RS_MAX_SAMPLER_SLOT]; + + void setupGL(); + +}; + + + +} +} +#endif //ANDROID_RS_SAMPLER_H + + + diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp new file mode 100644 index 0000000..ae85c9c --- /dev/null +++ b/libs/rs/rsScript.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" + +using namespace android; +using namespace android::renderscript; + +Script::Script() +{ + memset(&mEnviroment, 0, sizeof(mEnviroment)); + mEnviroment.mClearColor[0] = 0; + mEnviroment.mClearColor[1] = 0; + mEnviroment.mClearColor[2] = 0; + mEnviroment.mClearColor[3] = 1; + mEnviroment.mClearDepth = 1; +} + +Script::~Script() +{ +} + +namespace android { +namespace renderscript { + + +void rsi_ScriptDestroy(Context * rsc, RsScript vs) +{ + Script *s = static_cast<Script *>(vs); + s->decRef(); +} + +void rsi_ScriptBindAllocation(Context * rsc, RsScript vs, RsAllocation va, uint32_t slot) +{ + Script *s = static_cast<Script *>(vs); + s->mSlots[slot].set(static_cast<Allocation *>(va)); +} + + +} +} + diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h new file mode 100644 index 0000000..7dd2b61 --- /dev/null +++ b/libs/rs/rsScript.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_SCRIPT_H +#define ANDROID_RS_SCRIPT_H + +#include "rsAllocation.h" + + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + +class ProgramVertex; +class ProgramFragment; +class ProgramRaster; +class ProgramFragmentStore; + +class Script : public ObjectBase +{ +public: + + Script(); + virtual ~Script(); + + + struct Enviroment_t { + bool mIsRoot; + float mClearColor[4]; + float mClearDepth; + uint32_t mClearStencil; + + ObjectBaseRef<ProgramVertex> mVertex; + ObjectBaseRef<ProgramFragment> mFragment; + //ObjectBaseRef<ProgramRaster> mRaster; + ObjectBaseRef<ProgramFragmentStore> mFragmentStore; + + }; + Enviroment_t mEnviroment; + + const Type * mConstantBufferTypes; + uint32_t mCounstantBufferCount; + + ObjectBaseRef<Allocation> mSlots[16]; + + virtual bool run(Context *, uint32_t launchID) = 0; +}; + + + +} +} +#endif + diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp new file mode 100644 index 0000000..842c836 --- /dev/null +++ b/libs/rs/rsScriptC.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" +#include "rsScriptC.h" +#include "rsMatrix.h" + +#include "acc/acc.h" +#include "utils/String8.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> + +using namespace android; +using namespace android::renderscript; + +#define GET_TLS() Context::ScriptTLSStruct * tls = \ + (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \ + Context * rsc = tls->mContext; \ + ScriptC * sc = (ScriptC *) tls->mScript + + +ScriptC::ScriptC() +{ + mAccScript = NULL; + memset(&mProgram, 0, sizeof(mProgram)); +} + +ScriptC::~ScriptC() +{ + if (mAccScript) { + accDeleteScript(mAccScript); + } +} + + +bool ScriptC::run(Context *rsc, uint32_t launchIndex) +{ + Context::ScriptTLSStruct * tls = + (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); + + if (mEnviroment.mFragmentStore.get()) { + rsc->setFragmentStore(mEnviroment.mFragmentStore.get()); + } + if (mEnviroment.mFragment.get()) { + rsc->setFragment(mEnviroment.mFragment.get()); + } + if (mEnviroment.mVertex.get()) { + rsc->setVertex(mEnviroment.mVertex.get()); + } + + bool ret = false; + tls->mScript = this; + ret = mProgram.mScript(launchIndex) != 0; + tls->mScript = NULL; + return ret; +} + +ScriptCState::ScriptCState() +{ + clear(); +} + +ScriptCState::~ScriptCState() +{ + if (mAccScript) { + accDeleteScript(mAccScript); + } +} + +void ScriptCState::clear() +{ + memset(&mProgram, 0, sizeof(mProgram)); + + mConstantBufferTypes.clear(); + + memset(&mEnviroment, 0, sizeof(mEnviroment)); + mEnviroment.mClearColor[0] = 0; + mEnviroment.mClearColor[1] = 0; + mEnviroment.mClearColor[2] = 0; + mEnviroment.mClearColor[3] = 1; + mEnviroment.mClearDepth = 1; + mEnviroment.mClearStencil = 0; + mEnviroment.mIsRoot = false; + + mAccScript = NULL; + +} + +static ACCvoid* symbolLookup(ACCvoid* pContext, const ACCchar* name) +{ + const ScriptCState::SymbolTable_t *sym = ScriptCState::lookupSymbol(name); + if (sym) { + return sym->mPtr; + } + LOGE("ScriptC sym lookup failed for %s", name); + return NULL; +} + +void ScriptCState::runCompiler(Context *rsc) +{ + mAccScript = accCreateScript(); + String8 tmp; + + rsc->appendNameDefines(&tmp); + appendDecls(&tmp); + tmp.append("#line 1\n"); + + const char* scriptSource[] = {tmp.string(), mProgram.mScriptText}; + int scriptLength[] = {tmp.length(), mProgram.mScriptTextLength} ; + accScriptSource(mAccScript, sizeof(scriptLength) / sizeof(int), scriptSource, scriptLength); + accRegisterSymbolCallback(mAccScript, symbolLookup, NULL); + accCompileScript(mAccScript); + accGetScriptLabel(mAccScript, "main", (ACCvoid**) &mProgram.mScript); + rsAssert(mProgram.mScript); + + if (!mProgram.mScript) { + ACCchar buf[4096]; + ACCsizei len; + accGetScriptInfoLog(mAccScript, sizeof(buf), &len, buf); + LOGE(buf); + + } + + mEnviroment.mFragment.set(rsc->getDefaultProgramFragment()); + mEnviroment.mVertex.set(rsc->getDefaultProgramVertex()); + mEnviroment.mFragmentStore.set(rsc->getDefaultProgramFragmentStore()); + + if (mProgram.mScript) { + const static int pragmaMax = 16; + ACCsizei pragmaCount; + ACCchar * str[pragmaMax]; + accGetPragmas(mAccScript, &pragmaCount, pragmaMax, &str[0]); + + for (int ct=0; ct < pragmaCount; ct+=2) { + if (!strcmp(str[ct], "version")) { + continue; + } + + if (!strcmp(str[ct], "stateVertex")) { + if (!strcmp(str[ct+1], "default")) { + continue; + } + if (!strcmp(str[ct+1], "parent")) { + mEnviroment.mVertex.clear(); + continue; + } + ProgramVertex * pv = (ProgramVertex *)rsc->lookupName(str[ct+1]); + if (pv != NULL) { + mEnviroment.mVertex.set(pv); + continue; + } + LOGE("Unreconized value %s passed to stateVertex", str[ct+1]); + } + + if (!strcmp(str[ct], "stateRaster")) { + LOGE("Unreconized value %s passed to stateRaster", str[ct+1]); + } + + if (!strcmp(str[ct], "stateFragment")) { + if (!strcmp(str[ct+1], "default")) { + continue; + } + if (!strcmp(str[ct+1], "parent")) { + mEnviroment.mFragment.clear(); + continue; + } + ProgramFragment * pf = (ProgramFragment *)rsc->lookupName(str[ct+1]); + if (pf != NULL) { + mEnviroment.mFragment.set(pf); + continue; + } + LOGE("Unreconized value %s passed to stateFragment", str[ct+1]); + } + + if (!strcmp(str[ct], "stateFragmentStore")) { + if (!strcmp(str[ct+1], "default")) { + continue; + } + if (!strcmp(str[ct+1], "parent")) { + mEnviroment.mFragmentStore.clear(); + continue; + } + ProgramFragmentStore * pfs = + (ProgramFragmentStore *)rsc->lookupName(str[ct+1]); + if (pfs != NULL) { + mEnviroment.mFragmentStore.set(pfs); + continue; + } + LOGE("Unreconized value %s passed to stateFragmentStore", str[ct+1]); + } + + } + + + } else { + // Deal with an error. + } + +} + +namespace android { +namespace renderscript { + +void rsi_ScriptCBegin(Context * rsc) +{ + ScriptCState *ss = &rsc->mScriptC; + ss->clear(); +} + +void rsi_ScriptCSetClearColor(Context * rsc, float r, float g, float b, float a) +{ + ScriptCState *ss = &rsc->mScriptC; + ss->mEnviroment.mClearColor[0] = r; + ss->mEnviroment.mClearColor[1] = g; + ss->mEnviroment.mClearColor[2] = b; + ss->mEnviroment.mClearColor[3] = a; +} + +void rsi_ScriptCSetClearDepth(Context * rsc, float v) +{ + ScriptCState *ss = &rsc->mScriptC; + ss->mEnviroment.mClearDepth = v; +} + +void rsi_ScriptCSetClearStencil(Context * rsc, uint32_t v) +{ + ScriptCState *ss = &rsc->mScriptC; + ss->mEnviroment.mClearStencil = v; +} + +void rsi_ScriptCAddType(Context * rsc, RsType vt) +{ + ScriptCState *ss = &rsc->mScriptC; + ss->mConstantBufferTypes.add(static_cast<const Type *>(vt)); +} + +void rsi_ScriptCSetScript(Context * rsc, void *vp) +{ + ScriptCState *ss = &rsc->mScriptC; + ss->mProgram.mScript = reinterpret_cast<ScriptC::RunScript_t>(vp); +} + +void rsi_ScriptCSetRoot(Context * rsc, bool isRoot) +{ + ScriptCState *ss = &rsc->mScriptC; + ss->mEnviroment.mIsRoot = isRoot; +} + +void rsi_ScriptCSetText(Context *rsc, const char *text, uint32_t len) +{ + ScriptCState *ss = &rsc->mScriptC; + ss->mProgram.mScriptText = text; + ss->mProgram.mScriptTextLength = len; +} + + +RsScript rsi_ScriptCCreate(Context * rsc) +{ + ScriptCState *ss = &rsc->mScriptC; + + ss->runCompiler(rsc); + + ScriptC *s = new ScriptC(); + s->incRef(); + s->mAccScript = ss->mAccScript; + ss->mAccScript = NULL; + s->mEnviroment = ss->mEnviroment; + s->mProgram = ss->mProgram; + ss->clear(); + + return s; +} + +} +} + + diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h new file mode 100644 index 0000000..860b4d1 --- /dev/null +++ b/libs/rs/rsScriptC.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_SCRIPT_C_H +#define ANDROID_RS_SCRIPT_C_H + +#include "rsScript.h" + +#include "RenderScriptEnv.h" + +struct ACCscript; + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + + +class ScriptC : public Script +{ +public: + typedef int (*RunScript_t)(uint32_t launchIndex); + + ScriptC(); + virtual ~ScriptC(); + + struct Program_t { + const char * mScriptText; + uint32_t mScriptTextLength; + + + int mVersionMajor; + int mVersionMinor; + + RunScript_t mScript; + }; + + Program_t mProgram; + + ACCscript* mAccScript; + + virtual bool run(Context *, uint32_t launchID); +}; + +class ScriptCState +{ +public: + ScriptCState(); + ~ScriptCState(); + + ACCscript* mAccScript; + + ScriptC::Program_t mProgram; + Script::Enviroment_t mEnviroment; + + Vector<const Type *> mConstantBufferTypes; + + void clear(); + void runCompiler(Context *rsc); + + struct SymbolTable_t { + const char * mName; + void * mPtr; + const char * mRet; + const char * mParam; + }; + static SymbolTable_t gSyms[]; + static const SymbolTable_t * lookupSymbol(const char *); + static void appendDecls(String8 *str); +}; + + +} +} +#endif + + + diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp new file mode 100644 index 0000000..129b19f --- /dev/null +++ b/libs/rs/rsScriptC_Lib.cpp @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" +#include "rsScriptC.h" +#include "rsMatrix.h" + +#include "acc/acc.h" +#include "utils/String8.h" + +#include <GLES/gl.h> +#include <GLES/glext.h> + +using namespace android; +using namespace android::renderscript; + +#define GET_TLS() Context::ScriptTLSStruct * tls = \ + (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \ + Context * rsc = tls->mContext; \ + ScriptC * sc = (ScriptC *) tls->mScript + + +////////////////////////////////////////////////////////////////////////////// +// IO routines +////////////////////////////////////////////////////////////////////////////// + +static float SC_loadF(uint32_t bank, uint32_t offset) +{ + GET_TLS(); + const void *vp = sc->mSlots[bank]->getPtr(); + const float *f = static_cast<const float *>(vp); + //LOGE("loadF %i %i = %f %x", bank, offset, f, ((int *)&f)[0]); + return f[offset]; +} + +static int32_t SC_loadI32(uint32_t bank, uint32_t offset) +{ + GET_TLS(); + const void *vp = sc->mSlots[bank]->getPtr(); + const int32_t *i = static_cast<const int32_t *>(vp); + //LOGE("loadI32 %i %i = %i", bank, offset, t); + return i[offset]; +} + +static uint32_t SC_loadU32(uint32_t bank, uint32_t offset) +{ + GET_TLS(); + const void *vp = sc->mSlots[bank]->getPtr(); + const uint32_t *i = static_cast<const uint32_t *>(vp); + return i[offset]; +} + +static void SC_loadVec4(uint32_t bank, uint32_t offset, rsc_Vector4 *v) +{ + GET_TLS(); + const void *vp = sc->mSlots[bank]->getPtr(); + const float *f = static_cast<const float *>(vp); + memcpy(v, &f[offset], sizeof(rsc_Vector4)); +} + +static void SC_loadMatrix(uint32_t bank, uint32_t offset, rsc_Matrix *m) +{ + GET_TLS(); + const void *vp = sc->mSlots[bank]->getPtr(); + const float *f = static_cast<const float *>(vp); + memcpy(m, &f[offset], sizeof(rsc_Matrix)); +} + + +static void SC_storeF(uint32_t bank, uint32_t offset, float v) +{ + //LOGE("storeF %i %i %f", bank, offset, v); + GET_TLS(); + void *vp = sc->mSlots[bank]->getPtr(); + float *f = static_cast<float *>(vp); + f[offset] = v; +} + +static void SC_storeI32(uint32_t bank, uint32_t offset, int32_t v) +{ + GET_TLS(); + void *vp = sc->mSlots[bank]->getPtr(); + int32_t *f = static_cast<int32_t *>(vp); + static_cast<int32_t *>(sc->mSlots[bank]->getPtr())[offset] = v; +} + +static void SC_storeU32(uint32_t bank, uint32_t offset, uint32_t v) +{ + GET_TLS(); + void *vp = sc->mSlots[bank]->getPtr(); + uint32_t *f = static_cast<uint32_t *>(vp); + static_cast<uint32_t *>(sc->mSlots[bank]->getPtr())[offset] = v; +} + +static void SC_storeVec4(uint32_t bank, uint32_t offset, const rsc_Vector4 *v) +{ + GET_TLS(); + void *vp = sc->mSlots[bank]->getPtr(); + float *f = static_cast<float *>(vp); + memcpy(&f[offset], v, sizeof(rsc_Vector4)); +} + +static void SC_storeMatrix(uint32_t bank, uint32_t offset, const rsc_Matrix *m) +{ + GET_TLS(); + void *vp = sc->mSlots[bank]->getPtr(); + float *f = static_cast<float *>(vp); + memcpy(&f[offset], m, sizeof(rsc_Matrix)); +} + + +////////////////////////////////////////////////////////////////////////////// +// Math routines +////////////////////////////////////////////////////////////////////////////// + +static float SC_randf(float max) +{ + float r = (float)rand(); + return r / RAND_MAX * max; +} + + + + + +////////////////////////////////////////////////////////////////////////////// +// Matrix routines +////////////////////////////////////////////////////////////////////////////// + + +static void SC_matrixLoadIdentity(rsc_Matrix *mat) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->loadIdentity(); +} + +static void SC_matrixLoadFloat(rsc_Matrix *mat, const float *f) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->load(f); +} + +static void SC_matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->load(reinterpret_cast<const Matrix *>(newmat)); +} + +static void SC_matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->loadRotate(rot, x, y, z); +} + +static void SC_matrixLoadScale(rsc_Matrix *mat, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->loadScale(x, y, z); +} + +static void SC_matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->loadTranslate(x, y, z); +} + +static void SC_matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->loadMultiply(reinterpret_cast<const Matrix *>(lhs), + reinterpret_cast<const Matrix *>(rhs)); +} + +static void SC_matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->multiply(reinterpret_cast<const Matrix *>(rhs)); +} + +static void SC_matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->rotate(rot, x, y, z); +} + +static void SC_matrixScale(rsc_Matrix *mat, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->scale(x, y, z); +} + +static void SC_matrixTranslate(rsc_Matrix *mat, float x, float y, float z) +{ + Matrix *m = reinterpret_cast<Matrix *>(mat); + m->translate(x, y, z); +} + + + + +////////////////////////////////////////////////////////////////////////////// +// Context +////////////////////////////////////////////////////////////////////////////// + +static void SC_bindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va) +{ + GET_TLS(); + rsi_ProgramFragmentBindTexture(rsc, + static_cast<ProgramFragment *>(vpf), + slot, + static_cast<Allocation *>(va)); + +} + +static void SC_bindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs) +{ + GET_TLS(); + rsi_ProgramFragmentBindSampler(rsc, + static_cast<ProgramFragment *>(vpf), + slot, + static_cast<Sampler *>(vs)); + +} + +static void SC_bindProgramFragmentStore(RsProgramFragmentStore pfs) +{ + GET_TLS(); + rsi_ContextBindProgramFragmentStore(rsc, pfs); + +} + +static void SC_bindProgramFragment(RsProgramFragment pf) +{ + GET_TLS(); + rsi_ContextBindProgramFragment(rsc, pf); + +} + +static void SC_bindProgramVertex(RsProgramVertex pv) +{ + GET_TLS(); + rsi_ContextBindProgramVertex(rsc, pv); + +} + +////////////////////////////////////////////////////////////////////////////// +// Drawing +////////////////////////////////////////////////////////////////////////////// + +static void SC_drawTriangleMesh(RsTriangleMesh mesh) +{ + GET_TLS(); + rsi_TriangleMeshRender(rsc, mesh); +} + +static void SC_drawTriangleMeshRange(RsTriangleMesh mesh, uint32_t start, uint32_t count) +{ + GET_TLS(); + rsi_TriangleMeshRenderRange(rsc, mesh, start, count); +} + +// Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a +static void SC_drawTriangleArray(int ialloc, uint32_t count) +{ + GET_TLS(); + RsAllocation alloc = (RsAllocation)ialloc; + + const Allocation *a = (const Allocation *)alloc; + const uint32_t *ptr = (const uint32_t *)a->getPtr(); + + rsc->setupCheck(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); + + glEnableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + glVertexPointer(2, GL_FIXED, 12, ptr + 1); + //glTexCoordPointer(2, GL_FIXED, 24, ptr + 1); + glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr); + + glDrawArrays(GL_TRIANGLES, 0, count * 3); +} + +static void SC_drawQuad(float x1, float y1, float z1, + float x2, float y2, float z2, + float x3, float y3, float z3, + float x4, float y4, float z4) +{ + GET_TLS(); + + //LOGE("Quad"); + //LOGE("%4.2f, %4.2f, %4.2f", x1, y1, z1); + //LOGE("%4.2f, %4.2f, %4.2f", x2, y2, z2); + //LOGE("%4.2f, %4.2f, %4.2f", x3, y3, z3); + //LOGE("%4.2f, %4.2f, %4.2f", x4, y4, z4); + + float vtx[] = {x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4}; + static const float tex[] = {0,1, 1,1, 1,0, 0,0}; + + + rsc->setupCheck(); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, vtx); + + glClientActiveTexture(GL_TEXTURE0); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, tex); + glClientActiveTexture(GL_TEXTURE1); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_FLOAT, 0, tex); + glClientActiveTexture(GL_TEXTURE0); + + glDisableClientState(GL_NORMAL_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + //glColorPointer(4, GL_UNSIGNED_BYTE, 12, ptr); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); +} + +////////////////////////////////////////////////////////////////////////////// +// +////////////////////////////////////////////////////////////////////////////// + +extern "C" const void * loadVp(uint32_t bank, uint32_t offset) +{ + GET_TLS(); + return &static_cast<const uint8_t *>(sc->mSlots[bank]->getPtr())[offset]; +} + + + +static void SC_color(float r, float g, float b, float a) +{ + glColor4f(r, g, b, a); +} + + +extern "C" void materialDiffuse(float r, float g, float b, float a) +{ + float v[] = {r, g, b, a}; + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v); +} + +extern "C" void materialSpecular(float r, float g, float b, float a) +{ + float v[] = {r, g, b, a}; + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v); +} + +extern "C" void lightPosition(float x, float y, float z, float w) +{ + float v[] = {x, y, z, w}; + glLightfv(GL_LIGHT0, GL_POSITION, v); +} + +extern "C" void materialShininess(float s) +{ + glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &s); +} + +extern "C" void uploadToTexture(RsAllocation va, uint32_t baseMipLevel) +{ + GET_TLS(); + rsi_AllocationUploadToTexture(rsc, va, baseMipLevel); +} + +extern "C" void enable(uint32_t p) +{ + glEnable(p); +} + +extern "C" void disable(uint32_t p) +{ + glDisable(p); +} + + + +static void SC_ClearColor(float r, float g, float b, float a) +{ + //LOGE("c %f %f %f %f", r, g, b, a); + GET_TLS(); + sc->mEnviroment.mClearColor[0] = r; + sc->mEnviroment.mClearColor[1] = g; + sc->mEnviroment.mClearColor[2] = b; + sc->mEnviroment.mClearColor[3] = a; +} + + + +////////////////////////////////////////////////////////////////////////////// +// Class implementation +////////////////////////////////////////////////////////////////////////////// + +ScriptCState::SymbolTable_t ScriptCState::gSyms[] = { + // IO + { "loadI32", (void *)&SC_loadI32, + "int", "(int, int)" }, + //{ "loadU32", (void *)&SC_loadU32, "unsigned int", "(int, int)" }, + { "loadF", (void *)&SC_loadF, + "float", "(int, int)" }, + { "loadVec4", (void *)&SC_loadVec4, + "void", "(int, int, float *)" }, + { "loadMatrix", (void *)&SC_loadMatrix, + "void", "(int, int, float *)" }, + { "storeI32", (void *)&SC_storeI32, + "void", "(int, int, int)" }, + //{ "storeU32", (void *)&SC_storeU32, "void", "(int, int, unsigned int)" }, + { "storeF", (void *)&SC_storeF, + "void", "(int, int, float)" }, + { "storeVec4", (void *)&SC_storeVec4, + "void", "(int, int, float *)" }, + { "storeMatrix", (void *)&SC_storeMatrix, + "void", "(int, int, float *)" }, + + // math + { "sinf", (void *)&sinf, + "float", "(float)" }, + { "cosf", (void *)&cosf, + "float", "(float)" }, + { "fabs", (void *)&fabs, + "float", "(float)" }, + { "randf", (void *)&SC_randf, + "float", "(float)" }, + + // matrix + { "matrixLoadIdentity", (void *)&SC_matrixLoadIdentity, + "void", "(float *mat)" }, + { "matrixLoadFloat", (void *)&SC_matrixLoadFloat, + "void", "(float *mat, float *f)" }, + { "matrixLoadMat", (void *)&SC_matrixLoadMat, + "void", "(float *mat, float *newmat)" }, + { "matrixLoadRotate", (void *)&SC_matrixLoadRotate, + "void", "(float *mat, float rot, float x, float y, float z)" }, + { "matrixLoadScale", (void *)&SC_matrixLoadScale, + "void", "(float *mat, float x, float y, float z)" }, + { "matrixLoadTranslate", (void *)&SC_matrixLoadTranslate, + "void", "(float *mat, float x, float y, float z)" }, + { "matrixLoadMultiply", (void *)&SC_matrixLoadMultiply, + "void", "(float *mat, float *lhs, float *rhs)" }, + { "matrixMultiply", (void *)&SC_matrixMultiply, + "void", "(float *mat, float *rhs)" }, + { "matrixRotate", (void *)&SC_matrixRotate, + "void", "(float *mat, float rot, float x, float y, float z)" }, + { "matrixScale", (void *)&SC_matrixScale, + "void", "(float *mat, float x, float y, float z)" }, + { "matrixTranslate", (void *)&SC_matrixTranslate, + "void", "(float *mat, float x, float y, float z)" }, + + // context + { "bindProgramFragment", (void *)&SC_bindProgramFragment, + "void", "(int)" }, + { "bindProgramFragmentStore", (void *)&SC_bindProgramFragmentStore, + "void", "(int)" }, + { "bindProgramVertex", (void *)&SC_bindProgramVertex, + "void", "(int)" }, + { "bindSampler", (void *)&SC_bindSampler, + "void", "(int, int, int)" }, + { "bindTexture", (void *)&SC_bindTexture, + "void", "(int, int, int)" }, + + // drawing + { "drawQuad", (void *)&SC_drawQuad, + "void", "(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4)" }, + { "drawTriangleArray", (void *)&SC_drawTriangleArray, + "void", "(int ialloc, int count)" }, + { "drawTriangleMesh", (void *)&SC_drawTriangleMesh, + "void", "(int mesh)" }, + { "drawTriangleMeshRange", (void *)&SC_drawTriangleMeshRange, + "void", "(int mesh, int start, int count)" }, + + + // misc + { "pfClearColor", (void *)&SC_ClearColor, + "void", "(float, float, float, float)" }, + + { "color", (void *)&SC_color, + "void", "(float, float, float, float)" }, + + { NULL, NULL, NULL, NULL } +}; + +const ScriptCState::SymbolTable_t * ScriptCState::lookupSymbol(const char *sym) +{ + ScriptCState::SymbolTable_t *syms = gSyms; + + while (syms->mPtr) { + if (!strcmp(syms->mName, sym)) { + return syms; + } + syms++; + } + return NULL; +} + +void ScriptCState::appendDecls(String8 *str) +{ + ScriptCState::SymbolTable_t *syms = gSyms; + while (syms->mPtr) { + str->append(syms->mRet); + str->append(" "); + str->append(syms->mName); + str->append(syms->mParam); + str->append(";\n"); + syms++; + } +} + + diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp new file mode 100644 index 0000000..89df59d --- /dev/null +++ b/libs/rs/rsThreadIO.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" + +#include "rsThreadIO.h" + +using namespace android; +using namespace android::renderscript; + +ThreadIO *android::renderscript::gIO = NULL; + +ThreadIO::ThreadIO() +{ + mToCore.init(16 * 1024); +} + +ThreadIO::~ThreadIO() +{ +} + +bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand) +{ + uint32_t cmdID = 0; + uint32_t cmdSize = 0; + bool ret = false; + while(!mToCore.isEmpty() || waitForCommand) { + ret = true; + const void * data = mToCore.get(&cmdID, &cmdSize); + waitForCommand = false; + //LOGV("playCoreCommands 3 %i %i", cmdID, cmdSize); + + gPlaybackFuncs[cmdID](con, data); + mToCore.next(); + } + return ret; +} + + diff --git a/libs/rs/rsThreadIO.h b/libs/rs/rsThreadIO.h new file mode 100644 index 0000000..72746c5 --- /dev/null +++ b/libs/rs/rsThreadIO.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_THREAD_IO_H +#define ANDROID_RS_THREAD_IO_H + +#include "rsUtils.h" +#include "rsLocklessFifo.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + +class Context; + +class ThreadIO { +public: + ThreadIO(); + ~ThreadIO(); + + // Plays back commands from the client. + // Returns true if any commands were processed. + bool playCoreCommands(Context *con, bool waitForCommand); + + + LocklessCommandFifo mToCore; + //LocklessCommandFifo mToClient; + + intptr_t mToCoreRet; + +}; + +extern ThreadIO *gIO; + + + +} +} +#endif + diff --git a/libs/rs/rsTriangleMesh.cpp b/libs/rs/rsTriangleMesh.cpp new file mode 100644 index 0000000..8c7bc92 --- /dev/null +++ b/libs/rs/rsTriangleMesh.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" + +using namespace android; +using namespace android::renderscript; + +#include <GLES/gl.h> +#include <GLES/glext.h> + +TriangleMesh::TriangleMesh() +{ + mVertexElement = NULL; + mIndexElement = NULL; + mVertexData = NULL; + mIndexData = NULL; + mTriangleCount = 0; + mVertexDataSize = 0; + mIndexDataSize = 0; + + mBufferObjects[0] = 0; + mBufferObjects[1] = 0; + + mOffsetCoord = 0; + mOffsetTex = 0; + mOffsetNorm = 0; + + mSizeCoord = 0; + mSizeTex = 0; + mSizeNorm = 0; + +} + +TriangleMesh::~TriangleMesh() +{ + free(mVertexData); + free(mIndexData); +} + + + +TriangleMeshContext::TriangleMeshContext() +{ + clear(); +} + +TriangleMeshContext::~TriangleMeshContext() +{ +} + +void TriangleMeshContext::clear() +{ + mVertexElement = NULL; + mVertexSizeBits = 0; + mIndexElement = NULL; + mIndexSizeBits = 0; + mTriangleCount = 0; + mVertexData.clear(); + mIndexData.clear(); +} + +void TriangleMesh::analyzeElement() +{ + for (uint32_t ct=0; ct < mVertexElement->getComponentCount(); ct++) { + const Component *c = mVertexElement->getComponent(ct); + + if (c->getKind() == Component::X) { + rsAssert(mSizeCoord == 0); + mSizeCoord = 1; + mOffsetCoord = ct; + } + if (c->getKind() == Component::Y) { + rsAssert(mSizeCoord == 1); + mSizeCoord = 2; + } + if (c->getKind() == Component::Z) { + rsAssert(mSizeCoord == 2); + mSizeCoord = 3; + } + if (c->getKind() == Component::W) { + rsAssert(mSizeCoord == 4); + mSizeCoord = 4; + } + + if (c->getKind() == Component::NX) { + rsAssert(mSizeNorm == 0); + mSizeNorm = 1; + mOffsetNorm = ct; + } + if (c->getKind() == Component::NY) { + rsAssert(mSizeNorm == 1); + mSizeNorm = 2; + } + if (c->getKind() == Component::NZ) { + rsAssert(mSizeNorm == 2); + mSizeNorm = 3; + } + + if (c->getKind() == Component::S) { + rsAssert(mSizeTex == 0); + mSizeTex = 1; + mOffsetTex = ct; + } + if (c->getKind() == Component::T) { + rsAssert(mSizeTex == 1); + mSizeTex = 2; + } + } + LOGV("TriangleMesh %i,%i %i,%i %i,%i", mSizeCoord, mOffsetCoord, mSizeNorm, mOffsetNorm, mSizeTex, mOffsetTex); + +} + + +namespace android { +namespace renderscript { + +void rsi_TriangleMeshBegin(Context *rsc, RsElement vertex, RsElement index) +{ + TriangleMeshContext *tmc = &rsc->mStateTriangleMesh; + + tmc->clear(); + tmc->mVertexElement = static_cast<Element *>(vertex); + tmc->mVertexSizeBits = tmc->mVertexElement->getSizeBits(); + tmc->mIndexElement = static_cast<Element *>(index); + tmc->mIndexSizeBits = tmc->mIndexElement->getSizeBits(); + + assert(!(tmc->mVertexSizeBits & 0x7)); + assert(!(tmc->mIndexSizeBits & 0x7)); +} + +void rsi_TriangleMeshAddVertex(Context *rsc, const void *data) +{ + TriangleMeshContext *tmc = &rsc->mStateTriangleMesh; + + // todo: Make this efficient. + for (uint32_t ct = 0; (ct * 8) < tmc->mVertexSizeBits; ct++) { + tmc->mVertexData.add(static_cast<const uint8_t *>(data) [ct]); + } +} + +void rsi_TriangleMeshAddTriangle(Context *rsc, uint32_t idx1, uint32_t idx2, uint32_t idx3) +{ + TriangleMeshContext *tmc = &rsc->mStateTriangleMesh; + + // todo: Make this efficient. + switch(tmc->mIndexSizeBits) { + case 16: + tmc->mIndexData.add(idx1); + tmc->mIndexData.add(idx2); + tmc->mIndexData.add(idx3); + break; + default: + assert(0); + } + + tmc->mTriangleCount++; +} + +RsTriangleMesh rsi_TriangleMeshCreate(Context *rsc) +{ + TriangleMeshContext *tmc = &rsc->mStateTriangleMesh; + + TriangleMesh * tm = new TriangleMesh(); + if (!tm) { + LOGE("rsTriangleMeshCreate: Error OUT OF MEMORY"); + // error + return 0; + } + + tm->mTriangleCount = tmc->mTriangleCount; + tm->mIndexDataSize = tmc->mIndexData.size() * tmc->mIndexSizeBits >> 3; + tm->mVertexDataSize = tmc->mVertexData.size(); + tm->mIndexElement = tmc->mIndexElement; + tm->mVertexElement = tmc->mVertexElement; + + tm->mIndexData = malloc(tm->mIndexDataSize); + tm->mVertexData = malloc(tm->mVertexDataSize); + if (!tm->mIndexData || !tm->mVertexData) { + LOGE("rsTriangleMeshCreate: Error OUT OF MEMORY"); + delete tm; + return 0; + } + + memcpy(tm->mVertexData, tmc->mVertexData.array(), tm->mVertexDataSize); + memcpy(tm->mIndexData, tmc->mIndexData.array(), tm->mIndexDataSize); + tm->analyzeElement(); + + return tm; +} + +void rsi_TriangleMeshDestroy(Context *rsc, RsTriangleMesh vtm) +{ + TriangleMeshContext *tmc = &rsc->mStateTriangleMesh; + TriangleMesh * tm = static_cast<TriangleMesh *>(vtm); + + free(tm->mIndexData); + free(tm->mVertexData); + delete tm; +} + + + +void rsi_TriangleMeshRenderRange(Context *rsc, RsTriangleMesh vtm, uint32_t first, uint32_t count) +{ + TriangleMesh * tm = static_cast<TriangleMesh *>(vtm); + + rsc->setupCheck(); + + if (!tm->mBufferObjects[0]) { + glGenBuffers(2, &tm->mBufferObjects[0]); + + glBindBuffer(GL_ARRAY_BUFFER, tm->mBufferObjects[0]); + glBufferData(GL_ARRAY_BUFFER, tm->mVertexDataSize, tm->mVertexData, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, tm->mIndexDataSize, tm->mIndexData, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + if (first >= tm->mTriangleCount) { + return; + } + if (count >= (tm->mTriangleCount - first)) { + count = tm->mTriangleCount - first; + } + if (!count) { + return; + } + + const float *f = (const float *)tm->mVertexData; + + glBindBuffer(GL_ARRAY_BUFFER, tm->mBufferObjects[0]); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(tm->mSizeCoord, + GL_FLOAT, + tm->mVertexElement->getSizeBytes(), + (void *)tm->mVertexElement->getComponentOffsetBytes(tm->mOffsetCoord)); + + if (tm->mSizeTex) { + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(tm->mSizeTex, + GL_FLOAT, + tm->mVertexElement->getSizeBytes(), + (void *)tm->mVertexElement->getComponentOffsetBytes(tm->mOffsetTex)); + } else { + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + } + + if (tm->mSizeNorm) { + glEnableClientState(GL_NORMAL_ARRAY); + glNormalPointer(GL_FLOAT, + tm->mVertexElement->getSizeBytes(), + (void *)tm->mVertexElement->getComponentOffsetBytes(tm->mOffsetNorm)); + } else { + glDisableClientState(GL_NORMAL_ARRAY); + } + + glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_SHORT, (GLvoid *)(first * 3 * 2)); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +void rsi_TriangleMeshRender(Context *rsc, RsTriangleMesh vtm) +{ + rsi_TriangleMeshRenderRange(rsc, vtm, 0, 0xffffff); +} + +}} diff --git a/libs/rs/rsTriangleMesh.h b/libs/rs/rsTriangleMesh.h new file mode 100644 index 0000000..e56c7c2 --- /dev/null +++ b/libs/rs/rsTriangleMesh.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_TRIANGLE_MESH_H +#define ANDROID_RS_TRIANGLE_MESH_H + + +#include "RenderScript.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + +// An element is a group of Components that occupies one cell in a structure. +class TriangleMesh : public ObjectBase +{ +public: + TriangleMesh(); + ~TriangleMesh(); + + const Element * mVertexElement; + const Element * mIndexElement; + + void * mVertexData; + void * mIndexData; + + size_t mVertexDataSize; + size_t mIndexDataSize; + uint32_t mTriangleCount; + + size_t mOffsetCoord; + size_t mOffsetTex; + size_t mOffsetNorm; + + size_t mSizeCoord; + size_t mSizeTex; + size_t mSizeNorm; + + // GL buffer info + uint32_t mBufferObjects[2]; + + void analyzeElement(); +protected: +}; + +class TriangleMeshContext +{ +public: + TriangleMeshContext(); + ~TriangleMeshContext(); + + const Element * mVertexElement; + const Element * mIndexElement; + size_t mVertexSizeBits; + size_t mIndexSizeBits; + + Vector<uint8_t> mVertexData; + Vector<uint16_t> mIndexData; + + uint32_t mTriangleCount; + + void clear(); +}; + + +} +} +#endif //ANDROID_RS_TRIANGLE_MESH_H + diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp new file mode 100644 index 0000000..bbe9720 --- /dev/null +++ b/libs/rs/rsType.cpp @@ -0,0 +1,222 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "rsContext.h" + +using namespace android; +using namespace android::renderscript; + +Type::Type() +{ + mLODs = 0; + mLODCount = 0; + clear(); +} + +Type::~Type() +{ + if (mLODs) { + delete [] mLODs; + } +} + +void Type::clear() +{ + if (mLODs) { + delete [] mLODs; + mLODs = NULL; + } + mDimX = 0; + mDimY = 0; + mDimZ = 0; + mDimLOD = 0; + mFaces = false; + mElement.clear(); +} + +TypeState::TypeState() +{ +} + +TypeState::~TypeState() +{ +} + +size_t Type::getOffsetForFace(uint32_t face) const +{ + rsAssert(mFaces); + return 0; +} + +void Type::compute() +{ + uint32_t oldLODCount = mLODCount; + if (mDimLOD) { + uint32_t l2x = rsFindHighBit(mDimX) + 1; + uint32_t l2y = rsFindHighBit(mDimY) + 1; + uint32_t l2z = rsFindHighBit(mDimZ) + 1; + + mLODCount = rsMax(l2x, l2y); + mLODCount = rsMax(mLODCount, l2z); + } else { + mLODCount = 1; + } + if (mLODCount != oldLODCount) { + delete [] mLODs; + mLODs = new LOD[mLODCount]; + } + + uint32_t tx = mDimX; + uint32_t ty = mDimY; + uint32_t tz = mDimZ; + size_t offset = 0; + for (uint32_t lod=0; lod < mLODCount; lod++) { + mLODs[lod].mX = tx; + mLODs[lod].mY = ty; + mLODs[lod].mZ = tz; + mLODs[lod].mOffset = offset; + offset += tx * rsMax(ty, 1u) * rsMax(tz, 1u) * mElement->getSizeBytes(); + tx = (tx + 1) >> 1; + ty = (ty + 1) >> 1; + tz = (tz + 1) >> 1; + } + + // At this point the offset is the size of a mipmap chain; + mMipChainSizeBytes = offset; + + if (mFaces) { + offset *= 6; + } + mTotalSizeBytes = offset; + +} + +uint32_t Type::getLODOffset(uint32_t lod, uint32_t x) const +{ + uint32_t offset = mLODs[lod].mOffset; + offset += x * mElement->getSizeBytes(); + return offset; +} + +uint32_t Type::getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const +{ + uint32_t offset = mLODs[lod].mOffset; + offset += (x + y * mLODs[lod].mX) * mElement->getSizeBytes(); + return offset; +} + +uint32_t Type::getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const +{ + uint32_t offset = mLODs[lod].mOffset; + offset += (x + y*mLODs[lod].mX + z*mLODs[lod].mX*mLODs[lod].mY) * mElement->getSizeBytes(); + return offset; +} + + +////////////////////////////////////////////////// +// +namespace android { +namespace renderscript { + +void rsi_TypeBegin(Context *rsc, RsElement vse) +{ + TypeState * stc = &rsc->mStateType; + + stc->mX = 0; + stc->mY = 0; + stc->mZ = 0; + stc->mLOD = false; + stc->mFaces = false; + stc->mElement.set(static_cast<const Element *>(vse)); +} + +void rsi_TypeAdd(Context *rsc, RsDimension dim, size_t value) +{ + TypeState * stc = &rsc->mStateType; + + if (dim < 0) { + //error + return; + } + + + switch (dim) { + case RS_DIMENSION_X: + stc->mX = value; + return; + case RS_DIMENSION_Y: + stc->mY = value; + return; + case RS_DIMENSION_Z: + stc->mZ = value; + return; + case RS_DIMENSION_FACE: + stc->mFaces = (value != 0); + return; + case RS_DIMENSION_LOD: + stc->mLOD = (value != 0); + return; + default: + break; + } + + + int32_t arrayNum = dim - RS_DIMENSION_ARRAY_0; + if ((dim < 0) || (dim > RS_DIMENSION_MAX)) { + LOGE("rsTypeAdd: Bad dimension"); + //error + return; + } + + // todo: implement array support + +} + +RsType rsi_TypeCreate(Context *rsc) +{ + TypeState * stc = &rsc->mStateType; + + Type * st = new Type(); + st->setDimX(stc->mX); + st->setDimY(stc->mY); + st->setDimZ(stc->mZ); + st->setElement(stc->mElement.get()); + st->setDimLOD(stc->mLOD); + st->setDimFaces(stc->mFaces); + st->compute(); + + stc->mAllTypes.add(st); + + return st; +} + +void rsi_TypeDestroy(Context *rsc, RsType vst) +{ + TypeState * stc = &rsc->mStateType; + Type * st = static_cast<Type *>(vst); + + for (size_t ct = 0; ct < stc->mAllTypes.size(); ct++) { + if (stc->mAllTypes[ct] == st) { + stc->mAllTypes.removeAt(ct); + break; + } + } + delete st; +} + +} +} + diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h new file mode 100644 index 0000000..a717893 --- /dev/null +++ b/libs/rs/rsType.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_STRUCTURED_TYPE_H +#define ANDROID_STRUCTURED_TYPE_H + +#include "rsElement.h" + +// --------------------------------------------------------------------------- +namespace android { +namespace renderscript { + + +class Type : public ObjectBase +{ +public: + Type(); + virtual ~Type(); + + Type * createTex2D(const Element *, size_t w, size_t h, bool mip); + + + size_t getOffsetForFace(uint32_t face) const; + + size_t getSizeBytes() const {return mTotalSizeBytes;} + size_t getElementSizeBytes() const {return mElement->getSizeBytes();} + const Element * getElement() const {return mElement.get();} + + uint32_t getDimX() const {return mDimX;} + uint32_t getDimY() const {return mDimY;} + uint32_t getDimZ() const {return mDimZ;} + uint32_t getDimLOD() const {return mDimLOD;} + bool getDimFaces() const {return mFaces;} + + uint32_t getLODDimX(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mX;} + uint32_t getLODDimY(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mY;} + uint32_t getLODDimZ(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mZ;} + uint32_t getLODOffset(uint32_t lod) const {rsAssert(lod < mLODCount); return mLODs[lod].mOffset;} + + uint32_t getLODOffset(uint32_t lod, uint32_t x) const; + uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y) const; + uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const; + + uint32_t getLODCount() const {return mLODCount;} + + + void setElement(const Element *e) {mElement.set(e);} + void setDimX(uint32_t v) {mDimX = v;} + void setDimY(uint32_t v) {mDimY = v;} + void setDimZ(uint32_t v) {mDimZ = v;} + void setDimFaces(bool v) {mFaces = v;} + void setDimLOD(bool v) {mDimLOD = v;} + + void clear(); + void compute(); + + +protected: + struct LOD { + size_t mX; + size_t mY; + size_t mZ; + size_t mOffset; + }; + + void makeLODTable(); + + // Internal structure from most to least significant. + // * Array dimensions + // * Faces + // * Mipmaps + // * xyz + + ObjectBaseRef<const Element> mElement; + + // Size of the structure in the various dimensions. A missing Dimension is + // specified as a 0 and not a 1. + size_t mDimX; + size_t mDimY; + size_t mDimZ; + bool mDimLOD; + bool mFaces; + + // A list of array dimensions. The count is the number of array dimensions and the + // sizes is a per array size. + //Vector<size_t> mDimArraysSizes; + + // count of mipmap levels, 0 indicates no mipmapping + + size_t mMipChainSizeBytes; + size_t mTotalSizeBytes; + LOD *mLODs; + uint32_t mLODCount; + +private: + Type(const Type &); +}; + + +class TypeState { +public: + TypeState(); + ~TypeState(); + + Vector<Type *> mAllTypes; + + size_t mX; + size_t mY; + size_t mZ; + uint32_t mLOD; + bool mFaces; + ObjectBaseRef<const Element> mElement; + + + +}; + + +} +} +#endif //ANDROID_STRUCTURED_TYPE diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h new file mode 100644 index 0000000..b13e88f --- /dev/null +++ b/libs/rs/rsUtils.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_UTILS_H +#define ANDROID_RS_UTILS_H + +#define LOG_NDEBUG 0 +#define LOG_TAG "rs" +#include <utils/Log.h> +#include <utils/Vector.h> +#include <stdlib.h> +#include <pthread.h> + +#include <EGL/egl.h> +#include <math.h> + +#include "RenderScript.h" + +namespace android { +namespace renderscript { + +#if 1 +#define rsAssert(v) do {if(!(v)) LOGE("rsAssert failed: %s, in %s at %i", #v, __FILE__, __LINE__);} while(0) +#else +#define rsAssert(v) while(0) +#endif + +template<typename T> +T rsMin(T in1, T in2) +{ + if (in1 > in2) { + return in2; + } + return in1; +} + +template<typename T> +T rsMax(T in1, T in2) +{ + if (in1 < in2) { + return in2; + } + return in1; +} + +template<typename T> +T rsFindHighBit(T val) +{ + uint32_t bit = 0; + while(val > 1) { + bit++; + val>>=1; + } + return bit; +} + +template<typename T> +bool rsIsPow2(T val) +{ + return (val & (val-1)) == 0; +} + +template<typename T> +T rsHigherPow2(T v) +{ + if (rsIsPow2(v)) { + return v; + } + return 1 << (rsFindHighBit(v) + 1); +} + +template<typename T> +T rsLowerPow2(T v) +{ + if (rsIsPow2(v)) { + return v; + } + return 1 << rsFindHighBit(v); +} + + +static inline uint16_t rs888to565(uint32_t r, uint32_t g, uint32_t b) +{ + uint16_t t = 0; + t |= b >> 3; + t |= (g >> 2) << 5; + t |= (r >> 3) << 11; + return t; +} + +static inline uint16_t rsBoxFilter565(uint16_t i1, uint16_t i2, uint16_t i3, uint16_t i4) +{ + uint32_t r = ((i1 & 0x1f) + (i2 & 0x1f) + (i3 & 0x1f) + (i4 & 0x1f)); + uint32_t g = ((i1 >> 5) & 0x3f) + ((i2 >> 5) & 0x3f) + ((i3 >> 5) & 0x3f) + ((i4 >> 5) & 0x3f); + uint32_t b = ((i1 >> 11) + (i2 >> 11) + (i3 >> 11) + (i4 >> 11)); + return (r >> 2) | ((g >> 2) << 5) | ((b >> 2) << 11); +} + +static inline uint32_t rsBoxFilter8888(uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4) +{ + uint32_t r = (i1 & 0xff) + (i2 & 0xff) + (i3 & 0xff) + (i4 & 0xff); + uint32_t g = ((i1 >> 8) & 0xff) + ((i2 >> 8) & 0xff) + ((i3 >> 8) & 0xff) + ((i4 >> 8) & 0xff); + uint32_t b = ((i1 >> 16) & 0xff) + ((i2 >> 16) & 0xff) + ((i3 >> 16) & 0xff) + ((i4 >> 16) & 0xff); + uint32_t a = ((i1 >> 24) & 0xff) + ((i2 >> 24) & 0xff) + ((i3 >> 24) & 0xff) + ((i4 >> 24) & 0xff); + return (r >> 2) | ((g >> 2) << 8) | ((b >> 2) << 16) | ((a >> 2) << 24); +} + + + +} +} + +#endif //ANDROID_RS_OBJECT_BASE_H + + diff --git a/libs/rs/rsgApi.cpp.rsg b/libs/rs/rsgApi.cpp.rsg new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/libs/rs/rsgApi.cpp.rsg @@ -0,0 +1 @@ +2 diff --git a/libs/rs/rsgApiFuncDecl.h.rsg b/libs/rs/rsgApiFuncDecl.h.rsg new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/libs/rs/rsgApiFuncDecl.h.rsg @@ -0,0 +1 @@ +1 diff --git a/libs/rs/rsgApiReplay.cpp.rsg b/libs/rs/rsgApiReplay.cpp.rsg new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/libs/rs/rsgApiReplay.cpp.rsg @@ -0,0 +1 @@ +3 diff --git a/libs/rs/rsgApiStructs.h.rsg b/libs/rs/rsgApiStructs.h.rsg new file mode 100644 index 0000000..573541a --- /dev/null +++ b/libs/rs/rsgApiStructs.h.rsg @@ -0,0 +1 @@ +0 diff --git a/libs/rs/rsg_generator.c b/libs/rs/rsg_generator.c new file mode 100644 index 0000000..a4d659d --- /dev/null +++ b/libs/rs/rsg_generator.c @@ -0,0 +1,291 @@ + + +#include "lex.yy.c" + +void printFileHeader(FILE *f) +{ + fprintf(f, "/*\n"); + fprintf(f, " * Copyright (C) 2009 The Android Open Source Project\n"); + fprintf(f, " *\n"); + fprintf(f, " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"); + fprintf(f, " * you may not use this file except in compliance with the License.\n"); + fprintf(f, " * You may obtain a copy of the License at\n"); + fprintf(f, " *\n"); + fprintf(f, " * http://www.apache.org/licenses/LICENSE-2.0\n"); + fprintf(f, " *\n"); + fprintf(f, " * Unless required by applicable law or agreed to in writing, software\n"); + fprintf(f, " * distributed under the License is distributed on an \"AS IS\" BASIS,\n"); + fprintf(f, " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"); + fprintf(f, " * See the License for the specific language governing permissions and\n"); + fprintf(f, " * limitations under the License.\n"); + fprintf(f, " */\n\n"); +} + +void printVarType(FILE *f, const VarType *vt) +{ + int ct; + if (vt->isConst) { + fprintf(f, "const "); + } + + switch(vt->type) { + case 0: + fprintf(f, "void"); + break; + case 1: + fprintf(f, "int%i_t", vt->bits); + break; + case 2: + fprintf(f, "uint%i_t", vt->bits); + break; + case 3: + if (vt->bits == 32) + fprintf(f, "float"); + else + fprintf(f, "double"); + break; + case 4: + fprintf(f, "%s", vt->typename); + break; + } + + if(vt->ptrLevel) { + fprintf(f, " "); + for(ct=0; ct < vt->ptrLevel; ct++) { + fprintf(f, "*"); + } + } + + if(vt->name[0]) { + fprintf(f, " %s", vt->name); + } +} + +void printArgList(FILE *f, const ApiEntry * api, int assumePrevious) +{ + int ct; + for(ct=0; ct < api->paramCount; ct++) { + if (ct || assumePrevious) { + fprintf(f, ", "); + } + printVarType(f, &api->params[ct]); + } +} + +void printStructures(FILE *f) +{ + int ct; + int ct2; + + for(ct=0; ct < apiCount; ct++) { + fprintf(f, "typedef struct RS_CMD_%s_rec RS_CMD_%s;\n", apis[ct].name, apis[ct].name); + } + fprintf(f, "\n"); + + for(ct=0; ct < apiCount; ct++) { + const ApiEntry * api = &apis[ct]; + fprintf(f, "#define RS_CMD_ID_%s %i\n", api->name, ct+1); + fprintf(f, "struct RS_CMD_%s_rec {\n", api->name); + //fprintf(f, " RsCommandHeader _hdr;\n"); + + for(ct2=0; ct2 < api->paramCount; ct2++) { + fprintf(f, " "); + printVarType(f, &api->params[ct2]); + fprintf(f, ";\n"); + } + fprintf(f, "};\n\n"); + } +} + +void printFuncDecl(FILE *f, const ApiEntry *api, const char *prefix, int addContext) +{ + printVarType(f, &api->ret); + fprintf(f, " %s%s (", prefix, api->name); + if (addContext) { + fprintf(f, "Context *"); + } + printArgList(f, api, addContext); + fprintf(f, ")"); +} + +void printFuncDecls(FILE *f, const char *prefix, int addContext) +{ + int ct; + for(ct=0; ct < apiCount; ct++) { + printFuncDecl(f, &apis[ct], prefix, addContext); + fprintf(f, ";\n"); + } + fprintf(f, "\n\n"); +} + +void printPlaybackFuncs(FILE *f, const char *prefix) +{ + int ct; + for(ct=0; ct < apiCount; ct++) { + fprintf(f, "void %s%s (Context *, const void *);\n", prefix, apis[ct].name); + } +} + +void printApiCpp(FILE *f) +{ + int ct; + int ct2; + + fprintf(f, "#include \"rsDevice.h\"\n"); + fprintf(f, "#include \"rsContext.h\"\n"); + fprintf(f, "#include \"rsThreadIO.h\"\n"); + //fprintf(f, "#include \"rsgApiStructs.h\"\n"); + fprintf(f, "#include \"rsgApiFuncDecl.h\"\n"); + fprintf(f, "\n"); + fprintf(f, "using namespace android;\n"); + fprintf(f, "using namespace android::renderscript;\n"); + fprintf(f, "\n"); + + for(ct=0; ct < apiCount; ct++) { + int needFlush = 0; + const ApiEntry * api = &apis[ct]; + + printFuncDecl(f, api, "rs", 0); + fprintf(f, "\n{\n"); + fprintf(f, " ThreadIO *io = gIO;\n"); + //fprintf(f, " LOGE(\"add command %s\\n\");\n", api->name); + fprintf(f, " RS_CMD_%s *cmd = static_cast<RS_CMD_%s *>(io->mToCore.reserve(sizeof(RS_CMD_%s)));\n", api->name, api->name, api->name); + fprintf(f, " uint32_t size = sizeof(RS_CMD_%s);\n", api->name); + + for(ct2=0; ct2 < api->paramCount; ct2++) { + const VarType *vt = &api->params[ct2]; + needFlush += vt->ptrLevel; + fprintf(f, " cmd->%s = %s;\n", vt->name, vt->name); + } + if (api->ret.typename[0]) { + needFlush = 1; + } + + fprintf(f, " io->mToCore.commit"); + if (needFlush) { + fprintf(f, "Sync"); + } + fprintf(f, "(RS_CMD_ID_%s, size);\n", api->name); + + if (api->ret.typename[0]) { + fprintf(f, " return reinterpret_cast<"); + printVarType(f, &api->ret); + fprintf(f, ">(io->mToCoreRet);\n"); + } + fprintf(f, "};\n\n"); + } +} + +void printPlaybackCpp(FILE *f) +{ + int ct; + int ct2; + + fprintf(f, "#include \"rsDevice.h\"\n"); + fprintf(f, "#include \"rsContext.h\"\n"); + fprintf(f, "#include \"rsThreadIO.h\"\n"); + //fprintf(f, "#include \"rsgApiStructs.h\"\n"); + fprintf(f, "#include \"rsgApiFuncDecl.h\"\n"); + fprintf(f, "\n"); + fprintf(f, "namespace android {\n"); + fprintf(f, "namespace renderscript {\n"); + fprintf(f, "\n"); + + for(ct=0; ct < apiCount; ct++) { + const ApiEntry * api = &apis[ct]; + + fprintf(f, "void rsp_%s(Context *con, const void *vp)\n", api->name); + fprintf(f, "{\n"); + //fprintf(f, " LOGE(\"play command %s\\n\");\n", api->name); + fprintf(f, " const RS_CMD_%s *cmd = static_cast<const RS_CMD_%s *>(vp);\n", api->name, api->name); + fprintf(f, " "); + if (api->ret.typename[0]) { + fprintf(f, "gIO->mToCoreRet = (intptr_t)"); + } + fprintf(f, "rsi_%s(con", api->name); + for(ct2=0; ct2 < api->paramCount; ct2++) { + const VarType *vt = &api->params[ct2]; + fprintf(f, ","); + fprintf(f, "\n cmd->%s", vt->name); + } + fprintf(f, ");\n"); + + fprintf(f, "};\n\n"); + } + + fprintf(f, "RsPlaybackFunc gPlaybackFuncs[] = {\n"); + fprintf(f, " NULL,\n"); + for(ct=0; ct < apiCount; ct++) { + fprintf(f, " %s%s,\n", "rsp_", apis[ct].name); + } + fprintf(f, "};\n"); + + fprintf(f, "};\n"); + fprintf(f, "};\n"); +} + +int main(int argc, char **argv) +{ + if (argc != 3) { + fprintf(stderr, "usage: %s commandFile outFile\n", argv[0]); + return 1; + } + const char* rsgFile = argv[1]; + const char* outFile = argv[2]; + FILE* input = fopen(rsgFile, "r"); + + char choice = fgetc(input); + fclose(input); + + if (choice < '0' || choice > '3') { + fprintf(stderr, "Uknown command: \'%c\'\n", choice); + return -2; + } + + yylex(); + // printf("# of lines = %d\n", num_lines); + + FILE *f = fopen(outFile, "w"); + + printFileHeader(f); + switch(choice) { + case '0': // rsgApiStructs.h + { + fprintf(f, "\n"); + fprintf(f, "#include \"rsContext.h\"\n"); + fprintf(f, "\n"); + fprintf(f, "namespace android {\n"); + fprintf(f, "namespace renderscript {\n"); + printStructures(f); + printFuncDecls(f, "rsi_", 1); + printPlaybackFuncs(f, "rsp_"); + fprintf(f, "\n\ntypedef void (*RsPlaybackFunc)(Context *, const void *);\n"); + fprintf(f, "extern RsPlaybackFunc gPlaybackFuncs[];\n"); + + fprintf(f, "}\n"); + fprintf(f, "}\n"); + } + break; + + case '1': // rsgApiFuncDecl.h + { + printFuncDecls(f, "rs", 0); + } + break; + + case '2': // rsgApi.cpp + { + printApiCpp(f); + } + break; + + case '3': // rsgApiReplay.cpp + { + printFileHeader(f); + printPlaybackCpp(f); + } + break; + } + fclose(f); + return 0; +} diff --git a/libs/rs/spec.lex b/libs/rs/spec.lex new file mode 100644 index 0000000..0f8e9ab --- /dev/null +++ b/libs/rs/spec.lex @@ -0,0 +1,171 @@ +%option stack + +%x comment +%x api_entry +%x api_entry2 +%x api_entry_param +%x var_type + +DIGIT [0-9] +ID [a-zA-Z_][a-zA-Z0-9_]* + + + int num_lines = 0; + + typedef struct { + int isConst; + int type; + int bits; + int ptrLevel; + char name[256]; + char typename[256]; + } VarType; + + VarType *currType = 0; + + typedef struct { + char name[256]; + int sync; + int paramCount; + VarType ret; + VarType params[16]; + } ApiEntry; + + ApiEntry apis[128]; + int apiCount = 0; + + int typeNextState; + +%% + +"/*" BEGIN(comment); +<comment>[^*\n]* /* eat anything that's not a '*' */ +<comment>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */ +<comment>\n ++num_lines; +<comment>"*"+"/" BEGIN(INITIAL); + +<*>" " //printf("found ' '\n"); +<*>"\n" ++num_lines; //printf("found lf \n"); + +{ID} { + memset(&apis[apiCount], 0, sizeof(ApiEntry)); + memcpy(apis[apiCount].name, yytext, yyleng); + BEGIN(api_entry); + } + +<api_entry>"{" { + BEGIN(api_entry2); + } + +<api_entry2>"sync" { + apis[apiCount].sync = 1; + } + +<api_entry2>"ret" { + currType = &apis[apiCount].ret; + typeNextState = api_entry2; + BEGIN(var_type); + } + +<api_entry2>"param" { + currType = &apis[apiCount].params[apis[apiCount].paramCount]; + apis[apiCount].paramCount++; + typeNextState = api_entry_param; + BEGIN(var_type); + } + +<var_type>"const" { + currType->isConst = 1; + } + +<var_type>"i8" { + currType->type = 1; + currType->bits = 8; + BEGIN(typeNextState); + } + +<var_type>"i16" { + currType->type = 1; + currType->bits = 16; + BEGIN(typeNextState); + } + +<var_type>"i32" { + currType->type = 1; + currType->bits = 32; + BEGIN(typeNextState); + } + +<var_type>"i64" { + currType->type = 1; + currType->bits = 64; + BEGIN(typeNextState); + } + +<var_type>"u8" { + currType->type = 2; + currType->bits = 8; + BEGIN(typeNextState); + } + +<var_type>"u16" { + currType->type = 2; + currType->bits = 16; + BEGIN(typeNextState); + } + +<var_type>"u32" { + currType->type = 2; + currType->bits = 32; + BEGIN(typeNextState); + } + +<var_type>"u64" { + currType->type = 2; + currType->bits = 64; + BEGIN(typeNextState); + } + +<var_type>"f" { + currType->type = 3; + currType->bits = 32; + BEGIN(typeNextState); + } + +<var_type>"d" { + currType->type = 3; + currType->bits = 64; + BEGIN(typeNextState); + } + +<var_type>{ID} { + currType->type = 4; + currType->bits = 32; + memcpy(currType->typename, yytext, yyleng); + BEGIN(typeNextState); + } + +<api_entry_param>"*" { + currType->ptrLevel ++; + } + +<api_entry_param>{ID} { + memcpy(currType->name, yytext, yyleng); + BEGIN(api_entry2); + } + + +<api_entry2>"}" { + apiCount++; + BEGIN(INITIAL); + } + + +%% + + +int yywrap() +{ + return 1; +} + diff --git a/libs/surfaceflinger/Android.mk b/libs/surfaceflinger/Android.mk index ec5aa3f..88e76dc 100644 --- a/libs/surfaceflinger/Android.mk +++ b/libs/surfaceflinger/Android.mk @@ -5,44 +5,49 @@ LOCAL_SRC_FILES:= \ clz.cpp.arm \ DisplayHardware/DisplayHardware.cpp \ DisplayHardware/DisplayHardwareBase.cpp \ - GPUHardware/GPUHardware.cpp \ BlurFilter.cpp.arm \ - CPUGauge.cpp \ + BufferAllocator.cpp \ Layer.cpp \ LayerBase.cpp \ LayerBuffer.cpp \ LayerBlur.cpp \ LayerBitmap.cpp \ LayerDim.cpp \ - LayerOrientationAnim.cpp \ - OrientationAnimation.cpp \ + MessageQueue.cpp \ SurfaceFlinger.cpp \ Tokenizer.cpp \ - Transform.cpp \ - VRamHeap.cpp + Transform.cpp +LOCAL_CFLAGS:= -DLOG_TAG=\"SurfaceFlinger\" +LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES + +ifeq ($(TARGET_BOARD_PLATFORM), msm7k) + LOCAL_CFLAGS += -DDIM_WITH_TEXTURE +endif # need "-lrt" on Linux simulator to pick up clock_gettime ifeq ($(TARGET_SIMULATOR),true) ifeq ($(HOST_OS),linux) - LOCAL_LDLIBS += -lrt + LOCAL_LDLIBS += -lrt -lpthread endif endif LOCAL_SHARED_LIBRARIES := \ - libhardware \ - libutils \ libcutils \ - libui \ - libcorecg \ - libsgl \ libpixelflinger \ + libhardware \ + libutils \ + libskia \ libEGL \ - libGLESv1_CM + libGLESv1_CM \ + libbinder \ + libui LOCAL_C_INCLUDES := \ $(call include-path-for, corecg graphics) +LOCAL_C_INCLUDES += hardware/libhardware/modules/gralloc + LOCAL_MODULE:= libsurfaceflinger include $(BUILD_SHARED_LIBRARY) diff --git a/libs/surfaceflinger/BufferAllocator.cpp b/libs/surfaceflinger/BufferAllocator.cpp new file mode 100644 index 0000000..cee8b64 --- /dev/null +++ b/libs/surfaceflinger/BufferAllocator.cpp @@ -0,0 +1,118 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include <sys/mman.h> +#include <cutils/ashmem.h> +#include <cutils/log.h> + +#include <utils/Singleton.h> +#include <utils/String8.h> + +#include "BufferAllocator.h" + + +namespace android { +// --------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE( BufferAllocator ) + +Mutex BufferAllocator::sLock; +KeyedVector<buffer_handle_t, BufferAllocator::alloc_rec_t> BufferAllocator::sAllocList; + +BufferAllocator::BufferAllocator() + : mAllocDev(0) +{ + hw_module_t const* module; + int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); + LOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID); + if (err == 0) { + gralloc_open(module, &mAllocDev); + } +} + +BufferAllocator::~BufferAllocator() +{ + gralloc_close(mAllocDev); +} + +void BufferAllocator::dump(String8& result) const +{ + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + size_t total = 0; + const size_t SIZE = 512; + char buffer[SIZE]; + snprintf(buffer, SIZE, "Allocated buffers:\n"); + result.append(buffer); + const size_t c = list.size(); + for (size_t i=0 ; i<c ; i++) { + const alloc_rec_t& rec(list.valueAt(i)); + snprintf(buffer, SIZE, "%10p: %7.2f KiB | %4u x %4u | %2d | 0x%08x\n", + list.keyAt(i), rec.size/1024.0f, + rec.w, rec.h, rec.format, rec.usage); + result.append(buffer); + total += rec.size; + } + snprintf(buffer, SIZE, "Total allocated: %.2f KB\n", total/1024.0f); + result.append(buffer); +} + +status_t BufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format, + int usage, buffer_handle_t* handle, int32_t* stride) +{ + Mutex::Autolock _l(mLock); + + // we have a h/w allocator and h/w buffer is requested + status_t err = mAllocDev->alloc(mAllocDev, + w, h, format, usage, handle, stride); + LOGW_IF(err, "alloc(%u, %u, %d, %08x, ...) failed %d (%s)", + w, h, format, usage, err, strerror(-err)); + + if (err == NO_ERROR) { + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + alloc_rec_t rec; + rec.w = w; + rec.h = h; + rec.format = format; + rec.usage = usage; + rec.vaddr = 0; + rec.size = h * stride[0] * bytesPerPixel(format); + list.add(*handle, rec); + } + + return err; +} + +status_t BufferAllocator::free(buffer_handle_t handle) +{ + Mutex::Autolock _l(mLock); + + status_t err = mAllocDev->free(mAllocDev, handle); + LOGW_IF(err, "free(...) failed %d (%s)", err, strerror(-err)); + + if (err == NO_ERROR) { + Mutex::Autolock _l(sLock); + KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList); + list.removeItem(handle); + } + + return err; +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/surfaceflinger/BufferAllocator.h b/libs/surfaceflinger/BufferAllocator.h new file mode 100644 index 0000000..a279ded --- /dev/null +++ b/libs/surfaceflinger/BufferAllocator.h @@ -0,0 +1,96 @@ +/* +** +** Copyright 2009, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#ifndef ANDROID_BUFFER_ALLOCATOR_H +#define ANDROID_BUFFER_ALLOCATOR_H + +#include <stdint.h> + +#include <cutils/native_handle.h> + +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/threads.h> +#include <utils/Singleton.h> + +#include <ui/PixelFormat.h> + +#include <hardware/gralloc.h> + + +namespace android { +// --------------------------------------------------------------------------- + +class String8; + +class BufferAllocator : public Singleton<BufferAllocator> +{ +public: + enum { + USAGE_SW_READ_NEVER = GRALLOC_USAGE_SW_READ_NEVER, + USAGE_SW_READ_RARELY = GRALLOC_USAGE_SW_READ_RARELY, + USAGE_SW_READ_OFTEN = GRALLOC_USAGE_SW_READ_OFTEN, + USAGE_SW_READ_MASK = GRALLOC_USAGE_SW_READ_MASK, + + USAGE_SW_WRITE_NEVER = GRALLOC_USAGE_SW_WRITE_NEVER, + USAGE_SW_WRITE_RARELY = GRALLOC_USAGE_SW_WRITE_RARELY, + USAGE_SW_WRITE_OFTEN = GRALLOC_USAGE_SW_WRITE_OFTEN, + USAGE_SW_WRITE_MASK = GRALLOC_USAGE_SW_WRITE_MASK, + + USAGE_SOFTWARE_MASK = USAGE_SW_READ_MASK|USAGE_SW_WRITE_MASK, + + USAGE_HW_TEXTURE = GRALLOC_USAGE_HW_TEXTURE, + USAGE_HW_RENDER = GRALLOC_USAGE_HW_RENDER, + USAGE_HW_2D = GRALLOC_USAGE_HW_2D, + USAGE_HW_MASK = GRALLOC_USAGE_HW_MASK + }; + + static inline BufferAllocator& get() { return getInstance(); } + + + status_t alloc(uint32_t w, uint32_t h, PixelFormat format, int usage, + buffer_handle_t* handle, int32_t* stride); + + status_t free(buffer_handle_t handle); + + void dump(String8& res) const; + +private: + struct alloc_rec_t { + uint32_t w; + uint32_t h; + PixelFormat format; + uint32_t usage; + void* vaddr; + size_t size; + }; + + static Mutex sLock; + static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList; + + friend class Singleton<BufferAllocator>; + BufferAllocator(); + ~BufferAllocator(); + + mutable Mutex mLock; + alloc_device_t *mAllocDev; +}; + +// --------------------------------------------------------------------------- +}; // namespace android + +#endif // ANDROID_BUFFER_ALLOCATOR_H diff --git a/libs/surfaceflinger/CPUGauge.cpp b/libs/surfaceflinger/CPUGauge.cpp deleted file mode 100644 index 74a9270..0000000 --- a/libs/surfaceflinger/CPUGauge.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "CPUGauge" - -#include <stdint.h> -#include <limits.h> -#include <sys/types.h> -#include <math.h> - -#include <utils/threads.h> -#include <utils/Errors.h> -#include <utils/Log.h> - -#include <ui/PixelFormat.h> -#include <ui/Rect.h> -#include <ui/Region.h> -#include <ui/DisplayInfo.h> -#include <ui/ISurfaceComposer.h> -#include <ui/ISurfaceFlingerClient.h> - -#include <pixelflinger/pixelflinger.h> - -#include "CPUGauge.h" - -namespace android { - -CPUGauge::CPUGauge( const sp<ISurfaceComposer>& composer, - nsecs_t interval, - int clock, - int refclock) - : Thread(false), - mInterval(interval), mClock(clock), mRefClock(refclock), - mReferenceTime(0), - mReferenceWorkingTime(0), mCpuUsage(0), - mRefIdleTime(0), mIdleTime(0) -{ - mFd = fopen("/proc/stat", "r"); - setvbuf(mFd, NULL, _IONBF, 0); - - mSession = SurfaceComposerClient::clientForConnection( - composer->createConnection()->asBinder()); -} - -CPUGauge::~CPUGauge() -{ - fclose(mFd); -} - -const sp<SurfaceComposerClient>& CPUGauge::session() const -{ - return mSession; -} - -void CPUGauge::onFirstRef() -{ - run("CPU Gauge"); -} - -status_t CPUGauge::readyToRun() -{ - LOGI("Starting CPU gauge..."); - return NO_ERROR; -} - -bool CPUGauge::threadLoop() -{ - DisplayInfo dinfo; - session()->getDisplayInfo(0, &dinfo); - sp<Surface> s(session()->createSurface(getpid(), 0, dinfo.w, 4, PIXEL_FORMAT_OPAQUE)); - session()->openTransaction(); - s->setLayer(INT_MAX); - session()->closeTransaction(); - - static const GGLfixed colors[4][4] = { - { 0x00000, 0x10000, 0x00000, 0x10000 }, - { 0x10000, 0x10000, 0x00000, 0x10000 }, - { 0x10000, 0x00000, 0x00000, 0x10000 }, - { 0x00000, 0x00000, 0x00000, 0x10000 }, - }; - - GGLContext* gl; - gglInit(&gl); - gl->activeTexture(gl, 0); - gl->disable(gl, GGL_TEXTURE_2D); - gl->disable(gl, GGL_BLEND); - - const int w = dinfo.w; - - while(!exitPending()) - { - mLock.lock(); - const float cpuUsage = this->cpuUsage(); - const float totalCpuUsage = 1.0f - idle(); - mLock.unlock(); - - Surface::SurfaceInfo info; - s->lock(&info); - GGLSurface fb; - fb.version = sizeof(GGLSurface); - fb.width = info.w; - fb.height = info.h; - fb.stride = info.w; - fb.format = info.format; - fb.data = (GGLubyte*)info.bits; - - gl->colorBuffer(gl, &fb); - gl->color4xv(gl, colors[3]); - gl->recti(gl, 0, 0, w, 4); - gl->color4xv(gl, colors[2]); // red - gl->recti(gl, 0, 0, int(totalCpuUsage*w), 2); - gl->color4xv(gl, colors[0]); // green - gl->recti(gl, 0, 2, int(cpuUsage*w), 4); - - s->unlockAndPost(); - - usleep(ns2us(mInterval)); - } - - gglUninit(gl); - return false; -} - -void CPUGauge::sample() -{ - if (mLock.tryLock() == NO_ERROR) { - const nsecs_t now = systemTime(mRefClock); - const nsecs_t referenceTime = now-mReferenceTime; - if (referenceTime >= mInterval) { - const float reftime = 1.0f / referenceTime; - const nsecs_t nowWorkingTime = systemTime(mClock); - - char buf[256]; - fgets(buf, 256, mFd); - rewind(mFd); - char *str = buf+5; - char const * const usermode = strsep(&str, " "); (void)usermode; - char const * const usernice = strsep(&str, " "); (void)usernice; - char const * const systemmode = strsep(&str, " ");(void)systemmode; - char const * const idle = strsep(&str, " "); - const nsecs_t nowIdleTime = atoi(idle) * 10000000LL; - mIdleTime = float(nowIdleTime - mRefIdleTime) * reftime; - mRefIdleTime = nowIdleTime; - - const nsecs_t workingTime = nowWorkingTime - mReferenceWorkingTime; - const float newCpuUsage = float(workingTime) * reftime; - if (mCpuUsage != newCpuUsage) { - mCpuUsage = newCpuUsage; - mReferenceWorkingTime = nowWorkingTime; - mReferenceTime = now; - } - } - mLock.unlock(); - } -} - - -}; // namespace android diff --git a/libs/surfaceflinger/CPUGauge.h b/libs/surfaceflinger/CPUGauge.h deleted file mode 100644 index 5bb53c0..0000000 --- a/libs/surfaceflinger/CPUGauge.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_CPUGAUGE_H -#define ANDROID_CPUGAUGE_H - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Timers.h> - -#include <ui/SurfaceComposerClient.h> - -namespace android { - -class CPUGauge : public Thread -{ -public: - CPUGauge( const sp<ISurfaceComposer>& composer, - nsecs_t interval=s2ns(1), - int clock=SYSTEM_TIME_THREAD, - int refclock=SYSTEM_TIME_MONOTONIC); - - ~CPUGauge(); - - const sp<SurfaceComposerClient>& session() const; - - void sample(); - - inline float cpuUsage() const { return mCpuUsage; } - inline float idle() const { return mIdleTime; } - -private: - virtual void onFirstRef(); - virtual status_t readyToRun(); - virtual bool threadLoop(); - - Mutex mLock; - - sp<SurfaceComposerClient> mSession; - - const nsecs_t mInterval; - const int mClock; - const int mRefClock; - - nsecs_t mReferenceTime; - nsecs_t mReferenceWorkingTime; - float mCpuUsage; - nsecs_t mRefIdleTime; - float mIdleTime; - FILE* mFd; -}; - - -}; // namespace android - -#endif // ANDROID_CPUGAUGE_H diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index eec645e..21f87e37 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -23,18 +21,23 @@ #include <cutils/properties.h> +#include <utils/RefBase.h> #include <utils/Log.h> -#include <ui/EGLDisplaySurface.h> +#include <ui/PixelFormat.h> +#include <ui/FramebufferNativeWindow.h> #include <GLES/gl.h> +#include <EGL/egl.h> #include <EGL/eglext.h> +#include <pixelflinger/pixelflinger.h> #include "DisplayHardware/DisplayHardware.h" #include <hardware/copybit.h> #include <hardware/overlay.h> +#include <hardware/gralloc.h> using namespace android; @@ -108,17 +111,28 @@ PixelFormat DisplayHardware::getFormat() const { return mFormat; } void DisplayHardware::init(uint32_t dpy) { + hw_module_t const* module; + + mNativeWindow = new FramebufferNativeWindow(); + + mOverlayEngine = NULL; + if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { + overlay_control_open(module, &mOverlayEngine); + } + + framebuffer_device_t const * fbDev = mNativeWindow->getDevice(); + + PixelFormatInfo fbFormatInfo; + getPixelFormatInfo(PixelFormat(fbDev->format), &fbFormatInfo); + // initialize EGL const EGLint attribs[] = { - EGL_RED_SIZE, 5, - EGL_GREEN_SIZE, 6, - EGL_BLUE_SIZE, 5, + EGL_BUFFER_SIZE, fbFormatInfo.bitsPerPixel, EGL_DEPTH_SIZE, 0, EGL_NONE }; EGLint w, h, dummy; - EGLint numConfigs, n; - EGLConfig config; + EGLint numConfigs=0, n=0; EGLSurface surface; EGLContext context; mFlags = 0; @@ -129,7 +143,32 @@ void DisplayHardware::init(uint32_t dpy) EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, NULL, NULL); eglGetConfigs(display, NULL, 0, &numConfigs); - eglChooseConfig(display, attribs, &config, 1, &n); + + // Get all the "potential match" configs... + EGLConfig* const configs = new EGLConfig[numConfigs]; + eglChooseConfig(display, attribs, configs, numConfigs, &n); + LOGE_IF(n<=0, "no EGLConfig available!"); + EGLConfig config = configs[0]; + if (n > 1) { + // if there is more than one candidate, go through the list + // and pick one that matches our framebuffer format + int fbSzA = fbFormatInfo.getSize(PixelFormatInfo::INDEX_ALPHA); + int fbSzR = fbFormatInfo.getSize(PixelFormatInfo::INDEX_RED); + int fbSzG = fbFormatInfo.getSize(PixelFormatInfo::INDEX_GREEN); + int fbSzB = fbFormatInfo.getSize(PixelFormatInfo::INDEX_BLUE); + for (int i=0 ; i<n ; i++) { + EGLint r,g,b,a; + eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &r); + eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &g); + eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &b); + eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &a); + if (fbSzA == a && fbSzR == r && fbSzG == g && fbSzB == b) { + config = configs[i]; + break; + } + } + } + delete [] configs; /* * Gather EGL extensions @@ -145,12 +184,11 @@ void DisplayHardware::init(uint32_t dpy) LOGI("extensions: %s", egl_extensions); LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); - // TODO: get this from the devfb driver (probably should be HAL module) - mFlags |= SWAP_RECTANGLE_EXTENSION; - - // TODO: get the real "update_on_demand" behavior (probably should be HAL module) - mFlags |= UPDATE_ON_DEMAND; + if (mNativeWindow->isUpdateOnDemand()) { + mFlags |= UPDATE_ON_DEMAND; + } + if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) { if (dummy == EGL_SLOW_CONFIG) mFlags |= SLOW_CONFIG; @@ -160,33 +198,29 @@ void DisplayHardware::init(uint32_t dpy) * Create our main surface */ - mDisplaySurface = new EGLDisplaySurface(); - - surface = eglCreateWindowSurface(display, config, mDisplaySurface.get(), NULL); - //checkEGLErrors("eglCreateDisplaySurfaceANDROID"); + surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL); + checkEGLErrors("eglCreateWindowSurface"); if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) { if (dummy == EGL_BUFFER_PRESERVED) { mFlags |= BUFFER_PRESERVED; } } - - GLint value = EGL_UNKNOWN; - eglQuerySurface(display, surface, EGL_HORIZONTAL_RESOLUTION, &value); - if (value == EGL_UNKNOWN) { - mDpiX = 160.0f; - } else { - mDpiX = 25.4f * float(value)/EGL_DISPLAY_SCALING; - } - value = EGL_UNKNOWN; - eglQuerySurface(display, surface, EGL_VERTICAL_RESOLUTION, &value); - if (value == EGL_UNKNOWN) { - mDpiY = 160.0f; - } else { - mDpiY = 25.4f * float(value)/EGL_DISPLAY_SCALING; + +#ifdef EGL_ANDROID_swap_rectangle + if (strstr(egl_extensions, "EGL_ANDROID_swap_rectangle")) { + mFlags |= SWAP_RECTANGLE; } - mRefreshRate = 60.f; // TODO: get the real refresh rate + // when we have the choice between UPDATE_ON_DEMAND and SWAP_RECTANGLE + // choose UPDATE_ON_DEMAND, which is more efficient + if (mFlags & UPDATE_ON_DEMAND) + mFlags &= ~SWAP_RECTANGLE; +#endif + + mDpiX = mNativeWindow->xdpi; + mDpiY = mNativeWindow->ydpi; + mRefreshRate = fbDev->fps; char property[PROPERTY_VALUE_MAX]; /* Read density from build-specific ro.sf.lcd_density property @@ -230,7 +264,10 @@ void DisplayHardware::init(uint32_t dpy) if (strstr(gl_extensions, "GL_OES_draw_texture")) { mFlags |= DRAW_TEXTURE_EXTENSION; } - if (strstr(gl_extensions, "GL_ANDROID_direct_texture")) { + if (strstr( gl_extensions, "GL_OES_EGL_image") && + (strstr(egl_extensions, "EGL_KHR_image_base") || + strstr(egl_extensions, "EGL_KHR_image")) && + strstr(egl_extensions, "EGL_ANDROID_image_native_buffer")) { mFlags |= DIRECT_TEXTURE; } @@ -241,19 +278,8 @@ void DisplayHardware::init(uint32_t dpy) mConfig = config; mSurface = surface; mContext = context; - mFormat = GGL_PIXEL_FORMAT_RGB_565; - - hw_module_t const* module; - - mBlitEngine = NULL; - if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { - copybit_open(module, &mBlitEngine); - } - - mOverlayEngine = NULL; - if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { - overlay_control_open(module, &mOverlayEngine); - } + mFormat = fbDev->format; + mPageFlipCount = 0; } /* @@ -267,7 +293,6 @@ void DisplayHardware::fini() { eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mDisplay); - copybit_close(mBlitEngine); overlay_control_close(mOverlayEngine); } @@ -281,28 +306,8 @@ void DisplayHardware::acquireScreen() const DisplayHardwareBase::acquireScreen(); } -void DisplayHardware::getDisplaySurface(copybit_image_t* img) const -{ - img->w = mDisplaySurface->stride; - img->h = mDisplaySurface->height; - img->format = mDisplaySurface->format; - img->offset = mDisplaySurface->offset; - img->base = (void*)mDisplaySurface->base; - img->fd = mDisplaySurface->fd; -} - -void DisplayHardware::getDisplaySurface(GGLSurface* fb) const -{ - fb->version= sizeof(GGLSurface); - fb->width = mDisplaySurface->width; - fb->height = mDisplaySurface->height; - fb->stride = mDisplaySurface->stride; - fb->format = mDisplaySurface->format; - fb->data = (GGLubyte*)mDisplaySurface->base + mDisplaySurface->offset; -} - uint32_t DisplayHardware::getPageFlipCount() const { - return mDisplaySurface->getPageFlipCount(); + return mPageFlipCount; } /* @@ -316,21 +321,20 @@ void DisplayHardware::flip(const Region& dirty) const EGLDisplay dpy = mDisplay; EGLSurface surface = mSurface; - Region newDirty(dirty); - newDirty.andSelf(Rect(mWidth, mHeight)); - - if (mFlags & BUFFER_PRESERVED) { - const Region copyback(mDirty.subtract(newDirty)); - mDirty = newDirty; - mDisplaySurface->copyFrontToBack(copyback); - } - - if (mFlags & SWAP_RECTANGLE_EXTENSION) { - const Rect& b(newDirty.bounds()); - mDisplaySurface->setSwapRectangle( +#ifdef EGL_ANDROID_swap_rectangle + if (mFlags & SWAP_RECTANGLE) { + const Region newDirty(dirty.intersect(bounds())); + const Rect b(newDirty.getBounds()); + eglSetSwapRectangleANDROID(dpy, surface, b.left, b.top, b.width(), b.height()); + } +#endif + + if (mFlags & UPDATE_ON_DEMAND) { + mNativeWindow->setUpdateRectangle(dirty.getBounds()); } - + + mPageFlipCount++; eglSwapBuffers(dpy, surface); checkEGLErrors("eglSwapBuffers"); @@ -348,11 +352,3 @@ void DisplayHardware::makeCurrent() const { eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); } - -void DisplayHardware::copyFrontToImage(const copybit_image_t& front) const { - mDisplaySurface->copyFrontToImage(front); -} - -void DisplayHardware::copyBackToImage(const copybit_image_t& front) const { - mDisplaySurface->copyBackToImage(front); -} diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h index 550a4d1..8972d51 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.h +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.h @@ -22,31 +22,35 @@ #include <ui/PixelFormat.h> #include <ui/Region.h> +#include <GLES/gl.h> +#include <GLES/glext.h> #include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <pixelflinger/pixelflinger.h> #include "DisplayHardware/DisplayHardwareBase.h" struct overlay_control_device_t; -struct copybit_device_t; +struct framebuffer_device_t; struct copybit_image_t; -struct copybit_t; namespace android { -class EGLDisplaySurface; +class FramebufferNativeWindow; class DisplayHardware : public DisplayHardwareBase { public: enum { DIRECT_TEXTURE = 0x00000002, - SWAP_RECTANGLE_EXTENSION= 0x00000004, COPY_BITS_EXTENSION = 0x00000008, NPOT_EXTENSION = 0x00000100, DRAW_TEXTURE_EXTENSION = 0x00000200, BUFFER_PRESERVED = 0x00010000, UPDATE_ON_DEMAND = 0x00020000, // video driver feature SLOW_CONFIG = 0x00040000, // software + SWAP_RECTANGLE = 0x00080000, }; DisplayHardware( @@ -73,15 +77,9 @@ public: void makeCurrent() const; uint32_t getPageFlipCount() const; - void getDisplaySurface(copybit_image_t* img) const; - void getDisplaySurface(GGLSurface* fb) const; EGLDisplay getEGLDisplay() const { return mDisplay; } - copybit_device_t* getBlitEngine() const { return mBlitEngine; } overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; } - void copyFrontToImage(const copybit_image_t& front) const; - void copyBackToImage(const copybit_image_t& front) const; - Rect bounds() const { return Rect(mWidth, mHeight); } @@ -102,9 +100,9 @@ private: int mHeight; PixelFormat mFormat; uint32_t mFlags; - mutable Region mDirty; - sp<EGLDisplaySurface> mDisplaySurface; - copybit_device_t* mBlitEngine; + mutable uint32_t mPageFlipCount; + + sp<FramebufferNativeWindow> mNativeWindow; overlay_control_device_t* mOverlayEngine; }; diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp index f75e5c2..1d09f84 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <assert.h> #include <errno.h> #include <stdlib.h> diff --git a/libs/surfaceflinger/Layer.cpp b/libs/surfaceflinger/Layer.cpp index 96395a8..9916158 100644 --- a/libs/surfaceflinger/Layer.cpp +++ b/libs/surfaceflinger/Layer.cpp @@ -14,26 +14,24 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdint.h> #include <sys/types.h> #include <cutils/properties.h> +#include <cutils/native_handle.h> #include <utils/Errors.h> #include <utils/Log.h> #include <utils/StopWatch.h> #include <ui/PixelFormat.h> -#include <ui/EGLDisplaySurface.h> +#include <ui/Surface.h> #include "clz.h" #include "Layer.h" #include "LayerBitmap.h" #include "SurfaceFlinger.h" -#include "VRamHeap.h" #include "DisplayHardware/DisplayHardware.h" @@ -49,13 +47,12 @@ const char* const Layer::typeID = "Layer"; // --------------------------------------------------------------------------- -Layer::Layer(SurfaceFlinger* flinger, DisplayID display, Client* c, int32_t i) +Layer::Layer(SurfaceFlinger* flinger, DisplayID display, const sp<Client>& c, int32_t i) : LayerBaseClient(flinger, display, c, i), mSecure(false), mFrontBufferIndex(1), mNeedsBlending(true), - mResizeTransactionDone(false), - mTextureName(-1U), mTextureWidth(0), mTextureHeight(0) + mResizeTransactionDone(false) { // no OpenGL operation is possible here, since we might not be // in the OpenGL thread. @@ -63,12 +60,25 @@ Layer::Layer(SurfaceFlinger* flinger, DisplayID display, Client* c, int32_t i) Layer::~Layer() { - client->free(clientIndex()); - // this should always be called from the OpenGL thread - if (mTextureName != -1U) { - //glDeleteTextures(1, &mTextureName); - deletedTextures.add(mTextureName); + destroy(); + // the actual buffers will be destroyed here +} + +void Layer::destroy() +{ + for (int 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; + } + mBuffers[i].free(); } + mSurface.clear(); } void Layer::initStates(uint32_t w, uint32_t h, uint32_t flags) @@ -79,148 +89,206 @@ void Layer::initStates(uint32_t w, uint32_t h, uint32_t flags) lcblk->flags |= eNoCopyBack; } -sp<LayerBaseClient::Surface> Layer::getSurface() const +sp<LayerBaseClient::Surface> Layer::createSurface() const { return mSurface; } -status_t Layer::setBuffers( Client* client, - uint32_t w, uint32_t h, +status_t Layer::ditch() +{ + // the layer is not on screen anymore. free as much resources as possible + destroy(); + return NO_ERROR; +} + +status_t Layer::setBuffers( uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { PixelFormatInfo info; status_t err = getPixelFormatInfo(format, &info); if (err) return err; - // TODO: if eHardware is explicitly requested, we should fail - // on systems where we can't allocate memory that can be used with - // DMA engines for instance. - - // FIXME: we always ask for hardware for now (this should come from copybit) - flags |= ISurfaceComposer::eHardware; + uint32_t bufferFlags = 0; + if (flags & ISurfaceComposer::eGPU) + bufferFlags |= Buffer::GPU; - const uint32_t memory_flags = flags & - (ISurfaceComposer::eGPU | - ISurfaceComposer::eHardware | - ISurfaceComposer::eSecure); - - // pixel-alignment. the final alignment may be bigger because - // we always force a 4-byte aligned bpr. - uint32_t alignment = 1; + if (flags & ISurfaceComposer::eSecure) + bufferFlags |= Buffer::SECURE; - if ((flags & ISurfaceComposer::eGPU) && (mFlinger->getGPU() != 0)) { - // FIXME: this value should come from the h/w - alignment = 8; + /* FIXME we need this code for msm7201A + if (bufferFlags & Buffer::GPU) { // FIXME: this is msm7201A specific, as its GPU only supports // BGRA_8888. if (format == PIXEL_FORMAT_RGBA_8888) { format = PIXEL_FORMAT_BGRA_8888; } } + */ - mSecure = (flags & ISurfaceComposer::eSecure) ? true : false; + mSecure = (bufferFlags & Buffer::SECURE) ? true : false; mNeedsBlending = (info.h_alpha - info.l_alpha) > 0; - sp<MemoryDealer> allocators[2]; for (int i=0 ; i<2 ; i++) { - allocators[i] = client->createAllocator(memory_flags); - if (allocators[i] == 0) - return NO_MEMORY; - mBuffers[i].init(allocators[i]); - int err = mBuffers[i].setBits(w, h, alignment, format, LayerBitmap::SECURE_BITS); - if (err != NO_ERROR) + err = mBuffers[i].init(lcblk->surface + i, w, h, format, bufferFlags); + if (err != NO_ERROR) { return err; - mBuffers[i].clear(); // clear the bits for security - mBuffers[i].getInfo(lcblk->surface + i); + } } - - mSurface = new Surface(clientIndex(), - allocators[0]->getMemoryHeap(), - allocators[1]->getMemoryHeap(), - mIdentity); - + mSurface = new SurfaceLayer(mFlinger, clientIndex(), this); return NO_ERROR; } void Layer::reloadTexture(const Region& dirty) { - if (UNLIKELY(mTextureName == -1U)) { - // create the texture name the first time - // can't do that in the ctor, because it runs in another thread. - mTextureName = createTexture(); + const sp<Buffer>& buffer(frontBuffer().getBuffer()); + if (LIKELY(mFlags & DisplayHardware::DIRECT_TEXTURE)) { + int index = mFrontBufferIndex; + if (LIKELY(!mTextures[index].dirty)) { + glBindTexture(GL_TEXTURE_2D, mTextures[index].name); + } else { + // we need to recreate the texture + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + + // create the new texture name if needed + if (UNLIKELY(mTextures[index].name == -1U)) { + mTextures[index].name = createTexture(); + } else { + glBindTexture(GL_TEXTURE_2D, mTextures[index].name); + } + + // free the previous image + if (mTextures[index].image != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(dpy, mTextures[index].image); + mTextures[index].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 + }; + mTextures[index].image = eglCreateImageKHR( + dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + (EGLClientBuffer)clientBuf, attrs); + + LOGE_IF(mTextures[index].image == EGL_NO_IMAGE_KHR, + "eglCreateImageKHR() failed. err=0x%4x", + eglGetError()); + + if (mTextures[index].image != EGL_NO_IMAGE_KHR) { + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, + (GLeglImageOES)mTextures[index].image); + GLint error = glGetError(); + if (UNLIKELY(error != GL_NO_ERROR)) { + // this failed, for instance, because we don't support + // NPOT. + // FIXME: do something! + mFlags &= ~DisplayHardware::DIRECT_TEXTURE; + } else { + // Everything went okay! + mTextures[index].dirty = false; + mTextures[index].width = clientBuf->width; + mTextures[index].height = clientBuf->height; + } + } + } + } else { + GGLSurface t; + status_t res = buffer->lock(&t, GRALLOC_USAGE_SW_READ_RARELY); + LOGE_IF(res, "error %d (%s) locking buffer %p", + res, strerror(res), buffer.get()); + if (res == NO_ERROR) { + if (UNLIKELY(mTextures[0].name == -1U)) { + mTextures[0].name = createTexture(); + } + loadTexture(&mTextures[0], mTextures[0].name, dirty, t); + buffer->unlock(); + } } - const GGLSurface& t(frontBuffer().surface()); - loadTexture(dirty, mTextureName, t, mTextureWidth, mTextureHeight); } void Layer::onDraw(const Region& clip) const { - if (UNLIKELY(mTextureName == -1LU)) { - //LOGW("Layer %p doesn't have a texture", this); + const int index = (mFlags & DisplayHardware::DIRECT_TEXTURE) ? + mFrontBufferIndex : 0; + GLuint textureName = mTextures[index].name; + + if (UNLIKELY(textureName == -1LU)) { + LOGW("Layer %p doesn't have a texture", this); // the texture has not been created yet, this Layer has // in fact never been drawn into. this happens frequently with // SurfaceView. clearWithOpenGL(clip); return; } + drawWithOpenGL(clip, mTextures[index]); +} - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - const LayerBitmap& front(frontBuffer()); - const GGLSurface& t(front.surface()); +sp<SurfaceBuffer> Layer::peekBuffer() +{ + /* + * This is called from the client's Surface::lock(), after it locked + * the surface successfully. We're therefore guaranteed that the + * back-buffer is not in use by ourselves. + * Of course, we need to validate all this, which is not trivial. + * + * FIXME: A resize could happen at any time here. What to do about this? + * - resize() form post() + * - resize() from doTransaction() + * + * We'll probably need an internal lock for this. + * + * + * TODO: We need to make sure that post() doesn't swap + * the buffers under us. + */ - status_t err = NO_ERROR; - const int can_use_copybit = canUseCopybit(); - if (can_use_copybit) { - // StopWatch watch("copybit"); - const State& s(drawingState()); - - copybit_image_t dst; - hw.getDisplaySurface(&dst); - const copybit_rect_t& drect - = reinterpret_cast<const copybit_rect_t&>(mTransformedBounds); - - copybit_image_t src; - front.getBitmapSurface(&src); - copybit_rect_t srect = { 0, 0, t.width, t.height }; - - copybit_device_t* copybit = mFlinger->getBlitEngine(); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, getOrientation()); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, - s.flags & ISurfaceComposer::eLayerDither ? - COPYBIT_ENABLE : COPYBIT_DISABLE); - - region_iterator it(clip); - err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); + // it's okay to read swapState for the purpose of figuring out the + // backbuffer index, which cannot change (since the app has locked it). + const uint32_t state = lcblk->swapState; + const int32_t backBufferIndex = layer_cblk_t::backBuffer(state); + + // get rid of the EGL image, since we shouldn't need it anymore + // (note that we're in a different thread than where it is being used) + if (mTextures[backBufferIndex].image != EGL_NO_IMAGE_KHR) { + EGLDisplay dpy(mFlinger->graphicPlane(0).getEGLDisplay()); + eglDestroyImageKHR(dpy, mTextures[backBufferIndex].image); + mTextures[backBufferIndex].image = EGL_NO_IMAGE_KHR; } - - if (!can_use_copybit || err) { - drawWithOpenGL(clip, mTextureName, t); + + LayerBitmap& layerBitmap(mBuffers[backBufferIndex]); + sp<SurfaceBuffer> buffer = layerBitmap.allocate(); + + LOGD_IF(DEBUG_RESIZE, + "Layer::getBuffer(this=%p), index=%d, (%d,%d), (%d,%d)", + this, backBufferIndex, + layerBitmap.getWidth(), + layerBitmap.getHeight(), + layerBitmap.getBuffer()->getWidth(), + layerBitmap.getBuffer()->getHeight()); + + if (UNLIKELY(buffer == 0)) { + // XXX: what to do, what to do? + } else { + // texture is now dirty... + mTextures[backBufferIndex].dirty = true; + // ... so it the visible region (because we consider the surface's + // buffer size for visibility calculations) + forceVisibilityTransaction(); + mFlinger->setTransactionFlags(eTraversalNeeded); } + return buffer; } -status_t Layer::reallocateBuffer(int32_t index, uint32_t w, uint32_t h) +void Layer::scheduleBroadcast() { - LOGD_IF(DEBUG_RESIZE, - "reallocateBuffer (layer=%p), " - "requested (%dx%d), " - "index=%d, (%dx%d), (%dx%d)", - this, - int(w), int(h), - int(index), - int(mBuffers[0].width()), int(mBuffers[0].height()), - int(mBuffers[1].width()), int(mBuffers[1].height())); - - status_t err = mBuffers[index].resize(w, h); - if (err == NO_ERROR) { - mBuffers[index].getInfo(lcblk->surface + index); - } else { - LOGE("resizing buffer %d to (%u,%u) failed [%08x] %s", - index, w, h, err, strerror(err)); - // XXX: what to do, what to do? We could try to free some - // hidden surfaces, instead of killing this one? + sp<Client> ourClient(client.promote()); + if (ourClient != 0) { + mFlinger->scheduleBroadcast(ourClient); } - return err; } uint32_t Layer::doTransaction(uint32_t flags) @@ -232,7 +300,7 @@ uint32_t Layer::doTransaction(uint32_t flags) // that the size changed back to its previous value before the buffer // was resized (in the eLocked case below), in which case, we still // need to execute the code below so the clients have a chance to be - // release. resze() deals with the fact that the size can be the same. + // release. resize() deals with the fact that the size can be the same. /* * Various states we could be in... @@ -276,8 +344,8 @@ uint32_t Layer::doTransaction(uint32_t flags) int(temp.w), int(temp.h), int(drawingState().w), int(drawingState().h), int(clientBackBufferIndex), - int(mBuffers[0].width()), int(mBuffers[0].height()), - int(mBuffers[1].width()), int(mBuffers[1].height())); + int(mBuffers[0].getWidth()), int(mBuffers[0].getHeight()), + int(mBuffers[1].getWidth()), int(mBuffers[1].getHeight())); // if we get there we're pretty screwed. the only reasonable // thing to do is to pretend we should do the resize since // backbufferChanged is set (this also will give a chance to @@ -299,8 +367,8 @@ uint32_t Layer::doTransaction(uint32_t flags) int(temp.w), int(temp.h), int(drawingState().w), int(drawingState().h), int(clientBackBufferIndex), - int(mBuffers[0].width()), int(mBuffers[0].height()), - int(mBuffers[1].width()), int(mBuffers[1].height())); + int(mBuffers[0].getWidth()), int(mBuffers[0].getHeight()), + int(mBuffers[1].getWidth()), int(mBuffers[1].getHeight())); if (state & eLocked) { // if the buffer is locked, we can't resize anything because @@ -314,10 +382,11 @@ uint32_t Layer::doTransaction(uint32_t flags) status_t err = resize(clientBackBufferIndex, temp.w, temp.h, "transaction"); if (err == NO_ERROR) { - const uint32_t mask = clientBackBufferIndex ? eResizeBuffer1 : eResizeBuffer0; + const uint32_t mask = clientBackBufferIndex ? + eResizeBuffer1 : eResizeBuffer0; android_atomic_and(~mask, &(lcblk->swapState)); // since a buffer became available, we can let the client go... - mFlinger->scheduleBroadcast(client); + scheduleBroadcast(); mResizeTransactionDone = true; // we're being resized and there is a freeze display request, @@ -353,14 +422,15 @@ status_t Layer::resize( { /* * handle resize (backbuffer and frontbuffer reallocation) + * this is called from post() or from doTransaction() */ const LayerBitmap& clientBackBuffer(mBuffers[clientBackBufferIndex]); // if the new (transaction) size is != from the the backbuffer // then we need to reallocate the backbuffer - bool backbufferChanged = (clientBackBuffer.width() != width) || - (clientBackBuffer.height() != height); + bool backbufferChanged = (clientBackBuffer.getWidth() != width) || + (clientBackBuffer.getHeight() != height); LOGD_IF(!backbufferChanged, "(%s) eResizeRequested (layer=%p), but size not changed: " @@ -372,18 +442,28 @@ status_t Layer::resize( int(currentState().w), int(currentState().h), long(lcblk->swapState), int(clientBackBufferIndex), - int(mBuffers[0].width()), int(mBuffers[0].height()), - int(mBuffers[1].width()), int(mBuffers[1].height())); + int(mBuffers[0].getWidth()), int(mBuffers[0].getHeight()), + int(mBuffers[1].getWidth()), int(mBuffers[1].getHeight())); // this can happen when changing the size back and forth quickly status_t err = NO_ERROR; if (backbufferChanged) { - err = reallocateBuffer(clientBackBufferIndex, width, height); - } - if (UNLIKELY(err != NO_ERROR)) { - // couldn't reallocate the surface - android_atomic_write(eInvalidSurface, &lcblk->swapState); - memset(lcblk->surface+clientBackBufferIndex, 0, sizeof(surface_info_t)); + + LOGD_IF(DEBUG_RESIZE, + "resize (layer=%p), requested (%dx%d), " + "index=%d, (%dx%d), (%dx%d)", + this, int(width), int(height), int(clientBackBufferIndex), + int(mBuffers[0].getWidth()), int(mBuffers[0].getHeight()), + int(mBuffers[1].getWidth()), int(mBuffers[1].getHeight())); + + err = mBuffers[clientBackBufferIndex].setSize(width, height); + if (UNLIKELY(err != NO_ERROR)) { + // This really should never happen + LOGE("resizing buffer %d to (%u,%u) failed [%08x] %s", + clientBackBufferIndex, width, height, err, strerror(err)); + // couldn't reallocate the surface + android_atomic_write(eInvalidSurface, &lcblk->swapState); + } } return err; } @@ -415,7 +495,7 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) if (UNLIKELY(state & eInvalidSurface)) { // if eInvalidSurface is set, this means the surface // became invalid during a transaction (NO_MEMORY for instance) - mFlinger->scheduleBroadcast(client); + scheduleBroadcast(); return; } @@ -457,15 +537,21 @@ Region Layer::post(uint32_t* previousSate, bool& recomputeVisibleRegions) } while(android_atomic_cmpxchg(oldValue, newValue, &(lcblk->swapState))); *previousSate = oldValue; - + const int32_t index = (newValue & eIndex) ^ 1; mFrontBufferIndex = index; + /* NOTE: it's safe to set this flag here because this is only touched + * from LayerBitmap::allocate(), which by construction cannot happen + * while we're in post(). + */ + lcblk->surface[index].flags &= ~surface_info_t::eBufferDirty; + // ... post the new front-buffer Region dirty(lcblk->region + index); - dirty.andSelf(frontBuffer().bounds()); + dirty.andSelf(frontBuffer().getBounds()); - //LOGI("Did post oldValue=%08lx, newValue=%08lx, mFrontBufferIndex=%u\n", + //LOGD("Did post oldValue=%08lx, newValue=%08lx, mFrontBufferIndex=%u\n", // oldValue, newValue, mFrontBufferIndex); //dirty.dump("dirty"); @@ -476,8 +562,8 @@ Region Layer::post(uint32_t* previousSate, bool& recomputeVisibleRegions) "index=%d, (%dx%d), (%dx%d)", this, newValue, int(1-index), - int(mBuffers[0].width()), int(mBuffers[0].height()), - int(mBuffers[1].width()), int(mBuffers[1].height())); + int(mBuffers[0].getWidth()), int(mBuffers[0].getHeight()), + int(mBuffers[1].getWidth()), int(mBuffers[1].getHeight())); // here, we just posted the surface and we have resolved // the front/back buffer indices. The client is blocked, so @@ -522,8 +608,8 @@ Region Layer::post(uint32_t* previousSate, bool& recomputeVisibleRegions) Point Layer::getPhysicalSize() const { - const LayerBitmap& front(frontBuffer()); - return Point(front.width(), front.height()); + sp<const Buffer> front(frontBuffer().getBuffer()); + return Point(front->getWidth(), front->getHeight()); } void Layer::unlockPageFlip( @@ -547,7 +633,7 @@ void Layer::unlockPageFlip( // client could be blocked, so signal them so they get a // chance to reevaluate their condition. - mFlinger->scheduleBroadcast(client); + scheduleBroadcast(); } } @@ -558,9 +644,30 @@ void Layer::finishPageFlip() "layer %p wasn't locked!", this); android_atomic_and(~eBusy, &(lcblk->swapState)); } - mFlinger->scheduleBroadcast(client); + scheduleBroadcast(); } +// --------------------------------------------------------------------------- + +Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<Layer>& owner) + : Surface(flinger, id, owner->getIdentity(), owner) +{ +} + +Layer::SurfaceLayer::~SurfaceLayer() +{ +} + +sp<SurfaceBuffer> Layer::SurfaceLayer::getBuffer() +{ + sp<SurfaceBuffer> buffer = 0; + sp<Layer> owner(getOwner()); + if (owner != 0) { + buffer = owner->peekBuffer(); + } + return buffer; +} // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/Layer.h b/libs/surfaceflinger/Layer.h index 2867f2b..4c13d6e 100644 --- a/libs/surfaceflinger/Layer.h +++ b/libs/surfaceflinger/Layer.h @@ -27,6 +27,11 @@ #include <pixelflinger/pixelflinger.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES/gl.h> +#include <GLES/glext.h> + #include "LayerBitmap.h" #include "LayerBase.h" #include "Transform.h" @@ -37,11 +42,12 @@ namespace android { class Client; class LayerBitmap; -class MemoryDealer; class FreezeLock; // --------------------------------------------------------------------------- +const int NUM_BUFFERS = 2; + class Layer : public LayerBaseClient { public: @@ -51,16 +57,15 @@ public: virtual uint32_t getTypeInfo() const { return typeInfo; } Layer(SurfaceFlinger* flinger, DisplayID display, - Client* c, int32_t i); + const sp<Client>& client, int32_t i); virtual ~Layer(); inline PixelFormat pixelFormat() const { - return frontBuffer().pixelFormat(); + return frontBuffer().getPixelFormat(); } - status_t setBuffers( Client* client, - uint32_t w, uint32_t h, + status_t setBuffers( uint32_t w, uint32_t h, PixelFormat format, uint32_t flags=0); virtual void onDraw(const Region& clip) const; @@ -73,8 +78,8 @@ public: virtual void finishPageFlip(); virtual bool needsBlending() const { return mNeedsBlending; } virtual bool isSecure() const { return mSecure; } - virtual GLuint getTextureName() const { return mTextureName; } - virtual sp<Surface> getSurface() const; + virtual sp<Surface> createSurface() const; + virtual status_t ditch(); const LayerBitmap& getBuffer(int i) const { return mBuffers[i]; } LayerBitmap& getBuffer(int i) { return mBuffers[i]; } @@ -96,21 +101,37 @@ private: status_t resize(int32_t index, uint32_t w, uint32_t h, const char* what); Region post(uint32_t* oldState, bool& recomputeVisibleRegions); - status_t reallocateBuffer(int32_t index, uint32_t w, uint32_t h); - + sp<SurfaceBuffer> peekBuffer(); + void destroy(); + void scheduleBroadcast(); + + + class SurfaceLayer : public LayerBaseClient::Surface + { + public: + SurfaceLayer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<Layer>& owner); + ~SurfaceLayer(); + + private: + virtual sp<SurfaceBuffer> getBuffer(); + + sp<Layer> getOwner() const { + return static_cast<Layer*>(Surface::getOwner().get()); + } + }; + friend class SurfaceLayer; + sp<Surface> mSurface; bool mSecure; - LayerBitmap mBuffers[2]; + LayerBitmap mBuffers[NUM_BUFFERS]; + Texture mTextures[NUM_BUFFERS]; int32_t mFrontBufferIndex; bool mNeedsBlending; bool mResizeTransactionDone; Region mPostedDirtyRegion; sp<FreezeLock> mFreezeLock; - - GLuint mTextureName; - GLuint mTextureWidth; - GLuint mTextureHeight; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBase.cpp b/libs/surfaceflinger/LayerBase.cpp index 0cf53f7..a841ab3 100644 --- a/libs/surfaceflinger/LayerBase.cpp +++ b/libs/surfaceflinger/LayerBase.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdint.h> #include <sys/types.h> #include <utils/Errors.h> #include <utils/Log.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> #include <GLES/gl.h> #include <GLES/glext.h> @@ -53,19 +53,13 @@ const char* const LayerBaseClient::typeID = "LayerBaseClient"; // --------------------------------------------------------------------------- -Vector<GLuint> LayerBase::deletedTextures; - -int32_t LayerBase::sIdentity = 0; - LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) : dpy(display), contentDirty(false), mFlinger(flinger), mTransformed(false), mOrientation(0), - mCanUseCopyBit(false), mTransactionFlags(0), mPremultipliedAlpha(true), - mIdentity(uint32_t(android_atomic_inc(&sIdentity))), mInvalidate(0) { const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); @@ -265,43 +259,6 @@ void LayerBase::validateVisibility(const Transform& planeTransform) mTransformed = transformed; mLeft = tr.tx(); mTop = tr.ty(); - - // see if we can/should use 2D h/w with the new configuration - mCanUseCopyBit = false; - copybit_device_t* copybit = mFlinger->getBlitEngine(); - if (copybit) { - const int step = copybit->get(copybit, COPYBIT_ROTATION_STEP_DEG); - const int scaleBits = copybit->get(copybit, COPYBIT_SCALING_FRAC_BITS); - mCanUseCopyBit = true; - if ((mOrientation < 0) && (step > 1)) { - // arbitrary orientations not supported - mCanUseCopyBit = false; - } else if ((mOrientation > 0) && (step > 90)) { - // 90 deg rotations not supported - mCanUseCopyBit = false; - } else if ((tr.getType() & SkMatrix::kScale_Mask) && (scaleBits < 12)) { - // arbitrary scaling not supported - mCanUseCopyBit = false; - } -#if HONOR_PREMULTIPLIED_ALPHA - else if (needsBlending() && mPremultipliedAlpha) { - // pre-multiplied alpha not supported - mCanUseCopyBit = false; - } -#endif - else { - // here, we determined we can use copybit - if (tr.getType() & SkMatrix::kScale_Mask) { - // and we have scaling - if (!transparentRegionScreen.isRect()) { - // we punt because blending is cheap (h/w) and the region is - // complex, which may causes artifacts when copying - // scaled content - transparentRegionScreen.clear(); - } - } - } - } } void LayerBase::lockPageFlip(bool& recomputeVisibleRegions) @@ -329,8 +286,9 @@ void LayerBase::invalidate() void LayerBase::drawRegion(const Region& reg) const { - Region::iterator iterator(reg); - if (iterator) { + Region::const_iterator it = reg.begin(); + Region::const_iterator const end = reg.end(); + if (it != end) { Rect r; const DisplayHardware& hw(graphicPlane(0).displayHardware()); const int32_t fbWidth = hw.getWidth(); @@ -338,7 +296,8 @@ void LayerBase::drawRegion(const Region& reg) const const GLshort vertices[][2] = { { 0, 0 }, { fbWidth, 0 }, { fbWidth, fbHeight }, { 0, fbHeight } }; glVertexPointer(2, GL_SHORT, 0, vertices); - while (iterator.iterate(&r)) { + while (it != end) { + const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); glScissor(r.left, sy, r.width(), r.height()); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -403,12 +362,14 @@ void LayerBase::clearWithOpenGL(const Region& clip) const glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDisable(GL_DITHER); - Rect r; - Region::iterator iterator(clip); - if (iterator) { + + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + if (it != end) { glEnable(GL_SCISSOR_TEST); glVertexPointer(2, GL_FIXED, 0, mVertices); - while (iterator.iterate(&r)) { + while (it != end) { + const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); glScissor(r.left, sy, r.width(), r.height()); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -416,15 +377,17 @@ void LayerBase::clearWithOpenGL(const Region& clip) const } } -void LayerBase::drawWithOpenGL(const Region& clip, - GLint textureName, const GGLSurface& t, int transform) const +void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const { const DisplayHardware& hw(graphicPlane(0).displayHardware()); const uint32_t fbHeight = hw.getHeight(); const State& s(drawingState()); - + // bind our texture - validateTexture(textureName); + validateTexture(texture.name); + uint32_t width = texture.width; + uint32_t height = texture.height; + glEnable(GL_TEXTURE_2D); // Dithering... @@ -472,8 +435,9 @@ void LayerBase::drawWithOpenGL(const Region& clip, || !(mFlags & DisplayHardware::DRAW_TEXTURE_EXTENSION) )) { //StopWatch watch("GL transformed"); - Region::iterator iterator(clip); - if (iterator) { + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + if (it != end) { // always use high-quality filtering with fast configurations bool fast = !(mFlags & DisplayHardware::SLOW_CONFIG); if (!fast && s.flags & ISurfaceComposer::eLayerFilter) { @@ -490,21 +454,23 @@ void LayerBase::drawWithOpenGL(const Region& clip, glMatrixMode(GL_TEXTURE); glLoadIdentity(); - if (transform == HAL_TRANSFORM_ROT_90) { + // the texture's source is rotated + if (texture.transform == HAL_TRANSFORM_ROT_90) { + // TODO: handle the other orientations glTranslatef(0, 1, 0); glRotatef(-90, 0, 0, 1); } if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) { // find the smallest power-of-two that will accommodate our surface - GLuint tw = 1 << (31 - clz(t.width)); - GLuint th = 1 << (31 - clz(t.height)); - if (tw < t.width) tw <<= 1; - if (th < t.height) th <<= 1; + GLuint tw = 1 << (31 - clz(width)); + GLuint th = 1 << (31 - clz(height)); + if (tw < width) tw <<= 1; + if (th < height) th <<= 1; // this divide should be relatively fast because it's // a power-of-two (optimized path in libgcc) - GLfloat ws = GLfloat(t.width) /tw; - GLfloat hs = GLfloat(t.height)/th; + GLfloat ws = GLfloat(width) /tw; + GLfloat hs = GLfloat(height)/th; glScalef(ws, hs, 1.0f); } @@ -512,8 +478,8 @@ void LayerBase::drawWithOpenGL(const Region& clip, glVertexPointer(2, GL_FIXED, 0, mVertices); glTexCoordPointer(2, GL_FIXED, 0, texCoords); - Rect r; - while (iterator.iterate(&r)) { + while (it != end) { + const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); glScissor(r.left, sy, r.width(), r.height()); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -526,18 +492,19 @@ void LayerBase::drawWithOpenGL(const Region& clip, glDisableClientState(GL_TEXTURE_COORD_ARRAY); } } else { - Region::iterator iterator(clip); - if (iterator) { - Rect r; - GLint crop[4] = { 0, t.height, t.width, -t.height }; + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + if (it != end) { + GLint crop[4] = { 0, height, width, -height }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); int x = tx(); int y = ty(); - y = fbHeight - (y + t.height); - while (iterator.iterate(&r)) { + y = fbHeight - (y + height); + while (it != end) { + const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); glScissor(r.left, sy, r.width(), r.height()); - glDrawTexiOES(x, y, 0, t.width, t.height); + glDrawTexiOES(x, y, 0, width, height); } } } @@ -550,13 +517,16 @@ void LayerBase::validateTexture(GLint textureName) const // this is currently done in loadTexture() below } -void LayerBase::loadTexture(const Region& dirty, - GLint textureName, const GGLSurface& t, - GLuint& textureWidth, GLuint& textureHeight) const +void LayerBase::loadTexture(Texture* texture, GLint textureName, + const Region& dirty, const GGLSurface& t) const { // TODO: defer the actual texture reload until LayerBase::validateTexture // is called. + texture->name = textureName; + GLuint& textureWidth(texture->width); + GLuint& textureHeight(texture->height); + uint32_t flags = mFlags; glBindTexture(GL_TEXTURE_2D, textureName); @@ -565,8 +535,7 @@ void LayerBase::loadTexture(const Region& dirty, /* * In OpenGL ES we can't specify a stride with glTexImage2D (however, - * GL_UNPACK_ALIGNMENT is 4, which in essence allows a limited form of - * stride). + * 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). * @@ -579,9 +548,11 @@ void LayerBase::loadTexture(const Region& dirty, * * This should never be a problem with POT textures */ - - tw += (((t.stride - tw) * bytesPerPixel(t.format)) / 4); - + + 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 */ @@ -594,119 +565,98 @@ void LayerBase::loadTexture(const Region& dirty, texture_h = 1 << (31 - clz(t.height)); if (texture_w < t.width) texture_w <<= 1; if (texture_h < t.height) texture_h <<= 1; - if (texture_w != tw || texture_h != th) { - // we can't use DIRECT_TEXTURE since we changed the size - // of the texture - flags &= ~DisplayHardware::DIRECT_TEXTURE; - } } - - if (flags & DisplayHardware::DIRECT_TEXTURE) { - // here we're guaranteed that texture_{w|h} == t{w|h} - if (t.format == GGL_PIXEL_FORMAT_RGB_565) { - glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, - GL_RGB, tw, th, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, t.data); - } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { - glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, - GL_RGBA, tw, th, 0, - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, t.data); - } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { - glTexImage2D(GL_DIRECT_TEXTURE_2D_QUALCOMM, 0, - GL_RGBA, tw, th, 0, - GL_RGBA, GL_UNSIGNED_BYTE, t.data); - } else if (t.format == GGL_PIXEL_FORMAT_BGRA_8888) { - // TODO: add GL_BGRA extension - } else { - // oops, we don't handle this format, try the regular path - goto regular; - } - textureWidth = tw; - textureHeight = th; - } else { + regular: - Rect bounds(dirty.bounds()); - GLvoid* data = 0; - if (texture_w!=textureWidth || texture_h!=textureHeight) { - // texture size changed, we need to create a new one - - if (!textureWidth || !textureHeight) { - // this is the first time, load the whole texture - if (texture_w==tw && texture_h==th) { - // we can do it one pass - data = t.data; - } else { - // we have to create the texture first because it - // doesn't match the size of the buffer - bounds.set(Rect(tw, th)); - } - } - - if (t.format == GGL_PIXEL_FORMAT_RGB_565) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGB, texture_w, texture_h, 0, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); - } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, texture_w, texture_h, 0, - GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); - } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { - glTexImage2D(GL_TEXTURE_2D, 0, - GL_RGBA, texture_w, texture_h, 0, - GL_RGBA, GL_UNSIGNED_BYTE, data); - } else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP || - t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) { - // just show the Y plane of YUV buffers + Rect bounds(dirty.bounds()); + GLvoid* data = 0; + if (texture_w!=textureWidth || texture_h!=textureHeight) { + // texture size changed, we need to create a new one + + if (!textureWidth || !textureHeight) { + // this is the first time, load the whole texture + if (texture_w==tw && texture_h==th) { + // we can do it one pass data = t.data; - glTexImage2D(GL_TEXTURE_2D, 0, - GL_LUMINANCE, texture_w, texture_h, 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, textureName, t.format); - textureName = -1; + // we have to create the texture first because it + // doesn't match the size of the buffer + bounds.set(Rect(tw, th)); } - textureWidth = texture_w; - textureHeight = texture_h; } - if (!data && textureName>=0) { - if (t.format == GGL_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.width*2); - } else if (t.format == GGL_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.width*2); - } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { - glTexSubImage2D(GL_TEXTURE_2D, 0, - 0, bounds.top, t.width, bounds.height(), - GL_RGBA, GL_UNSIGNED_BYTE, - t.data + bounds.top*t.width*4); - } + + if (t.format == GGL_PIXEL_FORMAT_RGB_565) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGB, texture_w, texture_h, 0, + GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_4444) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture_w, texture_h, 0, + GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); + } else if (t.format == GGL_PIXEL_FORMAT_RGBA_8888) { + glTexImage2D(GL_TEXTURE_2D, 0, + GL_RGBA, texture_w, texture_h, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + } else if ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP || + t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) { + // just show the Y plane of YUV buffers + glTexImage2D(GL_TEXTURE_2D, 0, + GL_LUMINANCE, texture_w, texture_h, 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, textureName, t.format); + textureName = -1; + } + textureWidth = texture_w; + textureHeight = texture_h; + } + if (!data && textureName>=0) { + if (t.format == GGL_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 == GGL_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 == GGL_PIXEL_FORMAT_RGBA_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 ( t.format == GGL_PIXEL_FORMAT_YCbCr_422_SP || + t.format == GGL_PIXEL_FORMAT_YCbCr_420_SP) { + // 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); } } -} - -bool LayerBase::canUseCopybit() const -{ - return mCanUseCopyBit; } // --------------------------------------------------------------------------- +int32_t LayerBaseClient::sIdentity = 0; + LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, - Client* c, int32_t i) - : LayerBase(flinger, display), client(c), - lcblk( c ? &(c->ctrlblk->layers[i]) : 0 ), - mIndex(i) + const sp<Client>& client, int32_t i) + : LayerBase(flinger, display), client(client), + lcblk( client!=0 ? &(client->ctrlblk->layers[i]) : 0 ), + mIndex(i), + mIdentity(uint32_t(android_atomic_inc(&sIdentity))) { - if (client) { - client->bindLayer(this, i); +} +void LayerBaseClient::onFirstRef() +{ + sp<Client> client(this->client.promote()); + if (client != 0) { + client->bindLayer(this, mIndex); // Initialize this layer's control block memset(this->lcblk, 0, sizeof(layer_cblk_t)); this->lcblk->identity = mIdentity; @@ -717,23 +667,121 @@ LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, LayerBaseClient::~LayerBaseClient() { - if (client) { + sp<Client> client(this->client.promote()); + if (client != 0) { client->free(mIndex); } } -int32_t LayerBaseClient::serverIndex() const { - if (client) { +int32_t LayerBaseClient::serverIndex() const +{ + sp<Client> client(this->client.promote()); + if (client != 0) { return (client->cid<<16)|mIndex; } return 0xFFFF0000 | mIndex; } -sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() const +sp<LayerBaseClient::Surface> LayerBaseClient::getSurface() +{ + sp<Surface> s; + Mutex::Autolock _l(mLock); + s = mClientSurface.promote(); + if (s == 0) { + s = createSurface(); + mClientSurface = s; + } + return s; +} + +sp<LayerBaseClient::Surface> LayerBaseClient::createSurface() const +{ + return new Surface(mFlinger, clientIndex(), mIdentity, + const_cast<LayerBaseClient *>(this)); +} + +// --------------------------------------------------------------------------- + +LayerBaseClient::Surface::Surface( + const sp<SurfaceFlinger>& flinger, + SurfaceID id, int identity, + const sp<LayerBaseClient>& owner) + : mFlinger(flinger), mToken(id), mIdentity(identity), mOwner(owner) +{ +} + + +LayerBaseClient::Surface::~Surface() +{ + /* + * This is a good place to clean-up all client resources + */ + + // destroy client resources + sp<LayerBaseClient> layer = getOwner(); + if (layer != 0) { + mFlinger->destroySurface(layer); + } +} + +sp<LayerBaseClient> LayerBaseClient::Surface::getOwner() const { + sp<LayerBaseClient> owner(mOwner.promote()); + return owner; +} + + +void LayerBaseClient::Surface::getSurfaceData( + ISurfaceFlingerClient::surface_data_t* params) const +{ + params->token = mToken; + params->identity = mIdentity; +} + +status_t LayerBaseClient::Surface::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch (code) { + case REGISTER_BUFFERS: + case UNREGISTER_BUFFERS: + case CREATE_OVERLAY: + { + if (!mFlinger->mAccessSurfaceFlinger.checkCalling()) { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + } + } + return BnSurface::onTransact(code, data, reply, flags); +} + +sp<SurfaceBuffer> LayerBaseClient::Surface::getBuffer() { - return new Surface(clientIndex(), mIdentity); + return NULL; +} + +status_t LayerBaseClient::Surface::registerBuffers( + const ISurface::BufferHeap& buffers) +{ + return INVALID_OPERATION; } +void LayerBaseClient::Surface::postBuffer(ssize_t offset) +{ +} + +void LayerBaseClient::Surface::unregisterBuffers() +{ +} + +sp<OverlayRef> LayerBaseClient::Surface::createOverlay( + uint32_t w, uint32_t h, int32_t format) +{ + return NULL; +}; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBase.h b/libs/surfaceflinger/LayerBase.h index a020f44..6fb1d1c 100644 --- a/libs/surfaceflinger/LayerBase.h +++ b/libs/surfaceflinger/LayerBase.h @@ -20,8 +20,13 @@ #include <stdint.h> #include <sys/types.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + #include <private/ui/LayerState.h> +#include <utils/RefBase.h> + #include <ui/Region.h> #include <ui/Overlay.h> @@ -37,10 +42,12 @@ class SurfaceFlinger; class DisplayHardware; class GraphicPlane; class Client; +class SurfaceBuffer; +class Buffer; // --------------------------------------------------------------------------- -class LayerBase +class LayerBase : public RefBase { // poor man's dynamic_cast below template<typename T> @@ -69,10 +76,7 @@ public: } - static Vector<GLuint> deletedTextures; - LayerBase(SurfaceFlinger* flinger, DisplayID display); - virtual ~LayerBase(); DisplayID dpy; mutable bool contentDirty; @@ -201,21 +205,29 @@ public: /** * isSecure - true if this surface is secure, that is if it prevents - * screenshots or vns servers. + * screenshots or VNC servers. */ virtual bool isSecure() const { return false; } - enum { // flags for doTransaction() - eVisibleRegion = 0x00000002, - eRestartTransaction = 0x00000008 - }; + /** signal this layer that it's not needed any longer. called from the + * main thread */ + virtual status_t ditch() { return NO_ERROR; } + + + + enum { // flags for doTransaction() + eVisibleRegion = 0x00000002, + eRestartTransaction = 0x00000008 + }; inline const State& drawingState() const { return mDrawingState; } inline const State& currentState() const { return mCurrentState; } inline State& currentState() { return mCurrentState; } - static int compareCurrentStateZ(LayerBase*const* layerA, LayerBase*const* layerB) { + static int compareCurrentStateZ( + sp<LayerBase> const * layerA, + sp<LayerBase> const * layerB) { return layerA[0]->currentState().z - layerB[0]->currentState().z; } @@ -229,20 +241,24 @@ protected: GLuint createTexture() const; - void drawWithOpenGL(const Region& clip, - GLint textureName, - const GGLSurface& surface, - int transform = 0) const; - + struct Texture { + Texture() : name(-1U), width(0), height(0), + image(EGL_NO_IMAGE_KHR), transform(0), dirty(true) { } + GLuint name; + GLuint width; + GLuint height; + EGLImageKHR image; + uint32_t transform; + bool dirty; + }; + void clearWithOpenGL(const Region& clip) const; + void drawWithOpenGL(const Region& clip, const Texture& texture) const; + void loadTexture(Texture* texture, GLint textureName, + const Region& dirty, const GGLSurface& t) const; - void loadTexture(const Region& dirty, - GLint textureName, const GGLSurface& t, - GLuint& textureWidth, GLuint& textureHeight) const; - - bool canUseCopybit() const; - SurfaceFlinger* mFlinger; + sp<SurfaceFlinger> mFlinger; uint32_t mFlags; // cached during validateVisibility() @@ -250,7 +266,6 @@ protected: int32_t mOrientation; GLfixed mVertices[4][2]; Rect mTransformedBounds; - bool mCanUseCopyBit; int mLeft; int mTop; @@ -262,16 +277,16 @@ protected: // don't change, don't need a lock bool mPremultipliedAlpha; - // only read - const uint32_t mIdentity; - // atomic volatile int32_t mInvalidate; +protected: + virtual ~LayerBase(); + private: - void validateTexture(GLint textureName) const; - static int32_t sIdentity; + LayerBase(const LayerBase& rhs); + void validateTexture(GLint textureName) const; }; @@ -287,66 +302,63 @@ public: virtual uint32_t getTypeInfo() const { return typeInfo; } LayerBaseClient(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i); + const sp<Client>& client, int32_t i); virtual ~LayerBaseClient(); + virtual void onFirstRef(); - - Client* const client; + wp<Client> client; layer_cblk_t* const lcblk; + inline uint32_t getIdentity() const { return mIdentity; } inline int32_t clientIndex() const { return mIndex; } int32_t serverIndex() const; - virtual sp<Surface> getSurface() const; - uint32_t getIdentity() const { return mIdentity; } + sp<Surface> getSurface(); + virtual sp<Surface> createSurface() const; + class Surface : public BnSurface { public: - Surface(SurfaceID id, int identity) { - mParams.token = id; - mParams.identity = identity; - } - Surface(SurfaceID id, - const sp<IMemoryHeap>& heap0, - const sp<IMemoryHeap>& heap1, - int identity) - { - mParams.token = id; - mParams.identity = identity; - mParams.heap[0] = heap0; - mParams.heap[1] = heap1; - } - virtual ~Surface() { - // TODO: We now have a point here were we can clean-up the - // client's mess. - // This is also where surface id should be recycled. - //LOGD("Surface %d, heaps={%p, %p} destroyed", - // mId, mHeap[0].get(), mHeap[1].get()); - } - + virtual void getSurfaceData( - ISurfaceFlingerClient::surface_data_t* params) const { - *params = mParams; - } - - virtual status_t registerBuffers(const ISurface::BufferHeap& buffers) - { return INVALID_OPERATION; } - virtual void postBuffer(ssize_t offset) { } - virtual void unregisterBuffers() { }; - virtual sp<OverlayRef> createOverlay( - uint32_t w, uint32_t h, int32_t format) { - return NULL; - }; + ISurfaceFlingerClient::surface_data_t* params) const; + + protected: + Surface(const sp<SurfaceFlinger>& flinger, + SurfaceID id, int identity, + const sp<LayerBaseClient>& owner); + virtual ~Surface(); + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + sp<LayerBaseClient> getOwner() const; private: - ISurfaceFlingerClient::surface_data_t mParams; + virtual sp<SurfaceBuffer> getBuffer(); + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); + virtual void postBuffer(ssize_t offset); + virtual void unregisterBuffers(); + virtual sp<OverlayRef> createOverlay(uint32_t w, uint32_t h, + int32_t format); + + protected: + friend class LayerBaseClient; + sp<SurfaceFlinger> mFlinger; + int32_t mToken; + int32_t mIdentity; + wp<LayerBaseClient> mOwner; }; -private: - int32_t mIndex; + friend class Surface; +private: + int32_t mIndex; + mutable Mutex mLock; + mutable wp<Surface> mClientSurface; + // only read + const uint32_t mIdentity; + static int32_t sIdentity; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerBitmap.cpp b/libs/surfaceflinger/LayerBitmap.cpp index 397ddc8..9fffbbf 100644 --- a/libs/surfaceflinger/LayerBitmap.cpp +++ b/libs/surfaceflinger/LayerBitmap.cpp @@ -14,174 +14,197 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdint.h> #include <sys/types.h> -#include <cutils/memory.h> #include <utils/Errors.h> #include <utils/Log.h> -#include <utils/MemoryDealer.h> -#include <utils/IMemory.h> +#include <binder/MemoryBase.h> +#include <binder/IMemory.h> + #include <ui/PixelFormat.h> +#include <ui/Surface.h> #include <pixelflinger/pixelflinger.h> +#include "BufferAllocator.h" #include "LayerBitmap.h" #include "SurfaceFlinger.h" -#include "VRamHeap.h" namespace android { -// --------------------------------------------------------------------------- +// =========================================================================== +// Buffer and implementation of android_native_buffer_t +// =========================================================================== -LayerBitmap::LayerBitmap() - : mAllocFlags(0), mOffset(0), mSize(-1U), mAlignment(2) +Buffer::Buffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) + : SurfaceBuffer(), mInitCheck(NO_INIT), mFlags(flags), + mVStride(0) { - memset(&mSurface, 0, sizeof(mSurface)); + this->format = format; + if (w>0 && h>0) { + mInitCheck = initSize(w, h); + } } -LayerBitmap::~LayerBitmap() +Buffer::~Buffer() { - mSurface.data = 0; + if (handle) { + BufferAllocator& allocator(BufferAllocator::get()); + allocator.free(handle); + } } -status_t LayerBitmap::init(const sp<MemoryDealer>& allocator) -{ - if (mAllocator != NULL) - return BAD_VALUE; - mAllocator = allocator; - return NO_ERROR; +status_t Buffer::initCheck() const { + return mInitCheck; } -status_t LayerBitmap::setBits(uint32_t w, uint32_t h, uint32_t alignment, - PixelFormat format, uint32_t flags) +android_native_buffer_t* Buffer::getNativeBuffer() const { - const sp<MemoryDealer>& allocator(mAllocator); - if (allocator == NULL) - return NO_INIT; - - if (UNLIKELY(w == mSurface.width && h == mSurface.height && - format == mSurface.format)) - { // same format and size, do nothing. - return NO_ERROR; - } - - PixelFormatInfo info; - getPixelFormatInfo(format, &info); - - uint32_t allocFlags = MemoryDealer::PAGE_ALIGNED; - const uint32_t align = 4; // must match GL_UNPACK_ALIGNMENT - const uint32_t Bpp = info.bytesPerPixel; - uint32_t stride = (w + (alignment-1)) & ~(alignment-1); - stride = ((stride * Bpp + (align-1)) & ~(align-1)) / Bpp; - size_t size = info.getScanlineSize(stride) * h; - if (allocFlags & MemoryDealer::PAGE_ALIGNED) { - size_t pagesize = getpagesize(); - size = (size + (pagesize-1)) & ~(pagesize-1); - } + return static_cast<android_native_buffer_t*>(const_cast<Buffer*>(this)); +} - /* FIXME: we should be able to have a h/v stride because the user of the - * surface might have stride limitation (for instance h/w codecs often do) +status_t Buffer::initSize(uint32_t w, uint32_t h) +{ + status_t err = NO_ERROR; + + BufferAllocator& allocator = BufferAllocator::get(); + + /* + * buffers used for software rendering, but h/w composition + * are allocated with SW_READ_OFTEN | SW_WRITE_OFTEN | HW_TEXTURE + * + * buffers used for h/w rendering and h/w composition + * are allocated with HW_RENDER | HW_TEXTURE + * + * buffers used with h/w rendering and either NPOT or no egl_image_ext + * are allocated with SW_READ_RARELY | HW_RENDER + * */ - int32_t vstride = 0; - - mAlignment = alignment; - mAllocFlags = allocFlags; - mOffset = 0; - if (mSize != size) { - // would be nice to have a reallocate() api - mBitsMemory.clear(); // free-memory - mBitsMemory = allocator->allocate(size, allocFlags); - mSize = size; + + if (mFlags & Buffer::SECURE) { + // secure buffer, don't store it into the GPU + usage = BufferAllocator::USAGE_SW_READ_OFTEN | + BufferAllocator::USAGE_SW_WRITE_OFTEN; } else { - // don't erase memory if we didn't have to reallocate - flags &= ~SECURE_BITS; - } - if (mBitsMemory != 0) { - mOffset = mBitsMemory->offset(); - mSurface.data = static_cast<GGLubyte*>(mBitsMemory->pointer()); - mSurface.version = sizeof(GGLSurface); - mSurface.width = w; - mSurface.height = h; - mSurface.stride = stride; - mSurface.vstride = vstride; - mSurface.format = format; - if (flags & SECURE_BITS) - clear(); + if (mFlags & Buffer::GPU) { + // the client wants to do GL rendering + usage = BufferAllocator::USAGE_HW_RENDER | + BufferAllocator::USAGE_HW_TEXTURE; + } else { + // software rendering-client, h/w composition + usage = BufferAllocator::USAGE_SW_READ_OFTEN | + BufferAllocator::USAGE_SW_WRITE_OFTEN | + BufferAllocator::USAGE_HW_TEXTURE; + } } - if (mBitsMemory==0 || mSurface.data==0) { - LOGE("not enough memory for layer bitmap " - "size=%u (w=%d, h=%d, stride=%d, format=%d)", - size, int(w), int(h), int(stride), int(format)); - allocator->dump("LayerBitmap"); - mSurface.data = 0; - mSize = -1U; - return NO_MEMORY; + err = allocator.alloc(w, h, format, usage, &handle, &stride); + + if (err == NO_ERROR) { + if (err == NO_ERROR) { + width = w; + height = h; + mVStride = 0; + } } - return NO_ERROR; + + return err; } -void LayerBitmap::clear() +status_t Buffer::lock(GGLSurface* sur, uint32_t usage) { - // NOTE: this memset should not be necessary, at least for - // opaque surface. However, for security reasons it's better to keep it - // (in the case of pmem, it's possible that the memory contains old - // data) - if (mSurface.data) { - memset(mSurface.data, 0, mSize); - //if (bytesPerPixel(mSurface.format) == 4) { - // android_memset32((uint32_t*)mSurface.data, 0xFF0000FF, mSize); - //} else { - // android_memset16((uint16_t*)mSurface.data, 0xF800, mSize); - //} + void* vaddr; + status_t res = SurfaceBuffer::lock(usage, &vaddr); + if (res == NO_ERROR && sur) { + sur->version = sizeof(GGLSurface); + sur->width = width; + sur->height = height; + sur->stride = stride; + sur->format = format; + sur->vstride = mVStride; + sur->data = static_cast<GGLubyte*>(vaddr); } + return res; } -status_t LayerBitmap::getInfo(surface_info_t* info) const +// =========================================================================== +// LayerBitmap +// =========================================================================== + +LayerBitmap::LayerBitmap() + : mInfo(0), mWidth(0), mHeight(0) { - if (mSurface.data == 0) { - memset(info, 0, sizeof(surface_info_t)); - info->bits_offset = NO_MEMORY; - return NO_MEMORY; - } - info->w = uint16_t(width()); - info->h = uint16_t(height()); - info->stride= uint16_t(stride()); - info->bpr = uint16_t(stride() * bytesPerPixel(pixelFormat())); - info->format= uint8_t(pixelFormat()); - info->flags = surface_info_t::eBufferDirty; - info->bits_offset = ssize_t(mOffset); +} + +LayerBitmap::~LayerBitmap() +{ +} + +status_t LayerBitmap::init(surface_info_t* info, + uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) +{ + if (info == NULL) + return BAD_VALUE; + + mFormat = format; + mFlags = flags; + mWidth = w; + mHeight = h; + + mInfo = info; + memset(info, 0, sizeof(surface_info_t)); + info->flags = surface_info_t::eNeedNewBuffer; + + // init the buffer, but don't trigger an allocation + mBuffer = new Buffer(0, 0, format, flags); return NO_ERROR; } -status_t LayerBitmap::resize(uint32_t w, uint32_t h) +status_t LayerBitmap::setSize(uint32_t w, uint32_t h) { - int err = setBits(w, h, mAlignment, pixelFormat(), SECURE_BITS); - return err; + Mutex::Autolock _l(mLock); + if ((w != mWidth) || (h != mHeight)) { + mWidth = w; + mHeight = h; + // this will signal the client that it needs to asks us for a new buffer + mInfo->flags = surface_info_t::eNeedNewBuffer; + } + return NO_ERROR; } -size_t LayerBitmap::size() const +sp<Buffer> LayerBitmap::allocate() { - return mSize; + Mutex::Autolock _l(mLock); + sp<Buffer> buffer(mBuffer); + const uint32_t w = mWidth; + const uint32_t h = mHeight; + if (buffer!=0 && (w != buffer->getWidth() || h != buffer->getHeight())) { + surface_info_t* info = mInfo; + buffer = new Buffer(w, h, mFormat, mFlags); + status_t err = buffer->initCheck(); + if (LIKELY(err == NO_ERROR)) { + info->flags = surface_info_t::eBufferDirty; + info->status = NO_ERROR; + } else { + memset(info, 0, sizeof(surface_info_t)); + info->status = NO_MEMORY; + } + mBuffer = buffer; + } + return buffer; } -void LayerBitmap::getBitmapSurface(copybit_image_t* img) const +status_t LayerBitmap::free() { - const sp<IMemoryHeap>& mh(getAllocator()->getMemoryHeap()); - void* sbase = mh->base(); - const GGLSurface& t(surface()); - img->w = t.stride ?: t.width; - img->h = t.vstride ?: t.height; - img->format = t.format; - img->offset = intptr_t(t.data) - intptr_t(sbase); - img->base = sbase; - img->fd = mh->heapID(); + mBuffer.clear(); + mWidth = 0; + mHeight = 0; + return NO_ERROR; } + // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/surfaceflinger/LayerBitmap.h b/libs/surfaceflinger/LayerBitmap.h index 9ad64c4..22525ce 100644 --- a/libs/surfaceflinger/LayerBitmap.h +++ b/libs/surfaceflinger/LayerBitmap.h @@ -20,63 +20,114 @@ #include <stdint.h> #include <sys/types.h> +#include <hardware/gralloc.h> + #include <utils/Atomic.h> + #include <ui/PixelFormat.h> #include <ui/Rect.h> -#include <private/ui/SharedState.h> +#include <ui/Surface.h> + #include <pixelflinger/pixelflinger.h> +#include <private/ui/SharedState.h> +#include <private/ui/SurfaceBuffer.h> + class copybit_image_t; +struct android_native_buffer_t; namespace android { // --------------------------------------------------------------------------- - class IMemory; -class MemoryDealer; class LayerBitmap; -// --------------------------------------------------------------------------- +// =========================================================================== +// Buffer +// =========================================================================== -class LayerBitmap +class NativeBuffer; + +class Buffer : public SurfaceBuffer { public: - enum { - // erase memory to ensure security when necessary - SECURE_BITS = 0x00000001 + DONT_CLEAR = 0x00000001, + GPU = 0x00000002, + SECURE = 0x00000004 }; - LayerBitmap(); - ~LayerBitmap(); - status_t init(const sp<MemoryDealer>& allocator); + // creates w * h buffer + Buffer(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags = 0); + + // return status + status_t initCheck() const; + + uint32_t getWidth() const { return width; } + uint32_t getHeight() const { return height; } + uint32_t getStride() const { return stride; } + uint32_t getUsage() const { return usage; } + PixelFormat getPixelFormat() const { return format; } + Rect getBounds() const { return Rect(width, height); } + + status_t lock(GGLSurface* surface, uint32_t usage); + + android_native_buffer_t* getNativeBuffer() const; + +private: + friend class LightRefBase<Buffer>; + Buffer(const Buffer& rhs); + virtual ~Buffer(); + Buffer& operator = (const Buffer& rhs); + const Buffer& operator = (const Buffer& rhs) const; - status_t setBits(uint32_t w, uint32_t h, uint32_t alignment, - PixelFormat format, uint32_t flags = 0); - void clear(); + status_t initSize(uint32_t w, uint32_t h); - status_t getInfo(surface_info_t* info) const; - status_t resize(uint32_t w, uint32_t h); + ssize_t mInitCheck; + uint32_t mFlags; + uint32_t mVStride; +}; - const GGLSurface& surface() const { return mSurface; } - Rect bounds() const { return Rect(width(), height()); } - uint32_t width() const { return surface().width; } - uint32_t height() const { return surface().height; } - uint32_t stride() const { return surface().stride; } - PixelFormat pixelFormat() const { return surface().format; } - void* serverBits() const { return surface().data; } - size_t size() const; - const sp<MemoryDealer>& getAllocator() const { return mAllocator; } - void getBitmapSurface(copybit_image_t* img) const; +// =========================================================================== +// LayerBitmap +// =========================================================================== +class LayerBitmap +{ +public: + enum { + DONT_CLEAR = Buffer::DONT_CLEAR, + GPU = Buffer::GPU, + SECURE = Buffer::SECURE + }; + LayerBitmap(); + ~LayerBitmap(); + + status_t init(surface_info_t* info, + uint32_t w, uint32_t h, PixelFormat format, uint32_t flags = 0); + + status_t setSize(uint32_t w, uint32_t h); + + sp<Buffer> allocate(); + status_t free(); + + sp<const Buffer> getBuffer() const { return mBuffer; } + sp<Buffer> getBuffer() { return mBuffer; } + + uint32_t getWidth() const { return mWidth; } + uint32_t getHeight() const { return mHeight; } + PixelFormat getPixelFormat() const { return mBuffer->getPixelFormat(); } + Rect getBounds() const { return mBuffer->getBounds(); } + private: - sp<MemoryDealer> mAllocator; - sp<IMemory> mBitsMemory; - uint32_t mAllocFlags; - ssize_t mOffset; - GGLSurface mSurface; - size_t mSize; - uint32_t mAlignment; + surface_info_t* mInfo; + sp<Buffer> mBuffer; + uint32_t mWidth; + uint32_t mHeight; + PixelFormat mFormat; + uint32_t mFlags; + // protects setSize() and allocate() + mutable Mutex mLock; }; }; // namespace android diff --git a/libs/surfaceflinger/LayerBlur.cpp b/libs/surfaceflinger/LayerBlur.cpp index d3e456f..00abd5a 100644 --- a/libs/surfaceflinger/LayerBlur.cpp +++ b/libs/surfaceflinger/LayerBlur.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdint.h> #include <sys/types.h> @@ -40,7 +38,7 @@ const char* const LayerBlur::typeID = "LayerBlur"; // --------------------------------------------------------------------------- LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i) + const sp<Client>& client, int32_t i) : LayerBaseClient(flinger, display, client, i), mCacheDirty(true), mRefreshCache(true), mCacheAge(0), mTextureName(-1U) { @@ -49,8 +47,7 @@ LayerBlur::LayerBlur(SurfaceFlinger* flinger, DisplayID display, LayerBlur::~LayerBlur() { if (mTextureName != -1U) { - //glDeleteTextures(1, &mTextureName); - deletedTextures.add(mTextureName); + glDeleteTextures(1, &mTextureName); } } @@ -139,8 +136,9 @@ void LayerBlur::onDraw(const Region& clip) const glGenTextures(1, &mTextureName); } - Region::iterator iterator(clip); - if (iterator) { + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + if (it != end) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, mTextureName); @@ -201,27 +199,25 @@ void LayerBlur::onDraw(const Region& clip) const glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FIXED, 0, mVertices); glTexCoordPointer(2, GL_FIXED, 0, mVertices); - Rect r; - while (iterator.iterate(&r)) { + while (it != end) { + const Rect& r = *it++; const GLint sy = fbHeight - (r.top + r.height()); glScissor(r.left, sy, r.width(), r.height()); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } } else { - Region::iterator iterator(clip); - if (iterator) { - // NOTE: this is marginally faster with the software gl, because - // glReadPixels() reads the fb bottom-to-top, however we'll - // skip all the jaccobian computations. - Rect r; - GLint crop[4] = { 0, 0, w, h }; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); - y = fbHeight - (y + h); - while (iterator.iterate(&r)) { - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawTexiOES(x, y, 0, w, h); - } + // NOTE: this is marginally faster with the software gl, because + // glReadPixels() reads the fb bottom-to-top, however we'll + // skip all the jaccobian computations. + Rect r; + GLint crop[4] = { 0, 0, w, h }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); + y = fbHeight - (y + h); + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawTexiOES(x, y, 0, w, h); } } } diff --git a/libs/surfaceflinger/LayerBlur.h b/libs/surfaceflinger/LayerBlur.h index 24b1156..0c3e6eb 100644 --- a/libs/surfaceflinger/LayerBlur.h +++ b/libs/surfaceflinger/LayerBlur.h @@ -39,7 +39,7 @@ public: virtual uint32_t getTypeInfo() const { return typeInfo; } LayerBlur(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i); + const sp<Client>& client, int32_t i); virtual ~LayerBlur(); virtual void onDraw(const Region& clip) const; diff --git a/libs/surfaceflinger/LayerBuffer.cpp b/libs/surfaceflinger/LayerBuffer.cpp index 00fab70..90e7f50 100644 --- a/libs/surfaceflinger/LayerBuffer.cpp +++ b/libs/surfaceflinger/LayerBuffer.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdint.h> #include <math.h> @@ -25,17 +23,16 @@ #include <utils/Log.h> #include <utils/StopWatch.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> - #include <ui/PixelFormat.h> -#include <ui/EGLDisplaySurface.h> +#include <ui/FramebufferNativeWindow.h> + +#include <hardware/copybit.h> #include "LayerBuffer.h" #include "SurfaceFlinger.h" -#include "VRamHeap.h" #include "DisplayHardware/DisplayHardware.h" +#include "gralloc_priv.h" // needed for msm / copybit namespace android { @@ -47,7 +44,7 @@ const char* const LayerBuffer::typeID = "LayerBuffer"; // --------------------------------------------------------------------------- LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i) + const sp<Client>& client, int32_t i) : LayerBaseClient(flinger, display, client, i), mNeedsBlending(false) { @@ -55,30 +52,24 @@ LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display, LayerBuffer::~LayerBuffer() { - sp<SurfaceBuffer> s(getClientSurface()); - if (s != 0) { - s->disown(); - mClientSurface.clear(); - } } -sp<LayerBuffer::SurfaceBuffer> LayerBuffer::getClientSurface() const +void LayerBuffer::onFirstRef() { - Mutex::Autolock _l(mLock); - return mClientSurface.promote(); + LayerBaseClient::onFirstRef(); + mSurface = new SurfaceBuffer(mFlinger, clientIndex(), + const_cast<LayerBuffer *>(this)); } -sp<LayerBaseClient::Surface> LayerBuffer::getSurface() const +sp<LayerBaseClient::Surface> LayerBuffer::createSurface() const { - sp<SurfaceBuffer> s; - Mutex::Autolock _l(mLock); - s = mClientSurface.promote(); - if (s == 0) { - s = new SurfaceBuffer(clientIndex(), - const_cast<LayerBuffer *>(this)); - mClientSurface = s; - } - return s; + return mSurface; +} + +status_t LayerBuffer::ditch() +{ + mSurface.clear(); + return NO_ERROR; } bool LayerBuffer::needsBlending() const { @@ -192,82 +183,49 @@ sp<LayerBuffer::Source> LayerBuffer::clearSource() { // LayerBuffer::SurfaceBuffer // ============================================================================ -LayerBuffer::SurfaceBuffer::SurfaceBuffer(SurfaceID id, LayerBuffer* owner) -: LayerBaseClient::Surface(id, owner->getIdentity()), mOwner(owner) +LayerBuffer::SurfaceBuffer::SurfaceBuffer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<LayerBuffer>& owner) + : LayerBaseClient::Surface(flinger, id, owner->getIdentity(), owner) { } LayerBuffer::SurfaceBuffer::~SurfaceBuffer() { unregisterBuffers(); - mOwner = 0; -} - -status_t LayerBuffer::SurfaceBuffer::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch (code) { - case REGISTER_BUFFERS: - case UNREGISTER_BUFFERS: - case CREATE_OVERLAY: - { - // codes that require permission check - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - const int self_pid = getpid(); - if (LIKELY(pid != self_pid)) { - // we're called from a different process, do the real check - if (!checkCallingPermission( - String16("android.permission.ACCESS_SURFACE_FLINGER"))) - { - const int uid = ipc->getCallingUid(); - LOGE("Permission Denial: " - "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } - } - } - } - return LayerBaseClient::Surface::onTransact(code, data, reply, flags); } -status_t LayerBuffer::SurfaceBuffer::registerBuffers(const ISurface::BufferHeap& buffers) +status_t LayerBuffer::SurfaceBuffer::registerBuffers( + const ISurface::BufferHeap& buffers) { - LayerBuffer* owner(getOwner()); - if (owner) + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) return owner->registerBuffers(buffers); return NO_INIT; } void LayerBuffer::SurfaceBuffer::postBuffer(ssize_t offset) { - LayerBuffer* owner(getOwner()); - if (owner) + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) owner->postBuffer(offset); } void LayerBuffer::SurfaceBuffer::unregisterBuffers() { - LayerBuffer* owner(getOwner()); - if (owner) + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) owner->unregisterBuffers(); } sp<OverlayRef> LayerBuffer::SurfaceBuffer::createOverlay( uint32_t w, uint32_t h, int32_t format) { sp<OverlayRef> result; - LayerBuffer* owner(getOwner()); - if (owner) + sp<LayerBuffer> owner(getOwner()); + if (owner != 0) result = owner->createOverlay(w, h, format); return result; } -void LayerBuffer::SurfaceBuffer::disown() -{ - Mutex::Autolock _l(mLock); - mOwner = 0; -} - // ============================================================================ // LayerBuffer::Buffer // ============================================================================ @@ -276,20 +234,30 @@ LayerBuffer::Buffer::Buffer(const ISurface::BufferHeap& buffers, ssize_t offset) : mBufferHeap(buffers) { NativeBuffer& src(mNativeBuffer); + src.crop.l = 0; src.crop.t = 0; src.crop.r = buffers.w; src.crop.b = buffers.h; - src.img.w = buffers.hor_stride ?: buffers.w; - src.img.h = buffers.ver_stride ?: buffers.h; - src.img.format = buffers.format; - src.img.offset = offset; - src.img.base = buffers.heap->base(); - src.img.fd = buffers.heap->heapID(); + + src.img.w = buffers.hor_stride ?: buffers.w; + src.img.h = buffers.ver_stride ?: buffers.h; + src.img.format = buffers.format; + src.img.base = (void*)(intptr_t(buffers.heap->base()) + offset); + + // FIXME: gross hack, we should never access private_handle_t from here, + // but this is needed by msm drivers + private_handle_t* hnd = new private_handle_t( + buffers.heap->heapID(), buffers.heap->getSize(), 0); + hnd->offset = offset; + src.img.handle = hnd; } LayerBuffer::Buffer::~Buffer() { + NativeBuffer& src(mNativeBuffer); + if (src.img.handle) + delete (private_handle_t*)src.img.handle; } // ============================================================================ @@ -323,8 +291,7 @@ bool LayerBuffer::Source::transformed() const { LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, const ISurface::BufferHeap& buffers) - : Source(layer), mStatus(NO_ERROR), - mBufferSize(0), mTextureName(-1U) + : Source(layer), mStatus(NO_ERROR), mBufferSize(0) { if (buffers.heap == NULL) { // this is allowed, but in this case, it is illegal to receive @@ -363,13 +330,21 @@ LayerBuffer::BufferSource::BufferSource(LayerBuffer& layer, mLayer.setNeedsBlending((info.h_alpha - info.l_alpha) > 0); mBufferSize = info.getScanlineSize(buffers.hor_stride)*buffers.ver_stride; mLayer.forceVisibilityTransaction(); - + + hw_module_t const* module; + mBlitEngine = NULL; + if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { + copybit_open(module, &mBlitEngine); + } } LayerBuffer::BufferSource::~BufferSource() { - if (mTextureName != -1U) { - LayerBase::deletedTextures.add(mTextureName); + if (mTexture.name != -1U) { + glDeleteTextures(1, &mTexture.name); + } + if (mBlitEngine) { + copybit_close(mBlitEngine); } } @@ -427,19 +402,19 @@ bool LayerBuffer::BufferSource::transformed() const void LayerBuffer::BufferSource::onDraw(const Region& clip) const { - sp<Buffer> buffer(getBuffer()); - if (UNLIKELY(buffer == 0)) { + sp<Buffer> ourBuffer(getBuffer()); + if (UNLIKELY(ourBuffer == 0)) { // nothing to do, we don't have a buffer mLayer.clearWithOpenGL(clip); return; } status_t err = NO_ERROR; - NativeBuffer src(buffer->getBuffer()); + NativeBuffer src(ourBuffer->getBuffer()); const Rect& transformedBounds = mLayer.getTransformedBounds(); - const int can_use_copybit = mLayer.canUseCopybit(); + copybit_device_t* copybit = mBlitEngine; - if (can_use_copybit) { + if (copybit) { const int src_width = src.crop.r - src.crop.l; const int src_height = src.crop.b - src.crop.t; int W = transformedBounds.width(); @@ -448,89 +423,108 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const int t(W); W=H; H=t; } - /* With LayerBuffer, it is likely that we'll have to rescale the - * surface, because this is often used for video playback or - * camera-preview. Since we want these operation as fast as possible - * we make sure we can use the 2D H/W even if it doesn't support - * the requested scale factor, in which case we perform the scaling - * in several passes. */ - - copybit_device_t* copybit = mLayer.mFlinger->getBlitEngine(); - const float min = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT); - const float mag = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT); - - float xscale = 1.0f; - if (src_width > W*min) xscale = 1.0f / min; - else if (src_width*mag < W) xscale = mag; - - float yscale = 1.0f; - if (src_height > H*min) yscale = 1.0f / min; - else if (src_height*mag < H) yscale = mag; - - if (UNLIKELY(xscale!=1.0f || yscale!=1.0f)) { - if (UNLIKELY(mTemporaryDealer == 0)) { - // allocate a memory-dealer for this the first time - mTemporaryDealer = mLayer.mFlinger->getSurfaceHeapManager() - ->createHeap(ISurfaceComposer::eHardware); - mTempBitmap.init(mTemporaryDealer); +#ifdef EGL_ANDROID_get_render_buffer + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLSurface draw = eglGetCurrentSurface(EGL_DRAW); + EGLClientBuffer clientBuf = eglGetRenderBufferANDROID(dpy, draw); + android_native_buffer_t* nb = (android_native_buffer_t*)clientBuf; + if (nb == 0) { + err = BAD_VALUE; + } else { + copybit_image_t dst; + dst.w = nb->width; + dst.h = nb->height; + dst.format = nb->format; + dst.base = NULL; // unused by copybit on msm7k + dst.handle = (native_handle_t *)nb->handle; + + /* With LayerBuffer, it is likely that we'll have to rescale the + * surface, because this is often used for video playback or + * camera-preview. Since we want these operation as fast as possible + * we make sure we can use the 2D H/W even if it doesn't support + * the requested scale factor, in which case we perform the scaling + * in several passes. */ + + const float min = copybit->get(copybit, COPYBIT_MINIFICATION_LIMIT); + const float mag = copybit->get(copybit, COPYBIT_MAGNIFICATION_LIMIT); + + float xscale = 1.0f; + if (src_width > W*min) xscale = 1.0f / min; + else if (src_width*mag < W) xscale = mag; + + float yscale = 1.0f; + if (src_height > H*min) yscale = 1.0f / min; + else if (src_height*mag < H) yscale = mag; + + if (UNLIKELY(xscale!=1.0f || yscale!=1.0f)) { + const int tmp_w = floorf(src_width * xscale); + const int tmp_h = floorf(src_height * yscale); + + if (mTempBitmap==0 || + mTempBitmap->getWidth() < tmp_w || + mTempBitmap->getHeight() < tmp_h) { + mTempBitmap.clear(); + mTempBitmap = new android::Buffer(tmp_w, tmp_h, src.img.format); + err = mTempBitmap->initCheck(); + } + + if (LIKELY(err == NO_ERROR)) { + NativeBuffer tmp; + tmp.img.w = tmp_w; + tmp.img.h = tmp_h; + tmp.img.format = src.img.format; + tmp.img.handle = (native_handle_t*)mTempBitmap->getNativeBuffer()->handle; + tmp.crop.l = 0; + tmp.crop.t = 0; + tmp.crop.r = tmp.img.w; + tmp.crop.b = tmp.img.h; + + region_iterator tmp_it(Region(Rect(tmp.crop.r, tmp.crop.b))); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); + err = copybit->stretch(copybit, + &tmp.img, &src.img, &tmp.crop, &src.crop, &tmp_it); + src = tmp; + } } - const int tmp_w = floorf(src_width * xscale); - const int tmp_h = floorf(src_height * yscale); - err = mTempBitmap.setBits(tmp_w, tmp_h, 1, src.img.format); - - if (LIKELY(err == NO_ERROR)) { - NativeBuffer tmp; - mTempBitmap.getBitmapSurface(&tmp.img); - tmp.crop.l = 0; - tmp.crop.t = 0; - tmp.crop.r = tmp.img.w; - tmp.crop.b = tmp.img.h; - - region_iterator tmp_it(Region(Rect(tmp.crop.r, tmp.crop.b))); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); - err = copybit->stretch(copybit, - &tmp.img, &src.img, &tmp.crop, &src.crop, &tmp_it); - src = tmp; + const Rect& transformedBounds = mLayer.getTransformedBounds(); + const copybit_rect_t& drect = + reinterpret_cast<const copybit_rect_t&>(transformedBounds); + const State& s(mLayer.drawingState()); + region_iterator it(clip); + + // pick the right orientation for this buffer + int orientation = mLayer.getOrientation(); + if (UNLIKELY(mBufferHeap.transform)) { + Transform rot90; + GraphicPlane::orientationToTransfrom( + ISurfaceComposer::eOrientation90, 0, 0, &rot90); + const Transform& planeTransform(mLayer.graphicPlane(0).transform()); + const Layer::State& s(mLayer.drawingState()); + Transform tr(planeTransform * s.transform * rot90); + orientation = tr.getOrientation(); } - } - const DisplayHardware& hw(mLayer.graphicPlane(0).displayHardware()); - copybit_image_t dst; - hw.getDisplaySurface(&dst); - const copybit_rect_t& drect - = reinterpret_cast<const copybit_rect_t&>(transformedBounds); - const State& s(mLayer.drawingState()); - region_iterator it(clip); - - // pick the right orientation for this buffer - int orientation = mLayer.getOrientation(); - if (UNLIKELY(mBufferHeap.transform)) { - Transform rot90; - GraphicPlane::orientationToTransfrom( - ISurfaceComposer::eOrientation90, 0, 0, &rot90); - const Transform& planeTransform(mLayer.graphicPlane(0).transform()); - const Layer::State& s(mLayer.drawingState()); - Transform tr(planeTransform * s.transform * rot90); - orientation = tr.getOrientation(); - } - - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, orientation); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); - - err = copybit->stretch(copybit, - &dst, &src.img, &drect, &src.crop, &it); - if (err != NO_ERROR) { - LOGE("copybit failed (%s)", strerror(err)); + copybit->set_parameter(copybit, COPYBIT_TRANSFORM, orientation); + copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); + copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); + + err = copybit->stretch(copybit, + &dst, &src.img, &drect, &src.crop, &it); + if (err != NO_ERROR) { + LOGE("copybit failed (%s)", strerror(err)); + } } } - - if (!can_use_copybit || err) { - if (UNLIKELY(mTextureName == -1LU)) { - mTextureName = mLayer.createTexture(); +#endif + + if (!copybit || err) + { + // OpenGL fall-back + if (UNLIKELY(mTexture.name == -1LU)) { + mTexture.name = mLayer.createTexture(); } GLuint w = 0; GLuint h = 0; @@ -541,10 +535,11 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const t.stride = src.img.w; t.vstride= src.img.h; t.format = src.img.format; - t.data = (GGLubyte*)(intptr_t(src.img.base) + src.img.offset); + t.data = (GGLubyte*)src.img.base; const Region dirty(Rect(t.width, t.height)); - mLayer.loadTexture(dirty, mTextureName, t, w, h); - mLayer.drawWithOpenGL(clip, mTextureName, t, mBufferHeap.transform); + mLayer.loadTexture(&mTexture, mTexture.name, dirty, t); + mTexture.transform = mBufferHeap.transform; + mLayer.drawWithOpenGL(clip, mTexture); } } @@ -580,6 +575,7 @@ LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer, mFormat = overlay->format; mWidthStride = overlay->w_stride; mHeightStride = overlay->h_stride; + mInitialized = false; mOverlayHandle = overlay->getHandleRef(overlay); @@ -599,6 +595,11 @@ LayerBuffer::OverlaySource::~OverlaySource() } } +void LayerBuffer::OverlaySource::onDraw(const Region& clip) const +{ + mLayer.clearWithOpenGL(clip); +} + void LayerBuffer::OverlaySource::onTransaction(uint32_t flags) { const Layer::State& front(mLayer.drawingState()); @@ -614,8 +615,9 @@ void LayerBuffer::OverlaySource::onVisibilityResolved( // this code-path must be as tight as possible, it's called each time // the screen is composited. if (UNLIKELY(mOverlay != 0)) { - if (mVisibilityChanged) { + if (mVisibilityChanged || !mInitialized) { mVisibilityChanged = false; + mInitialized = true; const Rect& bounds = mLayer.getTransformedBounds(); int x = bounds.left; int y = bounds.top; @@ -627,8 +629,9 @@ void LayerBuffer::OverlaySource::onVisibilityResolved( if (mOverlay) { overlay_control_device_t* overlay_dev = mOverlayDevice; overlay_dev->setPosition(overlay_dev, mOverlay, x,y,w,h); - overlay_dev->setParameter(overlay_dev, mOverlay, + overlay_dev->setParameter(overlay_dev, mOverlay, OVERLAY_TRANSFORM, mLayer.getOrientation()); + overlay_dev->commit(overlay_dev, mOverlay); } } } diff --git a/libs/surfaceflinger/LayerBuffer.h b/libs/surfaceflinger/LayerBuffer.h index 2dc77f1..8057219 100644 --- a/libs/surfaceflinger/LayerBuffer.h +++ b/libs/surfaceflinger/LayerBuffer.h @@ -20,18 +20,18 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/IMemory.h> +#include <binder/IMemory.h> #include <private/ui/LayerState.h> -#include <EGL/eglnatives.h> #include "LayerBase.h" #include "LayerBitmap.h" +struct copybit_device_t; + namespace android { // --------------------------------------------------------------------------- -class MemoryDealer; class Region; class OverlayRef; @@ -51,7 +51,6 @@ class LayerBuffer : public LayerBaseClient LayerBuffer& mLayer; }; - public: static const uint32_t typeInfo; static const char* const typeID; @@ -59,12 +58,14 @@ public: virtual uint32_t getTypeInfo() const { return typeInfo; } LayerBuffer(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i); + const sp<Client>& client, int32_t i); virtual ~LayerBuffer(); + virtual void onFirstRef(); virtual bool needsBlending() const; - virtual sp<LayerBaseClient::Surface> getSurface() const; + virtual sp<LayerBaseClient::Surface> createSurface() const; + virtual status_t ditch(); virtual void onDraw(const Region& clip) const; virtual uint32_t doTransaction(uint32_t flags); virtual void unlockPageFlip(const Transform& planeTransform, Region& outDirtyRegion); @@ -121,14 +122,14 @@ private: virtual void unregisterBuffers(); virtual bool transformed() const; private: - mutable Mutex mLock; - sp<Buffer> mBuffer; - status_t mStatus; - ISurface::BufferHeap mBufferHeap; - size_t mBufferSize; - mutable sp<MemoryDealer> mTemporaryDealer; - mutable LayerBitmap mTempBitmap; - mutable GLuint mTextureName; + mutable Mutex mLock; + sp<Buffer> mBuffer; + status_t mStatus; + ISurface::BufferHeap mBufferHeap; + size_t mBufferSize; + mutable sp<android::Buffer> mTempBitmap; + mutable LayerBase::Texture mTexture; + copybit_device_t* mBlitEngine; }; class OverlaySource : public Source { @@ -137,6 +138,7 @@ private: sp<OverlayRef>* overlayRef, uint32_t w, uint32_t h, int32_t format); virtual ~OverlaySource(); + virtual void onDraw(const Region& clip) const; virtual void onTransaction(uint32_t flags); virtual void onVisibilityResolved(const Transform& planeTransform); private: @@ -173,40 +175,34 @@ private: int32_t mWidthStride; int32_t mHeightStride; mutable Mutex mLock; + bool mInitialized; }; class SurfaceBuffer : public LayerBaseClient::Surface { public: - SurfaceBuffer(SurfaceID id, LayerBuffer* owner); + SurfaceBuffer(const sp<SurfaceFlinger>& flinger, + SurfaceID id, const sp<LayerBuffer>& owner); virtual ~SurfaceBuffer(); - virtual status_t onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); + virtual status_t registerBuffers(const ISurface::BufferHeap& buffers); virtual void postBuffer(ssize_t offset); virtual void unregisterBuffers(); + virtual sp<OverlayRef> createOverlay( uint32_t w, uint32_t h, int32_t format); - void disown(); private: - LayerBuffer* getOwner() const { - Mutex::Autolock _l(mLock); - return mOwner; + sp<LayerBuffer> getOwner() const { + return static_cast<LayerBuffer*>(Surface::getOwner().get()); } - mutable Mutex mLock; - LayerBuffer* mOwner; }; - - friend class SurfaceFlinger; - sp<SurfaceBuffer> getClientSurface() const; - + mutable Mutex mLock; sp<Source> mSource; - + sp<Surface> mSurface; bool mInvalidate; bool mNeedsBlending; - mutable wp<SurfaceBuffer> mClientSurface; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerDim.cpp b/libs/surfaceflinger/LayerDim.cpp index 0c347cc..8e9df9c 100644 --- a/libs/surfaceflinger/LayerDim.cpp +++ b/libs/surfaceflinger/LayerDim.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdint.h> #include <sys/types.h> @@ -25,7 +23,6 @@ #include "LayerDim.h" #include "SurfaceFlinger.h" -#include "VRamHeap.h" #include "DisplayHardware/DisplayHardware.h" namespace android { @@ -33,27 +30,74 @@ namespace android { const uint32_t LayerDim::typeInfo = LayerBaseClient::typeInfo | 0x10; const char* const LayerDim::typeID = "LayerDim"; -sp<MemoryDealer> LayerDim::mDimmerDealer; -LayerBitmap LayerDim::mDimmerBitmap; + +bool LayerDim::sUseTexture; +GLuint LayerDim::sTexId; +EGLImageKHR LayerDim::sImage; +int32_t LayerDim::sWidth; +int32_t LayerDim::sHeight; // --------------------------------------------------------------------------- LayerDim::LayerDim(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i) - : LayerBaseClient(flinger, display, client, i) + const sp<Client>& client, int32_t i) + : LayerBaseClient(flinger, display, client, i) { } void LayerDim::initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h) { - // must only be called once. - mDimmerDealer = flinger->getSurfaceHeapManager() - ->createHeap(ISurfaceComposer::eHardware); - if (mDimmerDealer != 0) { - mDimmerBitmap.init(mDimmerDealer); - mDimmerBitmap.setBits(w, h, 1, PIXEL_FORMAT_RGB_565); - mDimmerBitmap.clear(); + sTexId = -1; + sImage = EGL_NO_IMAGE_KHR; + sWidth = w; + sHeight = h; + sUseTexture = false; + +#ifdef DIM_WITH_TEXTURE + +#warning "using a texture to implement LayerDim" + + /* On some h/w like msm7K, it is faster to use a texture because the + * software renderer will defer to copybit, for this to work we need to + * use an EGLImage texture so copybit can actually make use of it. + * This burns a full-screen worth of graphic memory. + */ + + const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); + uint32_t flags = hw.getFlags(); + + if (LIKELY(flags & DisplayHardware::DIRECT_TEXTURE)) { + // TODO: api to pass the usage flags + sp<Buffer> buffer = new Buffer(w, h, PIXEL_FORMAT_RGB_565); + android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); + + glGenTextures(1, &sTexId); + glBindTexture(GL_TEXTURE_2D, sTexId); + + EGLDisplay dpy = eglGetCurrentDisplay(); + sImage = eglCreateImageKHR(dpy, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer)clientBuf, 0); + if (sImage == EGL_NO_IMAGE_KHR) { + LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); + return; + } + + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)sImage); + GLint error = glGetError(); + if (error != GL_NO_ERROR) { + eglDestroyImageKHR(dpy, sImage); + LOGE("glEGLImageTargetTexture2DOES() failed. err=0x%4x", error); + return; + } + + // initialize the texture with zeros + GGLSurface t; + buffer->lock(&t, GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN); + memset(t.data, 0, t.stride * t.height * 2); + buffer->unlock(); + sUseTexture = true; } +#endif } LayerDim::~LayerDim() @@ -63,49 +107,56 @@ LayerDim::~LayerDim() void LayerDim::onDraw(const Region& clip) const { const State& s(drawingState()); - - Region::iterator iterator(clip); - if (s.alpha>0 && iterator) { + Region::const_iterator it = clip.begin(); + Region::const_iterator const end = clip.end(); + if (s.alpha>0 && (it != end)) { const DisplayHardware& hw(graphicPlane(0).displayHardware()); - - status_t err = NO_ERROR; - const int can_use_copybit = canUseCopybit(); - if (can_use_copybit) { - // StopWatch watch("copybit"); - copybit_image_t dst; - hw.getDisplaySurface(&dst); - const copybit_rect_t& drect - = reinterpret_cast<const copybit_rect_t&>(mTransformedBounds); - - copybit_image_t src; - mDimmerBitmap.getBitmapSurface(&src); - const copybit_rect_t& srect(drect); - - copybit_device_t* copybit = mFlinger->getBlitEngine(); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, s.alpha); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); - region_iterator it(clip); - err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); + const GGLfixed alpha = (s.alpha << 16)/255; + const uint32_t fbHeight = hw.getHeight(); + glDisable(GL_DITHER); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glColor4x(0, 0, 0, alpha); + +#ifdef DIM_WITH_TEXTURE + if (sUseTexture) { + glBindTexture(GL_TEXTURE_2D, sTexId); + glEnable(GL_TEXTURE_2D); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + const GLshort texCoords[4][2] = { + { 0, 0 }, + { 0, 1 }, + { 1, 1 }, + { 1, 0 } + }; + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glTexCoordPointer(2, GL_SHORT, 0, texCoords); + } else +#endif + { + glDisable(GL_TEXTURE_2D); } - if (!can_use_copybit || err) { - const GGLfixed alpha = (s.alpha << 16)/255; - const uint32_t fbHeight = hw.getHeight(); - glDisable(GL_TEXTURE_2D); - glDisable(GL_DITHER); - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glColor4x(0, 0, 0, alpha); - glVertexPointer(2, GL_FIXED, 0, mVertices); - Rect r; - while (iterator.iterate(&r)) { - const GLint sy = fbHeight - (r.top + r.height()); - glScissor(r.left, sy, r.width(), r.height()); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - } + GLshort w = sWidth; + GLshort h = sHeight; + const GLshort vertices[4][2] = { + { 0, 0 }, + { 0, h }, + { w, h }, + { w, 0 } + }; + glVertexPointer(2, GL_SHORT, 0, vertices); + + while (it != end) { + const Rect& r = *it++; + const GLint sy = fbHeight - (r.top + r.height()); + glScissor(r.left, sy, r.width(), r.height()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } } + glDisableClientState(GL_TEXTURE_COORD_ARRAY); } // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerDim.h b/libs/surfaceflinger/LayerDim.h index 3e37a47..33bd49d 100644 --- a/libs/surfaceflinger/LayerDim.h +++ b/libs/surfaceflinger/LayerDim.h @@ -20,6 +20,9 @@ #include <stdint.h> #include <sys/types.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + #include "LayerBase.h" #include "LayerBitmap.h" @@ -29,6 +32,11 @@ namespace android { class LayerDim : public LayerBaseClient { + static bool sUseTexture; + static GLuint sTexId; + static EGLImageKHR sImage; + static int32_t sWidth; + static int32_t sHeight; public: static const uint32_t typeInfo; static const char* const typeID; @@ -36,7 +44,7 @@ public: virtual uint32_t getTypeInfo() const { return typeInfo; } LayerDim(SurfaceFlinger* flinger, DisplayID display, - Client* client, int32_t i); + const sp<Client>& client, int32_t i); virtual ~LayerDim(); virtual void onDraw(const Region& clip) const; @@ -44,10 +52,6 @@ public: virtual bool isSecure() const { return false; } static void initDimmer(SurfaceFlinger* flinger, uint32_t w, uint32_t h); - -private: - static sp<MemoryDealer> mDimmerDealer; - static LayerBitmap mDimmerBitmap; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/LayerOrientationAnim.cpp b/libs/surfaceflinger/LayerOrientationAnim.cpp deleted file mode 100644 index 79e5328..0000000 --- a/libs/surfaceflinger/LayerOrientationAnim.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "SurfaceFlinger" - -#include <stdlib.h> -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Errors.h> -#include <utils/Log.h> -#include <utils/StopWatch.h> - -#include <core/SkBitmap.h> - -#include <ui/EGLDisplaySurface.h> - -#include "BlurFilter.h" -#include "LayerBase.h" -#include "LayerOrientationAnim.h" -#include "SurfaceFlinger.h" -#include "DisplayHardware/DisplayHardware.h" -#include "OrientationAnimation.h" - -namespace android { -// --------------------------------------------------------------------------- - -const uint32_t LayerOrientationAnim::typeInfo = LayerBase::typeInfo | 0x80; -const char* const LayerOrientationAnim::typeID = "LayerOrientationAnim"; - -// --------------------------------------------------------------------------- - -// Animation... -const float DURATION = ms2ns(200); -const float BOUNCES_PER_SECOND = 0.5f; -const float DIM_TARGET = 0.40f; -#define INTERPOLATED_TIME(_t) (_t) - -// --------------------------------------------------------------------------- - -LayerOrientationAnim::LayerOrientationAnim( - SurfaceFlinger* flinger, DisplayID display, - OrientationAnimation* anim, - const LayerBitmap& bitmapIn, - const LayerBitmap& bitmapOut) - : LayerOrientationAnimBase(flinger, display), mAnim(anim), - mBitmapIn(bitmapIn), mBitmapOut(bitmapOut), - mTextureName(-1), mTextureNameIn(-1) -{ - // blur that texture. - mOrientationCompleted = false; - mNeedsBlending = false; -} - -LayerOrientationAnim::~LayerOrientationAnim() -{ - if (mTextureName != -1U) { - LayerBase::deletedTextures.add(mTextureName); - } - if (mTextureNameIn != -1U) { - LayerBase::deletedTextures.add(mTextureNameIn); - } -} - -bool LayerOrientationAnim::needsBlending() const -{ - return mNeedsBlending; -} - -Point LayerOrientationAnim::getPhysicalSize() const -{ - const GraphicPlane& plane(graphicPlane(0)); - const DisplayHardware& hw(plane.displayHardware()); - return Point(hw.getWidth(), hw.getHeight()); -} - -void LayerOrientationAnim::validateVisibility(const Transform&) -{ - const Layer::State& s(drawingState()); - const Transform tr(s.transform); - const Point size(getPhysicalSize()); - uint32_t w = size.x; - uint32_t h = size.y; - mTransformedBounds = tr.makeBounds(w, h); - mLeft = tr.tx(); - mTop = tr.ty(); - transparentRegionScreen.clear(); - mTransformed = true; - mCanUseCopyBit = false; - copybit_device_t* copybit = mFlinger->getBlitEngine(); - if (copybit) { - mCanUseCopyBit = true; - } -} - -void LayerOrientationAnim::onOrientationCompleted() -{ - mAnim->onAnimationFinished(); -} - -void LayerOrientationAnim::onDraw(const Region& clip) const -{ - float alphaIn = DIM_TARGET; - - // clear screen - // TODO: with update on demand, we may be able - // to not erase the screen at all during the animation - if (!mOrientationCompleted) { - glDisable(GL_BLEND); - glDisable(GL_DITHER); - glDisable(GL_SCISSOR_TEST); - glClearColor(0,0,0,0); - glClear(GL_COLOR_BUFFER_BIT); - } - - copybit_image_t dst; - const GraphicPlane& plane(graphicPlane(0)); - const DisplayHardware& hw(plane.displayHardware()); - hw.getDisplaySurface(&dst); - - copybit_image_t src; - mBitmapIn.getBitmapSurface(&src); - - copybit_image_t srcOut; - mBitmapOut.getBitmapSurface(&srcOut); - - const int w = dst.w; - const int h = dst.h; - const int xc = uint32_t(dst.w-w)/2; - const int yc = uint32_t(dst.h-h)/2; - const copybit_rect_t drect = { xc, yc, xc+w, yc+h }; - const copybit_rect_t srect = { 0, 0, src.w, src.h }; - const Region reg(Rect( drect.l, drect.t, drect.r, drect.b )); - - int err = NO_ERROR; - const int can_use_copybit = canUseCopybit(); - if (can_use_copybit) { - copybit_device_t* copybit = mFlinger->getBlitEngine(); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE); - - if (alphaIn > 0) { - region_iterator it(reg); - copybit->set_parameter(copybit, COPYBIT_BLUR, COPYBIT_ENABLE); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, int(alphaIn*255)); - err = copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); - copybit->set_parameter(copybit, COPYBIT_BLUR, COPYBIT_DISABLE); - } - LOGE_IF(err != NO_ERROR, "copybit failed (%s)", strerror(err)); - } - if (!can_use_copybit || err) { - GGLSurface t; - t.version = sizeof(GGLSurface); - t.width = src.w; - t.height = src.h; - t.stride = src.w; - t.vstride= src.h; - t.format = src.format; - t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); - - Transform tr; - tr.set(xc, yc); - - // FIXME: we should not access mVertices and mDrawingState like that, - // but since we control the animation, we know it's going to work okay. - // eventually we'd need a more formal way of doing things like this. - LayerOrientationAnim& self(const_cast<LayerOrientationAnim&>(*this)); - tr.transform(self.mVertices[0], 0, 0); - tr.transform(self.mVertices[1], 0, src.h); - tr.transform(self.mVertices[2], src.w, src.h); - tr.transform(self.mVertices[3], src.w, 0); - if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { - // Too slow to do this in software - self.mDrawingState.flags |= ISurfaceComposer::eLayerFilter; - } - - if (alphaIn > 0.0f) { - t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); - if (UNLIKELY(mTextureNameIn == -1LU)) { - mTextureNameIn = createTexture(); - GLuint w=0, h=0; - const Region dirty(Rect(t.width, t.height)); - loadTexture(dirty, mTextureNameIn, t, w, h); - } - self.mDrawingState.alpha = int(alphaIn*255); - drawWithOpenGL(reg, mTextureNameIn, t); - } - } -} - -// --------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/surfaceflinger/MessageQueue.cpp b/libs/surfaceflinger/MessageQueue.cpp new file mode 100644 index 0000000..b43d801 --- /dev/null +++ b/libs/surfaceflinger/MessageQueue.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <errno.h> +#include <sys/types.h> + +#include <utils/threads.h> +#include <utils/Timers.h> +#include <utils/Log.h> +#include <binder/IPCThreadState.h> + +#include "MessageQueue.h" + +namespace android { + +// --------------------------------------------------------------------------- + +void MessageList::insert(const sp<MessageBase>& node) +{ + LIST::iterator cur(mList.begin()); + LIST::iterator end(mList.end()); + while (cur != end) { + if (*node < **cur) { + mList.insert(cur, node); + return; + } + ++cur; + } + mList.insert(++end, node); +} + +void MessageList::remove(MessageList::LIST::iterator pos) +{ + mList.erase(pos); +} + +// --------------------------------------------------------------------------- + +MessageQueue::MessageQueue() + : mInvalidate(false) +{ + mInvalidateMessage = new MessageBase(INVALIDATE); +} + +MessageQueue::~MessageQueue() +{ +} + +MessageList::value_type MessageQueue::waitMessage(nsecs_t timeout) +{ + MessageList::value_type result; + + bool again; + do { + const nsecs_t timeoutTime = systemTime() + timeout; + while (true) { + Mutex::Autolock _l(mLock); + nsecs_t now = systemTime(); + nsecs_t nextEventTime = -1; + + // invalidate messages are always handled first + if (mInvalidate) { + mInvalidate = false; + mInvalidateMessage->when = now; + result = mInvalidateMessage; + break; + } + + LIST::iterator cur(mMessages.begin()); + if (cur != mMessages.end()) { + result = *cur; + } + + if (result != 0) { + if (result->when <= now) { + // there is a message to deliver + mMessages.remove(cur); + break; + } + if (timeout>=0 && timeoutTime < now) { + // we timed-out, return a NULL message + result = 0; + break; + } + nextEventTime = result->when; + result = 0; + } + + if (timeout >= 0 && nextEventTime > 0) { + if (nextEventTime > timeoutTime) { + nextEventTime = timeoutTime; + } + } + + if (nextEventTime >= 0) { + //LOGD("nextEventTime = %lld ms", nextEventTime); + if (nextEventTime > 0) { + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + const nsecs_t reltime = nextEventTime - systemTime(); + if (reltime > 0) { + mCondition.waitRelative(mLock, reltime); + } + } + } else { + //LOGD("going to wait"); + // we're about to wait, flush the binder command buffer + IPCThreadState::self()->flushCommands(); + mCondition.wait(mLock); + } + } + // here we're not holding the lock anymore + + if (result == 0) + break; + + again = result->handler(); + if (again) { + // the message has been processed. release our reference to it + // without holding the lock. + result = 0; + } + + } while (again); + + return result; +} + +status_t MessageQueue::postMessage( + const MessageList::value_type& message, nsecs_t relTime, uint32_t flags) +{ + return queueMessage(message, relTime, flags); +} + +status_t MessageQueue::invalidate() { + Mutex::Autolock _l(mLock); + mInvalidate = true; + mCondition.signal(); + return NO_ERROR; +} + +status_t MessageQueue::queueMessage( + const MessageList::value_type& message, nsecs_t relTime, uint32_t flags) +{ + Mutex::Autolock _l(mLock); + message->when = systemTime() + relTime; + mMessages.insert(message); + + //LOGD("MessageQueue::queueMessage time = %lld ms", message->when); + //dumpLocked(message); + + mCondition.signal(); + return NO_ERROR; +} + +void MessageQueue::dump(const MessageList::value_type& message) +{ + Mutex::Autolock _l(mLock); + dumpLocked(message); +} + +void MessageQueue::dumpLocked(const MessageList::value_type& message) +{ + LIST::const_iterator cur(mMessages.begin()); + LIST::const_iterator end(mMessages.end()); + int c = 0; + while (cur != end) { + const char tick = (*cur == message) ? '>' : ' '; + LOGD("%c %d: msg{.what=%08x, when=%lld}", + tick, c, (*cur)->what, (*cur)->when); + ++cur; + c++; + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/MessageQueue.h b/libs/surfaceflinger/MessageQueue.h new file mode 100644 index 0000000..dc8138d --- /dev/null +++ b/libs/surfaceflinger/MessageQueue.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_MESSAGE_QUEUE_H +#define ANDROID_MESSAGE_QUEUE_H + +#include <stdint.h> +#include <errno.h> +#include <sys/types.h> + +#include <utils/threads.h> +#include <utils/Timers.h> +#include <utils/List.h> + + +namespace android { + +// --------------------------------------------------------------------------- + +class MessageBase; + +class MessageList +{ + List< sp<MessageBase> > mList; + typedef List< sp<MessageBase> > LIST; +public: + typedef sp<MessageBase> value_type; + inline LIST::iterator begin() { return mList.begin(); } + inline LIST::const_iterator begin() const { return mList.begin(); } + inline LIST::iterator end() { return mList.end(); } + inline LIST::const_iterator end() const { return mList.end(); } + inline bool isEmpty() const { return mList.empty(); } + void insert(const sp<MessageBase>& node); + void remove(LIST::iterator pos); +}; + +// ============================================================================ + +class MessageBase : + public LightRefBase<MessageBase> +{ +public: + nsecs_t when; + uint32_t what; + int32_t arg0; + + MessageBase() : when(0), what(0), arg0(0) { } + MessageBase(uint32_t what, int32_t arg0=0) + : when(0), what(what), arg0(arg0) { } + + // return true if message has a handler + virtual bool handler() { return false; } + +protected: + virtual ~MessageBase() { } + +private: + friend class LightRefBase<MessageBase>; +}; + +inline bool operator < (const MessageBase& lhs, const MessageBase& rhs) { + return lhs.when < rhs.when; +} + +// --------------------------------------------------------------------------- + +class MessageQueue +{ + typedef List< sp<MessageBase> > LIST; +public: + + // this is a work-around the multichar constant warning. A macro would + // work too, but would pollute the namespace. + template <int a, int b, int c, int d> + struct WHAT { + static const uint32_t Value = + (uint32_t(a&0xff)<<24)|(uint32_t(b&0xff)<<16)| + (uint32_t(c&0xff)<<8)|uint32_t(d&0xff); + }; + + MessageQueue(); + ~MessageQueue(); + + // pre-defined messages + enum { + INVALIDATE = WHAT<'_','p','d','t'>::Value + }; + + MessageList::value_type waitMessage(nsecs_t timeout = -1); + + status_t postMessage(const MessageList::value_type& message, + nsecs_t reltime=0, uint32_t flags = 0); + + status_t invalidate(); + + void dump(const MessageList::value_type& message); + +private: + status_t queueMessage(const MessageList::value_type& message, + nsecs_t reltime, uint32_t flags); + void dumpLocked(const MessageList::value_type& message); + + Mutex mLock; + Condition mCondition; + MessageList mMessages; + bool mInvalidate; + MessageList::value_type mInvalidateMessage; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif /* ANDROID_MESSAGE_QUEUE_H */ diff --git a/libs/surfaceflinger/SurfaceFlinger.cpp b/libs/surfaceflinger/SurfaceFlinger.cpp index fb25663..7a7574f 100644 --- a/libs/surfaceflinger/SurfaceFlinger.cpp +++ b/libs/surfaceflinger/SurfaceFlinger.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdio.h> #include <stdint.h> @@ -23,6 +21,7 @@ #include <fcntl.h> #include <errno.h> #include <math.h> +#include <limits.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> @@ -30,36 +29,30 @@ #include <cutils/log.h> #include <cutils/properties.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> -#include <utils/MemoryDealer.h> -#include <utils/MemoryBase.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/MemoryHeapBase.h> + #include <utils/String8.h> #include <utils/String16.h> #include <utils/StopWatch.h> #include <ui/PixelFormat.h> #include <ui/DisplayInfo.h> -#include <ui/EGLDisplaySurface.h> #include <pixelflinger/pixelflinger.h> #include <GLES/gl.h> #include "clz.h" -#include "CPUGauge.h" +#include "BufferAllocator.h" #include "Layer.h" #include "LayerBlur.h" #include "LayerBuffer.h" #include "LayerDim.h" #include "LayerBitmap.h" -#include "LayerOrientationAnim.h" -#include "OrientationAnimation.h" #include "SurfaceFlinger.h" -#include "VRamHeap.h" #include "DisplayHardware/DisplayHardware.h" -#include "GPUHardware/GPUHardware.h" - /* ideally AID_GRAPHICS would be in a semi-public header * or there would be a way to map a user/group name to its id @@ -93,30 +86,30 @@ SurfaceFlinger::LayerVector::LayerVector(const SurfaceFlinger::LayerVector& rhs) } ssize_t SurfaceFlinger::LayerVector::indexOf( - LayerBase* key, size_t guess) const + const sp<LayerBase>& key, size_t guess) const { if (guess<size() && lookup.keyAt(guess) == key) return guess; const ssize_t i = lookup.indexOfKey(key); if (i>=0) { const size_t idx = lookup.valueAt(i); - LOG_ASSERT(layers[idx]==key, + LOGE_IF(layers[idx]!=key, "LayerVector[%p]: layers[%d]=%p, key=%p", - this, int(idx), layers[idx], key); + this, int(idx), layers[idx].get(), key.get()); return idx; } return i; } ssize_t SurfaceFlinger::LayerVector::add( - LayerBase* layer, - Vector<LayerBase*>::compar_t cmp) + const sp<LayerBase>& layer, + Vector< sp<LayerBase> >::compar_t cmp) { size_t count = layers.size(); ssize_t l = 0; ssize_t h = count-1; ssize_t mid; - LayerBase* const* a = layers.array(); + sp<LayerBase> const* a = layers.array(); while (l <= h) { mid = l + (h - l)/2; const int c = cmp(a+mid, &layer); @@ -139,14 +132,14 @@ ssize_t SurfaceFlinger::LayerVector::add( return order; } -ssize_t SurfaceFlinger::LayerVector::remove(LayerBase* layer) +ssize_t SurfaceFlinger::LayerVector::remove(const sp<LayerBase>& layer) { const ssize_t keyIndex = lookup.indexOfKey(layer); if (keyIndex >= 0) { const size_t index = lookup.valueAt(keyIndex); - LOG_ASSERT(layers[index]==layer, + LOGE_IF(layers[index]!=layer, "LayerVector[%p]: layers[%u]=%p, layer=%p", - this, int(index), layers[index], layer); + this, int(index), layers[index].get(), layer.get()); layers.removeItemsAt(index); lookup.removeItemsAt(keyIndex); const size_t count = lookup.size(); @@ -161,8 +154,8 @@ ssize_t SurfaceFlinger::LayerVector::remove(LayerBase* layer) } ssize_t SurfaceFlinger::LayerVector::reorder( - LayerBase* layer, - Vector<LayerBase*>::compar_t cmp) + const sp<LayerBase>& layer, + Vector< sp<LayerBase> >::compar_t cmp) { // XXX: it's a little lame. but oh well... ssize_t err = remove(layer); @@ -180,7 +173,11 @@ SurfaceFlinger::SurfaceFlinger() : BnSurfaceComposer(), Thread(false), mTransactionFlags(0), mTransactionCount(0), + mLayersRemoved(false), mBootTime(systemTime()), + mHardwareTest("android.permission.HARDWARE_TEST"), + mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"), + mDump("android.permission.DUMP"), mLastScheduledBroadcast(NULL), mVisibleRegionsDirty(false), mDeferReleaseConsole(false), @@ -188,11 +185,7 @@ SurfaceFlinger::SurfaceFlinger() mFreezeCount(0), mFreezeDisplayTime(0), mDebugRegion(0), - mDebugCpu(0), - mDebugFps(0), mDebugBackground(0), - mSyncObject(), - mDeplayedTransactionPending(0), mConsoleSignals(0), mSecureFrameBuffer(0) { @@ -207,28 +200,16 @@ void SurfaceFlinger::init() char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.showupdates", value, "0"); mDebugRegion = atoi(value); - property_get("debug.sf.showcpu", value, "0"); - mDebugCpu = atoi(value); property_get("debug.sf.showbackground", value, "0"); mDebugBackground = atoi(value); - property_get("debug.sf.showfps", value, "0"); - mDebugFps = atoi(value); LOGI_IF(mDebugRegion, "showupdates enabled"); - LOGI_IF(mDebugCpu, "showcpu enabled"); LOGI_IF(mDebugBackground, "showbackground enabled"); - LOGI_IF(mDebugFps, "showfps enabled"); } SurfaceFlinger::~SurfaceFlinger() { glDeleteTextures(1, &mWormholeTexName); - delete mOrientationAnimation; -} - -copybit_device_t* SurfaceFlinger::getBlitEngine() const -{ - return graphicPlane(0).displayHardware().getBlitEngine(); } overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const @@ -236,29 +217,9 @@ overlay_control_device_t* SurfaceFlinger::getOverlayEngine() const return graphicPlane(0).displayHardware().getOverlayEngine(); } -sp<IMemory> SurfaceFlinger::getCblk() const +sp<IMemoryHeap> SurfaceFlinger::getCblk() const { - return mServerCblkMemory; -} - -status_t SurfaceFlinger::requestGPU(const sp<IGPUCallback>& callback, - gpu_info_t* gpu) -{ - if (mGPU == 0) - return INVALID_OPERATION; - - IPCThreadState* ipc = IPCThreadState::self(); - const int pid = ipc->getCallingPid(); - status_t err = mGPU->request(pid, callback, gpu); - return err; -} - -status_t SurfaceFlinger::revokeGPU() -{ - if (mGPU == 0) - return INVALID_OPERATION; - - return mGPU->friendlyRevoke(); + return mServerHeap; } sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection() @@ -266,33 +227,34 @@ sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection() Mutex::Autolock _l(mStateLock); uint32_t token = mTokens.acquire(); - Client* client = new Client(token, this); - if ((client == 0) || (client->ctrlblk == 0)) { + sp<Client> client = new Client(token, this); + if (client->ctrlblk == 0) { mTokens.release(token); return 0; } status_t err = mClientsMap.add(token, client); if (err < 0) { - delete client; mTokens.release(token); return 0; } sp<BClient> bclient = - new BClient(this, token, client->controlBlockMemory()); + new BClient(this, token, client->getControlBlockMemory()); return bclient; } void SurfaceFlinger::destroyConnection(ClientID cid) { Mutex::Autolock _l(mStateLock); - Client* const client = mClientsMap.valueFor(cid); - if (client) { + sp<Client> client = mClientsMap.valueFor(cid); + if (client != 0) { // free all the layers this client owns - const Vector<LayerBaseClient*>& layers = client->getLayers(); + Vector< wp<LayerBaseClient> > layers(client->getLayers()); const size_t count = layers.size(); for (size_t i=0 ; i<count ; i++) { - LayerBaseClient* const layer = layers[i]; - removeLayer_l(layer); + sp<LayerBaseClient> layer(layers[i].promote()); + if (layer != 0) { + purgatorizeLayer_l(layer); + } } // the resources associated with this client will be freed @@ -339,41 +301,15 @@ void SurfaceFlinger::onFirstRef() mReadyToRunBarrier.wait(); } - static inline uint16_t pack565(int r, int g, int b) { return (r<<11)|(g<<5)|b; } -// this is defined in libGLES_CM.so -extern ISurfaceComposer* GLES_localSurfaceManager; - status_t SurfaceFlinger::readyToRun() { LOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); - // create the shared control-block - mServerHeap = new MemoryDealer(4096, MemoryDealer::READ_ONLY); - LOGE_IF(mServerHeap==0, "can't create shared memory dealer"); - - mServerCblkMemory = mServerHeap->allocate(4096); - LOGE_IF(mServerCblkMemory==0, "can't create shared control block"); - - mServerCblk = static_cast<surface_flinger_cblk_t *>(mServerCblkMemory->pointer()); - LOGE_IF(mServerCblk==0, "can't get to shared control block's address"); - new(mServerCblk) surface_flinger_cblk_t; - - // get a reference to the GPU if we have one - mGPU = GPUFactory::getGPU(); - - // create the surface Heap manager, which manages the heaps - // (be it in RAM or VRAM) where surfaces are allocated - // We give 8 MB per client. - mSurfaceHeapManager = new SurfaceHeapManager(this, 8 << 20); - - - GLES_localSurfaceManager = static_cast<ISurfaceComposer*>(this); - // we only support one display currently int dpy = 0; @@ -384,6 +320,16 @@ status_t SurfaceFlinger::readyToRun() plane.setDisplayHardware(hw); } + // create the shared control-block + mServerHeap = new MemoryHeapBase(4096, + MemoryHeapBase::READ_ONLY, "SurfaceFlinger read-only heap"); + LOGE_IF(mServerHeap==0, "can't create shared memory dealer"); + + mServerCblk = static_cast<surface_flinger_cblk_t*>(mServerHeap->getBase()); + LOGE_IF(mServerCblk==0, "can't get to shared control block's address"); + + new(mServerCblk) surface_flinger_cblk_t; + // initialize primary screen // (other display should be initialized in the same manner, but // asynchronously, as they could come and go. None of this is supported @@ -450,13 +396,6 @@ status_t SurfaceFlinger::readyToRun() * We're now ready to accept clients... */ - mOrientationAnimation = new OrientationAnimation(this); - - // start CPU gauge display - if (mDebugCpu) - mCpuGauge = new CPUGauge(this, ms2ns(500)); - - // start boot animation property_set("ctl.start", "bootanim"); @@ -471,45 +410,53 @@ status_t SurfaceFlinger::readyToRun() void SurfaceFlinger::waitForEvent() { - // wait for something to do - if (UNLIKELY(isFrozen())) { - // wait 5 seconds - const nsecs_t freezeDisplayTimeout = ms2ns(5000); - const nsecs_t now = systemTime(); - if (mFreezeDisplayTime == 0) { - mFreezeDisplayTime = now; + while (true) { + nsecs_t timeout = -1; + if (UNLIKELY(isFrozen())) { + // wait 5 seconds + const nsecs_t freezeDisplayTimeout = ms2ns(5000); + const nsecs_t now = systemTime(); + if (mFreezeDisplayTime == 0) { + mFreezeDisplayTime = now; + } + nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime); + timeout = waitTime>0 ? waitTime : 0; } - nsecs_t waitTime = freezeDisplayTimeout - (now - mFreezeDisplayTime); - int err = (waitTime > 0) ? mSyncObject.wait(waitTime) : TIMED_OUT; - if (err != NO_ERROR) { + + MessageList::value_type msg = mEventQueue.waitMessage(timeout); + if (msg != 0) { + mFreezeDisplayTime = 0; + switch (msg->what) { + case MessageQueue::INVALIDATE: + // invalidate message, just return to the main loop + return; + } + } else { + // we timed out if (isFrozen()) { // we timed out and are still frozen LOGW("timeout expired mFreezeDisplay=%d, mFreezeCount=%d", mFreezeDisplay, mFreezeCount); mFreezeCount = 0; mFreezeDisplay = false; + return; } } - } else { - mFreezeDisplayTime = 0; - mSyncObject.wait(); } } void SurfaceFlinger::signalEvent() { - mSyncObject.open(); + mEventQueue.invalidate(); } void SurfaceFlinger::signal() const { - mSyncObject.open(); + // this is the IPC call + const_cast<SurfaceFlinger*>(this)->signalEvent(); } void SurfaceFlinger::signalDelayedEvent(nsecs_t delay) { - if (android_atomic_or(1, &mDeplayedTransactionPending) == 0) { - sp<DelayedTransaction> delayedEvent(new DelayedTransaction(this, delay)); - delayedEvent->run("DelayedeEvent", PRIORITY_URGENT_DISPLAY); - } + mEventQueue.postMessage( new MessageBase(MessageQueue::INVALIDATE), delay); } // ---------------------------------------------------------------------------- @@ -548,11 +495,6 @@ bool SurfaceFlinger::threadLoop() unlockClients(); executeScheduledBroadcasts(); - // sample the cpu gauge - if (UNLIKELY(mDebugCpu)) { - handleDebugCpu(); - } - postFramebuffer(); } else { // pretend we did the post @@ -565,28 +507,18 @@ bool SurfaceFlinger::threadLoop() void SurfaceFlinger::postFramebuffer() { - const bool skip = mOrientationAnimation->run(); - if (UNLIKELY(skip)) { + if (isFrozen()) { + // we are not allowed to draw, but pause a bit to make sure + // apps don't end up using the whole CPU, if they depend on + // surfaceflinger for synchronization. + usleep(8333); // 8.3ms ~ 120fps return; } if (!mInvalidRegion.isEmpty()) { const DisplayHardware& hw(graphicPlane(0).displayHardware()); - - if (UNLIKELY(mDebugFps)) { - debugShowFPS(); - } - hw.flip(mInvalidRegion); - mInvalidRegion.clear(); - - if (Layer::deletedTextures.size()) { - glDeleteTextures( - Layer::deletedTextures.size(), - Layer::deletedTextures.array()); - Layer::deletedTextures.clear(); - } } } @@ -601,15 +533,13 @@ void SurfaceFlinger::handleConsoleEvents() } if (mDeferReleaseConsole && hw.canDraw()) { - // We got the release signal before the aquire signal + // We got the release signal before the acquire signal mDeferReleaseConsole = false; - revokeGPU(); hw.releaseScreen(); } if (what & eConsoleReleased) { if (hw.canDraw()) { - revokeGPU(); hw.releaseScreen(); } else { mDeferReleaseConsole = true; @@ -621,9 +551,25 @@ void SurfaceFlinger::handleConsoleEvents() void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) { - Mutex::Autolock _l(mStateLock); + Vector< sp<LayerBase> > ditchedLayers; - const LayerVector& currentLayers = mCurrentState.layersSortedByZ; + { // scope for the lock + Mutex::Autolock _l(mStateLock); + handleTransactionLocked(transactionFlags, ditchedLayers); + } + + // do this without lock held + const size_t count = ditchedLayers.size(); + for (size_t i=0 ; i<count ; i++) { + //LOGD("ditching layer %p", ditchedLayers[i].get()); + ditchedLayers[i]->ditch(); + } +} + +void SurfaceFlinger::handleTransactionLocked( + uint32_t transactionFlags, Vector< sp<LayerBase> >& ditchedLayers) +{ + const LayerVector& currentLayers(mCurrentState.layersSortedByZ); const size_t count = currentLayers.size(); /* @@ -634,7 +580,7 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) const bool layersNeedTransaction = transactionFlags & eTraversalNeeded; if (layersNeedTransaction) { for (size_t i=0 ; i<count ; i++) { - LayerBase* const layer = currentLayers[i]; + const sp<LayerBase>& layer = currentLayers[i]; uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded); if (!trFlags) continue; @@ -681,7 +627,6 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) mVisibleRegionsDirty = true; mDirtyRegion.set(hw.bounds()); mFreezeDisplayTime = 0; - mOrientationAnimation->onOrientationChanged(type); } if (mCurrentState.freezeDisplay != mDrawingState.freezeDisplay) { @@ -689,17 +634,24 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) mFreezeDisplay = mCurrentState.freezeDisplay; } - // some layers might have been removed, so - // we need to update the regions they're exposing. - size_t c = mRemovedLayers.size(); - if (c) { + if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) { + // layers have been added mVisibleRegionsDirty = true; } - const LayerVector& currentLayers = mCurrentState.layersSortedByZ; - if (currentLayers.size() > mDrawingState.layersSortedByZ.size()) { - // layers have been added + // some layers might have been removed, so + // we need to update the regions they're exposing. + if (mLayersRemoved) { mVisibleRegionsDirty = true; + const LayerVector& previousLayers(mDrawingState.layersSortedByZ); + const size_t count = previousLayers.size(); + for (size_t i=0 ; i<count ; i++) { + const sp<LayerBase>& layer(previousLayers[i]); + if (currentLayers.indexOf( layer ) < 0) { + // this layer is not visible anymore + ditchedLayers.add(layer); + } + } } // get rid of all resources we don't need anymore @@ -729,7 +681,7 @@ void SurfaceFlinger::computeVisibleRegions( size_t i = currentLayers.size(); while (i--) { - LayerBase* const layer = currentLayers[i]; + const sp<LayerBase>& layer = currentLayers[i]; layer->validateVisibility(planeTransform); // start with the whole surface at its current location @@ -782,7 +734,7 @@ void SurfaceFlinger::computeVisibleRegions( // accumulate to the screen dirty region dirtyRegion.orSelf(dirty); - // updade aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer + // Update aboveOpaqueLayers/aboveCoveredLayers for next (lower) layer aboveOpaqueLayers.orSelf(opaqueRegion); aboveCoveredLayers.orSelf(visibleRegion); @@ -790,7 +742,7 @@ void SurfaceFlinger::computeVisibleRegions( layer->setVisibleRegion(visibleRegion); layer->setCoveredRegion(coveredRegion); - // If a secure layer is partially visible, lockdown the screen! + // If a secure layer is partially visible, lock down the screen! if (layer->isSecure() && !visibleRegion.isEmpty()) { secureFrameBuffer = true; } @@ -830,9 +782,9 @@ bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers) { bool recomputeVisibleRegions = false; size_t count = currentLayers.size(); - LayerBase* const* layers = currentLayers.array(); + sp<LayerBase> const* layers = currentLayers.array(); for (size_t i=0 ; i<count ; i++) { - LayerBase* const layer = layers[i]; + const sp<LayerBase>& layer = layers[i]; layer->lockPageFlip(recomputeVisibleRegions); } return recomputeVisibleRegions; @@ -843,37 +795,58 @@ void SurfaceFlinger::unlockPageFlip(const LayerVector& currentLayers) const GraphicPlane& plane(graphicPlane(0)); const Transform& planeTransform(plane.transform()); size_t count = currentLayers.size(); - LayerBase* const* layers = currentLayers.array(); + sp<LayerBase> const* layers = currentLayers.array(); for (size_t i=0 ; i<count ; i++) { - LayerBase* const layer = layers[i]; + const sp<LayerBase>& layer = layers[i]; layer->unlockPageFlip(planeTransform, mDirtyRegion); } } + void SurfaceFlinger::handleRepaint() { - // set the frame buffer - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + // compute the invalid region + mInvalidRegion.orSelf(mDirtyRegion); + if (mInvalidRegion.isEmpty()) { + // nothing to do + return; + } if (UNLIKELY(mDebugRegion)) { debugFlashRegions(); } - // compute the invalid region - mInvalidRegion.orSelf(mDirtyRegion); + // set the frame buffer + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); uint32_t flags = hw.getFlags(); - if (flags & DisplayHardware::BUFFER_PRESERVED) { - // here we assume DisplayHardware::flip()'s implementation - // performs the copy-back optimization. + if ((flags & DisplayHardware::SWAP_RECTANGLE) || + (flags & DisplayHardware::BUFFER_PRESERVED)) + { + // we can redraw only what's dirty, but since SWAP_RECTANGLE only + // takes a rectangle, we must make sure to update that whole + // rectangle in that case + if (flags & DisplayHardware::SWAP_RECTANGLE) { + // FIXME: we really should be able to pass a region to + // SWAP_RECTANGLE so that we don't have to redraw all this. + mDirtyRegion.set(mInvalidRegion.bounds()); + } else { + // in the BUFFER_PRESERVED case, obviously, we can update only + // what's needed and nothing more. + // NOTE: this is NOT a common case, as preserving the backbuffer + // is costly and usually involves copying the whole update back. + } } else { if (flags & DisplayHardware::UPDATE_ON_DEMAND) { - // we need to fully redraw the part that will be updated + // We need to redraw the rectangle that will be updated + // (pushed to the framebuffer). + // This is needed because UPDATE_ON_DEMAND only takes one + // rectangle instead of a region (see DisplayHardware::flip()) mDirtyRegion.set(mInvalidRegion.bounds()); } else { - // we need to redraw everything + // we need to redraw everything (the whole screen) mDirtyRegion.set(hw.bounds()); mInvalidRegion = mDirtyRegion; } @@ -896,9 +869,9 @@ void SurfaceFlinger::composeSurfaces(const Region& dirty) const SurfaceFlinger& flinger(*this); const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); const size_t count = drawingLayers.size(); - LayerBase const* const* const layers = drawingLayers.array(); + sp<LayerBase> const* const layers = drawingLayers.array(); for (size_t i=0 ; i<count ; ++i) { - LayerBase const * const layer = layers[i]; + const sp<LayerBase>& layer = layers[i]; const Region& visibleRegion(layer->visibleRegionScreen); if (!visibleRegion.isEmpty()) { const Region clip(dirty.intersect(visibleRegion)); @@ -913,14 +886,14 @@ void SurfaceFlinger::unlockClients() { const LayerVector& drawingLayers(mDrawingState.layersSortedByZ); const size_t count = drawingLayers.size(); - LayerBase* const* const layers = drawingLayers.array(); + sp<LayerBase> const* const layers = drawingLayers.array(); for (size_t i=0 ; i<count ; ++i) { - LayerBase* const layer = layers[i]; + const sp<LayerBase>& layer = layers[i]; layer->finishPageFlip(); } } -void SurfaceFlinger::scheduleBroadcast(Client* client) +void SurfaceFlinger::scheduleBroadcast(const sp<Client>& client) { if (mLastScheduledBroadcast != client) { mLastScheduledBroadcast = client; @@ -930,50 +903,56 @@ void SurfaceFlinger::scheduleBroadcast(Client* client) void SurfaceFlinger::executeScheduledBroadcasts() { - SortedVector<Client*>& list = mScheduledBroadcasts; + SortedVector< wp<Client> >& list(mScheduledBroadcasts); size_t count = list.size(); while (count--) { - per_client_cblk_t* const cblk = list[count]->ctrlblk; - if (cblk->lock.tryLock() == NO_ERROR) { - cblk->cv.broadcast(); - list.removeAt(count); - cblk->lock.unlock(); - } else { - // schedule another round - LOGW("executeScheduledBroadcasts() skipped, " - "contention on the client. We'll try again later..."); - signalDelayedEvent(ms2ns(4)); + sp<Client> client = list[count].promote(); + if (client != 0) { + per_client_cblk_t* const cblk = client->ctrlblk; + if (cblk->lock.tryLock() == NO_ERROR) { + cblk->cv.broadcast(); + list.removeAt(count); + cblk->lock.unlock(); + } else { + // schedule another round + LOGW("executeScheduledBroadcasts() skipped, " + "contention on the client. We'll try again later..."); + signalDelayedEvent(ms2ns(4)); + } } } mLastScheduledBroadcast = 0; } -void SurfaceFlinger::handleDebugCpu() -{ - Mutex::Autolock _l(mDebugLock); - if (mCpuGauge != 0) - mCpuGauge->sample(); -} - void SurfaceFlinger::debugFlashRegions() { - if (UNLIKELY(!mDirtyRegion.isRect())) { - // TODO: do this only if we don't have preserving - // swapBuffer. If we don't have update-on-demand, - // redraw everything. - composeSurfaces(Region(mDirtyRegion.bounds())); - } + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + const uint32_t flags = hw.getFlags(); + if (!((flags & DisplayHardware::SWAP_RECTANGLE) || + (flags & DisplayHardware::BUFFER_PRESERVED))) { + const Region repaint((flags & DisplayHardware::UPDATE_ON_DEMAND) ? + mDirtyRegion.bounds() : hw.bounds()); + composeSurfaces(repaint); + } + glDisable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDisable(GL_DITHER); glDisable(GL_SCISSOR_TEST); - glColor4x(0x10000, 0, 0x10000, 0x10000); + static int toggle = 0; + toggle = 1 - toggle; + if (toggle) { + glColor4x(0x10000, 0, 0x10000, 0x10000); + } else { + glColor4x(0x10000, 0x10000, 0, 0x10000); + } - Rect r; - Region::iterator iterator(mDirtyRegion); - while (iterator.iterate(&r)) { + Region::const_iterator it = mDirtyRegion.begin(); + Region::const_iterator const end = mDirtyRegion.end(); + while (it != end) { + const Rect& r = *it++; GLfloat vertices[][2] = { { r.left, r.top }, { r.left, r.bottom }, @@ -983,10 +962,12 @@ void SurfaceFlinger::debugFlashRegions() glVertexPointer(2, GL_FLOAT, 0, vertices); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } - - const DisplayHardware& hw(graphicPlane(0).displayHardware()); - hw.flip(mDirtyRegion.merge(mInvalidRegion)); - mInvalidRegion.clear(); + + if (mInvalidRegion.isEmpty()) { + mDirtyRegion.dump("mDirtyRegion"); + mInvalidRegion.dump("mInvalidRegion"); + } + hw.flip(mInvalidRegion); if (mDebugRegion > 1) usleep(mDebugRegion * 1000); @@ -1010,9 +991,10 @@ void SurfaceFlinger::drawWormhole() const if (LIKELY(!mDebugBackground)) { glClearColorx(0,0,0,0); - Rect r; - Region::iterator iterator(region); - while (iterator.iterate(&r)) { + Region::const_iterator it = region.begin(); + Region::const_iterator const end = region.end(); + while (it != end) { + const Rect& r = *it++; const GLint sy = height - (r.top + r.height()); glScissor(r.left, sy, r.width(), r.height()); glClear(GL_COLOR_BUFFER_BIT); @@ -1030,9 +1012,10 @@ void SurfaceFlinger::drawWormhole() const glMatrixMode(GL_TEXTURE); glLoadIdentity(); glScalef(width*(1.0f/32.0f), height*(1.0f/32.0f), 1); - Rect r; - Region::iterator iterator(region); - while (iterator.iterate(&r)) { + Region::const_iterator it = region.begin(); + Region::const_iterator const end = region.end(); + while (it != end) { + const Rect& r = *it++; const GLint sy = height - (r.top + r.height()); glScissor(r.left, sy, r.width(), r.height()); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -1058,7 +1041,7 @@ void SurfaceFlinger::debugShowFPS() const // XXX: mFPS has the value we want } -status_t SurfaceFlinger::addLayer(LayerBase* layer) +status_t SurfaceFlinger::addLayer(const sp<LayerBase>& layer) { Mutex::Autolock _l(mStateLock); addLayer_l(layer); @@ -1066,62 +1049,72 @@ status_t SurfaceFlinger::addLayer(LayerBase* layer) return NO_ERROR; } -status_t SurfaceFlinger::removeLayer(LayerBase* layer) +status_t SurfaceFlinger::removeLayer(const sp<LayerBase>& layer) { Mutex::Autolock _l(mStateLock); - removeLayer_l(layer); - setTransactionFlags(eTransactionNeeded); - return NO_ERROR; + status_t err = purgatorizeLayer_l(layer); + if (err == NO_ERROR) + setTransactionFlags(eTransactionNeeded); + return err; } -status_t SurfaceFlinger::invalidateLayerVisibility(LayerBase* layer) +status_t SurfaceFlinger::invalidateLayerVisibility(const sp<LayerBase>& layer) { layer->forceVisibilityTransaction(); setTransactionFlags(eTraversalNeeded); return NO_ERROR; } -status_t SurfaceFlinger::addLayer_l(LayerBase* layer) +status_t SurfaceFlinger::addLayer_l(const sp<LayerBase>& layer) { ssize_t i = mCurrentState.layersSortedByZ.add( layer, &LayerBase::compareCurrentStateZ); - LayerBaseClient* lbc = LayerBase::dynamicCast<LayerBaseClient*>(layer); - if (lbc) { + sp<LayerBaseClient> lbc = LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); + if (lbc != 0) { mLayerMap.add(lbc->serverIndex(), lbc); } - mRemovedLayers.remove(layer); return NO_ERROR; } -status_t SurfaceFlinger::removeLayer_l(LayerBase* layerBase) +status_t SurfaceFlinger::removeLayer_l(const sp<LayerBase>& layerBase) { ssize_t index = mCurrentState.layersSortedByZ.remove(layerBase); if (index >= 0) { - mRemovedLayers.add(layerBase); - LayerBaseClient* layer = LayerBase::dynamicCast<LayerBaseClient*>(layerBase); - if (layer) { + mLayersRemoved = true; + sp<LayerBaseClient> layer = + LayerBase::dynamicCast< LayerBaseClient* >(layerBase.get()); + if (layer != 0) { mLayerMap.removeItem(layer->serverIndex()); } return NO_ERROR; } + return status_t(index); +} + +status_t SurfaceFlinger::purgatorizeLayer_l(const sp<LayerBase>& layerBase) +{ + // remove the layer from the main list (through a transaction). + ssize_t err = removeLayer_l(layerBase); + // it's possible that we don't find a layer, because it might // have been destroyed already -- this is not technically an error - // from the user because there is a race between destroySurface, - // destroyclient and destroySurface-from-a-transaction. - return (index == NAME_NOT_FOUND) ? status_t(NO_ERROR) : index; + // from the user because there is a race between BClient::destroySurface(), + // ~BClient() and ~ISurface(). + return (err == NAME_NOT_FOUND) ? status_t(NO_ERROR) : err; } + void SurfaceFlinger::free_resources_l() { // Destroy layers that were removed - destroy_all_removed_layers_l(); - + mLayersRemoved = false; + // free resources associated with disconnected clients - SortedVector<Client*>& scheduledBroadcasts(mScheduledBroadcasts); - Vector<Client*>& disconnectedClients(mDisconnectedClients); + SortedVector< wp<Client> >& scheduledBroadcasts(mScheduledBroadcasts); + Vector< sp<Client> >& disconnectedClients(mDisconnectedClients); const size_t count = disconnectedClients.size(); for (size_t i=0 ; i<count ; i++) { - Client* client = disconnectedClients[i]; + sp<Client> client = disconnectedClients[i]; // if this client is the scheduled broadcast list, // remove it from there (and we don't need to signal it // since it is dead). @@ -1130,27 +1123,10 @@ void SurfaceFlinger::free_resources_l() scheduledBroadcasts.removeItemsAt(index); } mTokens.release(client->cid); - delete client; } disconnectedClients.clear(); } -void SurfaceFlinger::destroy_all_removed_layers_l() -{ - size_t c = mRemovedLayers.size(); - while (c--) { - LayerBase* const removed_layer = mRemovedLayers[c]; - - LOGE_IF(mCurrentState.layersSortedByZ.indexOf(removed_layer) >= 0, - "layer %p removed but still in the current state list", - removed_layer); - - delete removed_layer; - } - mRemovedLayers.clear(); -} - - uint32_t SurfaceFlinger::getTransactionFlags(uint32_t flags) { return android_atomic_and(~flags, &mTransactionFlags) & flags; @@ -1191,7 +1167,7 @@ status_t SurfaceFlinger::freezeDisplay(DisplayID dpy, uint32_t flags) setTransactionFlags(eTransactionNeeded); // flags is intended to communicate some sort of animation behavior - // (for instance fadding) + // (for instance fading) return NO_ERROR; } @@ -1205,7 +1181,7 @@ status_t SurfaceFlinger::unfreezeDisplay(DisplayID dpy, uint32_t flags) setTransactionFlags(eTransactionNeeded); // flags is intended to communicate some sort of animation behavior - // (for instance fadding) + // (for instance fading) return NO_ERROR; } @@ -1234,7 +1210,7 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, DisplayID d, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { - LayerBaseClient* layer = 0; + sp<LayerBaseClient> layer; sp<LayerBaseClient::Surface> surfaceHandle; if (int32_t(w|h) < 0) { @@ -1244,14 +1220,14 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, } Mutex::Autolock _l(mStateLock); - Client* const c = mClientsMap.valueFor(clientId); - if (UNLIKELY(!c)) { + sp<Client> client = mClientsMap.valueFor(clientId); + if (UNLIKELY(client == 0)) { LOGE("createSurface() failed, client not found (id=%d)", clientId); return surfaceHandle; } //LOGD("createSurface for pid %d (%d x %d)", pid, w, h); - int32_t id = c->generateId(pid); + int32_t id = client->generateId(pid); if (uint32_t(id) >= NUM_LAYERS_MAX) { LOGE("createSurface() failed, generateId = %d", id); return surfaceHandle; @@ -1260,20 +1236,20 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, switch (flags & eFXSurfaceMask) { case eFXSurfaceNormal: if (UNLIKELY(flags & ePushBuffers)) { - layer = createPushBuffersSurfaceLocked(c, d, id, w, h, flags); + layer = createPushBuffersSurfaceLocked(client, d, id, w, h, flags); } else { - layer = createNormalSurfaceLocked(c, d, id, w, h, format, flags); + layer = createNormalSurfaceLocked(client, d, id, w, h, format, flags); } break; case eFXSurfaceBlur: - layer = createBlurSurfaceLocked(c, d, id, w, h, flags); + layer = createBlurSurfaceLocked(client, d, id, w, h, flags); break; case eFXSurfaceDim: - layer = createDimSurfaceLocked(c, d, id, w, h, flags); + layer = createDimSurfaceLocked(client, d, id, w, h, flags); break; } - if (layer) { + if (layer != 0) { setTransactionFlags(eTransactionNeeded); surfaceHandle = layer->getSurface(); if (surfaceHandle != 0) @@ -1283,8 +1259,8 @@ sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid, return surfaceHandle; } -LayerBaseClient* SurfaceFlinger::createNormalSurfaceLocked( - Client* client, DisplayID display, +sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) { // initialize the surfaces @@ -1298,57 +1274,99 @@ LayerBaseClient* SurfaceFlinger::createNormalSurfaceLocked( break; } - Layer* layer = new Layer(this, display, client, id); - status_t err = layer->setBuffers(client, w, h, format, flags); + sp<Layer> layer = new Layer(this, display, client, id); + status_t err = layer->setBuffers(w, h, format, flags); if (LIKELY(err == NO_ERROR)) { layer->initStates(w, h, flags); addLayer_l(layer); } else { LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err)); - delete layer; - return 0; + layer.clear(); } return layer; } -LayerBaseClient* SurfaceFlinger::createBlurSurfaceLocked( - Client* client, DisplayID display, +sp<LayerBaseClient> SurfaceFlinger::createBlurSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags) { - LayerBlur* layer = new LayerBlur(this, display, client, id); + sp<LayerBlur> layer = new LayerBlur(this, display, client, id); layer->initStates(w, h, flags); addLayer_l(layer); return layer; } -LayerBaseClient* SurfaceFlinger::createDimSurfaceLocked( - Client* client, DisplayID display, +sp<LayerBaseClient> SurfaceFlinger::createDimSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags) { - LayerDim* layer = new LayerDim(this, display, client, id); + sp<LayerDim> layer = new LayerDim(this, display, client, id); layer->initStates(w, h, flags); addLayer_l(layer); return layer; } -LayerBaseClient* SurfaceFlinger::createPushBuffersSurfaceLocked( - Client* client, DisplayID display, +sp<LayerBaseClient> SurfaceFlinger::createPushBuffersSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags) { - LayerBuffer* layer = new LayerBuffer(this, display, client, id); + sp<LayerBuffer> layer = new LayerBuffer(this, display, client, id); layer->initStates(w, h, flags); addLayer_l(layer); return layer; } -status_t SurfaceFlinger::destroySurface(SurfaceID index) +status_t SurfaceFlinger::removeSurface(SurfaceID index) { + /* + * called by the window manager, when a surface should be marked for + * destruction. + * + * The surface is removed from the current and drawing lists, but placed + * in the purgatory queue, so it's not destroyed right-away (we need + * to wait for all client's references to go away first). + */ + Mutex::Autolock _l(mStateLock); - LayerBaseClient* const layer = getLayerUser_l(index); - status_t err = removeLayer_l(layer); - if (err < 0) - return err; - setTransactionFlags(eTransactionNeeded); + sp<LayerBaseClient> layer = getLayerUser_l(index); + status_t err = purgatorizeLayer_l(layer); + if (err == NO_ERROR) { + setTransactionFlags(eTransactionNeeded); + } + return err; +} + +status_t SurfaceFlinger::destroySurface(const sp<LayerBaseClient>& layer) +{ + // called by ~ISurface() when all references are gone + + class MessageDestroySurface : public MessageBase { + SurfaceFlinger* flinger; + sp<LayerBaseClient> layer; + public: + MessageDestroySurface( + SurfaceFlinger* flinger, const sp<LayerBaseClient>& layer) + : flinger(flinger), layer(layer) { } + virtual bool handler() { + sp<LayerBaseClient> l(layer); + layer.clear(); // clear it outside of the lock; + Mutex::Autolock _l(flinger->mStateLock); + /* + * remove the layer from the current list -- chances are that it's + * not in the list anyway, because it should have been removed + * already upon request of the client (eg: window manager). + * However, a buggy client could have not done that. + * Since we know we don't have any more clients, we don't need + * to use the purgatory. + */ + status_t err = flinger->removeLayer_l(l); + LOGE_IF(err<0 && err != NAME_NOT_FOUND, + "error removing layer=%p (%s)", l.get(), strerror(-err)); + return true; + } + }; + + mEventQueue.postMessage( new MessageDestroySurface(this, layer) ); return NO_ERROR; } @@ -1362,18 +1380,9 @@ status_t SurfaceFlinger::setClientState( cid <<= 16; for (int i=0 ; i<count ; i++) { const layer_state_t& s = states[i]; - LayerBaseClient* layer = getLayerUser_l(s.surface | cid); - if (layer) { + sp<LayerBaseClient> layer(getLayerUser_l(s.surface | cid)); + if (layer != 0) { const uint32_t what = s.what; - // check if it has been destroyed first - if (what & eDestroyed) { - if (removeLayer_l(layer) == NO_ERROR) { - flags |= eTransactionNeeded; - // we skip everything else... well, no, not really - // we skip ONLY that transaction. - continue; - } - } if (what & ePositionChanged) { if (layer->setPosition(s.x, s.y)) flags |= eTraversalNeeded; @@ -1415,9 +1424,10 @@ status_t SurfaceFlinger::setClientState( return NO_ERROR; } -LayerBaseClient* SurfaceFlinger::getLayerUser_l(SurfaceID s) const +sp<LayerBaseClient> SurfaceFlinger::getLayerUser_l(SurfaceID s) const { - return mLayerMap.valueFor(s); + sp<LayerBaseClient> layer = mLayerMap.valueFor(s); + return layer; } void SurfaceFlinger::screenReleased(int dpy) @@ -1439,9 +1449,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) const size_t SIZE = 1024; char buffer[SIZE]; String8 result; - if (checkCallingPermission( - String16("android.permission.DUMP")) == false) - { // not allowed + if (!mDump.checkCalling()) { snprintf(buffer, SIZE, "Permission Denial: " "can't dump SurfaceFlinger from pid=%d, uid=%d\n", IPCThreadState::self()->getCallingPid(), @@ -1452,7 +1460,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) size_t s = mClientsMap.size(); char name[64]; for (size_t i=0 ; i<s ; i++) { - Client* client = mClientsMap.valueAt(i); + sp<Client> client = mClientsMap.valueAt(i); sprintf(name, " Client (id=0x%08x)", client->cid); client->dump(name); } @@ -1460,7 +1468,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) const size_t count = currentLayers.size(); for (size_t i=0 ; i<count ; i++) { /*** LayerBase ***/ - LayerBase const * const layer = currentLayers[i]; + const sp<LayerBase>& layer = currentLayers[i]; const Layer::State& s = layer->drawingState(); snprintf(buffer, SIZE, "+ %s %p\n" @@ -1468,7 +1476,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) "z=%9d, pos=(%4d,%4d), size=(%4d,%4d), " "needsBlending=%1d, invalidate=%1d, " "alpha=0x%02x, flags=0x%08x, tr=[%.2f, %.2f][%.2f, %.2f]\n", - layer->getTypeID(), layer, + layer->getTypeID(), layer.get(), s.z, layer->tx(), layer->ty(), s.w, s.h, layer->needsBlending(), layer->contentDirty, s.alpha, s.flags, @@ -1477,30 +1485,33 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) result.append(buffer); buffer[0] = 0; /*** LayerBaseClient ***/ - LayerBaseClient* const lbc = - LayerBase::dynamicCast<LayerBaseClient*>((LayerBase*)layer); - if (lbc) { + sp<LayerBaseClient> lbc = + LayerBase::dynamicCast< LayerBaseClient* >(layer.get()); + if (lbc != 0) { + sp<Client> client(lbc->client.promote()); snprintf(buffer, SIZE, " " "id=0x%08x, client=0x%08x, identity=%u\n", - lbc->clientIndex(), lbc->client ? lbc->client->cid : 0, + lbc->clientIndex(), client.get() ? client->cid : 0, lbc->getIdentity()); } result.append(buffer); buffer[0] = 0; /*** Layer ***/ - Layer* const l = LayerBase::dynamicCast<Layer*>((LayerBase*)layer); - if (l) { + sp<Layer> l = LayerBase::dynamicCast< Layer* >(layer.get()); + if (l != 0) { const LayerBitmap& buf0(l->getBuffer(0)); const LayerBitmap& buf1(l->getBuffer(1)); snprintf(buffer, SIZE, " " - "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u], mTextureName=%d," + "format=%2d, [%3ux%3u:%3u] [%3ux%3u:%3u]," " freezeLock=%p, swapState=0x%08x\n", l->pixelFormat(), - buf0.width(), buf0.height(), buf0.stride(), - buf1.width(), buf1.height(), buf1.stride(), - l->getTextureName(), l->getFreezeLock().get(), + buf0.getWidth(), buf0.getHeight(), + buf0.getBuffer()->getStride(), + buf1.getWidth(), buf1.getHeight(), + buf1.getBuffer()->getStride(), + l->getFreezeLock().get(), l->lcblk->swapState); } result.append(buffer); @@ -1516,20 +1527,10 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) mFreezeDisplay?"yes":"no", mFreezeCount, mCurrentState.orientation, hw.canDraw()); result.append(buffer); - - sp<AllocatorInterface> allocator; - if (mGPU != 0) { - snprintf(buffer, SIZE, " GPU owner: %d\n", mGPU->getOwner()); - result.append(buffer); - allocator = mGPU->getAllocator(); - if (allocator != 0) { - allocator->dump(result, "GPU Allocator"); - } - } - allocator = mSurfaceHeapManager->getAllocator(NATIVE_MEMORY_TYPE_PMEM); - if (allocator != 0) { - allocator->dump(result, "PMEM Allocator"); - } + snprintf(buffer, SIZE, " client count: %d\n", mClientsMap.size()); + result.append(buffer); + const BufferAllocator& alloc(BufferAllocator::get()); + alloc.dump(result); } write(fd, result.string(), result.size()); return NO_ERROR; @@ -1546,57 +1547,34 @@ status_t SurfaceFlinger::onTransact( case FREEZE_DISPLAY: case UNFREEZE_DISPLAY: case BOOT_FINISHED: - case REVOKE_GPU: { // codes that require permission check IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); - const int self_pid = getpid(); - if (UNLIKELY(pid != self_pid && uid != AID_GRAPHICS)) { - // we're called from a different process, do the real check - if (!checkCallingPermission( - String16("android.permission.ACCESS_SURFACE_FLINGER"))) - { - LOGE("Permission Denial: " - "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); - return PERMISSION_DENIED; - } + if ((uid != AID_GRAPHICS) && !mAccessSurfaceFlinger.check(pid, uid)) { + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; } } } - status_t err = BnSurfaceComposer::onTransact(code, data, reply, flags); if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) { - // HARDWARE_TEST stuff... - if (UNLIKELY(checkCallingPermission( - String16("android.permission.HARDWARE_TEST")) == false)) - { // not allowed - LOGE("Permission Denial: pid=%d, uid=%d\n", - IPCThreadState::self()->getCallingPid(), - IPCThreadState::self()->getCallingUid()); + CHECK_INTERFACE(ISurfaceComposer, data, reply); + if (UNLIKELY(!mHardwareTest.checkCalling())) { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + LOGE("Permission Denial: " + "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); return PERMISSION_DENIED; } int n; switch (code) { - case 1000: // SHOW_CPU - n = data.readInt32(); - mDebugCpu = n ? 1 : 0; - if (mDebugCpu) { - if (mCpuGauge == 0) { - mCpuGauge = new CPUGauge(this, ms2ns(500)); - } - } else { - if (mCpuGauge != 0) { - mCpuGauge->requestExitAndWait(); - Mutex::Autolock _l(mDebugLock); - mCpuGauge.clear(); - } - } + case 1000: // SHOW_CPU, NOT SUPPORTED ANYMORE return NO_ERROR; - case 1001: // SHOW_FPS - n = data.readInt32(); - mDebugFps = n ? 1 : 0; + case 1001: // SHOW_FPS, NOT SUPPORTED ANYMORE return NO_ERROR; case 1002: // SHOW_UPDATES n = data.readInt32(); @@ -1613,21 +1591,11 @@ status_t SurfaceFlinger::onTransact( signalEvent(); } return NO_ERROR; - case 1005: // ask GPU revoke - if (mGPU != 0) { - mGPU->friendlyRevoke(); - } - return NO_ERROR; - case 1006: // revoke GPU - if (mGPU != 0) { - mGPU->unconditionalRevoke(); - } - return NO_ERROR; case 1007: // set mFreezeCount mFreezeCount = data.readInt32(); return NO_ERROR; case 1010: // interrogate. - reply->writeInt32(mDebugCpu); + reply->writeInt32(0); reply->writeInt32(0); reply->writeInt32(mDebugRegion); reply->writeInt32(mDebugBackground); @@ -1651,16 +1619,15 @@ status_t SurfaceFlinger::onTransact( Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger) : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger) { - mSharedHeapAllocator = getSurfaceHeapManager()->createHeap(); const int pgsize = getpagesize(); - const int cblksize=((sizeof(per_client_cblk_t)+(pgsize-1))&~(pgsize-1)); - mCblkHeap = new MemoryDealer(cblksize); - mCblkMemory = mCblkHeap->allocate(cblksize); - if (mCblkMemory != 0) { - ctrlblk = static_cast<per_client_cblk_t *>(mCblkMemory->pointer()); - if (ctrlblk) { // construct the shared structure in-place. - new(ctrlblk) per_client_cblk_t; - } + const int cblksize = ((sizeof(per_client_cblk_t)+(pgsize-1))&~(pgsize-1)); + + mCblkHeap = new MemoryHeapBase(cblksize, 0, + "SurfaceFlinger Client control-block"); + + ctrlblk = static_cast<per_client_cblk_t *>(mCblkHeap->getBase()); + if (ctrlblk) { // construct the shared structure in-place. + new(ctrlblk) per_client_cblk_t; } } @@ -1671,10 +1638,6 @@ Client::~Client() { } } -const sp<SurfaceHeapManager>& Client::getSurfaceHeapManager() const { - return mFlinger->getSurfaceHeapManager(); -} - int32_t Client::generateId(int pid) { const uint32_t i = clz( ~mBitmap ); @@ -1686,13 +1649,15 @@ int32_t Client::generateId(int pid) mBitmap |= 1<<(31-i); return i; } -status_t Client::bindLayer(LayerBaseClient* layer, int32_t id) + +status_t Client::bindLayer(const sp<LayerBaseClient>& layer, int32_t id) { ssize_t idx = mInUse.indexOf(id); if (idx < 0) return NAME_NOT_FOUND; return mLayers.insertAt(layer, idx); } + void Client::free(int32_t id) { ssize_t idx = mInUse.remove(uint8_t(id)); @@ -1702,27 +1667,18 @@ void Client::free(int32_t id) } } -sp<MemoryDealer> Client::createAllocator(uint32_t flags) -{ - sp<MemoryDealer> allocator; - allocator = getSurfaceHeapManager()->createHeap( - flags, getClientPid(), mSharedHeapAllocator); - return allocator; -} - bool Client::isValid(int32_t i) const { return (uint32_t(i)<NUM_LAYERS_MAX) && (mBitmap & (1<<(31-i))); } -const uint8_t* Client::inUseArray() const { - return mInUse.array(); -} -size_t Client::numActiveLayers() const { - return mInUse.size(); -} -LayerBaseClient* Client::getLayerUser(int32_t i) const { + +sp<LayerBaseClient> Client::getLayerUser(int32_t i) const { + sp<LayerBaseClient> lbc; ssize_t idx = mInUse.indexOf(uint8_t(i)); - if (idx<0) return 0; - return mLayers[idx]; + if (idx >= 0) { + lbc = mLayers[idx].promote(); + LOGE_IF(lbc==0, "getLayerUser(i=%d), idx=%d is dead", int(i), int(idx)); + } + return lbc; } void Client::dump(const char* what) @@ -1734,7 +1690,7 @@ void Client::dump(const char* what) #pragma mark - #endif -BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemory>& cblk) +BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk) : mId(cid), mFlinger(flinger), mCblk(cblk) { } @@ -1744,8 +1700,8 @@ BClient::~BClient() { mFlinger->destroyConnection(mId); } -void BClient::getControlBlocks(sp<IMemory>* ctrl) const { - *ctrl = mCblk; +sp<IMemoryHeap> BClient::getControlBlock() const { + return mCblk; } sp<ISurface> BClient::createSurface( @@ -1759,7 +1715,7 @@ sp<ISurface> BClient::createSurface( status_t BClient::destroySurface(SurfaceID sid) { sid |= (mId << 16); // add the client-part to id - return mFlinger->destroySurface(sid); + return mFlinger->removeSurface(sid); } status_t BClient::setState(int32_t count, const layer_state_t* states) @@ -1859,6 +1815,10 @@ const Transform& GraphicPlane::transform() const { return mGlobalTransform; } +EGLDisplay GraphicPlane::getEGLDisplay() const { + return mHw->getEGLDisplay(); +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/surfaceflinger/SurfaceFlinger.h b/libs/surfaceflinger/SurfaceFlinger.h index 15913f2..e8687a7 100644 --- a/libs/surfaceflinger/SurfaceFlinger.h +++ b/libs/surfaceflinger/SurfaceFlinger.h @@ -25,7 +25,10 @@ #include <utils/threads.h> #include <utils/Atomic.h> #include <utils/Errors.h> -#include <utils/MemoryDealer.h> +#include <utils/RefBase.h> + +#include <binder/IMemory.h> +#include <binder/Permission.h> #include <ui/PixelFormat.h> #include <ui/ISurfaceComposer.h> @@ -33,13 +36,13 @@ #include <private/ui/SharedState.h> #include <private/ui/LayerState.h> -#include <private/ui/SurfaceFlingerSynchro.h> #include "Barrier.h" -#include "CPUGauge.h" #include "Layer.h" #include "Tokenizer.h" +#include "MessageQueue.h" + struct copybit_device_t; struct overlay_device_t; @@ -51,13 +54,8 @@ class Client; class BClient; class DisplayHardware; class FreezeLock; -class GPUHardwareInterface; -class IGPUCallback; class Layer; class LayerBuffer; -class LayerOrientationAnim; -class OrientationAnimation; -class SurfaceHeapManager; typedef int32_t ClientID; @@ -66,7 +64,7 @@ typedef int32_t ClientID; // --------------------------------------------------------------------------- -class Client +class Client : public RefBase { public: Client(ClientID cid, const sp<SurfaceFlinger>& flinger); @@ -74,17 +72,19 @@ public: int32_t generateId(int pid); void free(int32_t id); - status_t bindLayer(LayerBaseClient* layer, int32_t id); - sp<MemoryDealer> createAllocator(uint32_t memory_type); + status_t bindLayer(const sp<LayerBaseClient>& layer, int32_t id); inline bool isValid(int32_t i) const; - inline const uint8_t* inUseArray() const; - inline size_t numActiveLayers() const; - LayerBaseClient* getLayerUser(int32_t i) const; - const Vector<LayerBaseClient*>& getLayers() const { return mLayers; } - const sp<IMemory>& controlBlockMemory() const { return mCblkMemory; } + sp<LayerBaseClient> getLayerUser(int32_t i) const; void dump(const char* what); - const sp<SurfaceHeapManager>& getSurfaceHeapManager() const; + + const Vector< wp<LayerBaseClient> >& getLayers() const { + return mLayers; + } + + const sp<IMemoryHeap>& getControlBlockMemory() const { + return mCblkHeap; + } // pointer to this client's control block per_client_cblk_t* ctrlblk; @@ -92,17 +92,14 @@ public: private: - int getClientPid() const { return mPid; } + int getClientPid() const { return mPid; } - int mPid; - uint32_t mBitmap; - SortedVector<uint8_t> mInUse; - Vector<LayerBaseClient*> mLayers; - sp<MemoryDealer> mCblkHeap; - sp<SurfaceFlinger> mFlinger; - sp<MemoryDealer> mSharedHeapAllocator; - sp<MemoryDealer> mPMemAllocator; - sp<IMemory> mCblkMemory; + int mPid; + uint32_t mBitmap; + SortedVector<uint8_t> mInUse; + Vector< wp<LayerBaseClient> > mLayers; + sp<IMemoryHeap> mCblkHeap; + sp<SurfaceFlinger> mFlinger; }; // --------------------------------------------------------------------------- @@ -125,6 +122,8 @@ public: const DisplayHardware& displayHardware() const; const Transform& transform() const; + EGLDisplay getEGLDisplay() const; + private: GraphicPlane(const GraphicPlane&); GraphicPlane operator = (const GraphicPlane&); @@ -160,7 +159,7 @@ public: // ISurfaceComposer interface virtual sp<ISurfaceFlingerClient> createConnection(); - virtual sp<IMemory> getCblk() const; + virtual sp<IMemoryHeap> getCblk() const; virtual void bootFinished(); virtual void openGlobalTransaction(); virtual void closeGlobalTransaction(); @@ -168,56 +167,52 @@ public: virtual status_t unfreezeDisplay(DisplayID dpy, uint32_t flags); virtual int setOrientation(DisplayID dpy, int orientation, uint32_t flags); virtual void signal() const; - virtual status_t requestGPU(const sp<IGPUCallback>& callback, - gpu_info_t* gpu); - virtual status_t revokeGPU(); void screenReleased(DisplayID dpy); void screenAcquired(DisplayID dpy); - const sp<SurfaceHeapManager>& getSurfaceHeapManager() const { - return mSurfaceHeapManager; - } - - const sp<GPUHardwareInterface>& getGPU() const { - return mGPU; - } - - copybit_device_t* getBlitEngine() const; overlay_control_device_t* getOverlayEngine() const; - status_t removeLayer(LayerBase* layer); - status_t addLayer(LayerBase* layer); - status_t invalidateLayerVisibility(LayerBase* layer); + status_t removeLayer(const sp<LayerBase>& layer); + status_t addLayer(const sp<LayerBase>& layer); + status_t invalidateLayerVisibility(const sp<LayerBase>& layer); private: friend class BClient; friend class LayerBase; friend class LayerBuffer; friend class LayerBaseClient; + friend class LayerBaseClient::Surface; friend class Layer; friend class LayerBlur; + friend class LayerDim; sp<ISurface> createSurface(ClientID client, int pid, ISurfaceFlingerClient::surface_data_t* params, DisplayID display, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags); - LayerBaseClient* createNormalSurfaceLocked(Client* client, DisplayID display, - int32_t id, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags); + sp<LayerBaseClient> createNormalSurfaceLocked( + const sp<Client>& client, DisplayID display, + int32_t id, uint32_t w, uint32_t h, + PixelFormat format, uint32_t flags); - LayerBaseClient* createBlurSurfaceLocked(Client* client, DisplayID display, + sp<LayerBaseClient> createBlurSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags); - LayerBaseClient* createDimSurfaceLocked(Client* client, DisplayID display, + sp<LayerBaseClient> createDimSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags); - LayerBaseClient* createPushBuffersSurfaceLocked(Client* client, DisplayID display, + sp<LayerBaseClient> createPushBuffersSurfaceLocked( + const sp<Client>& client, DisplayID display, int32_t id, uint32_t w, uint32_t h, uint32_t flags); - status_t destroySurface(SurfaceID surface_id); - status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states); + status_t removeSurface(SurfaceID surface_id); + status_t destroySurface(const sp<LayerBaseClient>& layer); + status_t setClientState(ClientID cid, int32_t count, const layer_state_t* states); class LayerVector { @@ -225,15 +220,15 @@ private: inline LayerVector() { } LayerVector(const LayerVector&); inline size_t size() const { return layers.size(); } - inline LayerBase*const* array() const { return layers.array(); } - ssize_t add(LayerBase*, Vector<LayerBase*>::compar_t); - ssize_t remove(LayerBase*); - ssize_t reorder(LayerBase*, Vector<LayerBase*>::compar_t); - ssize_t indexOf(LayerBase* key, size_t guess=0) const; - inline LayerBase* operator [] (size_t i) const { return layers[i]; } + inline sp<LayerBase> const* array() const { return layers.array(); } + ssize_t add(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t); + ssize_t remove(const sp<LayerBase>&); + ssize_t reorder(const sp<LayerBase>&, Vector< sp<LayerBase> >::compar_t); + ssize_t indexOf(const sp<LayerBase>& key, size_t guess=0) const; + inline sp<LayerBase> operator [] (size_t i) const { return layers[i]; } private: - KeyedVector<LayerBase*, size_t> lookup; - Vector<LayerBase*> layers; + KeyedVector< sp<LayerBase> , size_t> lookup; + Vector< sp<LayerBase> > layers; }; struct State { @@ -247,25 +242,6 @@ private: uint8_t freezeDisplay; }; - class DelayedTransaction : public Thread - { - friend class SurfaceFlinger; - sp<SurfaceFlinger> mFlinger; - nsecs_t mDelay; - public: - DelayedTransaction(const sp<SurfaceFlinger>& flinger, nsecs_t delay) - : Thread(false), mFlinger(flinger), mDelay(delay) { - } - virtual bool threadLoop() { - usleep(mDelay / 1000); - if (android_atomic_and(~1, - &mFlinger->mDeplayedTransactionPending) == 1) { - mFlinger->signalEvent(); - } - return false; - } - }; - virtual bool threadLoop(); virtual status_t readyToRun(); virtual void onFirstRef(); @@ -279,6 +255,9 @@ private: void handleConsoleEvents(); void handleTransaction(uint32_t transactionFlags); + void handleTransactionLocked( + uint32_t transactionFlags, + Vector< sp<LayerBase> >& ditchedLayers); void computeVisibleRegions( LayerVector& currentLayers, @@ -289,8 +268,7 @@ private: bool lockPageFlip(const LayerVector& currentLayers); void unlockPageFlip(const LayerVector& currentLayers); void handleRepaint(); - void handleDebugCpu(); - void scheduleBroadcast(Client* client); + void scheduleBroadcast(const sp<Client>& client); void executeScheduledBroadcasts(); void postFramebuffer(); void composeSurfaces(const Region& dirty); @@ -298,10 +276,10 @@ private: void destroyConnection(ClientID cid); - LayerBaseClient* getLayerUser_l(SurfaceID index) const; - status_t addLayer_l(LayerBase* layer); - status_t removeLayer_l(LayerBase* layer); - void destroy_all_removed_layers_l(); + sp<LayerBaseClient> getLayerUser_l(SurfaceID index) const; + status_t addLayer_l(const sp<LayerBase>& layer); + status_t removeLayer_l(const sp<LayerBase>& layer); + status_t purgatorizeLayer_l(const sp<LayerBase>& layer); void free_resources_l(); uint32_t getTransactionFlags(uint32_t flags); @@ -323,6 +301,11 @@ private: void debugShowFPS() const; void drawWormhole() const; + + mutable MessageQueue mEventQueue; + + + // access must be protected by mStateLock mutable Mutex mStateLock; State mCurrentState; @@ -330,53 +313,43 @@ private: volatile int32_t mTransactionFlags; volatile int32_t mTransactionCount; Condition mTransactionCV; - + // protected by mStateLock (but we could use another lock) Tokenizer mTokens; - DefaultKeyedVector<ClientID, Client*> mClientsMap; - DefaultKeyedVector<SurfaceID, LayerBaseClient*> mLayerMap; + DefaultKeyedVector<ClientID, sp<Client> > mClientsMap; + DefaultKeyedVector<SurfaceID, sp<LayerBaseClient> > mLayerMap; GraphicPlane mGraphicPlanes[1]; - SortedVector<LayerBase*> mRemovedLayers; - Vector<Client*> mDisconnectedClients; + bool mLayersRemoved; + Vector< sp<Client> > mDisconnectedClients; // constant members (no synchronization needed for access) - sp<MemoryDealer> mServerHeap; - sp<IMemory> mServerCblkMemory; + sp<IMemoryHeap> mServerHeap; surface_flinger_cblk_t* mServerCblk; - sp<SurfaceHeapManager> mSurfaceHeapManager; - sp<GPUHardwareInterface> mGPU; GLuint mWormholeTexName; nsecs_t mBootTime; + Permission mHardwareTest; + Permission mAccessSurfaceFlinger; + Permission mDump; // Can only accessed from the main thread, these members // don't need synchronization Region mDirtyRegion; Region mInvalidRegion; Region mWormholeRegion; - Client* mLastScheduledBroadcast; - SortedVector<Client*> mScheduledBroadcasts; + wp<Client> mLastScheduledBroadcast; + SortedVector< wp<Client> > mScheduledBroadcasts; bool mVisibleRegionsDirty; bool mDeferReleaseConsole; bool mFreezeDisplay; int32_t mFreezeCount; nsecs_t mFreezeDisplayTime; - friend class OrientationAnimation; - OrientationAnimation* mOrientationAnimation; - - // access protected by mDebugLock - mutable Mutex mDebugLock; - sp<CPUGauge> mCpuGauge; // don't use a lock for these, we don't care int mDebugRegion; - int mDebugCpu; - int mDebugFps; int mDebugBackground; // these are thread safe mutable Barrier mReadyToRunBarrier; - mutable SurfaceFlingerSynchro mSyncObject; - volatile int32_t mDeplayedTransactionPending; // atomic variables enum { @@ -409,11 +382,11 @@ class BClient : public BnSurfaceFlingerClient { public: BClient(SurfaceFlinger *flinger, ClientID cid, - const sp<IMemory>& cblk); + const sp<IMemoryHeap>& cblk); ~BClient(); // ISurfaceFlingerClient interface - virtual void getControlBlocks(sp<IMemory>* ctrl) const; + virtual sp<IMemoryHeap> getControlBlock() const; virtual sp<ISurface> createSurface( surface_data_t* params, int pid, @@ -426,7 +399,7 @@ public: private: ClientID mId; SurfaceFlinger* mFlinger; - sp<IMemory> mCblk; + sp<IMemoryHeap> mCblk; }; // --------------------------------------------------------------------------- diff --git a/libs/surfaceflinger/Tokenizer.cpp b/libs/surfaceflinger/Tokenizer.cpp index ef51d6a..be3a239 100644 --- a/libs/surfaceflinger/Tokenizer.cpp +++ b/libs/surfaceflinger/Tokenizer.cpp @@ -162,9 +162,10 @@ void Tokenizer::dump() const { const run_t* ranges = mRanges.array(); const size_t c = mRanges.size(); - printf("Tokenizer (%p, size = %lu)\n", this, c); + printf("Tokenizer (%p, size = %d)\n", this, int(c)); for (size_t i=0 ; i<c ; i++) { - printf("%lu: (%u, %u)\n", i, ranges[i].first, ranges[i].length); + printf("%u: (%u, %u)\n", i, + uint32_t(ranges[i].first), uint32_t(ranges[i].length)); } } diff --git a/libs/surfaceflinger/Transform.cpp b/libs/surfaceflinger/Transform.cpp index e8b0f45..1501536 100644 --- a/libs/surfaceflinger/Transform.cpp +++ b/libs/surfaceflinger/Transform.cpp @@ -177,10 +177,10 @@ Region Transform::transform(const Region& reg) const Region out; if (UNLIKELY(transformed())) { if (LIKELY(preserveRects())) { - Rect r; - Region::iterator iterator(reg); - while (iterator.iterate(&r)) { - out.orSelf(transform(r)); + Region::const_iterator it = reg.begin(); + Region::const_iterator const end = reg.end(); + while (it != end) { + out.orSelf(transform(*it++)); } } else { out.set(transform(reg.bounds())); diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp b/libs/surfaceflinger/purgatory/GPUHardware/GPUHardware.cpp index 7168bf2..2de628b 100644 --- a/libs/surfaceflinger/GPUHardware/GPUHardware.cpp +++ b/libs/surfaceflinger/purgatory/GPUHardware/GPUHardware.cpp @@ -30,12 +30,12 @@ #include <cutils/log.h> #include <cutils/properties.h> -#include <utils/IBinder.h> -#include <utils/MemoryDealer.h> -#include <utils/MemoryBase.h> -#include <utils/MemoryHeapPmem.h> -#include <utils/MemoryHeapBase.h> -#include <utils/IPCThreadState.h> +#include <binder/IBinder.h> +#include <binder/MemoryDealer.h> +#include <binder/MemoryBase.h> +#include <binder/MemoryHeapPmem.h> +#include <binder/MemoryHeapBase.h> +#include <binder/IPCThreadState.h> #include <utils/StopWatch.h> #include <ui/ISurfaceComposer.h> diff --git a/libs/surfaceflinger/GPUHardware/GPUHardware.h b/libs/surfaceflinger/purgatory/GPUHardware/GPUHardware.h index 3354528..3354528 100644 --- a/libs/surfaceflinger/GPUHardware/GPUHardware.h +++ b/libs/surfaceflinger/purgatory/GPUHardware/GPUHardware.h diff --git a/libs/surfaceflinger/purgatory/LayerOrientationAnim.cpp b/libs/surfaceflinger/purgatory/LayerOrientationAnim.cpp new file mode 100644 index 0000000..41c42d1 --- /dev/null +++ b/libs/surfaceflinger/purgatory/LayerOrientationAnim.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> +#include <utils/StopWatch.h> + +#include "BlurFilter.h" +#include "LayerBase.h" +#include "LayerOrientationAnim.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" +#include "OrientationAnimation.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerOrientationAnim::typeInfo = LayerBase::typeInfo | 0x80; +const char* const LayerOrientationAnim::typeID = "LayerOrientationAnim"; + +// --------------------------------------------------------------------------- + +// Animation... +const float DURATION = ms2ns(200); +const float BOUNCES_PER_SECOND = 0.5f; +//const float BOUNCES_AMPLITUDE = 1.0f/16.0f; +const float BOUNCES_AMPLITUDE = 0; +const float DIM_TARGET = 0.40f; +//#define INTERPOLATED_TIME(_t) ((_t)*(_t)) +#define INTERPOLATED_TIME(_t) (_t) + +// --------------------------------------------------------------------------- + +LayerOrientationAnim::LayerOrientationAnim( + SurfaceFlinger* flinger, DisplayID display, + OrientationAnimation* anim, + const sp<Buffer>& bitmapIn, + const sp<Buffer>& bitmapOut) + : LayerOrientationAnimBase(flinger, display), mAnim(anim), + mBitmapIn(bitmapIn), mBitmapOut(bitmapOut), + mTextureName(-1), mTextureNameIn(-1) +{ + // blur that texture. + mStartTime = systemTime(); + mFinishTime = 0; + mOrientationCompleted = false; + mFirstRedraw = false; + mLastNormalizedTime = 0; + mNeedsBlending = false; + mAlphaInLerp.set(1.0f, DIM_TARGET); + mAlphaOutLerp.set(0.5f, 1.0f); +} + +LayerOrientationAnim::~LayerOrientationAnim() +{ + if (mTextureName != -1U) { + glDeleteTextures(1, &mTextureName); + } + if (mTextureNameIn != -1U) { + glDeleteTextures(1, &mTextureNameIn); + } +} + +bool LayerOrientationAnim::needsBlending() const +{ + return mNeedsBlending; +} + +Point LayerOrientationAnim::getPhysicalSize() const +{ + const GraphicPlane& plane(graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + return Point(hw.getWidth(), hw.getHeight()); +} + +void LayerOrientationAnim::validateVisibility(const Transform&) +{ + const Layer::State& s(drawingState()); + const Transform tr(s.transform); + const Point size(getPhysicalSize()); + uint32_t w = size.x; + uint32_t h = size.y; + mTransformedBounds = tr.makeBounds(w, h); + mLeft = tr.tx(); + mTop = tr.ty(); + transparentRegionScreen.clear(); + mTransformed = true; +} + +void LayerOrientationAnim::onOrientationCompleted() +{ + mFinishTime = systemTime(); + mOrientationCompleted = true; + mFirstRedraw = true; + mNeedsBlending = true; + mFlinger->invalidateLayerVisibility(this); +} + +void LayerOrientationAnim::onDraw(const Region& clip) const +{ + const nsecs_t now = systemTime(); + float alphaIn, alphaOut; + + if (mOrientationCompleted) { + if (mFirstRedraw) { + mFirstRedraw = false; + + // make a copy of what's on screen + copybit_image_t image; + mBitmapOut->getBitmapSurface(&image); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + hw.copyBackToImage(image); + + // and erase the screen for this round + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + + // FIXME: code below is gross + mNeedsBlending = false; + LayerOrientationAnim* self(const_cast<LayerOrientationAnim*>(this)); + mFlinger->invalidateLayerVisibility(self); + } + + // make sure pick-up where we left off + const float duration = DURATION * mLastNormalizedTime; + const float normalizedTime = (float(now - mFinishTime) / duration); + if (normalizedTime <= 1.0f) { + const float interpolatedTime = INTERPOLATED_TIME(normalizedTime); + alphaIn = mAlphaInLerp.getOut(); + alphaOut = mAlphaOutLerp(interpolatedTime); + } else { + mAnim->onAnimationFinished(); + alphaIn = mAlphaInLerp.getOut(); + alphaOut = mAlphaOutLerp.getOut(); + } + } else { + const float normalizedTime = float(now - mStartTime) / DURATION; + if (normalizedTime <= 1.0f) { + mLastNormalizedTime = normalizedTime; + const float interpolatedTime = INTERPOLATED_TIME(normalizedTime); + alphaIn = mAlphaInLerp(interpolatedTime); + alphaOut = 0.0f; + } else { + mLastNormalizedTime = 1.0f; + const float to_seconds = DURATION / seconds(1); + alphaIn = mAlphaInLerp.getOut(); + if (BOUNCES_AMPLITUDE > 0.0f) { + const float phi = BOUNCES_PER_SECOND * + (((normalizedTime - 1.0f) * to_seconds)*M_PI*2); + if (alphaIn > 1.0f) alphaIn = 1.0f; + else if (alphaIn < 0.0f) alphaIn = 0.0f; + alphaIn += BOUNCES_AMPLITUDE * (1.0f - cosf(phi)); + } + alphaOut = 0.0f; + } + mAlphaOutLerp.setIn(alphaIn); + } + drawScaled(1.0f, alphaIn, alphaOut); +} + +void LayerOrientationAnim::drawScaled(float scale, float alphaIn, float alphaOut) const +{ + copybit_image_t dst; + const GraphicPlane& plane(graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + //hw.getDisplaySurface(&dst); + + // clear screen + // TODO: with update on demand, we may be able + // to not erase the screen at all during the animation + if (!mOrientationCompleted) { + if (scale==1.0f && (alphaIn>=1.0f || alphaOut>=1.0f)) { + // we don't need to erase the screen in that case + } else { + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + } + } + + copybit_image_t src; + mBitmapIn->getBitmapSurface(&src); + + copybit_image_t srcOut; + mBitmapOut->getBitmapSurface(&srcOut); + + const int w = dst.w*scale; + const int h = dst.h*scale; + const int xc = uint32_t(dst.w-w)/2; + const int yc = uint32_t(dst.h-h)/2; + const copybit_rect_t drect = { xc, yc, xc+w, yc+h }; + const copybit_rect_t srect = { 0, 0, src.w, src.h }; + const Region reg(Rect( drect.l, drect.t, drect.r, drect.b )); + + GGLSurface t; + t.version = sizeof(GGLSurface); + t.width = src.w; + t.height = src.h; + t.stride = src.w; + t.vstride= src.h; + t.format = src.format; + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + + Transform tr; + tr.set(scale,0,0,scale); + tr.set(xc, yc); + + // FIXME: we should not access mVertices and mDrawingState like that, + // but since we control the animation, we know it's going to work okay. + // eventually we'd need a more formal way of doing things like this. + LayerOrientationAnim& self(const_cast<LayerOrientationAnim&>(*this)); + tr.transform(self.mVertices[0], 0, 0); + tr.transform(self.mVertices[1], 0, src.h); + tr.transform(self.mVertices[2], src.w, src.h); + tr.transform(self.mVertices[3], src.w, 0); + if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { + // Too slow to do this in software + self.mDrawingState.flags |= ISurfaceComposer::eLayerFilter; + } + + if (alphaIn > 0.0f) { + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + if (UNLIKELY(mTextureNameIn == -1LU)) { + mTextureNameIn = createTexture(); + GLuint w=0, h=0; + const Region dirty(Rect(t.width, t.height)); + loadTexture(dirty, mTextureNameIn, t, w, h); + } + self.mDrawingState.alpha = int(alphaIn*255); + drawWithOpenGL(reg, mTextureNameIn, t); + } + + if (alphaOut > 0.0f) { + t.data = (GGLubyte*)(intptr_t(srcOut.base) + srcOut.offset); + if (UNLIKELY(mTextureName == -1LU)) { + mTextureName = createTexture(); + GLuint w=0, h=0; + const Region dirty(Rect(t.width, t.height)); + loadTexture(dirty, mTextureName, t, w, h); + } + self.mDrawingState.alpha = int(alphaOut*255); + drawWithOpenGL(reg, mTextureName, t); + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/purgatory/LayerOrientationAnim.h b/libs/surfaceflinger/purgatory/LayerOrientationAnim.h new file mode 100644 index 0000000..a1a2654 --- /dev/null +++ b/libs/surfaceflinger/purgatory/LayerOrientationAnim.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_LAYER_ORIENTATION_ANIM_H +#define ANDROID_LAYER_ORIENTATION_ANIM_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/threads.h> +#include <binder/Parcel.h> + +#include "LayerBase.h" +#include "LayerBitmap.h" + +namespace android { + +// --------------------------------------------------------------------------- +class OrientationAnimation; + + +class LayerOrientationAnimBase : public LayerBase +{ +public: + LayerOrientationAnimBase(SurfaceFlinger* flinger, DisplayID display) + : LayerBase(flinger, display) { + } + virtual void onOrientationCompleted() = 0; +}; + +// --------------------------------------------------------------------------- + +class LayerOrientationAnim : public LayerOrientationAnimBase +{ +public: + static const uint32_t typeInfo; + static const char* const typeID; + virtual char const* getTypeID() const { return typeID; } + virtual uint32_t getTypeInfo() const { return typeInfo; } + + LayerOrientationAnim(SurfaceFlinger* flinger, DisplayID display, + OrientationAnimation* anim, + const sp<Buffer>& bitmapIn, + const sp<Buffer>& bitmapOut); + virtual ~LayerOrientationAnim(); + + void onOrientationCompleted(); + + virtual void onDraw(const Region& clip) const; + virtual Point getPhysicalSize() const; + virtual void validateVisibility(const Transform& globalTransform); + virtual bool needsBlending() const; + virtual bool isSecure() const { return false; } +private: + void drawScaled(float scale, float alphaIn, float alphaOut) const; + + class Lerp { + float in; + float outMinusIn; + public: + Lerp() : in(0), outMinusIn(0) { } + Lerp(float in, float out) : in(in), outMinusIn(out-in) { } + float getIn() const { return in; }; + float getOut() const { return in + outMinusIn; } + void set(float in, float out) { + this->in = in; + this->outMinusIn = out-in; + } + void setIn(float in) { + this->in = in; + } + void setOut(float out) { + this->outMinusIn = out - this->in; + } + float operator()(float t) const { + return outMinusIn*t + in; + } + }; + + OrientationAnimation* mAnim; + sp<Buffer> mBitmapIn; + sp<Buffer> mBitmapOut; + nsecs_t mStartTime; + nsecs_t mFinishTime; + bool mOrientationCompleted; + mutable bool mFirstRedraw; + mutable float mLastNormalizedTime; + mutable GLuint mTextureName; + mutable GLuint mTextureNameIn; + mutable bool mNeedsBlending; + + mutable Lerp mAlphaInLerp; + mutable Lerp mAlphaOutLerp; +}; + +// --------------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_LAYER_ORIENTATION_ANIM_H diff --git a/libs/surfaceflinger/purgatory/LayerOrientationAnimRotate.cpp b/libs/surfaceflinger/purgatory/LayerOrientationAnimRotate.cpp new file mode 100644 index 0000000..dc6b632 --- /dev/null +++ b/libs/surfaceflinger/purgatory/LayerOrientationAnimRotate.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SurfaceFlinger" + +#include <stdlib.h> +#include <stdint.h> +#include <sys/types.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include "LayerBase.h" +#include "LayerOrientationAnim.h" +#include "LayerOrientationAnimRotate.h" +#include "SurfaceFlinger.h" +#include "DisplayHardware/DisplayHardware.h" +#include "OrientationAnimation.h" + +namespace android { +// --------------------------------------------------------------------------- + +const uint32_t LayerOrientationAnimRotate::typeInfo = LayerBase::typeInfo | 0x100; +const char* const LayerOrientationAnimRotate::typeID = "LayerOrientationAnimRotate"; + +// --------------------------------------------------------------------------- + +const float ROTATION = M_PI * 0.5f; +const float ROTATION_FACTOR = 1.0f; // 1.0 or 2.0 +const float DURATION = ms2ns(200); +const float BOUNCES_PER_SECOND = 0.8; +const float BOUNCES_AMPLITUDE = (5.0f/180.f) * M_PI; + +LayerOrientationAnimRotate::LayerOrientationAnimRotate( + SurfaceFlinger* flinger, DisplayID display, + OrientationAnimation* anim, + const sp<Buffer>& bitmapIn, + const sp<Buffer>& bitmapOut) + : LayerOrientationAnimBase(flinger, display), mAnim(anim), + mBitmapIn(bitmapIn), mBitmapOut(bitmapOut), + mTextureName(-1), mTextureNameIn(-1) +{ + mStartTime = systemTime(); + mFinishTime = 0; + mOrientationCompleted = false; + mFirstRedraw = false; + mLastNormalizedTime = 0; + mLastAngle = 0; + mLastScale = 0; + mNeedsBlending = false; + const GraphicPlane& plane(graphicPlane(0)); + mOriginalTargetOrientation = plane.getOrientation(); +} + +LayerOrientationAnimRotate::~LayerOrientationAnimRotate() +{ + if (mTextureName != -1U) { + glDeleteTextures(1, &mTextureName); + } + if (mTextureNameIn != -1U) { + glDeleteTextures(1, &mTextureNameIn); + } +} + +bool LayerOrientationAnimRotate::needsBlending() const +{ + return mNeedsBlending; +} + +Point LayerOrientationAnimRotate::getPhysicalSize() const +{ + const GraphicPlane& plane(graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + return Point(hw.getWidth(), hw.getHeight()); +} + +void LayerOrientationAnimRotate::validateVisibility(const Transform&) +{ + const Layer::State& s(drawingState()); + const Transform tr(s.transform); + const Point size(getPhysicalSize()); + uint32_t w = size.x; + uint32_t h = size.y; + mTransformedBounds = tr.makeBounds(w, h); + mLeft = tr.tx(); + mTop = tr.ty(); + transparentRegionScreen.clear(); + mTransformed = true; +} + +void LayerOrientationAnimRotate::onOrientationCompleted() +{ + mFinishTime = systemTime(); + mOrientationCompleted = true; + mFirstRedraw = true; + mNeedsBlending = true; + mFlinger->invalidateLayerVisibility(this); +} + +void LayerOrientationAnimRotate::onDraw(const Region& clip) const +{ + // Animation... + + const nsecs_t now = systemTime(); + float angle, scale, alpha; + + if (mOrientationCompleted) { + if (mFirstRedraw) { + // make a copy of what's on screen + copybit_image_t image; + mBitmapIn->getBitmapSurface(&image); + const DisplayHardware& hw(graphicPlane(0).displayHardware()); + hw.copyBackToImage(image); + + // FIXME: code below is gross + mFirstRedraw = false; + mNeedsBlending = false; + LayerOrientationAnimRotate* self(const_cast<LayerOrientationAnimRotate*>(this)); + mFlinger->invalidateLayerVisibility(self); + } + + // make sure pick-up where we left off + const float duration = DURATION * mLastNormalizedTime; + const float normalizedTime = (float(now - mFinishTime) / duration); + if (normalizedTime <= 1.0f) { + const float squaredTime = normalizedTime*normalizedTime; + angle = (ROTATION*ROTATION_FACTOR - mLastAngle)*squaredTime + mLastAngle; + scale = (1.0f - mLastScale)*squaredTime + mLastScale; + alpha = normalizedTime; + } else { + mAnim->onAnimationFinished(); + angle = ROTATION; + alpha = 1.0f; + scale = 1.0f; + } + } else { + // FIXME: works only for portrait framebuffers + const Point size(getPhysicalSize()); + const float TARGET_SCALE = size.x * (1.0f / size.y); + const float normalizedTime = float(now - mStartTime) / DURATION; + if (normalizedTime <= 1.0f) { + mLastNormalizedTime = normalizedTime; + const float squaredTime = normalizedTime*normalizedTime; + angle = ROTATION * squaredTime; + scale = (TARGET_SCALE - 1.0f)*squaredTime + 1.0f; + alpha = 0; + } else { + mLastNormalizedTime = 1.0f; + angle = ROTATION; + if (BOUNCES_AMPLITUDE) { + const float to_seconds = DURATION / seconds(1); + const float phi = BOUNCES_PER_SECOND * + (((normalizedTime - 1.0f) * to_seconds)*M_PI*2); + angle += BOUNCES_AMPLITUDE * sinf(phi); + } + scale = TARGET_SCALE; + alpha = 0; + } + mLastAngle = angle; + mLastScale = scale; + } + drawScaled(angle, scale, alpha); +} + +void LayerOrientationAnimRotate::drawScaled(float f, float s, float alpha) const +{ + copybit_image_t dst; + const GraphicPlane& plane(graphicPlane(0)); + const DisplayHardware& hw(plane.displayHardware()); + //hw.getDisplaySurface(&dst); + + // clear screen + // TODO: with update on demand, we may be able + // to not erase the screen at all during the animation + glDisable(GL_BLEND); + glDisable(GL_DITHER); + glDisable(GL_SCISSOR_TEST); + glClearColor(0,0,0,0); + glClear(GL_COLOR_BUFFER_BIT); + + const int w = dst.w; + const int h = dst.h; + + copybit_image_t src; + mBitmapIn->getBitmapSurface(&src); + const copybit_rect_t srect = { 0, 0, src.w, src.h }; + + + GGLSurface t; + t.version = sizeof(GGLSurface); + t.width = src.w; + t.height = src.h; + t.stride = src.w; + t.vstride= src.h; + t.format = src.format; + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + + if (!mOriginalTargetOrientation) { + f = -f; + } + + Transform tr; + tr.set(f, w*0.5f, h*0.5f); + tr.scale(s, w*0.5f, h*0.5f); + + // FIXME: we should not access mVertices and mDrawingState like that, + // but since we control the animation, we know it's going to work okay. + // eventually we'd need a more formal way of doing things like this. + LayerOrientationAnimRotate& self(const_cast<LayerOrientationAnimRotate&>(*this)); + tr.transform(self.mVertices[0], 0, 0); + tr.transform(self.mVertices[1], 0, src.h); + tr.transform(self.mVertices[2], src.w, src.h); + tr.transform(self.mVertices[3], src.w, 0); + + if (!(mFlags & DisplayHardware::SLOW_CONFIG)) { + // Too slow to do this in software + self.mDrawingState.flags |= ISurfaceComposer::eLayerFilter; + } + + if (UNLIKELY(mTextureName == -1LU)) { + mTextureName = createTexture(); + GLuint w=0, h=0; + const Region dirty(Rect(t.width, t.height)); + loadTexture(dirty, mTextureName, t, w, h); + } + self.mDrawingState.alpha = 255; //-int(alpha*255); + const Region clip(Rect( srect.l, srect.t, srect.r, srect.b )); + drawWithOpenGL(clip, mTextureName, t); + + if (alpha > 0) { + const float sign = (!mOriginalTargetOrientation) ? 1.0f : -1.0f; + tr.set(f + sign*(M_PI * 0.5f * ROTATION_FACTOR), w*0.5f, h*0.5f); + tr.scale(s, w*0.5f, h*0.5f); + tr.transform(self.mVertices[0], 0, 0); + tr.transform(self.mVertices[1], 0, src.h); + tr.transform(self.mVertices[2], src.w, src.h); + tr.transform(self.mVertices[3], src.w, 0); + + copybit_image_t src; + mBitmapIn->getBitmapSurface(&src); + t.data = (GGLubyte*)(intptr_t(src.base) + src.offset); + if (UNLIKELY(mTextureNameIn == -1LU)) { + mTextureNameIn = createTexture(); + GLuint w=0, h=0; + const Region dirty(Rect(t.width, t.height)); + loadTexture(dirty, mTextureNameIn, t, w, h); + } + self.mDrawingState.alpha = int(alpha*255); + const Region clip(Rect( srect.l, srect.t, srect.r, srect.b )); + drawWithOpenGL(clip, mTextureNameIn, t); + } +} + +// --------------------------------------------------------------------------- + +}; // namespace android diff --git a/libs/surfaceflinger/LayerOrientationAnim.h b/libs/surfaceflinger/purgatory/LayerOrientationAnimRotate.h index 12b6f1c..a88eec0 100644 --- a/libs/surfaceflinger/LayerOrientationAnim.h +++ b/libs/surfaceflinger/purgatory/LayerOrientationAnimRotate.h @@ -14,13 +14,13 @@ * limitations under the License. */ -#ifndef ANDROID_LAYER_ORIENTATION_ANIM_H -#define ANDROID_LAYER_ORIENTATION_ANIM_H +#ifndef ANDROID_LAYER_ORIENTATION_ANIM_ROTATE_H +#define ANDROID_LAYER_ORIENTATION_ANIM_ROTATE_H #include <stdint.h> #include <sys/types.h> #include <utils/threads.h> -#include <utils/Parcel.h> +#include <binder/Parcel.h> #include "LayerBase.h" #include "LayerBitmap.h" @@ -30,19 +30,7 @@ namespace android { // --------------------------------------------------------------------------- class OrientationAnimation; - -class LayerOrientationAnimBase : public LayerBase -{ -public: - LayerOrientationAnimBase(SurfaceFlinger* flinger, DisplayID display) - : LayerBase(flinger, display) { - } - virtual void onOrientationCompleted() = 0; -}; - -// --------------------------------------------------------------------------- - -class LayerOrientationAnim : public LayerOrientationAnimBase +class LayerOrientationAnimRotate : public LayerOrientationAnimBase { public: static const uint32_t typeInfo; @@ -50,11 +38,11 @@ public: virtual char const* getTypeID() const { return typeID; } virtual uint32_t getTypeInfo() const { return typeInfo; } - LayerOrientationAnim(SurfaceFlinger* flinger, DisplayID display, + LayerOrientationAnimRotate(SurfaceFlinger* flinger, DisplayID display, OrientationAnimation* anim, - const LayerBitmap& bitmapIn, - const LayerBitmap& bitmapOut); - virtual ~LayerOrientationAnim(); + const sp<Buffer>& bitmapIn, + const sp<Buffer>& bitmapOut); + virtual ~LayerOrientationAnimRotate(); void onOrientationCompleted(); @@ -64,10 +52,19 @@ public: virtual bool needsBlending() const; virtual bool isSecure() const { return false; } private: + void drawScaled(float angle, float scale, float alpha) const; + OrientationAnimation* mAnim; - LayerBitmap mBitmapIn; - LayerBitmap mBitmapOut; + sp<Buffer> mBitmapIn; + sp<Buffer> mBitmapOut; + nsecs_t mStartTime; + nsecs_t mFinishTime; bool mOrientationCompleted; + int mOriginalTargetOrientation; + mutable bool mFirstRedraw; + mutable float mLastNormalizedTime; + mutable float mLastAngle; + mutable float mLastScale; mutable GLuint mTextureName; mutable GLuint mTextureNameIn; mutable bool mNeedsBlending; @@ -77,4 +74,4 @@ private: }; // namespace android -#endif // ANDROID_LAYER_ORIENTATION_ANIM_H +#endif // ANDROID_LAYER_ORIENTATION_ANIM_ROTATE_H diff --git a/libs/surfaceflinger/OrientationAnimation.cpp b/libs/surfaceflinger/purgatory/OrientationAnimation.cpp index 12c0eef..a6c9c28 100644 --- a/libs/surfaceflinger/OrientationAnimation.cpp +++ b/libs/surfaceflinger/purgatory/OrientationAnimation.cpp @@ -14,16 +14,14 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdint.h> #include <sys/types.h> #include <limits.h> #include "LayerOrientationAnim.h" +#include "LayerOrientationAnimRotate.h" #include "OrientationAnimation.h" #include "SurfaceFlinger.h" -#include "VRamHeap.h" #include "DisplayHardware/DisplayHardware.h" @@ -34,9 +32,6 @@ namespace android { OrientationAnimation::OrientationAnimation(const sp<SurfaceFlinger>& flinger) : mFlinger(flinger), mLayerOrientationAnim(NULL), mState(DONE) { - // allocate a memory-dealer for this the first time - mTemporaryDealer = mFlinger->getSurfaceHeapManager()->createHeap( - ISurfaceComposer::eHardware); } OrientationAnimation::~OrientationAnimation() @@ -97,22 +92,22 @@ bool OrientationAnimation::prepare() const uint32_t w = hw.getWidth(); const uint32_t h = hw.getHeight(); - LayerBitmap bitmap; - bitmap.init(mTemporaryDealer); - bitmap.setBits(w, h, 1, hw.getFormat()); - - LayerBitmap bitmapIn; - bitmapIn.init(mTemporaryDealer); - bitmapIn.setBits(w, h, 1, hw.getFormat()); + sp<Buffer> bitmap = new Buffer(w, h, hw.getFormat()); + sp<Buffer> bitmapIn = new Buffer(w, h, hw.getFormat()); copybit_image_t front; - bitmap.getBitmapSurface(&front); - hw.copyFrontToImage(front); + bitmap->getBitmapSurface(&front); + hw.copyFrontToImage(front); // FIXME: we need an extension to do this - LayerOrientationAnimBase* l; + sp<LayerOrientationAnimBase> l; - l = new LayerOrientationAnim( - mFlinger.get(), 0, this, bitmap, bitmapIn); + if (mType & 0x80) { + l = new LayerOrientationAnimRotate( + mFlinger.get(), 0, this, bitmap, bitmapIn); + } else { + l = new LayerOrientationAnim( + mFlinger.get(), 0, this, bitmap, bitmapIn); + } l->initStates(w, h, 0); l->setLayer(INT_MAX-1); @@ -131,7 +126,7 @@ bool OrientationAnimation::phase1() return true; } - //mLayerOrientationAnim->invalidate(); + mLayerOrientationAnim->invalidate(); return false; } @@ -146,7 +141,7 @@ bool OrientationAnimation::finished() { mState = DONE; mFlinger->removeLayer(mLayerOrientationAnim); - mLayerOrientationAnim = NULL; + mLayerOrientationAnim.clear(); return true; } diff --git a/libs/surfaceflinger/OrientationAnimation.h b/libs/surfaceflinger/purgatory/OrientationAnimation.h index cafa38d..8ba6621 100644 --- a/libs/surfaceflinger/OrientationAnimation.h +++ b/libs/surfaceflinger/purgatory/OrientationAnimation.h @@ -72,8 +72,7 @@ private: bool finished(); sp<SurfaceFlinger> mFlinger; - sp<MemoryDealer> mTemporaryDealer; - LayerOrientationAnimBase* mLayerOrientationAnim; + sp< LayerOrientationAnimBase > mLayerOrientationAnim; int mState; uint32_t mType; }; diff --git a/libs/surfaceflinger/VRamHeap.cpp b/libs/surfaceflinger/purgatory/VRamHeap.cpp index 5f633bd..f3ed790 100644 --- a/libs/surfaceflinger/VRamHeap.cpp +++ b/libs/surfaceflinger/purgatory/VRamHeap.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlinger" - #include <stdlib.h> #include <stdio.h> #include <stdint.h> @@ -35,8 +33,6 @@ #include <utils/MemoryHeapPmem.h> #include <utils/MemoryHeapBase.h> -#include <EGL/eglnatives.h> - #include "GPUHardware/GPUHardware.h" #include "SurfaceFlinger.h" #include "VRamHeap.h" @@ -100,7 +96,7 @@ sp<MemoryDealer> SurfaceHeapManager::createHeap( } } - if ((flags & ISurfaceComposer::eGPU) && (mFlinger->getGPU() != 0)) { + if (flags & ISurfaceComposer::eGPU) { // FIXME: this is msm7201A specific, where gpu surfaces may not be secure if (!(flags & ISurfaceComposer::eSecure)) { // if GPU doesn't work, we try eHardware diff --git a/libs/surfaceflinger/VRamHeap.h b/libs/surfaceflinger/purgatory/VRamHeap.h index 9140167..9140167 100644 --- a/libs/surfaceflinger/VRamHeap.h +++ b/libs/surfaceflinger/purgatory/VRamHeap.h diff --git a/libs/surfaceflinger/tests/overlays/overlays.cpp b/libs/surfaceflinger/tests/overlays/overlays.cpp index f3c046f..0b9322e 100644 --- a/libs/surfaceflinger/tests/overlays/overlays.cpp +++ b/libs/surfaceflinger/tests/overlays/overlays.cpp @@ -1,6 +1,6 @@ -#include <utils/IPCThreadState.h> -#include <utils/ProcessState.h> -#include <utils/IServiceManager.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <binder/IServiceManager.h> #include <utils/Log.h> #include <ui/Surface.h> diff --git a/libs/surfaceflinger/tests/resize/Android.mk b/libs/surfaceflinger/tests/resize/Android.mk new file mode 100644 index 0000000..ef1532f --- /dev/null +++ b/libs/surfaceflinger/tests/resize/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + resize.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui + +LOCAL_MODULE:= test-resize + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libs/surfaceflinger/tests/resize/resize.cpp b/libs/surfaceflinger/tests/resize/resize.cpp new file mode 100644 index 0000000..21c6ab6 --- /dev/null +++ b/libs/surfaceflinger/tests/resize/resize.cpp @@ -0,0 +1,60 @@ +#include <cutils/memory.h> + +#include <utils/IPCThreadState.h> +#include <utils/ProcessState.h> +#include <utils/IServiceManager.h> +#include <utils/Log.h> + +#include <ui/Surface.h> +#include <ui/ISurface.h> +#include <ui/Overlay.h> +#include <ui/SurfaceComposerClient.h> + +using namespace android; + +namespace android { +class Test { +public: + static const sp<ISurface>& getISurface(const sp<Surface>& s) { + return s->getISurface(); + } +}; +}; + +int main(int argc, char** argv) +{ + // set up the thread-pool + sp<ProcessState> proc(ProcessState::self()); + ProcessState::self()->startThreadPool(); + + // create a client to surfaceflinger + sp<SurfaceComposerClient> client = new SurfaceComposerClient(); + + // create pushbuffer surface + sp<Surface> surface = client->createSurface(getpid(), 0, 160, 240, + PIXEL_FORMAT_RGB_565); + + + client->openTransaction(); + surface->setLayer(100000); + client->closeTransaction(); + + Surface::SurfaceInfo info; + surface->lock(&info); + ssize_t bpr = info.s * bytesPerPixel(info.format); + android_memset16((uint16_t*)info.bits, 0xF800, bpr*info.h); + surface->unlockAndPost(); + + surface->lock(&info); + android_memset16((uint16_t*)info.bits, 0x07E0, bpr*info.h); + surface->unlockAndPost(); + + client->openTransaction(); + surface->setSize(320, 240); + client->closeTransaction(); + + + IPCThreadState::self()->joinThreadPool(); + + return 0; +} diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index 7bbe38b..49939ca 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -2,12 +2,12 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + BufferMapper.cpp \ Camera.cpp \ CameraParameters.cpp \ - EGLDisplaySurface.cpp \ - EGLNativeWindowSurface.cpp \ EventHub.cpp \ EventRecurrence.cpp \ + FramebufferNativeWindow.cpp \ KeyLayoutMap.cpp \ KeyCharacterMap.cpp \ ICamera.cpp \ @@ -27,9 +27,9 @@ LOCAL_SRC_FILES:= \ SurfaceFlingerSynchro.cpp LOCAL_SHARED_LIBRARIES := \ - libcorecg \ libcutils \ libutils \ + libbinder \ libpixelflinger \ libhardware \ libhardware_legacy diff --git a/libs/ui/BufferMapper.cpp b/libs/ui/BufferMapper.cpp new file mode 100644 index 0000000..92a9a86 --- /dev/null +++ b/libs/ui/BufferMapper.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BufferMapper" + +#include <stdint.h> +#include <errno.h> + +#include <utils/Errors.h> +#include <utils/Log.h> + +#include <ui/BufferMapper.h> +#include <ui/Rect.h> + +#include <hardware/gralloc.h> + + +namespace android { +// --------------------------------------------------------------------------- + +ANDROID_SINGLETON_STATIC_INSTANCE( BufferMapper ) + +BufferMapper::BufferMapper() + : mAllocMod(0) +{ + hw_module_t const* module; + int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module); + LOGE_IF(err, "FATAL: can't find the %s module", GRALLOC_HARDWARE_MODULE_ID); + if (err == 0) { + mAllocMod = (gralloc_module_t const *)module; + } +} + +status_t BufferMapper::registerBuffer(buffer_handle_t handle) +{ + status_t err = mAllocMod->registerBuffer(mAllocMod, handle); + LOGW_IF(err, "registerBuffer(%p) failed %d (%s)", + handle, err, strerror(-err)); + return err; +} + +status_t BufferMapper::unregisterBuffer(buffer_handle_t handle) +{ + status_t err = mAllocMod->unregisterBuffer(mAllocMod, handle); + LOGW_IF(err, "unregisterBuffer(%p) failed %d (%s)", + handle, err, strerror(-err)); + return err; +} + +status_t BufferMapper::lock(buffer_handle_t handle, + int usage, const Rect& bounds, void** vaddr) +{ + status_t err = mAllocMod->lock(mAllocMod, handle, usage, + bounds.left, bounds.top, bounds.width(), bounds.height(), vaddr); + LOGW_IF(err, "unlock(...) failed %d (%s)", err, strerror(-err)); + return err; +} + +status_t BufferMapper::unlock(buffer_handle_t handle) +{ + status_t err = mAllocMod->unlock(mAllocMod, handle); + LOGW_IF(err, "unlock(...) failed %d (%s)", err, strerror(-err)); + return err; +} + +// --------------------------------------------------------------------------- +}; // namespace android diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp index 5015379..12a7725 100644 --- a/libs/ui/Camera.cpp +++ b/libs/ui/Camera.cpp @@ -19,9 +19,9 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "Camera" #include <utils/Log.h> -#include <utils/IServiceManager.h> +#include <binder/IServiceManager.h> #include <utils/threads.h> -#include <utils/IMemory.h> +#include <binder/IMemory.h> #include <ui/Surface.h> #include <ui/Camera.h> #include <ui/ICameraService.h> diff --git a/libs/ui/EGLDisplaySurface.cpp b/libs/ui/EGLDisplaySurface.cpp deleted file mode 100644 index d06c98b..0000000 --- a/libs/ui/EGLDisplaySurface.cpp +++ /dev/null @@ -1,519 +0,0 @@ -/* - ** - ** Copyright 2007 The Android Open Source Project - ** - ** Licensed under the Apache License Version 2.0(the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing software - ** distributed under the License is distributed on an "AS IS" BASIS - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#define LOG_TAG "EGLDisplaySurface" - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/mman.h> - -#include <cutils/log.h> -#include <cutils/atomic.h> -#include <cutils/properties.h> - -#include <hardware/copybit.h> - -#include <ui/SurfaceComposerClient.h> -#include <ui/DisplayInfo.h> -#include <ui/Rect.h> -#include <ui/Region.h> -#include <ui/EGLDisplaySurface.h> - -#if HAVE_ANDROID_OS -#include <linux/msm_mdp.h> -#endif - -#include <EGL/egl.h> - -#include <pixelflinger/format.h> - - -// ---------------------------------------------------------------------------- - -egl_native_window_t* android_createDisplaySurface() -{ - egl_native_window_t* s = new android::EGLDisplaySurface(); - s->memory_type = NATIVE_MEMORY_TYPE_GPU; - return s; -} - -#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true )) -#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false )) - -// ---------------------------------------------------------------------------- -namespace android { -// ---------------------------------------------------------------------------- - -EGLDisplaySurface::EGLDisplaySurface() - : EGLNativeSurface<EGLDisplaySurface>() -{ - egl_native_window_t::version = sizeof(egl_native_window_t); - egl_native_window_t::ident = 0; - egl_native_window_t::incRef = &EGLDisplaySurface::hook_incRef; - egl_native_window_t::decRef = &EGLDisplaySurface::hook_decRef; - egl_native_window_t::swapBuffers = &EGLDisplaySurface::hook_swapBuffers; - egl_native_window_t::connect = 0; - egl_native_window_t::disconnect = 0; - - mFb[0].data = 0; - mFb[1].data = 0; - mBlitEngine = 0; - egl_native_window_t::fd = mapFrameBuffer(); - if (egl_native_window_t::fd >= 0) { - - hw_module_t const* module; - if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { - copybit_open(module, &mBlitEngine); - } - - const float in2mm = 25.4f; - float refreshRate = 1000000000000000LLU / ( - float( mInfo.upper_margin + mInfo.lower_margin + mInfo.yres ) - * ( mInfo.left_margin + mInfo.right_margin + mInfo.xres ) - * mInfo.pixclock); - - const GGLSurface& buffer = mFb[1 - mIndex]; - egl_native_window_t::width = buffer.width; - egl_native_window_t::height = buffer.height; - egl_native_window_t::stride = buffer.stride; - egl_native_window_t::format = buffer.format; - egl_native_window_t::base = intptr_t(mFb[0].data); - egl_native_window_t::offset = - intptr_t(buffer.data) - egl_native_window_t::base; - egl_native_window_t::flags = 0; - egl_native_window_t::xdpi = (mInfo.xres * in2mm) / mInfo.width; - egl_native_window_t::ydpi = (mInfo.yres * in2mm) / mInfo.height; - egl_native_window_t::fps = refreshRate; - egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_FB; - // no error, set the magic word - egl_native_window_t::magic = 0x600913; - } - mSwapCount = -1; - mPageFlipCount = 0; -} - -EGLDisplaySurface::~EGLDisplaySurface() -{ - magic = 0; - copybit_close(mBlitEngine); - mBlitEngine = 0; - close(egl_native_window_t::fd); - munmap(mFb[0].data, mSize); - if (!(mFlags & PAGE_FLIP)) - free((void*)mFb[1].data); -} - -void EGLDisplaySurface::hook_incRef(NativeWindowType window) { - EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window); - that->incStrong(that); -} -void EGLDisplaySurface::hook_decRef(NativeWindowType window) { - EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window); - that->decStrong(that); -} -uint32_t EGLDisplaySurface::hook_swapBuffers(NativeWindowType window) { - EGLDisplaySurface* that = static_cast<EGLDisplaySurface*>(window); - return that->swapBuffers(); -} - -void EGLDisplaySurface::setSwapRectangle(int l, int t, int w, int h) -{ - mInfo.reserved[0] = 0x54445055; // "UPDT"; - mInfo.reserved[1] = (uint16_t)l | ((uint32_t)t << 16); - mInfo.reserved[2] = (uint16_t)(l+w) | ((uint32_t)(t+h) << 16); -} - -uint32_t EGLDisplaySurface::swapBuffers() -{ -#define SHOW_FPS 0 -#if SHOW_FPS - nsecs_t now = systemTime(); - if (mSwapCount == -1) { - mTime = now; - mSwapCount = 0; - mSleep = 0; - } else { - nsecs_t d = now-mTime; - if (d >= seconds(1)) { - double fps = (mSwapCount * double(seconds(1))) / double(d); - LOGD("%f fps, sleep=%d / frame", - fps, (int)ns2us(mSleep / mSwapCount)); - mSwapCount = 0; - mTime = now; - mSleep = 0; - } else { - mSwapCount++; - } - } -#endif - /* If we can't do the page_flip, just copy the back buffer to the front */ - if (!(mFlags & PAGE_FLIP)) { - memcpy(mFb[0].data, mFb[1].data, mInfo.xres*mInfo.yres*2); - return 0; - } - - // do the actual flip - mIndex = 1 - mIndex; - mInfo.activate = FB_ACTIVATE_VBL; - mInfo.yoffset = mIndex ? mInfo.yres : 0; - if (ioctl(egl_native_window_t::fd, FBIOPUT_VSCREENINFO, &mInfo) == -1) { - LOGE("FBIOPUT_VSCREENINFO failed"); - return 0; - } - - /* - * this is a monstrous hack: Because the h/w accelerator is not able - * to render directly into the framebuffer, we need to copy its - * internal framebuffer out to the fb. - * oem[0] is used to access the fd of internal fb. - * All this is needed only in standalone mode, in SurfaceFlinger mode - * we control where the GPU renders. - * We do this only if we have copybit, since this hack is needed only - * with msm7k. - */ - if (egl_native_window_t::memory_type == NATIVE_MEMORY_TYPE_GPU && oem[0] && mBlitEngine) { - copybit_device_t *copybit = mBlitEngine; - copybit_rect_t sdrect = { 0, 0, - egl_native_window_t::width, egl_native_window_t::height }; - copybit_image_t dst = { - egl_native_window_t::width, - egl_native_window_t::height, - egl_native_window_t::format, - egl_native_window_t::offset, - (void*)egl_native_window_t::base, - egl_native_window_t::fd - }; - copybit_image_t src = { - egl_native_window_t::width, - egl_native_window_t::height, - egl_native_window_t::format, // XXX: use proper format - egl_native_window_t::offset, - (void*)egl_native_window_t::base, // XXX: use proper base - egl_native_window_t::oem[0] - }; - region_iterator it(Region(Rect( - egl_native_window_t::width, egl_native_window_t::height))); - copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0); - copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF); - copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_DISABLE); - copybit->stretch(copybit, &dst, &src, &sdrect, &sdrect, &it); - } - - // update the address of the buffer to draw to next - const GGLSurface& buffer = mFb[1 - mIndex]; - egl_native_window_t::offset = - intptr_t(buffer.data) - egl_native_window_t::base; - -#if SHOW_FPS - mSleep += systemTime()-now; -#endif - - mPageFlipCount++; - - // We don't support screen-size changes for now - return 0; -} - -int32_t EGLDisplaySurface::getPageFlipCount() const -{ - return mPageFlipCount; -} - -void EGLDisplaySurface::copyFrontToBack(const Region& copyback) -{ -#if HAVE_ANDROID_OS - if (mBlitEngine) { - copybit_image_t dst = { - w: egl_native_window_t::stride, - h: egl_native_window_t::height, - format: egl_native_window_t::format, - offset: mFb[1-mIndex].data - mFb[0].data, - base: (void*)egl_native_window_t::base, - fd: egl_native_window_t::fd - }; - copybit_image_t src = { - w: egl_native_window_t::stride, - h: egl_native_window_t::height, - format: egl_native_window_t::format, - offset: mFb[mIndex].data - mFb[0].data, - base: (void*)egl_native_window_t::base, - fd: egl_native_window_t::fd - }; - region_iterator it(copyback); - mBlitEngine->blit(mBlitEngine, &dst, &src, &it); - } else -#endif - { - /* no extra copy needed since we copied back to front instead of - * flipping */ - if (!(mFlags & PAGE_FLIP)) { - return; - } - - Region::iterator iterator(copyback); - if (iterator) { - Rect r; - uint8_t* const screen_src = mFb[ mIndex].data; - uint8_t* const screen_dst = mFb[1-mIndex].data; - const size_t bpp = bytesPerPixel(egl_native_window_t::format); - const size_t bpr = egl_native_window_t::stride * bpp; - while (iterator.iterate(&r)) { - ssize_t h = r.bottom - r.top; - if (h) { - size_t size = (r.right - r.left) * bpp; - size_t o = (r.left + egl_native_window_t::stride * r.top) * bpp; - uint8_t* s = screen_src + o; - uint8_t* d = screen_dst + o; - if (size == bpr) { - size *= h; - h = 1; - } - do { - memcpy(d, s, size); - d += bpr; - s += bpr; - } while (--h > 0); - } - } - } - } -} - -void EGLDisplaySurface::copyFrontToImage(const copybit_image_t& dst) -{ -#if HAVE_ANDROID_OS - if (mBlitEngine) { - copybit_image_t src = { - w: egl_native_window_t::stride, - h: egl_native_window_t::height, - format: egl_native_window_t::format, - offset: mFb[mIndex].data - mFb[0].data, - base: (void*)egl_native_window_t::base, - fd: egl_native_window_t::fd - }; - region_iterator it(Region(Rect( - egl_native_window_t::width, egl_native_window_t::height))); - mBlitEngine->blit(mBlitEngine, &dst, &src, &it); - } else -#endif - { - uint8_t* const screen_src = mFb[ mIndex].data; - const size_t bpp = bytesPerPixel(egl_native_window_t::format); - const size_t bpr = egl_native_window_t::stride * bpp; - memcpy((char*)dst.base + dst.offset, screen_src, - bpr*egl_native_window_t::height); - } -} - -void EGLDisplaySurface::copyBackToImage(const copybit_image_t& dst) -{ -#if HAVE_ANDROID_OS - if (mBlitEngine) { - copybit_image_t src = { - w: egl_native_window_t::stride, - h: egl_native_window_t::height, - format: egl_native_window_t::format, - offset: mFb[1-mIndex].data - mFb[0].data, - base: (void*)egl_native_window_t::base, - fd: egl_native_window_t::fd - }; - region_iterator it(Region(Rect( - egl_native_window_t::width, egl_native_window_t::height))); - mBlitEngine->blit(mBlitEngine, &dst, &src, &it); - } else -#endif - { - uint8_t* const screen_src = mFb[1-mIndex].data; - const size_t bpp = bytesPerPixel(egl_native_window_t::format); - const size_t bpr = egl_native_window_t::stride * bpp; - memcpy((char*)dst.base + dst.offset, screen_src, - bpr*egl_native_window_t::height); - } -} - - -status_t EGLDisplaySurface::mapFrameBuffer() -{ - char const * const device_template[] = { - "/dev/graphics/fb%u", - "/dev/fb%u", - 0 }; - int fd = -1; - int i=0; - char name[64]; - while ((fd==-1) && device_template[i]) { - snprintf(name, 64, device_template[i], 0); - fd = open(name, O_RDWR, 0); - i++; - } - if (fd < 0) - return -errno; - - struct fb_fix_screeninfo finfo; - if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) - return -errno; - - struct fb_var_screeninfo info; - if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) - return -errno; - - info.reserved[0] = 0; - info.reserved[1] = 0; - info.reserved[2] = 0; - info.xoffset = 0; - info.yoffset = 0; - info.yres_virtual = info.yres * 2; - info.bits_per_pixel = 16; - /* Explicitly request 5/6/5 */ - info.red.offset = 11; - info.red.length = 5; - info.green.offset = 5; - info.green.length = 6; - info.blue.offset = 0; - info.blue.length = 5; - info.transp.offset = 0; - info.transp.length = 0; - info.activate = FB_ACTIVATE_NOW; - - uint32_t flags = PAGE_FLIP; - if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { - info.yres_virtual = info.yres; - flags &= ~PAGE_FLIP; - LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); - } - - if (info.yres_virtual < info.yres * 2) { - info.yres_virtual = info.yres; - flags &= ~PAGE_FLIP; - LOGW("page flipping not supported (yres_virtual=%d, requested=%d)", - info.yres_virtual, info.yres*2); - } - - if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) - return -errno; - - int refreshRate = 1000000000000000LLU / - ( - uint64_t( info.upper_margin + info.lower_margin + info.yres ) - * ( info.left_margin + info.right_margin + info.xres ) - * info.pixclock - ); - - if (refreshRate == 0) { - // bleagh, bad info from the driver - refreshRate = 60*1000; // 60 Hz - } - if (int(info.width) <= 0 || int(info.height) <= 0) { - // the driver doesn't return that information - // default to 160 dpi - info.width = ((info.xres * 25.4f)/160.0f + 0.5f); - info.height = ((info.yres * 25.4f)/160.0f + 0.5f); - } - - float xdpi = (info.xres * 25.4f) / info.width; - float ydpi = (info.yres * 25.4f) / info.height; - float fps = refreshRate / 1000.0f; - - LOGI( "using (fd=%d)\n" - "id = %s\n" - "xres = %d px\n" - "yres = %d px\n" - "xres_virtual = %d px\n" - "yres_virtual = %d px\n" - "bpp = %d\n" - "r = %2u:%u\n" - "g = %2u:%u\n" - "b = %2u:%u\n", - fd, - finfo.id, - info.xres, - info.yres, - info.xres_virtual, - info.yres_virtual, - info.bits_per_pixel, - info.red.offset, info.red.length, - info.green.offset, info.green.length, - info.blue.offset, info.blue.length - ); - - LOGI( "width = %d mm (%f dpi)\n" - "height = %d mm (%f dpi)\n" - "refresh rate = %.2f Hz\n", - info.width, xdpi, - info.height, ydpi, - fps - ); - - - if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) - return -errno; - - if (finfo.smem_len <= 0) - return -errno; - - /* - * Open and map the display. - */ - - void* buffer = (uint16_t*) mmap( - 0, finfo.smem_len, - PROT_READ | PROT_WRITE, - MAP_SHARED, - fd, 0); - - if (buffer == MAP_FAILED) - return -errno; - - // at least for now, always clear the fb - memset(buffer, 0, finfo.smem_len); - - uint8_t* offscreen[2]; - offscreen[0] = (uint8_t*)buffer; - if (flags & PAGE_FLIP) { - offscreen[1] = (uint8_t*)buffer + finfo.line_length*info.yres; - } else { - offscreen[1] = (uint8_t*)malloc(finfo.smem_len); - if (offscreen[1] == 0) { - munmap(buffer, finfo.smem_len); - return NO_MEMORY; - } - } - - mFlags = flags; - mInfo = info; - mFinfo = finfo; - mSize = finfo.smem_len; - mIndex = 0; - for (int i=0 ; i<2 ; i++) { - mFb[i].version = sizeof(GGLSurface); - mFb[i].width = info.xres; - mFb[i].height = info.yres; - mFb[i].stride = finfo.line_length / (info.bits_per_pixel >> 3); - mFb[i].data = (GGLubyte*)(offscreen[i]); - mFb[i].format = GGL_PIXEL_FORMAT_RGB_565; - } - return fd; -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- diff --git a/libs/ui/EGLNativeWindowSurface.cpp b/libs/ui/EGLNativeWindowSurface.cpp deleted file mode 100644 index f1071cf..0000000 --- a/libs/ui/EGLNativeWindowSurface.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* -** -** Copyright 2007 The Android Open Source Project -** -** Licensed under the Apache License Version 2.0(the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing software -** distributed under the License is distributed on an "AS IS" BASIS -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "EGLNativeWindowSurface" - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> - -#include <cutils/log.h> -#include <cutils/atomic.h> - -#include <ui/SurfaceComposerClient.h> -#include <ui/DisplayInfo.h> -#include <ui/Rect.h> - -#include <EGL/egl.h> - -#include <pixelflinger/format.h> - -#include <ui/EGLNativeWindowSurface.h> - -// ---------------------------------------------------------------------------- -namespace android { -// ---------------------------------------------------------------------------- - -EGLNativeWindowSurface::EGLNativeWindowSurface(const sp<Surface>& surface) - : EGLNativeSurface<EGLNativeWindowSurface>(), - mSurface(surface), mConnected(false) -{ - egl_native_window_t::magic = 0x600913; - egl_native_window_t::version = sizeof(egl_native_window_t); - egl_native_window_t::ident = 0; - egl_native_window_t::incRef = &EGLNativeWindowSurface::hook_incRef; - egl_native_window_t::decRef = &EGLNativeWindowSurface::hook_decRef; - egl_native_window_t::swapBuffers = &EGLNativeWindowSurface::hook_swapBuffers; - egl_native_window_t::connect = &EGLNativeWindowSurface::hook_connect; - egl_native_window_t::disconnect = &EGLNativeWindowSurface::hook_disconnect; - - DisplayInfo dinfo; - SurfaceComposerClient::getDisplayInfo(0, &dinfo); - egl_native_window_t::xdpi = dinfo.xdpi; - egl_native_window_t::ydpi = dinfo.ydpi; - egl_native_window_t::fps = dinfo.fps; - egl_native_window_t::flags= EGL_NATIVES_FLAG_DESTROY_BACKBUFFER; -} - -EGLNativeWindowSurface::~EGLNativeWindowSurface() -{ - disconnect(); - mSurface.clear(); - magic = 0; -} - -void EGLNativeWindowSurface::hook_incRef(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); - that->incStrong(that); -} - -void EGLNativeWindowSurface::hook_decRef(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); - that->decStrong(that); -} - -void EGLNativeWindowSurface::hook_connect(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); - that->connect(); -} - -void EGLNativeWindowSurface::hook_disconnect(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); - that->disconnect(); -} - -uint32_t EGLNativeWindowSurface::hook_swapBuffers(NativeWindowType window) -{ - EGLNativeWindowSurface* that = static_cast<EGLNativeWindowSurface*>(window); - return that->swapBuffers(); -} - -void EGLNativeWindowSurface::setSwapRectangle(int l, int t, int w, int h) -{ - mSurface->setSwapRectangle(Rect(l, t, l+w, t+h)); -} - -uint32_t EGLNativeWindowSurface::swapBuffers() -{ - const int w = egl_native_window_t::width; - const int h = egl_native_window_t::height; - const sp<Surface>& surface(mSurface); - Surface::SurfaceInfo info; - surface->unlockAndPost(); - surface->lock(&info); - // update the address of the buffer to draw to next - egl_native_window_t::base = intptr_t(info.base); - egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base); - - // update size if it changed - if (w != int(info.w) || h != int(info.h)) { - egl_native_window_t::width = info.w; - egl_native_window_t::height = info.h; - egl_native_window_t::stride = info.bpr / bytesPerPixel(info.format); - egl_native_window_t::format = info.format; - return EGL_NATIVES_FLAG_SIZE_CHANGED; - } - return 0; -} - -void EGLNativeWindowSurface::connect() -{ - if (!mConnected) { - Surface::SurfaceInfo info; - mSurface->lock(&info); - mSurface->setSwapRectangle(Rect(info.w, info.h)); - mConnected = true; - - egl_native_window_t::width = info.w; - egl_native_window_t::height = info.h; - egl_native_window_t::stride = info.bpr / bytesPerPixel(info.format); - egl_native_window_t::format = info.format; - egl_native_window_t::base = intptr_t(info.base); - egl_native_window_t::offset = intptr_t(info.bits) - intptr_t(info.base); - // FIXME: egl_native_window_t::memory_type used to be set from - // mSurface, but we wanted to break this dependency. We set it to - // GPU because the software rendered doesn't care, but the h/w - // accelerator needs it. Eventually, this value should go away - // completely, since memory will be managed by OpenGL. - egl_native_window_t::memory_type = NATIVE_MEMORY_TYPE_GPU; - egl_native_window_t::fd = 0; - } -} - -void EGLNativeWindowSurface::disconnect() -{ - if (mConnected) { - mSurface->unlock(); - mConnected = false; - } -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp index 7c2fc8e..59c9476 100644 --- a/libs/ui/EventHub.cpp +++ b/libs/ui/EventHub.cpp @@ -19,10 +19,10 @@ #include <hardware_legacy/power.h> #include <cutils/properties.h> -#include <utils/IServiceManager.h> #include <utils/Log.h> #include <utils/Timers.h> -#include <utils.h> +#include <utils/threads.h> +#include <utils/Errors.h> #include <stdlib.h> #include <stdio.h> @@ -83,7 +83,7 @@ EventHub::EventHub(void) : mError(NO_INIT), mHaveFirstKeyboard(false), mFirstKeyboardId(0) , mDevicesById(0), mNumDevicesById(0) , mOpeningDevices(0), mClosingDevices(0) - , mDevices(0), mFDs(0), mFDCount(0) + , mDevices(0), mFDs(0), mFDCount(0), mOpened(false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); #ifdef EV_SW @@ -100,11 +100,6 @@ EventHub::~EventHub(void) // we should free stuff here... } -void EventHub::onFirstRef() -{ - mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; -} - status_t EventHub::errorCheck() const { return mError; @@ -239,6 +234,41 @@ int EventHub::getKeycodeState(int32_t deviceId, int code) const return 0; } +status_t EventHub::scancodeToKeycode(int32_t deviceId, int scancode, + int32_t* outKeycode, uint32_t* outFlags) const +{ + AutoMutex _l(mLock); + device_t* device = getDevice(deviceId); + + if (device != NULL && device->layoutMap != NULL) { + status_t err = device->layoutMap->map(scancode, outKeycode, outFlags); + if (err == NO_ERROR) { + return NO_ERROR; + } + } + + if (mHaveFirstKeyboard) { + device = getDevice(mFirstKeyboardId); + + if (device != NULL && device->layoutMap != NULL) { + status_t err = device->layoutMap->map(scancode, outKeycode, outFlags); + if (err == NO_ERROR) { + return NO_ERROR; + } + } + } + + *outKeycode = 0; + *outFlags = 0; + return NAME_NOT_FOUND; +} + +void EventHub::addExcludedDevice(const char* deviceName) +{ + String8 name(deviceName); + mExcludedDevices.push_back(name); +} + EventHub::device_t* EventHub::getDevice(int32_t deviceId) const { if (deviceId == 0) deviceId = mFirstKeyboardId; @@ -276,7 +306,12 @@ bool EventHub::getEvent(int32_t* outDeviceId, int32_t* outType, // Note that we only allow one caller to getEvent(), so don't need // to do locking here... only when adding/removing devices. - + + if (!mOpened) { + mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; + mOpened = true; + } + while(1) { // First, report any devices that had last been added/removed. @@ -474,6 +509,20 @@ int EventHub::open_device(const char *deviceName) //fprintf(stderr, "could not get device name for %s, %s\n", deviceName, strerror(errno)); name[0] = '\0'; } + + // check to see if the device is on our excluded list + List<String8>::iterator iter = mExcludedDevices.begin(); + List<String8>::iterator end = mExcludedDevices.end(); + for ( ; iter != end; iter++) { + const char* test = *iter; + if (strcmp(name, test) == 0) { + LOGI("ignoring event id %s driver %s\n", deviceName, test); + close(fd); + fd = -1; + return -1; + } + } + if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) { //fprintf(stderr, "could not get location for %s, %s\n", deviceName, strerror(errno)); location[0] = '\0'; @@ -733,6 +782,7 @@ int EventHub::read_notify(int nfd) int event_pos = 0; struct inotify_event *event; +LOGD("EventHub::read_notify nfd: %d\n", nfd); res = read(nfd, event_buf, sizeof(event_buf)); if(res < (int)sizeof(*event)) { if(errno == EINTR) diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp new file mode 100644 index 0000000..8c8fd6b --- /dev/null +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -0,0 +1,210 @@ +/* +** +** Copyright 2007 The Android Open Source Project +** +** Licensed under the Apache License Version 2.0(the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing software +** distributed under the License is distributed on an "AS IS" BASIS +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#define LOG_TAG "FramebufferNativeWindow" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <cutils/log.h> +#include <cutils/atomic.h> +#include <utils/threads.h> + +#include <ui/SurfaceComposerClient.h> +#include <ui/Rect.h> +#include <ui/FramebufferNativeWindow.h> + +#include <EGL/egl.h> + +#include <pixelflinger/format.h> +#include <pixelflinger/pixelflinger.h> + +#include <hardware/hardware.h> +#include <hardware/gralloc.h> + +#include <private/ui/android_natives_priv.h> + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +class NativeBuffer + : public EGLNativeBase< + android_native_buffer_t, + NativeBuffer, + LightRefBase<NativeBuffer> > +{ +public: + NativeBuffer(int w, int h, int f, int u) : BASE() { + android_native_buffer_t::width = w; + android_native_buffer_t::height = h; + android_native_buffer_t::format = f; + android_native_buffer_t::usage = u; + } +private: + friend class LightRefBase<NativeBuffer>; + ~NativeBuffer() { }; // this class cannot be overloaded +}; + + +/* + * This implements the (main) framebuffer management. This class is used + * mostly by SurfaceFlinger, but also by command line GL application. + * + * In fact this is an implementation of android_native_window_t on top of + * the framebuffer. + * + * Currently it is pretty simple, it manages only two buffers (the front and + * back buffer). + * + */ + +FramebufferNativeWindow::FramebufferNativeWindow() + : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false) +{ + hw_module_t const* module; + if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { + int stride; + framebuffer_open(module, &fbDev); + gralloc_open(module, &grDev); + int err; + + + mUpdateOnDemand = (fbDev->setUpdateRect != 0); + + // initialize the buffer FIFO + mNumBuffers = 2; + mNumFreeBuffers = 2; + mBufferHead = mNumBuffers-1; + buffers[0] = new NativeBuffer( + fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB); + buffers[1] = new NativeBuffer( + fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB); + + err = grDev->alloc(grDev, + fbDev->width, fbDev->height, fbDev->format, + GRALLOC_USAGE_HW_FB, &buffers[0]->handle, &buffers[0]->stride); + + LOGE_IF(err, "fb buffer 0 allocation failed w=%d, h=%d, err=%s", + fbDev->width, fbDev->height, strerror(-err)); + + err = grDev->alloc(grDev, + fbDev->width, fbDev->height, fbDev->format, + GRALLOC_USAGE_HW_FB, &buffers[1]->handle, &buffers[1]->stride); + + LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s", + fbDev->width, fbDev->height, strerror(-err)); + } + + const_cast<uint32_t&>(android_native_window_t::flags) = fbDev->flags; + const_cast<float&>(android_native_window_t::xdpi) = fbDev->xdpi; + const_cast<float&>(android_native_window_t::ydpi) = fbDev->ydpi; + const_cast<int&>(android_native_window_t::minSwapInterval) = + fbDev->minSwapInterval; + const_cast<int&>(android_native_window_t::maxSwapInterval) = + fbDev->maxSwapInterval; + + android_native_window_t::setSwapInterval = setSwapInterval; + android_native_window_t::dequeueBuffer = dequeueBuffer; + android_native_window_t::lockBuffer = lockBuffer; + android_native_window_t::queueBuffer = queueBuffer; +} + +FramebufferNativeWindow::~FramebufferNativeWindow() { + grDev->free(grDev, buffers[0]->handle); + grDev->free(grDev, buffers[1]->handle); + gralloc_close(grDev); + framebuffer_close(fbDev); +} + +status_t FramebufferNativeWindow::setUpdateRectangle(const Rect& r) +{ + if (!mUpdateOnDemand) { + return INVALID_OPERATION; + } + return fbDev->setUpdateRect(fbDev, r.left, r.top, r.width(), r.height()); +} + +int FramebufferNativeWindow::setSwapInterval( + android_native_window_t* window, int interval) +{ + framebuffer_device_t* fb = getSelf(window)->fbDev; + return fb->setSwapInterval(fb, interval); +} + +int FramebufferNativeWindow::dequeueBuffer(android_native_window_t* window, + android_native_buffer_t** buffer) +{ + FramebufferNativeWindow* self = getSelf(window); + Mutex::Autolock _l(self->mutex); + framebuffer_device_t* fb = self->fbDev; + + // wait for a free buffer + while (!self->mNumFreeBuffers) { + self->mCondition.wait(self->mutex); + } + // get this buffer + self->mNumFreeBuffers--; + int index = self->mBufferHead++; + if (self->mBufferHead >= self->mNumBuffers) + self->mBufferHead = 0; + + *buffer = self->buffers[index].get(); + + return 0; +} + +int FramebufferNativeWindow::lockBuffer(android_native_window_t* window, + android_native_buffer_t* buffer) +{ + FramebufferNativeWindow* self = getSelf(window); + Mutex::Autolock _l(self->mutex); + + // wait that the buffer we're locking is not front anymore + while (self->front == buffer) { + self->mCondition.wait(self->mutex); + } + + return NO_ERROR; +} + +int FramebufferNativeWindow::queueBuffer(android_native_window_t* window, + android_native_buffer_t* buffer) +{ + FramebufferNativeWindow* self = getSelf(window); + Mutex::Autolock _l(self->mutex); + framebuffer_device_t* fb = self->fbDev; + buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle; + int res = fb->post(fb, handle); + self->front = static_cast<NativeBuffer*>(buffer); + self->mNumFreeBuffers++; + self->mCondition.broadcast(); + return res; +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- + + +EGLNativeWindowType android_createDisplaySurface(void) +{ + return new android::FramebufferNativeWindow(); +} + diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp index ab0fef1..805c2ca 100644 --- a/libs/ui/ICamera.cpp +++ b/libs/ui/ICamera.cpp @@ -20,7 +20,7 @@ #include <utils/Log.h> #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> +#include <binder/Parcel.h> #include <ui/ICamera.h> namespace android { @@ -221,12 +221,6 @@ IMPLEMENT_META_INTERFACE(Camera, "android.hardware.ICamera"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnCamera::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/ui/ICameraClient.cpp b/libs/ui/ICameraClient.cpp index 59a6cf2..42b4da4 100644 --- a/libs/ui/ICameraClient.cpp +++ b/libs/ui/ICameraClient.cpp @@ -78,12 +78,6 @@ IMPLEMENT_META_INTERFACE(CameraClient, "android.hardware.ICameraClient"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnCameraClient::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/ui/ICameraService.cpp b/libs/ui/ICameraService.cpp index e5687fe..84986c6 100644 --- a/libs/ui/ICameraService.cpp +++ b/libs/ui/ICameraService.cpp @@ -18,9 +18,9 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> #include <ui/ICameraService.h> @@ -49,12 +49,6 @@ IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnCameraService::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/ui/IOverlay.cpp b/libs/ui/IOverlay.cpp index fed47c2..65e6b4f 100644 --- a/libs/ui/IOverlay.cpp +++ b/libs/ui/IOverlay.cpp @@ -18,8 +18,8 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> -#include <utils/IInterface.h> +#include <binder/Parcel.h> +#include <binder/IInterface.h> #include <ui/IOverlay.h> @@ -49,12 +49,6 @@ IMPLEMENT_META_INTERFACE(Overlay, "android.ui.IOverlay"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnOverlay::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/ui/ISurface.cpp b/libs/ui/ISurface.cpp index d5e9f81..9fbae1e 100644 --- a/libs/ui/ISurface.cpp +++ b/libs/ui/ISurface.cpp @@ -14,19 +14,25 @@ * limitations under the License. */ +#define LOG_TAG "ISurface" + #include <stdio.h> #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> -#include <utils/IMemory.h> +#include <binder/Parcel.h> +#include <binder/IMemory.h> #include <ui/ISurface.h> #include <ui/Overlay.h> +#include <ui/Surface.h> +#include <private/ui/SurfaceBuffer.h> namespace android { +// ---------------------------------------------------------------------- + ISurface::BufferHeap::BufferHeap() : w(0), h(0), hor_stride(0), ver_stride(0), format(0), transform(0), flags(0) @@ -55,6 +61,8 @@ ISurface::BufferHeap::~BufferHeap() { } +// ---------------------------------------------------------------------- + class BpSurface : public BpInterface<ISurface> { public: @@ -63,6 +71,15 @@ public: { } + virtual sp<SurfaceBuffer> getBuffer() + { + Parcel data, reply; + data.writeInterfaceToken(ISurface::getInterfaceDescriptor()); + remote()->transact(GET_BUFFER, data, &reply); + sp<SurfaceBuffer> buffer = new SurfaceBuffer(reply); + return buffer; + } + virtual status_t registerBuffers(const BufferHeap& buffers) { Parcel data, reply; @@ -112,16 +129,15 @@ IMPLEMENT_META_INTERFACE(Surface, "android.ui.ISurface"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnSurface::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { + case GET_BUFFER: { + CHECK_INTERFACE(ISurface, data, reply); + sp<SurfaceBuffer> buffer(getBuffer()); + return SurfaceBuffer::writeToParcel(reply, buffer.get()); + } case REGISTER_BUFFERS: { CHECK_INTERFACE(ISurface, data, reply); BufferHeap buffer; diff --git a/libs/ui/ISurfaceComposer.cpp b/libs/ui/ISurfaceComposer.cpp index 76597e1..fd2a590 100644 --- a/libs/ui/ISurfaceComposer.cpp +++ b/libs/ui/ISurfaceComposer.cpp @@ -20,10 +20,10 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> -#include <utils/IMemory.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/IMemory.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> #include <ui/ISurfaceComposer.h> #include <ui/DisplayInfo.h> @@ -54,12 +54,12 @@ public: return interface_cast<ISurfaceFlingerClient>(reply.readStrongBinder()); } - virtual sp<IMemory> getCblk() const + virtual sp<IMemoryHeap> getCblk() const { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); remote()->transact(BnSurfaceComposer::GET_CBLK, data, &reply); - return interface_cast<IMemory>(reply.readStrongBinder()); + return interface_cast<IMemoryHeap>(reply.readStrongBinder()); } virtual void openGlobalTransaction() @@ -114,36 +114,6 @@ public: remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply); } - virtual status_t requestGPU( - const sp<IGPUCallback>& callback, gpu_info_t* gpu) - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - data.writeStrongBinder(callback->asBinder()); - remote()->transact(BnSurfaceComposer::REQUEST_GPU, data, &reply); - gpu->regs = interface_cast<IMemory>(reply.readStrongBinder()); - gpu->count = reply.readInt32(); - - // FIXME: for now, we don't dynamically allocate the regions array - size_t maxCount = sizeof(gpu->regions)/sizeof(*gpu->regions); - if (gpu->count > maxCount) - return BAD_VALUE; - - for (size_t i=0 ; i<gpu->count ; i++) { - gpu->regions[i].region = interface_cast<IMemory>(reply.readStrongBinder()); - gpu->regions[i].reserved = reply.readInt32(); - } - return reply.readInt32(); - } - - virtual status_t revokeGPU() - { - Parcel data, reply; - data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); - remote()->transact(BnSurfaceComposer::REVOKE_GPU, data, &reply); - return reply.readInt32(); - } - virtual void signal() const { Parcel data, reply; @@ -156,124 +126,61 @@ IMPLEMENT_META_INTERFACE(SurfaceComposer, "android.ui.ISurfaceComposer"); // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnSurfaceComposer::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { - status_t err = BnInterface<ISurfaceComposer>::onTransact(code, data, reply, flags); - if (err == NO_ERROR) - return err; - - CHECK_INTERFACE(ISurfaceComposer, data, reply); - switch(code) { case CREATE_CONNECTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = createConnection()->asBinder(); reply->writeStrongBinder(b); } break; case OPEN_GLOBAL_TRANSACTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); openGlobalTransaction(); } break; case CLOSE_GLOBAL_TRANSACTION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); closeGlobalTransaction(); } break; case SET_ORIENTATION: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); DisplayID dpy = data.readInt32(); int orientation = data.readInt32(); uint32_t flags = data.readInt32(); reply->writeInt32( setOrientation(dpy, orientation, flags) ); } break; case FREEZE_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); DisplayID dpy = data.readInt32(); uint32_t flags = data.readInt32(); reply->writeInt32( freezeDisplay(dpy, flags) ); } break; case UNFREEZE_DISPLAY: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); DisplayID dpy = data.readInt32(); uint32_t flags = data.readInt32(); reply->writeInt32( unfreezeDisplay(dpy, flags) ); } break; case BOOT_FINISHED: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); bootFinished(); } break; - case REVOKE_GPU: { - reply->writeInt32( revokeGPU() ); - } break; case SIGNAL: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); signal(); } break; case GET_CBLK: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); sp<IBinder> b = getCblk()->asBinder(); reply->writeStrongBinder(b); } break; - case REQUEST_GPU: { - // TODO: this should be protected by a permission - gpu_info_t info; - sp<IGPUCallback> callback - = interface_cast<IGPUCallback>(data.readStrongBinder()); - status_t res = requestGPU(callback, &info); - - // FIXME: for now, we don't dynamically allocate the regions array - size_t maxCount = sizeof(info.regions)/sizeof(*info.regions); - if (info.count > maxCount) - return BAD_VALUE; - - reply->writeStrongBinder(info.regs->asBinder()); - reply->writeInt32(info.count); - for (size_t i=0 ; i<info.count ; i++) { - reply->writeStrongBinder(info.regions[i].region->asBinder()); - reply->writeInt32(info.regions[i].reserved); - } - reply->writeInt32(res); - } break; default: - return UNKNOWN_TRANSACTION; + return BBinder::onTransact(code, data, reply, flags); } return NO_ERROR; } // ---------------------------------------------------------------------------- -enum { - // Note: BOOT_FINISHED must remain this value, it is called by ActivityManagerService. - GPU_LOST = IBinder::FIRST_CALL_TRANSACTION -}; - -class BpGPUCallback : public BpInterface<IGPUCallback> -{ -public: - BpGPUCallback(const sp<IBinder>& impl) - : BpInterface<IGPUCallback>(impl) - { - } - - virtual void gpuLost() - { - Parcel data, reply; - data.writeInterfaceToken(IGPUCallback::getInterfaceDescriptor()); - remote()->transact(GPU_LOST, data, &reply, IBinder::FLAG_ONEWAY); - } -}; - -IMPLEMENT_META_INTERFACE(GPUCallback, "android.ui.IGPUCallback"); - -status_t BnGPUCallback::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) { - case GPU_LOST: { - CHECK_INTERFACE(IGPUCallback, data, reply); - gpuLost(); - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - }; diff --git a/libs/ui/ISurfaceFlingerClient.cpp b/libs/ui/ISurfaceFlingerClient.cpp index dab5f71..51e8422 100644 --- a/libs/ui/ISurfaceFlingerClient.cpp +++ b/libs/ui/ISurfaceFlingerClient.cpp @@ -21,10 +21,10 @@ #include <stdint.h> #include <sys/types.h> -#include <utils/Parcel.h> -#include <utils/IMemory.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/IMemory.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> #include <ui/ISurface.h> #include <ui/ISurfaceFlingerClient.h> @@ -64,12 +64,12 @@ public: { } - virtual void getControlBlocks(sp<IMemory>* ctl) const + virtual sp<IMemoryHeap> getControlBlock() const { Parcel data, reply; data.writeInterfaceToken(ISurfaceFlingerClient::getInterfaceDescriptor()); remote()->transact(GET_CBLK, data, &reply); - *ctl = interface_cast<IMemory>(reply.readStrongBinder()); + return interface_cast<IMemoryHeap>(reply.readStrongBinder()); } virtual sp<ISurface> createSurface( surface_data_t* params, @@ -118,12 +118,6 @@ IMPLEMENT_META_INTERFACE(SurfaceFlingerClient, "android.ui.ISurfaceFlingerClient // ---------------------------------------------------------------------- -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - status_t BnSurfaceFlingerClient::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -132,8 +126,7 @@ status_t BnSurfaceFlingerClient::onTransact( switch(code) { case GET_CBLK: { CHECK_INTERFACE(ISurfaceFlingerClient, data, reply); - sp<IMemory> ctl; - getControlBlocks(&ctl); + sp<IMemoryHeap> ctl(getControlBlock()); reply->writeStrongBinder(ctl->asBinder()); return NO_ERROR; } break; @@ -198,8 +191,6 @@ status_t ISurfaceFlingerClient::surface_data_t::readFromParcel(const Parcel& par { token = parcel.readInt32(); identity = parcel.readInt32(); - heap[0] = interface_cast<IMemoryHeap>(parcel.readStrongBinder()); - heap[1] = interface_cast<IMemoryHeap>(parcel.readStrongBinder()); return NO_ERROR; } @@ -207,8 +198,6 @@ status_t ISurfaceFlingerClient::surface_data_t::writeToParcel(Parcel* parcel) co { parcel->writeInt32(token); parcel->writeInt32(identity); - parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL); - parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL); return NO_ERROR; } diff --git a/libs/ui/LayerState.cpp b/libs/ui/LayerState.cpp index 0b6374b..a53ffb7 100644 --- a/libs/ui/LayerState.cpp +++ b/libs/ui/LayerState.cpp @@ -15,7 +15,7 @@ */ #include <utils/Errors.h> -#include <utils/Parcel.h> +#include <binder/Parcel.h> #include <private/ui/LayerState.h> namespace android { diff --git a/libs/ui/Overlay.cpp b/libs/ui/Overlay.cpp index 59c6514..4854d6a 100644 --- a/libs/ui/Overlay.cpp +++ b/libs/ui/Overlay.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include <utils/IMemory.h> -#include <utils/Parcel.h> +#include <binder/IMemory.h> +#include <binder/Parcel.h> #include <utils/Errors.h> -#include <utils/MemoryHeapBase.h> +#include <binder/MemoryHeapBase.h> #include <ui/IOverlay.h> #include <ui/Overlay.h> @@ -59,6 +59,18 @@ status_t Overlay::queueBuffer(overlay_buffer_t buffer) return mOverlayData->queueBuffer(mOverlayData, buffer); } +status_t Overlay::setCrop(uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->setCrop(mOverlayData, x, y, w, h); +} + +status_t Overlay::getCrop(uint32_t* x, uint32_t* y, uint32_t* w, uint32_t* h) +{ + if (mStatus != NO_ERROR) return mStatus; + return mOverlayData->getCrop(mOverlayData, x, y, w, h); +} + int32_t Overlay::getBufferCount() const { if (mStatus != NO_ERROR) return mStatus; @@ -73,6 +85,15 @@ void* Overlay::getBufferAddress(overlay_buffer_t buffer) void Overlay::destroy() { if (mStatus != NO_ERROR) return; + + // Must delete the objects in reverse creation order, thus the + // data side must be closed first and then the destroy send to + // the control side. + if (mOverlayData) { + overlay_data_close(mOverlayData); + mOverlayData = NULL; + } + mOverlayRef->mOverlayChannel->destroy(); } diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp index 26e694a..d21ed57 100644 --- a/libs/ui/Region.cpp +++ b/libs/ui/Region.cpp @@ -16,295 +16,661 @@ #define LOG_TAG "Region" -#include <stdio.h> -#include <utils/Atomic.h> -#include <utils/Debug.h> +#include <limits.h> + +#include <utils/Log.h> #include <utils/String8.h> + +#include <ui/Rect.h> #include <ui/Region.h> +#include <ui/Point.h> + +#include <private/ui/RegionHelper.h> + +// ---------------------------------------------------------------------------- +#define VALIDATE_REGIONS (false) +#define VALIDATE_WITH_CORECG (false) +// ---------------------------------------------------------------------------- + +#if VALIDATE_WITH_CORECG +#include <core/SkRegion.h> +#endif namespace android { +// ---------------------------------------------------------------------------- + +enum { + op_nand = region_operator<Rect>::op_nand, + op_and = region_operator<Rect>::op_and, + op_or = region_operator<Rect>::op_or, + op_xor = region_operator<Rect>::op_xor +}; // ---------------------------------------------------------------------------- Region::Region() + : mBounds(0,0) { } Region::Region(const Region& rhs) - : mRegion(rhs.mRegion) -{ -} - -Region::Region(const SkRegion& rhs) - : mRegion(rhs) -{ -} - -Region::~Region() + : mBounds(rhs.mBounds), mStorage(rhs.mStorage) { } Region::Region(const Rect& rhs) + : mBounds(rhs) { - set(rhs); } Region::Region(const Parcel& parcel) { - read(parcel); + status_t err = read(parcel); + LOGE_IF(err<0, "error %s reading Region from parcel", strerror(err)); } Region::Region(const void* buffer) { - read(buffer); + status_t err = read(buffer); + LOGE_IF(err<0, "error %s reading Region from parcel", strerror(err)); } -Region& Region::operator = (const Region& rhs) +Region::~Region() { - mRegion = rhs.mRegion; - return *this; } -const SkRegion& Region::toSkRegion() const +Region& Region::operator = (const Region& rhs) { - return mRegion; +#if VALIDATE_REGIONS + validate(rhs, "operator="); +#endif + mBounds = rhs.mBounds; + mStorage = rhs.mStorage; + return *this; } -Rect Region::bounds() const +Region& Region::makeBoundsSelf() { - const SkIRect& b(mRegion.getBounds()); - return Rect(b.fLeft, b.fTop, b.fRight, b.fBottom); + mStorage.clear(); + return *this; } void Region::clear() { - mRegion.setEmpty(); + mBounds.clear(); + mStorage.clear(); } void Region::set(const Rect& r) { - SkIRect ir; - ir.set(r.left, r.top, r.right, r.bottom); - mRegion.setRect(ir); + mBounds = r; + mStorage.clear(); +} + +void Region::set(uint32_t w, uint32_t h) +{ + mBounds = Rect(int(w), int(h)); + mStorage.clear(); } // ---------------------------------------------------------------------------- -Region& Region::orSelf(const Rect& r) +void Region::addRectUnchecked(int l, int t, int r, int b) { - SkIRect ir; - ir.set(r.left, r.top, r.right, r.bottom); - mRegion.op(ir, SkRegion::kUnion_Op); - return *this; + mStorage.add(Rect(l,t,r,b)); +#if VALIDATE_REGIONS + validate(*this, "addRectUnchecked"); +#endif } -Region& Region::andSelf(const Rect& r) -{ - SkIRect ir; - ir.set(r.left, r.top, r.right, r.bottom); - mRegion.op(ir, SkRegion::kIntersect_Op); +// ---------------------------------------------------------------------------- + +Region& Region::orSelf(const Rect& r) { + return operationSelf(r, op_or); +} +Region& Region::andSelf(const Rect& r) { + return operationSelf(r, op_and); +} +Region& Region::subtractSelf(const Rect& r) { + return operationSelf(r, op_nand); +} +Region& Region::operationSelf(const Rect& r, int op) { + Region lhs(*this); + boolean_operation(op, *this, lhs, r); return *this; } // ---------------------------------------------------------------------------- Region& Region::orSelf(const Region& rhs) { - mRegion.op(rhs.mRegion, SkRegion::kUnion_Op); - return *this; + return operationSelf(rhs, op_or); } - Region& Region::andSelf(const Region& rhs) { - mRegion.op(rhs.mRegion, SkRegion::kIntersect_Op); - return *this; + return operationSelf(rhs, op_and); } - Region& Region::subtractSelf(const Region& rhs) { - mRegion.op(rhs.mRegion, SkRegion::kDifference_Op); + return operationSelf(rhs, op_nand); +} +Region& Region::operationSelf(const Region& rhs, int op) { + Region lhs(*this); + boolean_operation(op, *this, lhs, rhs); return *this; } Region& Region::translateSelf(int x, int y) { - if (x|y) mRegion.translate(x, y); + if (x|y) translate(*this, x, y); return *this; } -Region Region::merge(const Region& rhs) const { - Region result; - result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kUnion_Op); - return result; -} +// ---------------------------------------------------------------------------- -Region Region::intersect(const Region& rhs) const { +const Region Region::merge(const Rect& rhs) const { + return operation(rhs, op_or); +} +const Region Region::intersect(const Rect& rhs) const { + return operation(rhs, op_and); +} +const Region Region::subtract(const Rect& rhs) const { + return operation(rhs, op_nand); +} +const Region Region::operation(const Rect& rhs, int op) const { Region result; - result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kIntersect_Op); + boolean_operation(op, result, *this, rhs); return result; } -Region Region::subtract(const Region& rhs) const { +// ---------------------------------------------------------------------------- + +const Region Region::merge(const Region& rhs) const { + return operation(rhs, op_or); +} +const Region Region::intersect(const Region& rhs) const { + return operation(rhs, op_and); +} +const Region Region::subtract(const Region& rhs) const { + return operation(rhs, op_nand); +} +const Region Region::operation(const Region& rhs, int op) const { Region result; - result.mRegion.op(mRegion, rhs.mRegion, SkRegion::kDifference_Op); + boolean_operation(op, result, *this, rhs); return result; } -Region Region::translate(int x, int y) const { +const Region Region::translate(int x, int y) const { Region result; - mRegion.translate(x, y, &result.mRegion); + translate(result, *this, x, y); return result; } // ---------------------------------------------------------------------------- Region& Region::orSelf(const Region& rhs, int dx, int dy) { - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - mRegion.op(r, SkRegion::kUnion_Op); - return *this; + return operationSelf(rhs, dx, dy, op_or); } - Region& Region::andSelf(const Region& rhs, int dx, int dy) { - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - mRegion.op(r, SkRegion::kIntersect_Op); - return *this; + return operationSelf(rhs, dx, dy, op_and); } - Region& Region::subtractSelf(const Region& rhs, int dx, int dy) { - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - mRegion.op(r, SkRegion::kDifference_Op); + return operationSelf(rhs, dx, dy, op_nand); +} +Region& Region::operationSelf(const Region& rhs, int dx, int dy, int op) { + Region lhs(*this); + boolean_operation(op, *this, lhs, rhs, dx, dy); return *this; } -Region Region::merge(const Region& rhs, int dx, int dy) const { +// ---------------------------------------------------------------------------- + +const Region Region::merge(const Region& rhs, int dx, int dy) const { + return operation(rhs, dx, dy, op_or); +} +const Region Region::intersect(const Region& rhs, int dx, int dy) const { + return operation(rhs, dx, dy, op_and); +} +const Region Region::subtract(const Region& rhs, int dx, int dy) const { + return operation(rhs, dx, dy, op_nand); +} +const Region Region::operation(const Region& rhs, int dx, int dy, int op) const { Region result; - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - result.mRegion.op(mRegion, r, SkRegion::kUnion_Op); + boolean_operation(op, result, *this, rhs, dx, dy); return result; } -Region Region::intersect(const Region& rhs, int dx, int dy) const { - Region result; - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - result.mRegion.op(mRegion, r, SkRegion::kIntersect_Op); +// ---------------------------------------------------------------------------- + +// This is our region rasterizer, which merges rects and spans together +// to obtain an optimal region. +class Region::rasterizer : public region_operator<Rect>::region_rasterizer +{ + Rect& bounds; + Vector<Rect>& storage; + Rect* head; + Rect* tail; + Vector<Rect> span; + Rect* cur; +public: + rasterizer(Region& reg) + : bounds(reg.mBounds), storage(reg.mStorage), head(), tail(), cur() { + bounds.top = bounds.bottom = 0; + bounds.left = INT_MAX; + bounds.right = INT_MIN; + storage.clear(); + } + + ~rasterizer() { + if (span.size()) { + flushSpan(); + } + if (storage.size()) { + bounds.top = storage.itemAt(0).top; + bounds.bottom = storage.top().bottom; + if (storage.size() == 1) { + storage.clear(); + } + } else { + bounds.left = 0; + bounds.right = 0; + } + } + + virtual void operator()(const Rect& rect) { + //LOGD(">>> %3d, %3d, %3d, %3d", + // rect.left, rect.top, rect.right, rect.bottom); + if (span.size()) { + if (cur->top != rect.top) { + flushSpan(); + } else if (cur->right == rect.left) { + cur->right = rect.right; + return; + } + } + span.add(rect); + cur = span.editArray() + (span.size() - 1); + } +private: + template<typename T> + static inline T min(T rhs, T lhs) { return rhs < lhs ? rhs : lhs; } + template<typename T> + static inline T max(T rhs, T lhs) { return rhs > lhs ? rhs : lhs; } + void flushSpan() { + bool merge = false; + if (tail-head == ssize_t(span.size())) { + Rect const* p = cur; + Rect const* q = head; + if (p->top == q->bottom) { + merge = true; + while (q != tail) { + if ((p->left != q->left) || (p->right != q->right)) { + merge = false; + break; + } + p++, q++; + } + } + } + if (merge) { + const int bottom = span[0].bottom; + Rect* r = head; + while (r != tail) { + r->bottom = bottom; + r++; + } + } else { + bounds.left = min(span.itemAt(0).left, bounds.left); + bounds.right = max(span.top().right, bounds.right); + storage.appendVector(span); + tail = storage.editArray() + storage.size(); + head = tail - span.size(); + } + span.clear(); + } +}; + +bool Region::validate(const Region& reg, const char* name) +{ + bool result = true; + const_iterator cur = reg.begin(); + const_iterator const tail = reg.end(); + const_iterator prev = cur++; + Rect b(*prev); + while (cur != tail) { + b.left = b.left < cur->left ? b.left : cur->left; + b.top = b.top < cur->top ? b.top : cur->top; + b.right = b.right > cur->right ? b.right : cur->right; + b.bottom = b.bottom > cur->bottom ? b.bottom : cur->bottom; + if (cur->top == prev->top) { + if (cur->bottom != prev->bottom) { + LOGE("%s: invalid span %p", name, cur); + result = false; + } else if (cur->left < prev->right) { + LOGE("%s: spans overlap horizontally prev=%p, cur=%p", + name, prev, cur); + result = false; + } + } else if (cur->top < prev->bottom) { + LOGE("%s: spans overlap vertically prev=%p, cur=%p", + name, prev, cur); + result = false; + } + prev = cur; + cur++; + } + if (b != reg.getBounds()) { + result = false; + LOGE("%s: invalid bounds [%d,%d,%d,%d] vs. [%d,%d,%d,%d]", name, + b.left, b.top, b.right, b.bottom, + reg.getBounds().left, reg.getBounds().top, + reg.getBounds().right, reg.getBounds().bottom); + } + if (result == false) { + reg.dump(name); + } return result; } -Region Region::subtract(const Region& rhs, int dx, int dy) const { - Region result; - SkRegion r(rhs.mRegion); - r.translate(dx, dy); - result.mRegion.op(mRegion, r, SkRegion::kDifference_Op); - return result; +void Region::boolean_operation(int op, Region& dst, + const Region& lhs, + const Region& rhs, int dx, int dy) +{ + size_t lhs_count; + Rect const * const lhs_rects = lhs.getArray(&lhs_count); + + size_t rhs_count; + Rect const * const rhs_rects = rhs.getArray(&rhs_count); + + region_operator<Rect>::region lhs_region(lhs_rects, lhs_count); + region_operator<Rect>::region rhs_region(rhs_rects, rhs_count, dx, dy); + region_operator<Rect> operation(op, lhs_region, rhs_region); + { // scope for rasterizer (dtor has side effects) + rasterizer r(dst); + operation(r); + } + +#if VALIDATE_REGIONS + validate(lhs, "boolean_operation: lhs"); + validate(rhs, "boolean_operation: rhs"); + validate(dst, "boolean_operation: dst"); +#endif + +#if VALIDATE_WITH_CORECG + SkRegion sk_lhs; + SkRegion sk_rhs; + SkRegion sk_dst; + + for (size_t i=0 ; i<lhs_count ; i++) + sk_lhs.op( + lhs_rects[i].left + dx, + lhs_rects[i].top + dy, + lhs_rects[i].right + dx, + lhs_rects[i].bottom + dy, + SkRegion::kUnion_Op); + + for (size_t i=0 ; i<rhs_count ; i++) + sk_rhs.op( + rhs_rects[i].left + dx, + rhs_rects[i].top + dy, + rhs_rects[i].right + dx, + rhs_rects[i].bottom + dy, + SkRegion::kUnion_Op); + + const char* name = "---"; + SkRegion::Op sk_op; + switch (op) { + case op_or: sk_op = SkRegion::kUnion_Op; name="OR"; break; + case op_and: sk_op = SkRegion::kIntersect_Op; name="AND"; break; + case op_nand: sk_op = SkRegion::kDifference_Op; name="NAND"; break; + } + sk_dst.op(sk_lhs, sk_rhs, sk_op); + + if (sk_dst.isEmpty() && dst.isEmpty()) + return; + + bool same = true; + Region::const_iterator head = dst.begin(); + Region::const_iterator const tail = dst.end(); + SkRegion::Iterator it(sk_dst); + while (!it.done()) { + if (head != tail) { + if ( + head->left != it.rect().fLeft || + head->top != it.rect().fTop || + head->right != it.rect().fRight || + head->bottom != it.rect().fBottom + ) { + same = false; + break; + } + } else { + same = false; + break; + } + head++; + it.next(); + } + + if (head != tail) { + same = false; + } + + if(!same) { + LOGD("---\nregion boolean %s failed", name); + lhs.dump("lhs"); + rhs.dump("rhs"); + dst.dump("dst"); + LOGD("should be"); + SkRegion::Iterator it(sk_dst); + while (!it.done()) { + LOGD(" [%3d, %3d, %3d, %3d]", + it.rect().fLeft, + it.rect().fTop, + it.rect().fRight, + it.rect().fBottom); + it.next(); + } + } +#endif } -// ---------------------------------------------------------------------------- +void Region::boolean_operation(int op, Region& dst, + const Region& lhs, + const Rect& rhs, int dx, int dy) +{ +#if VALIDATE_WITH_CORECG || VALIDATE_REGIONS + boolean_operation(op, dst, lhs, Region(rhs), dx, dy); +#else + size_t lhs_count; + Rect const * const lhs_rects = lhs.getArray(&lhs_count); + + region_operator<Rect>::region lhs_region(lhs_rects, lhs_count); + region_operator<Rect>::region rhs_region(&rhs, 1, dx, dy); + region_operator<Rect> operation(op, lhs_region, rhs_region); + { // scope for rasterizer (dtor has side effects) + rasterizer r(dst); + operation(r); + } + +#endif +} -Region::iterator::iterator(const Region& r) - : mIt(r.mRegion) +void Region::boolean_operation(int op, Region& dst, + const Region& lhs, const Region& rhs) { + boolean_operation(op, dst, lhs, rhs, 0, 0); } -int Region::iterator::iterate(Rect* rect) +void Region::boolean_operation(int op, Region& dst, + const Region& lhs, const Rect& rhs) { - if (mIt.done()) - return 0; - const SkIRect& r(mIt.rect()); - rect->left = r.fLeft; - rect->top = r.fTop; - rect->right = r.fRight; - rect->bottom= r.fBottom; - mIt.next(); - return 1; + boolean_operation(op, dst, lhs, rhs, 0, 0); } -// ---------------------------------------------------------------------------- +void Region::translate(Region& reg, int dx, int dy) +{ + if (!reg.isEmpty()) { +#if VALIDATE_REGIONS + validate(reg, "translate (before)"); +#endif + reg.mBounds.translate(dx, dy); + size_t count = reg.mStorage.size(); + Rect* rects = reg.mStorage.editArray(); + while (count) { + rects->translate(dx, dy); + rects++; + count--; + } +#if VALIDATE_REGIONS + validate(reg, "translate (after)"); +#endif + } +} + +void Region::translate(Region& dst, const Region& reg, int dx, int dy) +{ + dst = reg; + translate(dst, dx, dy); +} -// we write a 4byte size ahead of the actual region, so we know how much we'll need for reading +// ---------------------------------------------------------------------------- status_t Region::write(Parcel& parcel) const { - int32_t size = mRegion.flatten(NULL); - parcel.writeInt32(size); - mRegion.flatten(parcel.writeInplace(size)); +#if VALIDATE_REGIONS + validate(*this, "write(Parcel)"); +#endif + status_t err; + const size_t count = mStorage.size(); + const size_t sizeNeeded = sizeof(int32_t) + (1+count)*sizeof(Rect); + void* buffer = parcel.writeInplace(sizeNeeded); + if (!buffer) return NO_MEMORY; + ssize_t written = Region::write(buffer, sizeNeeded); + if (written < 0) return status_t(written); return NO_ERROR; } status_t Region::read(const Parcel& parcel) { - size_t size = parcel.readInt32(); - mRegion.unflatten(parcel.readInplace(size)); + void const* buffer = parcel.readInplace(sizeof(int32_t)); + if (!buffer) return NO_MEMORY; + const size_t count = *static_cast<int32_t const *>(buffer); + void const* dummy = parcel.readInplace((1+count)*sizeof(Rect)); + if (!dummy) return NO_MEMORY; + const size_t sizeNeeded = sizeof(int32_t) + (1+count)*sizeof(Rect); + const ssize_t read = Region::read(buffer); + if (read < 0) return status_t(read); +#if VALIDATE_REGIONS + validate(*this, "read(Parcel)"); +#endif return NO_ERROR; } ssize_t Region::write(void* buffer, size_t size) const { - size_t sizeNeeded = mRegion.flatten(NULL); +#if VALIDATE_REGIONS + validate(*this, "write(buffer)"); +#endif + const size_t count = mStorage.size(); + const size_t sizeNeeded = sizeof(int32_t) + (1+count)*sizeof(Rect); if (sizeNeeded > size) return NO_MEMORY; - return mRegion.flatten(buffer); + int32_t* const p = static_cast<int32_t*>(buffer); + *p = count; + memcpy(p+1, &mBounds, sizeof(Rect)); + if (count) { + memcpy(p+5, mStorage.array(), count*sizeof(Rect)); + } + return ssize_t(sizeNeeded); } ssize_t Region::read(const void* buffer) { - return mRegion.unflatten(buffer); + int32_t const* const p = static_cast<int32_t const*>(buffer); + const size_t count = *p; + memcpy(&mBounds, p+1, sizeof(Rect)); + mStorage.clear(); + if (count) { + mStorage.insertAt(0, count); + memcpy(mStorage.editArray(), p+5, count*sizeof(Rect)); + } +#if VALIDATE_REGIONS + validate(*this, "read(buffer)"); +#endif + return ssize_t(sizeof(int32_t) + (1+count)*sizeof(Rect)); } ssize_t Region::writeEmpty(void* buffer, size_t size) { - if (size < 4) return NO_MEMORY; - // this needs to stay in sync with SkRegion - *static_cast<int32_t*>(buffer) = -1; - return 4; + const size_t sizeNeeded = sizeof(int32_t) + sizeof(Rect); + if (sizeNeeded > size) return NO_MEMORY; + int32_t* const p = static_cast<int32_t*>(buffer); + memset(p, 0, sizeNeeded); + return ssize_t(sizeNeeded); } bool Region::isEmpty(void* buffer) { - // this needs to stay in sync with SkRegion - return *static_cast<int32_t*>(buffer) == -1; + int32_t const* const p = static_cast<int32_t const*>(buffer); + Rect const* const b = reinterpret_cast<Rect const *>(p+1); + return b->isEmpty(); +} + +// ---------------------------------------------------------------------------- + +Region::const_iterator Region::begin() const { + return isRect() ? &mBounds : mStorage.array(); +} + +Region::const_iterator Region::end() const { + return isRect() ? ((&mBounds) + 1) : (mStorage.array() + mStorage.size()); +} + +Rect const* Region::getArray(size_t* count) const { + const_iterator const b(begin()); + const_iterator const e(end()); + if (count) *count = e-b; + return b; } -size_t Region::rects(Vector<Rect>& rectList) const +size_t Region::getRects(Vector<Rect>& rectList) const { - rectList.clear(); - if (!isEmpty()) { - SkRegion::Iterator iterator(mRegion); - while( !iterator.done() ) { - const SkIRect& ir(iterator.rect()); - rectList.push(Rect(ir.fLeft, ir.fTop, ir.fRight, ir.fBottom)); - iterator.next(); - } + rectList = mStorage; + if (rectList.isEmpty()) { + rectList.clear(); + rectList.add(mBounds); } return rectList.size(); } +// ---------------------------------------------------------------------------- + void Region::dump(String8& out, const char* what, uint32_t flags) const { (void)flags; - Vector<Rect> r; - rects(r); - + const_iterator head = begin(); + const_iterator const tail = end(); + size_t SIZE = 256; char buffer[SIZE]; - - snprintf(buffer, SIZE, " Region %s (this=%p, count=%d)\n", what, this, r.size()); + + snprintf(buffer, SIZE, " Region %s (this=%p, count=%d)\n", + what, this, tail-head); out.append(buffer); - for (size_t i=0 ; i<r.size() ; i++) { + while (head != tail) { snprintf(buffer, SIZE, " [%3d, %3d, %3d, %3d]\n", - r[i].left, r[i].top,r[i].right,r[i].bottom); + head->left, head->top, head->right, head->bottom); out.append(buffer); + head++; } } void Region::dump(const char* what, uint32_t flags) const { (void)flags; - Vector<Rect> r; - rects(r); - LOGD(" Region %s (this=%p, count=%d)\n", what, this, r.size()); - for (size_t i=0 ; i<r.size() ; i++) { + const_iterator head = begin(); + const_iterator const tail = end(); + LOGD(" Region %s (this=%p, count=%d)\n", what, this, tail-head); + while (head != tail) { LOGD(" [%3d, %3d, %3d, %3d]\n", - r[i].left, r[i].top,r[i].right,r[i].bottom); + head->left, head->top, head->right, head->bottom); + head++; } } diff --git a/libs/ui/Surface.cpp b/libs/ui/Surface.cpp index 4ea9ae2..aef47fd 100644 --- a/libs/ui/Surface.cpp +++ b/libs/ui/Surface.cpp @@ -23,202 +23,333 @@ #include <sys/types.h> #include <sys/stat.h> -#include <utils/Atomic.h> #include <utils/Errors.h> #include <utils/threads.h> -#include <utils/IPCThreadState.h> -#include <utils/IMemory.h> +#include <binder/IPCThreadState.h> +#include <binder/IMemory.h> #include <utils/Log.h> +#include <ui/DisplayInfo.h> +#include <ui/BufferMapper.h> #include <ui/ISurface.h> #include <ui/Surface.h> #include <ui/SurfaceComposerClient.h> #include <ui/Rect.h> +#include <pixelflinger/pixelflinger.h> + #include <private/ui/SharedState.h> #include <private/ui/LayerState.h> +#include <private/ui/SurfaceBuffer.h> namespace android { -// --------------------------------------------------------------------------- +// ============================================================================ +// SurfaceBuffer +// ============================================================================ -Surface::Surface(const sp<SurfaceComposerClient>& client, - const sp<ISurface>& surface, - const ISurfaceFlingerClient::surface_data_t& data, - uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, - bool owner) - : mClient(client), mSurface(surface), - mToken(data.token), mIdentity(data.identity), - mFormat(format), mFlags(flags), mOwner(owner) +SurfaceBuffer::SurfaceBuffer() + : BASE(), mOwner(false), mBufferMapper(BufferMapper::get()) { - mSwapRectangle.makeInvalid(); - mSurfaceHeapBase[0] = 0; - mSurfaceHeapBase[1] = 0; - mHeap[0] = data.heap[0]; - mHeap[1] = data.heap[1]; -} - -Surface::Surface(Surface const* rhs) - : mOwner(false) -{ - mToken = rhs->mToken; - mIdentity= rhs->mIdentity; - mClient = rhs->mClient; - mSurface = rhs->mSurface; - mHeap[0] = rhs->mHeap[0]; - mHeap[1] = rhs->mHeap[1]; - mFormat = rhs->mFormat; - mFlags = rhs->mFlags; - mSurfaceHeapBase[0] = rhs->mSurfaceHeapBase[0]; - mSurfaceHeapBase[1] = rhs->mSurfaceHeapBase[1]; - mSwapRectangle.makeInvalid(); + width = + height = + stride = + format = + usage = 0; + handle = NULL; } -Surface::~Surface() +SurfaceBuffer::SurfaceBuffer(const Parcel& data) + : BASE(), mOwner(true), mBufferMapper(BufferMapper::get()) { - if (mOwner && mToken>=0 && mClient!=0) { - mClient->destroySurface(mToken); - } - mClient.clear(); - mSurface.clear(); - mHeap[0].clear(); - mHeap[1].clear(); - IPCThreadState::self()->flushCommands(); + // we own the handle in this case + width = data.readInt32(); + height = data.readInt32(); + stride = data.readInt32(); + format = data.readInt32(); + usage = data.readInt32(); + handle = data.readNativeHandle(); } -sp<Surface> Surface::dup() const +SurfaceBuffer::~SurfaceBuffer() { - Surface const * r = this; - if (this && mOwner) { - // the only reason we need to do this is because of Java's garbage - // collector: because we're creating a copy of the Surface - // instead of a reference, we can garantee that when our last - // reference goes away, the real surface will be deleted. - // Without this hack (the code is correct too), we'd have to - // wait for a GC for the surface to go away. - r = new Surface(this); + if (handle && mOwner) { + native_handle_close(handle); + native_handle_delete(const_cast<native_handle*>(handle)); } - return const_cast<Surface*>(r); } -status_t Surface::nextBuffer(SurfaceInfo* info) { - return mClient->nextBuffer(this, info); +status_t SurfaceBuffer::lock(uint32_t usage, void** vaddr) +{ + const Rect lockBounds(width, height); + status_t res = lock(usage, lockBounds, vaddr); + return res; } -status_t Surface::lock(SurfaceInfo* info, bool blocking) { - return Surface::lock(info, NULL, blocking); +status_t SurfaceBuffer::lock(uint32_t usage, const Rect& rect, void** vaddr) +{ + if (rect.left < 0 || rect.right > this->width || + rect.top < 0 || rect.bottom > this->height) { + LOGE("locking pixels (%d,%d,%d,%d) outside of buffer (w=%d, h=%d)", + rect.left, rect.top, rect.right, rect.bottom, + this->width, this->height); + return BAD_VALUE; + } + status_t res = getBufferMapper().lock(handle, usage, rect, vaddr); + return res; } -status_t Surface::lock(SurfaceInfo* info, Region* dirty, bool blocking) { - if (heapBase(0) == 0) return INVALID_OPERATION; - if (heapBase(1) == 0) return INVALID_OPERATION; - return mClient->lockSurface(this, info, dirty, blocking); +status_t SurfaceBuffer::unlock() +{ + status_t res = getBufferMapper().unlock(handle); + return res; } -status_t Surface::unlockAndPost() { - if (heapBase(0) == 0) return INVALID_OPERATION; - if (heapBase(1) == 0) return INVALID_OPERATION; - return mClient->unlockAndPostSurface(this); +status_t SurfaceBuffer::writeToParcel(Parcel* reply, + android_native_buffer_t const* buffer) +{ + reply->writeInt32(buffer->width); + reply->writeInt32(buffer->height); + reply->writeInt32(buffer->stride); + reply->writeInt32(buffer->format); + reply->writeInt32(buffer->usage); + reply->writeNativeHandle(buffer->handle); + return NO_ERROR; } -status_t Surface::unlock() { - if (heapBase(0) == 0) return INVALID_OPERATION; - if (heapBase(1) == 0) return INVALID_OPERATION; - return mClient->unlockSurface(this); +// ---------------------------------------------------------------------- + +static status_t copyBlt( + const sp<SurfaceBuffer>& dst, + const sp<SurfaceBuffer>& src, + const Region& reg) +{ + status_t err; + uint8_t const * src_bits = NULL; + err = src->lock(GRALLOC_USAGE_SW_READ_OFTEN, reg.bounds(), (void**)&src_bits); + LOGE_IF(err, "error locking src buffer %s", strerror(-err)); + + uint8_t* dst_bits = NULL; + err = dst->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, reg.bounds(), (void**)&dst_bits); + LOGE_IF(err, "error locking dst buffer %s", strerror(-err)); + + Region::const_iterator head(reg.begin()); + Region::const_iterator tail(reg.end()); + if (head != tail && src_bits && dst_bits) { + // NOTE: dst and src must be the same format + const size_t bpp = bytesPerPixel(src->format); + const size_t dbpr = dst->stride * bpp; + const size_t sbpr = src->stride * bpp; + + while (head != tail) { + const Rect& r(*head++); + ssize_t h = r.height(); + if (h <= 0) continue; + size_t size = r.width() * bpp; + uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp; + uint8_t * d = dst_bits + (r.left + dst->stride * r.top) * bpp; + if (dbpr==sbpr && size==sbpr) { + size *= h; + h = 1; + } + do { + memcpy(d, s, size); + d += dbpr; + s += sbpr; + } while (--h > 0); + } + } + + if (src_bits) + src->unlock(); + + if (dst_bits) + dst->unlock(); + + return err; } -status_t Surface::setLayer(int32_t layer) { - return mClient->setLayer(this, layer); +// ============================================================================ +// SurfaceControl +// ============================================================================ + +SurfaceControl::SurfaceControl( + const sp<SurfaceComposerClient>& client, + const sp<ISurface>& surface, + const ISurfaceFlingerClient::surface_data_t& data, + uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) + : mClient(client), mSurface(surface), + mToken(data.token), mIdentity(data.identity), + mFormat(format), mFlags(flags) +{ } -status_t Surface::setPosition(int32_t x, int32_t y) { - return mClient->setPosition(this, x, y); + +SurfaceControl::~SurfaceControl() +{ + destroy(); } -status_t Surface::setSize(uint32_t w, uint32_t h) { - return mClient->setSize(this, w, h); + +void SurfaceControl::destroy() +{ + if (isValid()) { + mClient->destroySurface(mToken); + } + + // clear all references and trigger an IPC now, to make sure things + // happen without delay, since these resources are quite heavy. + mClient.clear(); + mSurface.clear(); + IPCThreadState::self()->flushCommands(); } -status_t Surface::hide() { - return mClient->hide(this); + +void SurfaceControl::clear() +{ + // here, the window manager tells us explicitly that we should destroy + // the surface's resource. Soon after this call, it will also release + // its last reference (which will call the dtor); however, it is possible + // that a client living in the same process still holds references which + // would delay the call to the dtor -- that is why we need this explicit + // "clear()" call. + destroy(); } -status_t Surface::show(int32_t layer) { - return mClient->show(this, layer); + +bool SurfaceControl::isSameSurface( + const sp<SurfaceControl>& lhs, const sp<SurfaceControl>& rhs) +{ + if (lhs == 0 || rhs == 0) + return false; + return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); } -status_t Surface::freeze() { - return mClient->freeze(this); + +status_t SurfaceControl::setLayer(int32_t layer) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setLayer(mToken, layer); } -status_t Surface::unfreeze() { - return mClient->unfreeze(this); +status_t SurfaceControl::setPosition(int32_t x, int32_t y) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setPosition(mToken, x, y); } -status_t Surface::setFlags(uint32_t flags, uint32_t mask) { - return mClient->setFlags(this, flags, mask); +status_t SurfaceControl::setSize(uint32_t w, uint32_t h) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setSize(mToken, w, h); } -status_t Surface::setTransparentRegionHint(const Region& transparent) { - return mClient->setTransparentRegionHint(this, transparent); +status_t SurfaceControl::hide() { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->hide(mToken); } -status_t Surface::setAlpha(float alpha) { - return mClient->setAlpha(this, alpha); +status_t SurfaceControl::show(int32_t layer) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->show(mToken, layer); } -status_t Surface::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { - return mClient->setMatrix(this, dsdx, dtdx, dsdy, dtdy); +status_t SurfaceControl::freeze() { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->freeze(mToken); } -status_t Surface::setFreezeTint(uint32_t tint) { - return mClient->setFreezeTint(this, tint); +status_t SurfaceControl::unfreeze() { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->unfreeze(mToken); } - -Region Surface::dirtyRegion() const { - return mDirtyRegion; +status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setFlags(mToken, flags, mask); } -void Surface::setDirtyRegion(const Region& region) const { - mDirtyRegion = region; +status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setTransparentRegionHint(mToken, transparent); } -const Rect& Surface::swapRectangle() const { - return mSwapRectangle; +status_t SurfaceControl::setAlpha(float alpha) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setAlpha(mToken, alpha); } -void Surface::setSwapRectangle(const Rect& r) { - mSwapRectangle = r; +status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy); +} +status_t SurfaceControl::setFreezeTint(uint32_t tint) { + const sp<SurfaceComposerClient>& client(mClient); + if (client == 0) return NO_INIT; + status_t err = validate(client->mControl); + if (err < 0) return err; + return client->setFreezeTint(mToken, tint); } -sp<Surface> Surface::readFromParcel(Parcel* parcel) +status_t SurfaceControl::validate(per_client_cblk_t const* cblk) const { - sp<SurfaceComposerClient> client; - ISurfaceFlingerClient::surface_data_t data; - sp<IBinder> clientBinder= parcel->readStrongBinder(); - sp<ISurface> surface = interface_cast<ISurface>(parcel->readStrongBinder()); - data.heap[0] = interface_cast<IMemoryHeap>(parcel->readStrongBinder()); - data.heap[1] = interface_cast<IMemoryHeap>(parcel->readStrongBinder()); - data.token = parcel->readInt32(); - data.identity = parcel->readInt32(); - PixelFormat format = parcel->readInt32(); - uint32_t flags = parcel->readInt32(); - - if (clientBinder != NULL) - client = SurfaceComposerClient::clientForConnection(clientBinder); - - return new Surface(client, surface, data, 0, 0, format, flags, false); + if (mToken<0 || mClient==0) { + LOGE("invalid token (%d, identity=%u) or client (%p)", + mToken, mIdentity, mClient.get()); + return NO_INIT; + } + if (cblk == 0) { + LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); + return NO_INIT; + } + status_t err = cblk->validate(mToken); + if (err != NO_ERROR) { + LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", + mToken, mIdentity, err, strerror(-err)); + return err; + } + if (mIdentity != uint32_t(cblk->layers[mToken].identity)) { + LOGE("using an invalid surface id=%d, identity=%u should be %d", + mToken, mIdentity, cblk->layers[mToken].identity); + return NO_INIT; + } + return NO_ERROR; } -status_t Surface::writeToParcel(const sp<Surface>& surface, Parcel* parcel) +status_t SurfaceControl::writeSurfaceToParcel( + const sp<SurfaceControl>& control, Parcel* parcel) { - uint32_t flags=0; - uint32_t format=0; + uint32_t flags = 0; + uint32_t format = 0; SurfaceID token = -1; uint32_t identity = 0; sp<SurfaceComposerClient> client; sp<ISurface> sur; - sp<IMemoryHeap> heap[2]; - if (surface->isValid()) { - token = surface->mToken; - identity = surface->mIdentity; - client = surface->mClient; - sur = surface->mSurface; - heap[0] = surface->mHeap[0]; - heap[1] = surface->mHeap[1]; - format = surface->mFormat; - flags = surface->mFlags; + if (SurfaceControl::isValid(control)) { + token = control->mToken; + identity = control->mIdentity; + client = control->mClient; + sur = control->mSurface; + format = control->mFormat; + flags = control->mFlags; } parcel->writeStrongBinder(client!=0 ? client->connection() : NULL); parcel->writeStrongBinder(sur!=0 ? sur->asBinder() : NULL); - parcel->writeStrongBinder(heap[0]!=0 ? heap[0]->asBinder() : NULL); - parcel->writeStrongBinder(heap[1]!=0 ? heap[1]->asBinder() : NULL); parcel->writeInt32(token); parcel->writeInt32(identity); parcel->writeInt32(format); @@ -226,30 +357,351 @@ status_t Surface::writeToParcel(const sp<Surface>& surface, Parcel* parcel) return NO_ERROR; } -bool Surface::isSameSurface(const sp<Surface>& lhs, const sp<Surface>& rhs) +sp<Surface> SurfaceControl::getSurface() const +{ + Mutex::Autolock _l(mLock); + if (mSurfaceData == 0) { + mSurfaceData = new Surface(const_cast<SurfaceControl*>(this)); + } + return mSurfaceData; +} + +// ============================================================================ +// Surface +// ============================================================================ + +Surface::Surface(const sp<SurfaceControl>& surface) + : mClient(surface->mClient), mSurface(surface->mSurface), + mToken(surface->mToken), mIdentity(surface->mIdentity), + mFormat(surface->mFormat), mFlags(surface->mFlags), + mBufferMapper(BufferMapper::get()) +{ + init(); +} + +Surface::Surface(const Parcel& parcel) + : mBufferMapper(BufferMapper::get()) +{ + sp<IBinder> clientBinder = parcel.readStrongBinder(); + mSurface = interface_cast<ISurface>(parcel.readStrongBinder()); + mToken = parcel.readInt32(); + mIdentity = parcel.readInt32(); + mFormat = parcel.readInt32(); + mFlags = parcel.readInt32(); + + if (clientBinder != NULL) + mClient = SurfaceComposerClient::clientForConnection(clientBinder); + + init(); +} + +void Surface::init() +{ + android_native_window_t::setSwapInterval = setSwapInterval; + android_native_window_t::dequeueBuffer = dequeueBuffer; + android_native_window_t::lockBuffer = lockBuffer; + android_native_window_t::queueBuffer = queueBuffer; + mSwapRectangle.makeInvalid(); + DisplayInfo dinfo; + SurfaceComposerClient::getDisplayInfo(0, &dinfo); + const_cast<float&>(android_native_window_t::xdpi) = dinfo.xdpi; + const_cast<float&>(android_native_window_t::ydpi) = dinfo.ydpi; + // FIXME: set real values here + const_cast<int&>(android_native_window_t::minSwapInterval) = 1; + const_cast<int&>(android_native_window_t::maxSwapInterval) = 1; + const_cast<uint32_t&>(android_native_window_t::flags) = 0; +} + + +Surface::~Surface() +{ + // this is a client-side operation, the surface is destroyed, unmap + // its buffers in this process. + for (int i=0 ; i<2 ; i++) { + if (mBuffers[i] != 0) { + getBufferMapper().unregisterBuffer(mBuffers[i]->handle); + } + } + + // clear all references and trigger an IPC now, to make sure things + // happen without delay, since these resources are quite heavy. + mClient.clear(); + mSurface.clear(); + IPCThreadState::self()->flushCommands(); +} + +status_t Surface::validate(per_client_cblk_t const* cblk) const +{ + if (mToken<0 || mClient==0) { + LOGE("invalid token (%d, identity=%u) or client (%p)", + mToken, mIdentity, mClient.get()); + return NO_INIT; + } + if (cblk == 0) { + LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity); + return NO_INIT; + } + status_t err = cblk->validate(mToken); + if (err != NO_ERROR) { + LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", + mToken, mIdentity, err, strerror(-err)); + return err; + } + if (mIdentity != uint32_t(cblk->layers[mToken].identity)) { + LOGE("using an invalid surface id=%d, identity=%u should be %d", + mToken, mIdentity, cblk->layers[mToken].identity); + return NO_INIT; + } + return NO_ERROR; +} + + +bool Surface::isSameSurface( + const sp<Surface>& lhs, const sp<Surface>& rhs) { if (lhs == 0 || rhs == 0) return false; return lhs->mSurface->asBinder() == rhs->mSurface->asBinder(); } -void* Surface::heapBase(int i) const +// ---------------------------------------------------------------------------- + +int Surface::setSwapInterval(android_native_window_t* window, int interval) +{ + return 0; +} + +int Surface::dequeueBuffer(android_native_window_t* window, + android_native_buffer_t** buffer) { - void* heapBase = mSurfaceHeapBase[i]; - // map lazily so it doesn't get mapped in clients that don't need it - if (heapBase == 0) { - const sp<IMemoryHeap>& heap(mHeap[i]); - if (heap != 0) { - heapBase = static_cast<uint8_t*>(heap->base()); - if (heapBase == MAP_FAILED) { - heapBase = NULL; - LOGE("Couldn't map Surface's heap (binder=%p, heap=%p)", - heap->asBinder().get(), heap.get()); + Surface* self = getSelf(window); + return self->dequeueBuffer(buffer); +} + +int Surface::lockBuffer(android_native_window_t* window, + android_native_buffer_t* buffer) +{ + Surface* self = getSelf(window); + return self->lockBuffer(buffer); +} + +int Surface::queueBuffer(android_native_window_t* window, + android_native_buffer_t* buffer) +{ + Surface* self = getSelf(window); + return self->queueBuffer(buffer); +} + +// ---------------------------------------------------------------------------- + +status_t Surface::dequeueBuffer(sp<SurfaceBuffer>* buffer) +{ + android_native_buffer_t* out; + status_t err = dequeueBuffer(&out); + *buffer = SurfaceBuffer::getSelf(out); + return err; +} + +status_t Surface::lockBuffer(const sp<SurfaceBuffer>& buffer) +{ + return lockBuffer(buffer.get()); +} + +status_t Surface::queueBuffer(const sp<SurfaceBuffer>& buffer) +{ + return queueBuffer(buffer.get()); +} + +// ---------------------------------------------------------------------------- + +int Surface::dequeueBuffer(android_native_buffer_t** buffer) +{ + // FIXME: dequeueBuffer() needs proper implementation + + Mutex::Autolock _l(mSurfaceLock); + + per_client_cblk_t* const cblk = mClient->mControl; + status_t err = validate(cblk); + if (err != NO_ERROR) + return err; + + SurfaceID index(mToken); + + int32_t backIdx = cblk->lock_layer(size_t(index), + per_client_cblk_t::BLOCKING); + + if (backIdx < 0) + return status_t(backIdx); + + mBackbufferIndex = backIdx; + layer_cblk_t* const lcblk = &(cblk->layers[index]); + + volatile const surface_info_t* const back = lcblk->surface + backIdx; + if (back->flags & surface_info_t::eNeedNewBuffer) { + getBufferLocked(backIdx); + } + + const sp<SurfaceBuffer>& backBuffer(mBuffers[backIdx]); + mDirtyRegion.set(backBuffer->width, backBuffer->height); + *buffer = backBuffer.get(); + + return NO_ERROR; +} + +int Surface::lockBuffer(android_native_buffer_t* buffer) +{ + Mutex::Autolock _l(mSurfaceLock); + + per_client_cblk_t* const cblk = mClient->mControl; + status_t err = validate(cblk); + if (err != NO_ERROR) + return err; + + // FIXME: lockBuffer() needs proper implementation + return 0; +} + +int Surface::queueBuffer(android_native_buffer_t* buffer) +{ + Mutex::Autolock _l(mSurfaceLock); + + per_client_cblk_t* const cblk = mClient->mControl; + status_t err = validate(cblk); + if (err != NO_ERROR) + return err; + + if (mSwapRectangle.isValid()) { + mDirtyRegion.set(mSwapRectangle); + } + + // transmit the dirty region + SurfaceID index(mToken); + layer_cblk_t* const lcblk = &(cblk->layers[index]); + _send_dirty_region(lcblk, mDirtyRegion); + + uint32_t newstate = cblk->unlock_layer_and_post(size_t(index)); + if (!(newstate & eNextFlipPending)) + mClient->signalServer(); + + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +status_t Surface::lock(SurfaceInfo* info, bool blocking) { + return Surface::lock(info, NULL, blocking); +} + +status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn, bool blocking) +{ + // FIXME: needs some locking here + + sp<SurfaceBuffer> backBuffer; + status_t err = dequeueBuffer(&backBuffer); + if (err == NO_ERROR) { + err = lockBuffer(backBuffer); + if (err == NO_ERROR) { + // we handle copy-back here... + + const Rect bounds(backBuffer->width, backBuffer->height); + Region scratch(bounds); + Region& newDirtyRegion(dirtyIn ? *dirtyIn : scratch); + + per_client_cblk_t* const cblk = mClient->mControl; + layer_cblk_t* const lcblk = &(cblk->layers[SurfaceID(mToken)]); + volatile const surface_info_t* const back = lcblk->surface + mBackbufferIndex; + if (back->flags & surface_info_t::eBufferDirty) { + // content is meaningless in this case and the whole surface + // needs to be redrawn. + newDirtyRegion.set(bounds); + } else { + newDirtyRegion.andSelf(bounds); + const sp<SurfaceBuffer>& frontBuffer(mBuffers[1-mBackbufferIndex]); + if (backBuffer->width == frontBuffer->width && + backBuffer->height == frontBuffer->height && + !(lcblk->flags & eNoCopyBack)) + { + const Region copyback(mOldDirtyRegion.subtract(newDirtyRegion)); + if (!copyback.isEmpty() && frontBuffer!=0) { + // copy front to back + copyBlt(backBuffer, frontBuffer, copyback); + } + } } - mSurfaceHeapBase[i] = heapBase; + mDirtyRegion = newDirtyRegion; + mOldDirtyRegion = newDirtyRegion; + + void* vaddr; + status_t res = backBuffer->lock( + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + newDirtyRegion.bounds(), &vaddr); + + LOGW_IF(res, "failed locking buffer %d (%p)", + mBackbufferIndex, backBuffer->handle); + + mLockedBuffer = backBuffer; + other->w = backBuffer->width; + other->h = backBuffer->height; + other->s = backBuffer->stride; + other->usage = backBuffer->usage; + other->format = backBuffer->format; + other->bits = vaddr; + } + } + return err; +} + +status_t Surface::unlockAndPost() +{ + // FIXME: needs some locking here + + if (mLockedBuffer == 0) + return BAD_VALUE; + + status_t res = mLockedBuffer->unlock(); + LOGW_IF(res, "failed unlocking buffer %d (%p)", + mBackbufferIndex, mLockedBuffer->handle); + + status_t err = queueBuffer(mLockedBuffer); + mLockedBuffer = 0; + return err; +} + +void Surface::_send_dirty_region( + layer_cblk_t* lcblk, const Region& dirty) +{ + const int32_t index = (lcblk->flags & eBufferIndex) >> eBufferIndexShift; + flat_region_t* flat_region = lcblk->region + index; + status_t err = dirty.write(flat_region, sizeof(flat_region_t)); + if (err < NO_ERROR) { + // region doesn't fit, use the bounds + const Region reg(dirty.bounds()); + reg.write(flat_region, sizeof(flat_region_t)); + } +} + +void Surface::setSwapRectangle(const Rect& r) { + mSwapRectangle = r; +} + +status_t Surface::getBufferLocked(int index) +{ + status_t err = NO_MEMORY; + sp<SurfaceBuffer> buffer = mSurface->getBuffer(); + LOGE_IF(buffer==0, "ISurface::getBuffer() returned NULL"); + if (buffer != 0) { + sp<SurfaceBuffer>& currentBuffer(mBuffers[index]); + if (currentBuffer != 0) { + getBufferMapper().unregisterBuffer(currentBuffer->handle); + currentBuffer.clear(); + } + err = getBufferMapper().registerBuffer(buffer->handle); + LOGW_IF(err, "map(...) failed %d (%s)", err, strerror(-err)); + if (err == NO_ERROR) { + currentBuffer = buffer; } } - return heapBase; + return err; } }; // namespace android diff --git a/libs/ui/SurfaceComposerClient.cpp b/libs/ui/SurfaceComposerClient.cpp index fe803ff..d2cef78 100644 --- a/libs/ui/SurfaceComposerClient.cpp +++ b/libs/ui/SurfaceComposerClient.cpp @@ -29,27 +29,21 @@ #include <utils/Errors.h> #include <utils/threads.h> #include <utils/KeyedVector.h> -#include <utils/IPCThreadState.h> -#include <utils/IServiceManager.h> -#include <utils/IMemory.h> +#include <binder/IServiceManager.h> +#include <binder/IMemory.h> #include <utils/Log.h> +#include <ui/DisplayInfo.h> #include <ui/ISurfaceComposer.h> #include <ui/ISurfaceFlingerClient.h> #include <ui/ISurface.h> #include <ui/SurfaceComposerClient.h> -#include <ui/DisplayInfo.h> #include <ui/Rect.h> -#include <ui/Point.h> #include <private/ui/SharedState.h> #include <private/ui/LayerState.h> #include <private/ui/SurfaceFlingerSynchro.h> -#include <pixelflinger/pixelflinger.h> - -#include <utils/BpBinder.h> - #define VERBOSE(...) ((void)0) //#define VERBOSE LOGD @@ -65,7 +59,7 @@ static Mutex gLock; static sp<ISurfaceComposer> gSurfaceManager; static DefaultKeyedVector< sp<IBinder>, sp<SurfaceComposerClient> > gActiveConnections; static SortedVector<sp<SurfaceComposerClient> > gOpenTransactions; -static sp<IMemory> gServerCblkMemory; +static sp<IMemoryHeap> gServerCblkMemory; static volatile surface_flinger_cblk_t* gServerCblk; const sp<ISurfaceComposer>& _get_surface_manager() @@ -100,7 +94,7 @@ static volatile surface_flinger_cblk_t const * get_cblk() if (gServerCblk == 0) { gServerCblkMemory = sm->getCblk(); LOGE_IF(gServerCblkMemory==0, "Can't get server control block"); - gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->pointer(); + gServerCblk = (surface_flinger_cblk_t *)gServerCblkMemory->getBase(); LOGE_IF(gServerCblk==0, "Can't get server control block address"); } } @@ -109,50 +103,8 @@ static volatile surface_flinger_cblk_t const * get_cblk() // --------------------------------------------------------------------------- -static void copyBlt(const GGLSurface& dst, - const GGLSurface& src, const Region& reg) -{ - Region::iterator iterator(reg); - if (iterator) { - // NOTE: dst and src must be the same format - Rect r; - const size_t bpp = bytesPerPixel(src.format); - const size_t dbpr = dst.stride * bpp; - const size_t sbpr = src.stride * bpp; - while (iterator.iterate(&r)) { - ssize_t h = r.bottom - r.top; - if (h) { - size_t size = (r.right - r.left) * bpp; - uint8_t* s = src.data + (r.left + src.stride * r.top) * bpp; - uint8_t* d = dst.data + (r.left + dst.stride * r.top) * bpp; - if (dbpr==sbpr && size==sbpr) { - size *= h; - h = 1; - } - do { - memcpy(d, s, size); - d += dbpr; - s += sbpr; - } while (--h > 0); - } - } - } -} - -// --------------------------------------------------------------------------- - -surface_flinger_cblk_t::surface_flinger_cblk_t() -{ -} - -// --------------------------------------------------------------------------- - -per_client_cblk_t::per_client_cblk_t() -{ -} - // these functions are used by the clients -inline status_t per_client_cblk_t::validate(size_t i) const { +status_t per_client_cblk_t::validate(size_t i) const { if (uint32_t(i) >= NUM_LAYERS_MAX) return BAD_INDEX; if (layers[i].swapState & eInvalidSurface) @@ -248,8 +200,9 @@ int32_t per_client_cblk_t::lock_layer(size_t i, uint32_t flags) index = (state&eIndex) ^ ((state&eFlipRequested)>>1); // make sure this buffer is valid - if (layer->surface[index].bits_offset < 0) { - return status_t(layer->surface[index].bits_offset); + status_t err = layer->surface[index].status; + if (err < 0) { + return err; } if (inspect) { @@ -273,7 +226,7 @@ done: uint32_t per_client_cblk_t::unlock_layer_and_post(size_t i) { - // atomically set eFlipRequested and clear eLocked and optionnaly + // atomically set eFlipRequested and clear eLocked and optionally // set eNextFlipPending if eFlipRequested was already set layer_cblk_t * const layer = layers + i; @@ -290,7 +243,7 @@ uint32_t per_client_cblk_t::unlock_layer_and_post(size_t i) if (oldvalue & eFlipRequested) newvalue |= eNextFlipPending; - // if eFlipRequested was alread set, set eNextFlipPending + // if eFlipRequested was already set, set eNextFlipPending } while (android_atomic_cmpxchg(oldvalue, newvalue, &(layer->swapState))); @@ -298,9 +251,9 @@ uint32_t per_client_cblk_t::unlock_layer_and_post(size_t i) int(i), int((layer->flags & eBufferIndex) >> eBufferIndexShift), int(newvalue)); - // from this point, the server can kick in at anytime and use the first + // from this point, the server can kick in at any time and use the first // buffer, so we cannot use it anymore, and we must use the 'other' - // buffer instead (or wait if it is not availlable yet, see lock_layer). + // buffer instead (or wait if it is not available yet, see lock_layer). return newvalue; } @@ -360,9 +313,9 @@ void SurfaceComposerClient::_init( return; } - mClient->getControlBlocks(&mControlMemory); + mControlMemory = mClient->getControlBlock(); mSignalServer = new SurfaceFlingerSynchro(sm); - mControl = static_cast<per_client_cblk_t *>(mControlMemory->pointer()); + mControl = static_cast<per_client_cblk_t *>(mControlMemory->getBase()); } SurfaceComposerClient::~SurfaceComposerClient() @@ -376,32 +329,6 @@ status_t SurfaceComposerClient::initCheck() const return mStatus; } -status_t SurfaceComposerClient::validateSurface( - per_client_cblk_t const* cblk, Surface const * surface) -{ - SurfaceID index = surface->ID(); - if (cblk == 0) { - LOGE("cblk is null (surface id=%d, identity=%u)", - index, surface->getIdentity()); - return NO_INIT; - } - - status_t err = cblk->validate(index); - if (err != NO_ERROR) { - LOGE("surface (id=%d, identity=%u) is invalid, err=%d (%s)", - index, surface->getIdentity(), err, strerror(-err)); - return err; - } - - if (surface->getIdentity() != uint32_t(cblk->layers[index].identity)) { - LOGE("using an invalid surface id=%d, identity=%u should be %d", - index, surface->getIdentity(), cblk->layers[index].identity); - return NO_INIT; - } - - return NO_ERROR; -} - sp<IBinder> SurfaceComposerClient::connection() const { return (mClient != 0) ? mClient->asBinder() : 0; @@ -437,9 +364,8 @@ void SurfaceComposerClient::dispose() { // this can be called more than once. - sp<IMemory> controlMemory; + sp<IMemoryHeap> controlMemory; sp<ISurfaceFlingerClient> client; - sp<IMemoryHeap> surfaceHeap; { Mutex::Autolock _lg(gLock); @@ -462,9 +388,7 @@ void SurfaceComposerClient::dispose() delete mPrebuiltLayerState; mPrebuiltLayerState = 0; controlMemory = mControlMemory; - surfaceHeap = mSurfaceHeap; mControlMemory.clear(); - mSurfaceHeap.clear(); mControl = 0; mStatus = NO_INIT; } @@ -528,7 +452,13 @@ ssize_t SurfaceComposerClient::getNumberOfDisplays() return n; } -sp<Surface> SurfaceComposerClient::createSurface( + +void SurfaceComposerClient::signalServer() +{ + mSignalServer->signal(); +} + +sp<SurfaceControl> SurfaceComposerClient::createSurface( int pid, DisplayID display, uint32_t w, @@ -536,14 +466,14 @@ sp<Surface> SurfaceComposerClient::createSurface( PixelFormat format, uint32_t flags) { - sp<Surface> result; + sp<SurfaceControl> result; if (mStatus == NO_ERROR) { ISurfaceFlingerClient::surface_data_t data; sp<ISurface> surface = mClient->createSurface(&data, pid, display, w, h, format, flags); if (surface != 0) { if (uint32_t(data.token) < NUM_LAYERS_MAX) { - result = new Surface(this, surface, data, w, h, format, flags); + result = new SurfaceControl(this, surface, data, w, h, format, flags); } } } @@ -568,186 +498,6 @@ status_t SurfaceComposerClient::destroySurface(SurfaceID sid) return err; } -status_t SurfaceComposerClient::nextBuffer(Surface* surface, - Surface::SurfaceInfo* info) -{ - SurfaceID index = surface->ID(); - per_client_cblk_t* const cblk = mControl; - status_t err = validateSurface(cblk, surface); - if (err != NO_ERROR) - return err; - - int32_t backIdx = surface->mBackbufferIndex; - layer_cblk_t* const lcblk = &(cblk->layers[index]); - const surface_info_t* const front = lcblk->surface + (1-backIdx); - info->w = front->w; - info->h = front->h; - info->format = front->format; - info->base = surface->heapBase(1-backIdx); - info->bits = reinterpret_cast<void*>(intptr_t(info->base) + front->bits_offset); - info->bpr = front->bpr; - - return 0; -} - -status_t SurfaceComposerClient::lockSurface( - Surface* surface, - Surface::SurfaceInfo* other, - Region* dirty, - bool blocking) -{ - Mutex::Autolock _l(surface->getLock()); - - SurfaceID index = surface->ID(); - per_client_cblk_t* const cblk = mControl; - status_t err = validateSurface(cblk, surface); - if (err != NO_ERROR) - return err; - - int32_t backIdx = cblk->lock_layer(size_t(index), - per_client_cblk_t::BLOCKING); - if (backIdx >= 0) { - surface->mBackbufferIndex = backIdx; - layer_cblk_t* const lcblk = &(cblk->layers[index]); - const surface_info_t* const back = lcblk->surface + backIdx; - const surface_info_t* const front = lcblk->surface + (1-backIdx); - other->w = back->w; - other->h = back->h; - other->format = back->format; - other->base = surface->heapBase(backIdx); - other->bits = reinterpret_cast<void*>(intptr_t(other->base) + back->bits_offset); - other->bpr = back->bpr; - - const Rect bounds(other->w, other->h); - Region newDirtyRegion; - - if (back->flags & surface_info_t::eBufferDirty) { - /* it is safe to write *back here, because we're guaranteed - * SurfaceFlinger is not touching it (since it just granted - * access to us) */ - const_cast<surface_info_t*>(back)->flags &= - ~surface_info_t::eBufferDirty; - - // content is meaningless in this case and the whole surface - // needs to be redrawn. - - newDirtyRegion.set(bounds); - if (dirty) { - *dirty = newDirtyRegion; - } - - //if (bytesPerPixel(other->format) == 4) { - // android_memset32( - // (uint32_t*)other->bits, 0xFF00FF00, other->h * other->bpr); - //} else { - // android_memset16( // fill with green - // (uint16_t*)other->bits, 0x7E0, other->h * other->bpr); - //} - } - else - { - if (dirty) { - dirty->andSelf(Region(bounds)); - newDirtyRegion = *dirty; - } else { - newDirtyRegion.set(bounds); - } - - Region copyback; - if (!(lcblk->flags & eNoCopyBack)) { - const Region previousDirtyRegion(surface->dirtyRegion()); - copyback = previousDirtyRegion.subtract(newDirtyRegion); - } - - if (!copyback.isEmpty()) { - // copy front to back - GGLSurface cb; - cb.version = sizeof(GGLSurface); - cb.width = back->w; - cb.height = back->h; - cb.stride = back->stride; - cb.data = (GGLubyte*)surface->heapBase(backIdx); - cb.data += back->bits_offset; - cb.format = back->format; - - GGLSurface t; - t.version = sizeof(GGLSurface); - t.width = front->w; - t.height = front->h; - t.stride = front->stride; - t.data = (GGLubyte*)surface->heapBase(1-backIdx); - t.data += front->bits_offset; - t.format = front->format; - - //const Region copyback(lcblk->region + 1-backIdx); - copyBlt(cb, t, copyback); - } - } - - // update dirty region - surface->setDirtyRegion(newDirtyRegion); - } - return (backIdx < 0) ? status_t(backIdx) : status_t(NO_ERROR); -} - -void SurfaceComposerClient::_signal_server() -{ - mSignalServer->signal(); -} - -void SurfaceComposerClient::_send_dirty_region( - layer_cblk_t* lcblk, const Region& dirty) -{ - const int32_t index = (lcblk->flags & eBufferIndex) >> eBufferIndexShift; - flat_region_t* flat_region = lcblk->region + index; - status_t err = dirty.write(flat_region, sizeof(flat_region_t)); - if (err < NO_ERROR) { - // region doesn't fit, use the bounds - const Region reg(dirty.bounds()); - reg.write(flat_region, sizeof(flat_region_t)); - } -} - -status_t SurfaceComposerClient::unlockAndPostSurface(Surface* surface) -{ - Mutex::Autolock _l(surface->getLock()); - - SurfaceID index = surface->ID(); - per_client_cblk_t* const cblk = mControl; - status_t err = validateSurface(cblk, surface); - if (err != NO_ERROR) - return err; - - Region dirty(surface->dirtyRegion()); - const Rect& swapRect(surface->swapRectangle()); - if (swapRect.isValid()) { - dirty.set(swapRect); - } - - // transmit the dirty region - layer_cblk_t* const lcblk = &(cblk->layers[index]); - _send_dirty_region(lcblk, dirty); - uint32_t newstate = cblk->unlock_layer_and_post(size_t(index)); - if (!(newstate & eNextFlipPending)) - _signal_server(); - return NO_ERROR; -} - -status_t SurfaceComposerClient::unlockSurface(Surface* surface) -{ - Mutex::Autolock _l(surface->getLock()); - - SurfaceID index = surface->ID(); - per_client_cblk_t* const cblk = mControl; - status_t err = validateSurface(cblk, surface); - if (err != NO_ERROR) - return err; - - layer_cblk_t* const lcblk = &(cblk->layers[index]); - cblk->unlock_layer(size_t(index)); - return NO_ERROR; -} - void SurfaceComposerClient::openGlobalTransaction() { Mutex::Autolock _l(gLock); @@ -866,14 +616,8 @@ status_t SurfaceComposerClient::closeTransaction() return NO_ERROR; } -layer_state_t* SurfaceComposerClient::_get_state_l(const sp<Surface>& surface) +layer_state_t* SurfaceComposerClient::_get_state_l(SurfaceID index) { - SurfaceID index = surface->ID(); - per_client_cblk_t* const cblk = mControl; - status_t err = validateSurface(cblk, surface.get()); - if (err != NO_ERROR) - return 0; - // API usage error, do nothing. if (mTransactionOpen<=0) { LOGE("Not in transaction (client=%p, SurfaceID=%d, mTransactionOpen=%d", @@ -892,11 +636,11 @@ layer_state_t* SurfaceComposerClient::_get_state_l(const sp<Surface>& surface) return mStates.editArray() + i; } -layer_state_t* SurfaceComposerClient::_lockLayerState(const sp<Surface>& surface) +layer_state_t* SurfaceComposerClient::_lockLayerState(SurfaceID id) { layer_state_t* s; mLock.lock(); - s = _get_state_l(surface); + s = _get_state_l(id); if (!s) mLock.unlock(); return s; } @@ -906,9 +650,9 @@ void SurfaceComposerClient::_unlockLayerState() mLock.unlock(); } -status_t SurfaceComposerClient::setPosition(Surface* surface, int32_t x, int32_t y) +status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::ePositionChanged; s->x = x; @@ -917,9 +661,9 @@ status_t SurfaceComposerClient::setPosition(Surface* surface, int32_t x, int32_t return NO_ERROR; } -status_t SurfaceComposerClient::setSize(Surface* surface, uint32_t w, uint32_t h) +status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eSizeChanged; s->w = w; @@ -928,9 +672,9 @@ status_t SurfaceComposerClient::setSize(Surface* surface, uint32_t w, uint32_t h return NO_ERROR; } -status_t SurfaceComposerClient::setLayer(Surface* surface, int32_t z) +status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eLayerChanged; s->z = z; @@ -938,32 +682,32 @@ status_t SurfaceComposerClient::setLayer(Surface* surface, int32_t z) return NO_ERROR; } -status_t SurfaceComposerClient::hide(Surface* surface) +status_t SurfaceComposerClient::hide(SurfaceID id) { - return setFlags(surface, ISurfaceComposer::eLayerHidden, + return setFlags(id, ISurfaceComposer::eLayerHidden, ISurfaceComposer::eLayerHidden); } -status_t SurfaceComposerClient::show(Surface* surface, int32_t) +status_t SurfaceComposerClient::show(SurfaceID id, int32_t) { - return setFlags(surface, 0, ISurfaceComposer::eLayerHidden); + return setFlags(id, 0, ISurfaceComposer::eLayerHidden); } -status_t SurfaceComposerClient::freeze(Surface* surface) +status_t SurfaceComposerClient::freeze(SurfaceID id) { - return setFlags(surface, ISurfaceComposer::eLayerFrozen, + return setFlags(id, ISurfaceComposer::eLayerFrozen, ISurfaceComposer::eLayerFrozen); } -status_t SurfaceComposerClient::unfreeze(Surface* surface) +status_t SurfaceComposerClient::unfreeze(SurfaceID id) { - return setFlags(surface, 0, ISurfaceComposer::eLayerFrozen); + return setFlags(id, 0, ISurfaceComposer::eLayerFrozen); } -status_t SurfaceComposerClient::setFlags(Surface* surface, +status_t SurfaceComposerClient::setFlags(SurfaceID id, uint32_t flags, uint32_t mask) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eVisibilityChanged; s->flags &= ~mask; @@ -973,11 +717,10 @@ status_t SurfaceComposerClient::setFlags(Surface* surface, return NO_ERROR; } - status_t SurfaceComposerClient::setTransparentRegionHint( - Surface* surface, const Region& transparentRegion) + SurfaceID id, const Region& transparentRegion) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eTransparentRegionChanged; s->transparentRegion = transparentRegion; @@ -985,9 +728,9 @@ status_t SurfaceComposerClient::setTransparentRegionHint( return NO_ERROR; } -status_t SurfaceComposerClient::setAlpha(Surface* surface, float alpha) +status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eAlphaChanged; s->alpha = alpha; @@ -996,11 +739,11 @@ status_t SurfaceComposerClient::setAlpha(Surface* surface, float alpha) } status_t SurfaceComposerClient::setMatrix( - Surface* surface, + SurfaceID id, float dsdx, float dtdx, float dsdy, float dtdy ) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eMatrixChanged; layer_state_t::matrix22_t matrix; @@ -1013,9 +756,9 @@ status_t SurfaceComposerClient::setMatrix( return NO_ERROR; } -status_t SurfaceComposerClient::setFreezeTint(Surface* surface, uint32_t tint) +status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) { - layer_state_t* s = _lockLayerState(surface); + layer_state_t* s = _lockLayerState(id); if (!s) return BAD_INDEX; s->what |= ISurfaceComposer::eFreezeTintChanged; s->tint = tint; diff --git a/libs/ui/SurfaceFlingerSynchro.cpp b/libs/ui/SurfaceFlingerSynchro.cpp index 5cd9755..c81db71 100644 --- a/libs/ui/SurfaceFlingerSynchro.cpp +++ b/libs/ui/SurfaceFlingerSynchro.cpp @@ -14,19 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceFlingerSynchro" - #include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <limits.h> -#include <sys/types.h> -#include <sys/stat.h> - -#include <utils/IPCThreadState.h> -#include <utils/Log.h> #include <private/ui/SurfaceFlingerSynchro.h> @@ -34,61 +22,10 @@ namespace android { // --------------------------------------------------------------------------- -SurfaceFlingerSynchro::Barrier::Barrier() - : state(CLOSED) { -} - -SurfaceFlingerSynchro::Barrier::~Barrier() { -} - -void SurfaceFlingerSynchro::Barrier::open() { - asm volatile ("":::"memory"); - Mutex::Autolock _l(lock); - state = OPENED; - cv.broadcast(); -} - -void SurfaceFlingerSynchro::Barrier::close() { - Mutex::Autolock _l(lock); - state = CLOSED; -} - -void SurfaceFlingerSynchro::Barrier::waitAndClose() -{ - Mutex::Autolock _l(lock); - while (state == CLOSED) { - // we're about to wait, flush the binder command buffer - IPCThreadState::self()->flushCommands(); - cv.wait(lock); - } - state = CLOSED; -} - -status_t SurfaceFlingerSynchro::Barrier::waitAndClose(nsecs_t timeout) -{ - Mutex::Autolock _l(lock); - while (state == CLOSED) { - // we're about to wait, flush the binder command buffer - IPCThreadState::self()->flushCommands(); - int err = cv.waitRelative(lock, timeout); - if (err != 0) - return err; - } - state = CLOSED; - return NO_ERROR; -} - -// --------------------------------------------------------------------------- - SurfaceFlingerSynchro::SurfaceFlingerSynchro(const sp<ISurfaceComposer>& flinger) : mSurfaceComposer(flinger) { } - -SurfaceFlingerSynchro::SurfaceFlingerSynchro() -{ -} - SurfaceFlingerSynchro::~SurfaceFlingerSynchro() { } @@ -99,24 +36,6 @@ status_t SurfaceFlingerSynchro::signal() return NO_ERROR; } -status_t SurfaceFlingerSynchro::wait() -{ - mBarrier.waitAndClose(); - return NO_ERROR; -} - -status_t SurfaceFlingerSynchro::wait(nsecs_t timeout) -{ - if (timeout == 0) - return SurfaceFlingerSynchro::wait(); - return mBarrier.waitAndClose(timeout); -} - -void SurfaceFlingerSynchro::open() -{ - mBarrier.open(); -} - // --------------------------------------------------------------------------- }; // namespace android diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk new file mode 100644 index 0000000..6cc4a5a --- /dev/null +++ b/libs/ui/tests/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + region.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libutils \ + libui + +LOCAL_MODULE:= test-region + +LOCAL_MODULE_TAGS := tests + +include $(BUILD_EXECUTABLE) diff --git a/libs/ui/tests/region.cpp b/libs/ui/tests/region.cpp new file mode 100644 index 0000000..0deb2ba --- /dev/null +++ b/libs/ui/tests/region.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Region" + +#include <stdio.h> +#include <utils/Debug.h> +#include <ui/Rect.h> +#include <ui/Region.h> + +using namespace android; + +int main() +{ + Region reg0( Rect( 0, 0, 100, 100 ) ); + Region reg1 = reg0; + Region reg2, reg3; + + reg0.dump("reg0"); + reg1.dump("reg1"); + + reg0 = reg0 | reg0.translate(150, 0); + reg0.dump("reg0"); + reg1.dump("reg1"); + + reg0 = reg0 | reg0.translate(300, 0); + reg0.dump("reg0"); + reg1.dump("reg1"); + + //reg2 = reg0 | reg0.translate(0, 100); + //reg0.dump("reg0"); + //reg1.dump("reg1"); + //reg2.dump("reg2"); + + //reg3 = reg0 | reg0.translate(0, 150); + //reg0.dump("reg0"); + //reg1.dump("reg1"); + //reg2.dump("reg2"); + //reg3.dump("reg3"); + + LOGD("---"); + reg2 = reg0 | reg0.translate(100, 0); + reg0.dump("reg0"); + reg1.dump("reg1"); + reg2.dump("reg2"); + + return 0; +} + diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 9bdd64a..59409a2 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -32,52 +32,24 @@ commonSources:= \ StopWatch.cpp \ String8.cpp \ String16.cpp \ + StringArray.cpp \ SystemClock.cpp \ TextOutput.cpp \ Threads.cpp \ - TimerProbe.cpp \ Timers.cpp \ VectorImpl.cpp \ ZipFileCRO.cpp \ ZipFileRO.cpp \ ZipUtils.cpp \ - misc.cpp \ - ported.cpp \ - LogSocket.cpp + misc.cpp -# -# The cpp files listed here do not belong in the device -# build. Consult with the swetland before even thinking about -# putting them in commonSources. -# -# They're used by the simulator runtime and by host-side tools like -# aapt and the simulator front-end. -# -hostSources:= \ - InetAddress.cpp \ - Pipe.cpp \ - Socket.cpp \ - ZipEntry.cpp \ - ZipFile.cpp # For the host # ===================================================== include $(CLEAR_VARS) -LOCAL_SRC_FILES:= $(commonSources) $(hostSources) - -ifeq ($(HOST_OS),linux) -# Use the futex based mutex and condition variable -# implementation from android-arm because it's shared mem safe - LOCAL_SRC_FILES += \ - futex_synchro.c \ - executablepath_linux.cpp -endif -ifeq ($(HOST_OS),darwin) - LOCAL_SRC_FILES += \ - executablepath_darwin.cpp -endif +LOCAL_SRC_FILES:= $(commonSources) LOCAL_MODULE:= libutils @@ -103,37 +75,18 @@ include $(CLEAR_VARS) # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ - Binder.cpp \ - BpBinder.cpp \ - IInterface.cpp \ - IMemory.cpp \ - IPCThreadState.cpp \ - MemoryDealer.cpp \ - MemoryBase.cpp \ - MemoryHeapBase.cpp \ - MemoryHeapPmem.cpp \ - Parcel.cpp \ - ProcessState.cpp \ - IPermissionController.cpp \ - IServiceManager.cpp \ Unicode.cpp \ BackupData.cpp \ BackupHelpers.cpp -ifeq ($(TARGET_SIMULATOR),true) -LOCAL_SRC_FILES += $(hostSources) -endif - ifeq ($(TARGET_OS),linux) -# Use the futex based mutex and condition variable -# implementation from android-arm because it's shared mem safe -LOCAL_SRC_FILES += futex_synchro.c LOCAL_LDLIBS += -lrt -ldl endif LOCAL_C_INCLUDES += \ external/zlib \ external/icu4c/common + LOCAL_LDLIBS += -lpthread LOCAL_SHARED_LIBRARIES := \ @@ -144,15 +97,10 @@ LOCAL_SHARED_LIBRARIES := \ ifneq ($(TARGET_SIMULATOR),true) ifeq ($(TARGET_OS)-$(TARGET_ARCH),linux-x86) # This is needed on x86 to bring in dl_iterate_phdr for CallStack.cpp -LOCAL_SHARED_LIBRARIES += \ - libdl +LOCAL_SHARED_LIBRARIES += libdl endif # linux-x86 endif # sim LOCAL_MODULE:= libutils - -#LOCAL_CFLAGS+= -#LOCAL_LDFLAGS:= - include $(BUILD_SHARED_LIBRARY) diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index cce754a..be04777 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -298,10 +298,12 @@ BackupDataReader::SkipEntityData() } if (m_header.entity.dataSize > 0) { int pos = lseek(m_fd, m_dataEndPos, SEEK_SET); - return pos == -1 ? (int)errno : (int)NO_ERROR; - } else { - return NO_ERROR; + if (pos == -1) { + return errno; + } } + SKIP_PADDING(); + return NO_ERROR; } ssize_t diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 2fdaa71..55b6024 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -311,7 +311,8 @@ String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const } else { void const* start = 0; name = MapInfo::mapAddressToName(ip, "<unknown>", &start); - snprintf(tmp, 256, "pc %08lx %s", uintptr_t(ip)-uintptr_t(start), name); + snprintf(tmp, 256, "pc %08lx %s", + long(uintptr_t(ip)-uintptr_t(start)), name); res.append(tmp); } res.append("\n"); diff --git a/libs/utils/IDataConnection.cpp b/libs/utils/IDataConnection.cpp deleted file mode 100644 index c6d49aa..0000000 --- a/libs/utils/IDataConnection.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdint.h> -#include <sys/types.h> - -#include <utils/Parcel.h> - -#include <utils/IDataConnection.h> - -namespace android { - -// --------------------------------------------------------------------------- - -enum -{ - CONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, - DISCONNECT_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION + 1 -}; - -class BpDataConnection : public BpInterface<IDataConnection> -{ -public: - BpDataConnection::BpDataConnection(const sp<IBinder>& impl) - : BpInterface<IDataConnection>(impl) - { - } - - virtual void connect() - { - Parcel data, reply; - data.writeInterfaceToken(IDataConnection::descriptor()); - remote()->transact(CONNECT_TRANSACTION, data, &reply); - } - - virtual void disconnect() - { - Parcel data, reply; - remote()->transact(DISCONNECT_TRANSACTION, data, &reply); - } -}; - -IMPLEMENT_META_INTERFACE(DataConnection, "android.utils.IDataConnection"); - -#define CHECK_INTERFACE(interface, data, reply) \ - do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \ - LOGW("Call incorrectly routed to " #interface); \ - return PERMISSION_DENIED; \ - } } while (0) - -status_t BnDataConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch(code) - { - case CONNECT_TRANSACTION: - { - CHECK_INTERFACE(IDataConnection, data, reply); - connect(); - return NO_ERROR; - } - - case DISCONNECT_TRANSACTION: - { - CHECK_INTERFACE(IDataConnection, data, reply); - disconnect(); - return NO_ERROR; - } - - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -// ---------------------------------------------------------------------------- - -}; // namespace android diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp deleted file mode 100644 index 39a0a68..0000000 --- a/libs/utils/InetAddress.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Internet address class. -// -#ifdef HAVE_WINSOCK -# include <winsock2.h> -#else -# include <sys/types.h> -# include <sys/socket.h> -# include <netinet/in.h> -//# include <arpa/inet.h> -# include <netdb.h> -#endif - -#include <utils/inet_address.h> -#include <utils/threads.h> -#include <utils/Log.h> - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -using namespace android; - - -/* - * =========================================================================== - * InetAddress - * =========================================================================== - */ - -// lock for the next couple of functions; could tuck into InetAddress -static Mutex* gGHBNLock; - -/* - * Lock/unlock access to the hostent struct returned by gethostbyname(). - */ -static inline void lock_gethostbyname(void) -{ - if (gGHBNLock == NULL) - gGHBNLock = new Mutex; - gGHBNLock->lock(); -} -static inline void unlock_gethostbyname(void) -{ - assert(gGHBNLock != NULL); - gGHBNLock->unlock(); -} - - -/* - * Constructor -- just init members. This is private so that callers - * are required to use getByName(). - */ -InetAddress::InetAddress(void) - : mAddress(NULL), mLength(-1), mName(NULL) -{ -} - -/* - * Destructor -- free address storage. - */ -InetAddress::~InetAddress(void) -{ - delete[] (char*) mAddress; - delete[] mName; -} - -/* - * Copy constructor. - */ -InetAddress::InetAddress(const InetAddress& orig) -{ - *this = orig; // use assignment code -} - -/* - * Assignment operator. - */ -InetAddress& InetAddress::operator=(const InetAddress& addr) -{ - // handle self-assignment - if (this == &addr) - return *this; - // copy mLength and mAddress - mLength = addr.mLength; - if (mLength > 0) { - mAddress = new char[mLength]; - memcpy(mAddress, addr.mAddress, mLength); - LOG(LOG_DEBUG, "socket", - "HEY: copied %d bytes in assignment operator\n", mLength); - } else { - mAddress = NULL; - } - // copy mName - mName = new char[strlen(addr.mName)+1]; - strcpy(mName, addr.mName); - - return *this; -} - -/* - * Create a new object from a name or a dotted-number IP notation. - * - * Returns NULL on failure. - */ -InetAddress* -InetAddress::getByName(const char* host) -{ - InetAddress* newAddr = NULL; - struct sockaddr_in addr; - struct hostent* he; - DurationTimer hostTimer, lockTimer; - - // gethostbyname() isn't reentrant, so we need to lock things until - // we can copy the data out. - lockTimer.start(); - lock_gethostbyname(); - hostTimer.start(); - - he = gethostbyname(host); - if (he == NULL) { - LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host); - unlock_gethostbyname(); - return NULL; - } - - memcpy(&addr.sin_addr, he->h_addr, he->h_length); - addr.sin_family = he->h_addrtype; - addr.sin_port = 0; - - // got it, unlock us - hostTimer.stop(); - he = NULL; - unlock_gethostbyname(); - - lockTimer.stop(); - if ((long) lockTimer.durationUsecs() > 100000) { - long lockTime = (long) lockTimer.durationUsecs(); - long hostTime = (long) hostTimer.durationUsecs(); - LOG(LOG_DEBUG, "socket", - "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n", - host, lockTime / 1000000.0, hostTime / 1000000.0, - (lockTime - hostTime) / 1000000.0); - } - - // Alloc storage and copy it over. - newAddr = new InetAddress(); - if (newAddr == NULL) - return NULL; - - newAddr->mLength = sizeof(struct sockaddr_in); - newAddr->mAddress = new char[sizeof(struct sockaddr_in)]; - if (newAddr->mAddress == NULL) { - delete newAddr; - return NULL; - } - memcpy(newAddr->mAddress, &addr, newAddr->mLength); - - // Keep this for debug messages. - newAddr->mName = new char[strlen(host)+1]; - if (newAddr->mName == NULL) { - delete newAddr; - return NULL; - } - strcpy(newAddr->mName, host); - - return newAddr; -} - - -/* - * =========================================================================== - * InetSocketAddress - * =========================================================================== - */ - -/* - * Create an address with the host wildcard (INADDR_ANY). - */ -bool InetSocketAddress::create(int port) -{ - assert(mAddress == NULL); - - mAddress = InetAddress::getByName("0.0.0.0"); - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - -/* - * Create address with host and port specified. - */ -bool InetSocketAddress::create(const InetAddress* addr, int port) -{ - assert(mAddress == NULL); - - mAddress = new InetAddress(*addr); // make a copy - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - -/* - * Create address with host and port specified. - */ -bool InetSocketAddress::create(const char* host, int port) -{ - assert(mAddress == NULL); - - mAddress = InetAddress::getByName(host); - if (mAddress == NULL) - return false; - mPort = port; - return true; -} - diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp deleted file mode 100644 index 55c1b99..0000000 --- a/libs/utils/LogSocket.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef HAVE_WINSOCK -//#define SOCKETLOG -#endif - -#ifdef SOCKETLOG - -#define LOG_TAG "SOCKETLOG" - -#include <string.h> -#include <cutils/log.h> -#include "utils/LogSocket.h" -#include "utils/logger.h" -#include "cutils/hashmap.h" - -// defined in //device/data/etc/event-log-tags -#define SOCKET_CLOSE_LOG 51000 - -static Hashmap* statsMap = NULL; - -#define LOG_LIST_NUMBER 5 - -typedef struct SocketStats { - int fd; - unsigned int send; - unsigned int recv; - unsigned int ip; - unsigned short port; - short reason; -}SocketStats; - -SocketStats *get_socket_stats(int fd) { - if (statsMap == NULL) { - statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals); - } - - SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); - if (s == NULL) { - // LOGD("create SocketStats for fd %d", fd); - s = (SocketStats*) malloc(sizeof(SocketStats)); - memset(s, 0, sizeof(SocketStats)); - s->fd = fd; - hashmapPut(statsMap, &s->fd, s); - } - return s; -} - -void log_socket_connect(int fd, unsigned int ip, unsigned short port) { - // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port); - SocketStats *s = get_socket_stats(fd); - s->ip = ip; - s->port = port; -} - -void add_send_stats(int fd, int send) { - if (send <=0) { - LOGE("add_send_stats send %d", send); - return; - } - SocketStats *s = get_socket_stats(fd); - s->send += send; - // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port); -} - -void add_recv_stats(int fd, int recv) { - if (recv <=0) { - LOGE("add_recv_stats recv %d", recv); - return; - } - SocketStats *s = get_socket_stats(fd); - s->recv += recv; - // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port); -} - -char* put_int(char* buf, int value) { - *buf = EVENT_TYPE_INT; - buf++; - memcpy(buf, &value, sizeof(int)); - return buf + sizeof(int); -} - -void log_socket_close(int fd, short reason) { - if (statsMap) { - SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd); - if (s != NULL) { - if (s->send != 0 || s->recv != 0) { - s->reason = reason; - // 5 int + list type need 2 bytes - char buf[LOG_LIST_NUMBER * 5 + 2]; - buf[0] = EVENT_TYPE_LIST; - buf[1] = LOG_LIST_NUMBER; - char* writePos = buf + 2; - writePos = put_int(writePos, s->send); - writePos = put_int(writePos, s->recv); - writePos = put_int(writePos, s->ip); - writePos = put_int(writePos, s->port); - writePos = put_int(writePos, s->reason); - - android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf)); - // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason); - } - hashmapRemove(statsMap, &s->fd); - free(s); - } - } -} - -#else -void add_send_stats(int fd, int send) {} -void add_recv_stats(int fd, int recv) {} -void log_socket_close(int fd, short reason) {} -void log_socket_connect(int fd, unsigned int ip, unsigned short port) {} -#endif diff --git a/libs/utils/Pipe.cpp b/libs/utils/Pipe.cpp deleted file mode 100644 index 613906b..0000000 --- a/libs/utils/Pipe.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Unidirectional pipe. -// - -#include <utils/Pipe.h> -#include <utils/Log.h> - -#if defined(HAVE_WIN32_IPC) -# include <windows.h> -#else -# include <fcntl.h> -# include <unistd.h> -# include <errno.h> -#endif - -#include <stdlib.h> -#include <stdio.h> -#include <assert.h> -#include <string.h> - -using namespace android; - -const unsigned long kInvalidHandle = (unsigned long) -1; - - -/* - * Constructor. Do little. - */ -Pipe::Pipe(void) - : mReadNonBlocking(false), mReadHandle(kInvalidHandle), - mWriteHandle(kInvalidHandle) -{ -} - -/* - * Destructor. Use the system-appropriate close call. - */ -Pipe::~Pipe(void) -{ -#if defined(HAVE_WIN32_IPC) - if (mReadHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mReadHandle)) - LOG(LOG_WARN, "pipe", "failed closing read handle (%ld)\n", - mReadHandle); - } - if (mWriteHandle != kInvalidHandle) { - FlushFileBuffers((HANDLE)mWriteHandle); - if (!CloseHandle((HANDLE)mWriteHandle)) - LOG(LOG_WARN, "pipe", "failed closing write handle (%ld)\n", - mWriteHandle); - } -#else - if (mReadHandle != kInvalidHandle) { - if (close((int) mReadHandle) != 0) - LOG(LOG_WARN, "pipe", "failed closing read fd (%d)\n", - (int) mReadHandle); - } - if (mWriteHandle != kInvalidHandle) { - if (close((int) mWriteHandle) != 0) - LOG(LOG_WARN, "pipe", "failed closing write fd (%d)\n", - (int) mWriteHandle); - } -#endif -} - -/* - * Create the pipe. - * - * Use the POSIX stuff for everything but Windows. - */ -bool Pipe::create(void) -{ - assert(mReadHandle == kInvalidHandle); - assert(mWriteHandle == kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - /* we use this across processes, so they need to be inheritable */ - HANDLE handles[2]; - SECURITY_ATTRIBUTES saAttr; - - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - - if (!CreatePipe(&handles[0], &handles[1], &saAttr, 0)) { - LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); - return false; - } - mReadHandle = (unsigned long) handles[0]; - mWriteHandle = (unsigned long) handles[1]; - return true; -#else - int fds[2]; - - if (pipe(fds) != 0) { - LOG(LOG_ERROR, "pipe", "unable to create pipe\n"); - return false; - } - mReadHandle = fds[0]; - mWriteHandle = fds[1]; - return true; -#endif -} - -/* - * Create a "half pipe". Please, no Segway riding. - */ -bool Pipe::createReader(unsigned long handle) -{ - mReadHandle = handle; - assert(mWriteHandle == kInvalidHandle); - return true; -} - -/* - * Create a "half pipe" for writing. - */ -bool Pipe::createWriter(unsigned long handle) -{ - mWriteHandle = handle; - assert(mReadHandle == kInvalidHandle); - return true; -} - -/* - * Return "true" if create() has been called successfully. - */ -bool Pipe::isCreated(void) -{ - // one or the other should be open - return (mReadHandle != kInvalidHandle || mWriteHandle != kInvalidHandle); -} - - -/* - * Read data from the pipe. - * - * For Linux and Darwin, just call read(). For Windows, implement - * non-blocking reads by calling PeekNamedPipe first. - */ -int Pipe::read(void* buf, int count) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD totalBytesAvail = count; - DWORD bytesRead; - - if (mReadNonBlocking) { - // use PeekNamedPipe to adjust read count expectations - if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, - &totalBytesAvail, NULL)) - { - LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); - return -1; - } - - if (totalBytesAvail == 0) - return 0; - } - - if (!ReadFile((HANDLE) mReadHandle, buf, totalBytesAvail, &bytesRead, - NULL)) - { - DWORD err = GetLastError(); - if (err == ERROR_HANDLE_EOF || err == ERROR_BROKEN_PIPE) - return 0; - LOG(LOG_ERROR, "pipe", "ReadFile failed (err=%ld)\n", err); - return -1; - } - - return (int) bytesRead; -#else - int cc; - cc = ::read(mReadHandle, buf, count); - if (cc < 0 && errno == EAGAIN) - return 0; - return cc; -#endif -} - -/* - * Write data to the pipe. - * - * POSIX systems are trivial, Windows uses a different call and doesn't - * handle non-blocking writes. - * - * If we add non-blocking support here, we probably want to make it an - * all-or-nothing write. - * - * DO NOT use LOG() here, we could be writing a log message. - */ -int Pipe::write(const void* buf, int count) -{ - assert(mWriteHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD bytesWritten; - - if (mWriteNonBlocking) { - // BUG: can't use PeekNamedPipe() to get the amount of space - // left. Looks like we need to use "overlapped I/O" functions. - // I just don't care that much. - } - - if (!WriteFile((HANDLE) mWriteHandle, buf, count, &bytesWritten, NULL)) { - // can't LOG, use stderr - fprintf(stderr, "WriteFile failed (err=%ld)\n", GetLastError()); - return -1; - } - - return (int) bytesWritten; -#else - int cc; - cc = ::write(mWriteHandle, buf, count); - if (cc < 0 && errno == EAGAIN) - return 0; - return cc; -#endif -} - -/* - * Figure out if there is data available on the read fd. - * - * We return "true" on error because we want the caller to try to read - * from the pipe. They'll notice the read failure and do something - * appropriate. - */ -bool Pipe::readReady(void) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - DWORD totalBytesAvail; - - if (!PeekNamedPipe((HANDLE) mReadHandle, NULL, 0, NULL, - &totalBytesAvail, NULL)) - { - LOG(LOG_ERROR, "pipe", "PeekNamedPipe failed\n"); - return true; - } - - return (totalBytesAvail != 0); -#else - errno = 0; - fd_set readfds; - struct timeval tv = { 0, 0 }; - int cc; - - FD_ZERO(&readfds); - FD_SET(mReadHandle, &readfds); - - cc = select(mReadHandle+1, &readfds, NULL, NULL, &tv); - if (cc < 0) { - LOG(LOG_ERROR, "pipe", "select() failed\n"); - return true; - } else if (cc == 0) { - /* timed out, nothing available */ - return false; - } else if (cc == 1) { - /* our fd is ready */ - return true; - } else { - LOG(LOG_ERROR, "pipe", "HUH? select() returned > 1\n"); - return true; - } -#endif -} - -/* - * Enable or disable non-blocking mode for the read descriptor. - * - * NOTE: the calls succeed under Mac OS X, but the pipe doesn't appear to - * actually be in non-blocking mode. If this matters -- i.e. you're not - * using a select() call -- put a call to readReady() in front of the - * ::read() call, with a PIPE_NONBLOCK_BROKEN #ifdef in the Makefile for - * Darwin. - */ -bool Pipe::setReadNonBlocking(bool val) -{ - assert(mReadHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - // nothing to do -#else - int flags; - - if (fcntl(mReadHandle, F_GETFL, &flags) == -1) { - LOG(LOG_ERROR, "pipe", "couldn't get flags for pipe read fd\n"); - return false; - } - if (val) - flags |= O_NONBLOCK; - else - flags &= ~(O_NONBLOCK); - if (fcntl(mReadHandle, F_SETFL, &flags) == -1) { - LOG(LOG_ERROR, "pipe", "couldn't set flags for pipe read fd\n"); - return false; - } -#endif - - mReadNonBlocking = val; - return true; -} - -/* - * Enable or disable non-blocking mode for the write descriptor. - * - * As with setReadNonBlocking(), this does not work on the Mac. - */ -bool Pipe::setWriteNonBlocking(bool val) -{ - assert(mWriteHandle != kInvalidHandle); - -#if defined(HAVE_WIN32_IPC) - // nothing to do -#else - int flags; - - if (fcntl(mWriteHandle, F_GETFL, &flags) == -1) { - LOG(LOG_WARN, "pipe", - "Warning: couldn't get flags for pipe write fd (errno=%d)\n", - errno); - return false; - } - if (val) - flags |= O_NONBLOCK; - else - flags &= ~(O_NONBLOCK); - if (fcntl(mWriteHandle, F_SETFL, &flags) == -1) { - LOG(LOG_WARN, "pipe", - "Warning: couldn't set flags for pipe write fd (errno=%d)\n", - errno); - return false; - } -#endif - - mWriteNonBlocking = val; - return true; -} - -/* - * Specify whether a file descriptor can be inherited by a child process. - * Under Linux this means setting the close-on-exec flag, under Windows - * this is SetHandleInformation(HANDLE_FLAG_INHERIT). - */ -bool Pipe::disallowReadInherit(void) -{ - if (mReadHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (SetHandleInformation((HANDLE) mReadHandle, HANDLE_FLAG_INHERIT, 0) == 0) - return false; -#else - if (fcntl((int) mReadHandle, F_SETFD, FD_CLOEXEC) != 0) - return false; -#endif - return true; -} -bool Pipe::disallowWriteInherit(void) -{ - if (mWriteHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (SetHandleInformation((HANDLE) mWriteHandle, HANDLE_FLAG_INHERIT, 0) == 0) - return false; -#else - if (fcntl((int) mWriteHandle, F_SETFD, FD_CLOEXEC) != 0) - return false; -#endif - return true; -} - -/* - * Close read descriptor. - */ -bool Pipe::closeRead(void) -{ - if (mReadHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (mReadHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mReadHandle)) { - LOG(LOG_WARN, "pipe", "failed closing read handle\n"); - return false; - } - } -#else - if (mReadHandle != kInvalidHandle) { - if (close((int) mReadHandle) != 0) { - LOG(LOG_WARN, "pipe", "failed closing read fd\n"); - return false; - } - } -#endif - mReadHandle = kInvalidHandle; - return true; -} - -/* - * Close write descriptor. - */ -bool Pipe::closeWrite(void) -{ - if (mWriteHandle == kInvalidHandle) - return false; - -#if defined(HAVE_WIN32_IPC) - if (mWriteHandle != kInvalidHandle) { - if (!CloseHandle((HANDLE)mWriteHandle)) { - LOG(LOG_WARN, "pipe", "failed closing write handle\n"); - return false; - } - } -#else - if (mWriteHandle != kInvalidHandle) { - if (close((int) mWriteHandle) != 0) { - LOG(LOG_WARN, "pipe", "failed closing write fd\n"); - return false; - } - } -#endif - mWriteHandle = kInvalidHandle; - return true; -} - -/* - * Get the read handle. - */ -unsigned long Pipe::getReadHandle(void) -{ - assert(mReadHandle != kInvalidHandle); - - return mReadHandle; -} - -/* - * Get the write handle. - */ -unsigned long Pipe::getWriteHandle(void) -{ - assert(mWriteHandle != kInvalidHandle); - - return mWriteHandle; -} - diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp deleted file mode 100644 index 51509a3..0000000 --- a/libs/utils/Socket.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Internet address class. -// - -#ifdef HAVE_WINSOCK -// This needs to come first, or Cygwin gets concerned about a potential -// clash between WinSock and <sys/types.h>. -# include <winsock2.h> -#endif - -#include <utils/Socket.h> -#include <utils/inet_address.h> -#include <utils/Log.h> -#include <utils/Timers.h> - -#ifndef HAVE_WINSOCK -# include <sys/types.h> -# include <sys/socket.h> -# include <netinet/in.h> -# include <arpa/inet.h> -#endif - -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <errno.h> -#include <assert.h> - -using namespace android; - - -/* - * =========================================================================== - * Socket - * =========================================================================== - */ - -#ifndef INVALID_SOCKET -# define INVALID_SOCKET (-1) -#endif -#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET) - -/*static*/ bool Socket::mBootInitialized = false; - -/* - * Extract system-dependent error code. - */ -static inline int getSocketError(void) { -#ifdef HAVE_WINSOCK - return WSAGetLastError(); -#else - return errno; -#endif -} - -/* - * One-time initialization for socket code. - */ -/*static*/ bool Socket::bootInit(void) -{ -#ifdef HAVE_WINSOCK - WSADATA wsaData; - int err; - - err = WSAStartup(MAKEWORD(2, 0), &wsaData); - if (err != 0) { - LOG(LOG_ERROR, "socket", "Unable to start WinSock\n"); - return false; - } - - LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n", - LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion)); -#endif - - mBootInitialized = true; - return true; -} - -/* - * One-time shutdown for socket code. - */ -/*static*/ void Socket::finalShutdown(void) -{ -#ifdef HAVE_WINSOCK - WSACleanup(); -#endif - mBootInitialized = false; -} - - -/* - * Simple constructor. Allow the application to create us and then make - * bind/connect calls. - */ -Socket::Socket(void) - : mSock(UNDEF_SOCKET) -{ - if (!mBootInitialized) - LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n"); -} - -/* - * Destructor. Closes the socket and resets our storage. - */ -Socket::~Socket(void) -{ - close(); -} - - -/* - * Create a socket and connect to the specified host and port. - */ -int Socket::connect(const char* host, int port) -{ - if (mSock != UNDEF_SOCKET) { - LOG(LOG_WARN, "socket", "Socket already connected\n"); - return -1; - } - - InetSocketAddress sockAddr; - if (!sockAddr.create(host, port)) - return -1; - - //return doConnect(sockAddr); - int foo; - foo = doConnect(sockAddr); - return foo; -} - -/* - * Create a socket and connect to the specified host and port. - */ -int Socket::connect(const InetAddress* addr, int port) -{ - if (mSock != UNDEF_SOCKET) { - LOG(LOG_WARN, "socket", "Socket already connected\n"); - return -1; - } - - InetSocketAddress sockAddr; - if (!sockAddr.create(addr, port)) - return -1; - - return doConnect(sockAddr); -} - -/* - * Finish creating a socket by connecting to the remote host. - * - * Returns 0 on success. - */ -int Socket::doConnect(const InetSocketAddress& sockAddr) -{ -#ifdef HAVE_WINSOCK - SOCKET sock; -#else - int sock; -#endif - const InetAddress* addr = sockAddr.getAddress(); - int port = sockAddr.getPort(); - struct sockaddr_in inaddr; - DurationTimer connectTimer; - - assert(sizeof(struct sockaddr_in) == addr->getAddressLength()); - memcpy(&inaddr, addr->getAddress(), addr->getAddressLength()); - inaddr.sin_port = htons(port); - - //fprintf(stderr, "--- connecting to %s:%d\n", - // sockAddr.getHostName(), port); - - sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sock == INVALID_SOCKET) { - int err = getSocketError(); - LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err); - return (err != 0) ? err : -1; - } - - connectTimer.start(); - - if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) { - int err = getSocketError(); - LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n", - sockAddr.getHostName(), port, err); - return (err != 0) ? err : -1; - } - - connectTimer.stop(); - if ((long) connectTimer.durationUsecs() > 100000) { - LOG(LOG_INFO, "socket", - "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(), - port, ((long) connectTimer.durationUsecs()) / 1000000.0); - } - - mSock = (unsigned long) sock; - LOG(LOG_VERBOSE, "socket", - "--- connected to %s:%d\n", sockAddr.getHostName(), port); - return 0; -} - - -/* - * Close the socket if it needs closing. - */ -bool Socket::close(void) -{ - if (mSock != UNDEF_SOCKET) { - //fprintf(stderr, "--- closing socket %lu\n", mSock); -#ifdef HAVE_WINSOCK - if (::closesocket((SOCKET) mSock) != 0) - return false; -#else - if (::close((int) mSock) != 0) - return false; -#endif - } - - mSock = UNDEF_SOCKET; - - return true; -} - -/* - * Read data from socket. - * - * Standard semantics: read up to "len" bytes into "buf". Returns the - * number of bytes read, or less than zero on error. - */ -int Socket::read(void* buf, ssize_t len) const -{ - if (mSock == UNDEF_SOCKET) { - LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n"); - return -500; - } - -#ifdef HAVE_WINSOCK - SOCKET sock = (SOCKET) mSock; -#else - int sock = (int) mSock; -#endif - int cc; - - cc = recv(sock, (char*)buf, len, 0); - if (cc < 0) { - int err = getSocketError(); - return (err > 0) ? -err : -1; - } - - return cc; -} - -/* - * Write data to a socket. - * - * Standard semantics: write up to "len" bytes into "buf". Returns the - * number of bytes written, or less than zero on error. - */ -int Socket::write(const void* buf, ssize_t len) const -{ - if (mSock == UNDEF_SOCKET) { - LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n"); - return -500; - } - -#ifdef HAVE_WINSOCK - SOCKET sock = (SOCKET) mSock; -#else - int sock = (int) mSock; -#endif - int cc; - - cc = send(sock, (const char*)buf, len, 0); - if (cc < 0) { - int err = getSocketError(); - return (err > 0) ? -err : -1; - } - - return cc; -} - - -/* - * =========================================================================== - * Socket tests - * =========================================================================== - */ - -/* - * Read all data from the socket. The data is read into a buffer that - * expands as needed. - * - * On exit, the buffer is returned, and the length of the data is stored - * in "*sz". A null byte is added to the end, but is not included in - * the length. - */ -static char* socketReadAll(const Socket& s, int *sz) -{ - int max, r; - char *data, *ptr, *tmp; - - data = (char*) malloc(max = 32768); - if (data == NULL) - return NULL; - - ptr = data; - - for (;;) { - if ((ptr - data) == max) { - tmp = (char*) realloc(data, max *= 2); - if(tmp == 0) { - free(data); - return 0; - } - } - r = s.read(ptr, max - (ptr - data)); - if (r == 0) - break; - if (r < 0) { - LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r); - break; - } - ptr += r; - } - - if ((ptr - data) == max) { - tmp = (char*) realloc(data, max + 1); - if (tmp == NULL) { - free(data); - return NULL; - } - } - *ptr = '\0'; - *sz = (ptr - data); - return data; -} - -/* - * Exercise the Socket class. - */ -void android::TestSockets(void) -{ - printf("----- SOCKET TEST ------\n"); - Socket::bootInit(); - - char* buf = NULL; - int len, cc; - const char* kTestStr = - "GET / HTTP/1.0\n" - "Connection: close\n" - "\n"; - - Socket sock; - if (sock.connect("www.google.com", 80) != 0) { - fprintf(stderr, "socket connected failed\n"); - goto bail; - } - - cc = sock.write(kTestStr, strlen(kTestStr)); - if (cc != (int) strlen(kTestStr)) { - fprintf(stderr, "write failed, res=%d\n", cc); - goto bail; - } - buf = socketReadAll(sock, &len); - - printf("GOT '%s'\n", buf); - -bail: - sock.close(); - free(buf); -} - diff --git a/libs/utils/Static.cpp b/libs/utils/Static.cpp index 93f7e4f..4dfa578 100644 --- a/libs/utils/Static.cpp +++ b/libs/utils/Static.cpp @@ -20,7 +20,6 @@ #include <private/utils/Static.h> #include <utils/BufferedTextOutput.h> -#include <utils/IPCThreadState.h> #include <utils/Log.h> namespace android { @@ -87,34 +86,4 @@ TextOutput& alog(gLogTextOutput); TextOutput& aout(gStdoutTextOutput); TextOutput& aerr(gStderrTextOutput); -#ifndef LIBUTILS_NATIVE - -// ------------ ProcessState.cpp - -Mutex gProcessMutex; -sp<ProcessState> gProcess; - -class LibUtilsIPCtStatics -{ -public: - LibUtilsIPCtStatics() - { - } - - ~LibUtilsIPCtStatics() - { - IPCThreadState::shutdown(); - } -}; - -static LibUtilsIPCtStatics gIPCStatics; - -// ------------ ServiceManager.cpp - -Mutex gDefaultServiceManagerLock; -sp<IServiceManager> gDefaultServiceManager; -sp<IPermissionController> gPermissionController; - -#endif - } // namespace android diff --git a/libs/utils/StringArray.cpp b/libs/utils/StringArray.cpp new file mode 100644 index 0000000..aa42d68 --- /dev/null +++ b/libs/utils/StringArray.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Sortable array of strings. STL-ish, but STL-free. +// + +#include <stdlib.h> +#include <string.h> + +#include <utils/StringArray.h> + +namespace android { + +// +// An expanding array of strings. Add, get, sort, delete. +// +StringArray::StringArray() + : mMax(0), mCurrent(0), mArray(NULL) +{ +} + +StringArray:: ~StringArray() { + for (int i = 0; i < mCurrent; i++) + delete[] mArray[i]; + delete[] mArray; +} + +// +// Add a string. A copy of the string is made. +// +bool StringArray::push_back(const char* str) { + if (mCurrent >= mMax) { + char** tmp; + + if (mMax == 0) + mMax = 16; // initial storage + else + mMax *= 2; + + tmp = new char*[mMax]; + if (tmp == NULL) + return false; + + memcpy(tmp, mArray, mCurrent * sizeof(char*)); + delete[] mArray; + mArray = tmp; + } + + int len = strlen(str); + mArray[mCurrent] = new char[len+1]; + memcpy(mArray[mCurrent], str, len+1); + mCurrent++; + + return true; +} + +// +// Delete an entry. +// +void StringArray::erase(int idx) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + if (idx < mCurrent-1) { + memmove(&mArray[idx], &mArray[idx+1], + (mCurrent-1 - idx) * sizeof(char*)); + } + mCurrent--; +} + +// +// Sort the array. +// +void StringArray::sort(int (*compare)(const void*, const void*)) { + qsort(mArray, mCurrent, sizeof(char*), compare); +} + +// +// Pass this to the sort routine to do an ascending alphabetical sort. +// +int StringArray::cmpAscendingAlpha(const void* pstr1, const void* pstr2) { + return strcmp(*(const char**)pstr1, *(const char**)pstr2); +} + +// +// Set entry N to specified string. +// [should use operator[] here] +// +void StringArray::setEntry(int idx, const char* str) { + if (idx < 0 || idx >= mCurrent) + return; + delete[] mArray[idx]; + int len = strlen(str); + mArray[idx] = new char[len+1]; + memcpy(mArray[idx], str, len+1); +} + + +}; // namespace android diff --git a/libs/utils/TextOutput.cpp b/libs/utils/TextOutput.cpp index cebee99..e04823d 100644 --- a/libs/utils/TextOutput.cpp +++ b/libs/utils/TextOutput.cpp @@ -22,9 +22,17 @@ #include <stdlib.h> #include <string.h> +namespace android { + // --------------------------------------------------------------------------- -namespace android { +TextOutput::TextOutput() { +} + +TextOutput::~TextOutput() { +} + +// --------------------------------------------------------------------------- TextOutput& operator<<(TextOutput& to, bool val) { diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index 9287c0b..4036c49 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -38,10 +38,6 @@ # define HAVE_CREATETHREAD // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW #endif -#if defined(HAVE_FUTEX) -#include <private/utils/futex_synchro.h> -#endif - #if defined(HAVE_PRCTL) #include <sys/prctl.h> #endif @@ -56,10 +52,6 @@ using namespace android; // ---------------------------------------------------------------------------- #if defined(HAVE_PTHREADS) -#if 0 -#pragma mark - -#pragma mark PTHREAD -#endif // ---------------------------------------------------------------------------- /* @@ -163,10 +155,6 @@ android_thread_id_t androidGetThreadId() // ---------------------------------------------------------------------------- #elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#pragma mark WIN32_THREADS -#endif // ---------------------------------------------------------------------------- /* @@ -252,11 +240,6 @@ android_thread_id_t androidGetThreadId() // ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Common Thread functions -#endif - int androidCreateThread(android_thread_func_t fn, void* arg) { return createThreadEtc(fn, arg); @@ -294,109 +277,9 @@ namespace android { * =========================================================================== */ -#if 0 -#pragma mark - -#pragma mark Mutex -#endif - -#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) -/* - * Simple pthread wrapper. - */ - -Mutex::Mutex() -{ - _init(); -} - -Mutex::Mutex(const char* name) -{ - // XXX: name not used for now - _init(); -} - -void Mutex::_init() -{ - pthread_mutex_t* pMutex = new pthread_mutex_t; - pthread_mutex_init(pMutex, NULL); - mState = pMutex; -} - -Mutex::~Mutex() -{ - delete (pthread_mutex_t*) mState; -} - -status_t Mutex::lock() -{ - int res; - while ((res=pthread_mutex_lock((pthread_mutex_t*) mState)) == EINTR) ; - return -res; -} - -void Mutex::unlock() -{ - pthread_mutex_unlock((pthread_mutex_t*) mState); -} - -status_t Mutex::tryLock() -{ - int res; - while ((res=pthread_mutex_trylock((pthread_mutex_t*) mState)) == EINTR) ; - return -res; -} - -#elif defined(HAVE_FUTEX) -#if 0 -#pragma mark - -#endif - -#define STATE ((futex_mutex_t*) (&mState)) - -Mutex::Mutex() -{ - _init(); -} - -Mutex::Mutex(const char* name) -{ - _init(); -} - -void -Mutex::_init() -{ - futex_mutex_init(STATE); -} - -Mutex::~Mutex() -{ -} - -status_t Mutex::lock() -{ - int res; - while ((res=futex_mutex_lock(STATE, FUTEX_WAIT_INFINITE)) == EINTR) ; - return -res; -} - -void Mutex::unlock() -{ - futex_mutex_unlock(STATE); -} - -status_t Mutex::tryLock() -{ - int res; - while ((res=futex_mutex_trylock(STATE)) == EINTR) ; - return -res; -} -#undef STATE - +#if defined(HAVE_PTHREADS) +// implemented as inlines in threads.h #elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#endif Mutex::Mutex() { @@ -456,161 +339,9 @@ status_t Mutex::tryLock() * =========================================================================== */ -#if 0 -#pragma mark - -#pragma mark Condition -#endif - -#if defined(HAVE_PTHREADS) && !defined(HAVE_FUTEX) - -/* - * Constructor. This is a simple pthread wrapper. - */ -Condition::Condition() -{ - pthread_cond_t* pCond = new pthread_cond_t; - - pthread_cond_init(pCond, NULL); - mState = pCond; -} - -/* - * Destructor. - */ -Condition::~Condition() -{ - pthread_cond_destroy((pthread_cond_t*) mState); - delete (pthread_cond_t*) mState; -} - -/* - * Wait on a condition variable. Lock the mutex before calling. - */ - -status_t Condition::wait(Mutex& mutex) -{ - assert(mutex.mState != NULL); - - int cc; - while ((cc = pthread_cond_wait((pthread_cond_t*)mState, - (pthread_mutex_t*) mutex.mState)) == EINTR) ; - return -cc; -} - -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - assert(mutex.mState != NULL); - - struct timespec ts; - ts.tv_sec = abstime/1000000000; - ts.tv_nsec = abstime-(ts.tv_sec*1000000000); - - int cc; - while ((cc = pthread_cond_timedwait((pthread_cond_t*)mState, - (pthread_mutex_t*) mutex.mState, &ts)) == EINTR) ; - return -cc; -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - return wait(mutex, systemTime()+reltime); -} - -/* - * Signal the condition variable, allowing one thread to continue. - */ -void Condition::signal() -{ - pthread_cond_signal((pthread_cond_t*) mState); -} - -/* - * Signal the condition variable, allowing all threads to continue. - */ -void Condition::broadcast() -{ - pthread_cond_broadcast((pthread_cond_t*) mState); -} - -#elif defined(HAVE_FUTEX) -#if 0 -#pragma mark - -#endif - -#define STATE ((futex_cond_t*) (&mState)) - -/* - * Constructor. This is a simple pthread wrapper. - */ -Condition::Condition() -{ - futex_cond_init(STATE); -} - -/* - * Destructor. - */ -Condition::~Condition() -{ -} - -/* - * Wait on a condition variable. Lock the mutex before calling. - */ - -status_t Condition::wait(Mutex& mutex) -{ - assert(mutex.mState != NULL); - - int res; - while ((res = futex_cond_wait(STATE, - (futex_mutex_t*)(&mutex.mState), FUTEX_WAIT_INFINITE)) == -EINTR) ; - - return -res; -} - -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - nsecs_t reltime = abstime - systemTime(); - if (reltime <= 0) return true; - return waitRelative(mutex, reltime); -} - -status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) -{ - assert(mutex.mState != NULL); - int res; - unsigned msec = ns2ms(reltime); - if(msec == 0) - return true; - // This code will not time out at the correct time if interrupted by signals - while ((res = futex_cond_wait(STATE, - (futex_mutex_t*)(&mutex.mState), msec)) == -EINTR) ; - return res; -} - -/* - * Signal the condition variable, allowing one thread to continue. - */ -void Condition::signal() -{ - futex_cond_signal(STATE); -} - -/* - * Signal the condition variable, allowing all threads to continue. - */ -void Condition::broadcast() -{ - futex_cond_broadcast(STATE); -} - -#undef STATE - +#if defined(HAVE_PTHREADS) +// implemented as inlines in threads.h #elif defined(HAVE_WIN32_THREADS) -#if 0 -#pragma mark - -#endif /* * Windows doesn't have a condition variable solution. It's possible @@ -753,14 +484,6 @@ status_t Condition::wait(Mutex& mutex) return ((WinCondition*)mState)->wait(condState, hMutex, NULL); } -status_t Condition::wait(Mutex& mutex, nsecs_t abstime) -{ - WinCondition* condState = (WinCondition*) mState; - HANDLE hMutex = (HANDLE) mutex.mState; - - return ((WinCondition*)mState)->wait(condState, hMutex, &abstime); -} - status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) { return wait(mutex, systemTime()+reltime); @@ -841,11 +564,6 @@ void Condition::broadcast() // ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Thread::Thread -#endif - /* * This is our thread object! */ diff --git a/libs/utils/TimerProbe.cpp b/libs/utils/TimerProbe.cpp deleted file mode 100644 index 835480d..0000000 --- a/libs/utils/TimerProbe.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <utils/TimerProbe.h> - -#if ENABLE_TIMER_PROBE - -#ifdef LOG_TAG -#undef LOG_TAG -#endif -#define LOG_TAG "time" - -namespace android { - -Vector<TimerProbe::Bucket> TimerProbe::gBuckets; -TimerProbe* TimerProbe::gExecuteChain; -int TimerProbe::gIndent; -timespec TimerProbe::gRealBase; - -TimerProbe::TimerProbe(const char tag[], int* slot) : mTag(tag) -{ - mNext = gExecuteChain; - gExecuteChain = this; - mIndent = gIndent; - gIndent += 1; - if (mIndent > 0) { - if (*slot == 0) { - int count = gBuckets.add(); - *slot = count; - Bucket& bucket = gBuckets.editItemAt(count); - memset(&bucket, 0, sizeof(Bucket)); - bucket.mTag = tag; - bucket.mSlotPtr = slot; - bucket.mIndent = mIndent; - } - mBucket = *slot; - } - clock_gettime(CLOCK_REALTIME, &mRealStart); - if (gRealBase.tv_sec == 0) - gRealBase = mRealStart; - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &mPStart); - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &mTStart); -} - -void TimerProbe::end() -{ - timespec realEnd, pEnd, tEnd; - clock_gettime(CLOCK_REALTIME, &realEnd); - clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &pEnd); - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tEnd); - print(realEnd, pEnd, tEnd); - mTag = NULL; -} - -TimerProbe::~TimerProbe() -{ - if (mTag != NULL) - end(); - gExecuteChain = mNext; - gIndent--; -} - - -uint32_t TimerProbe::ElapsedTime(const timespec& start, const timespec& end) -{ - int sec = end.tv_sec - start.tv_sec; - int nsec = end.tv_nsec - start.tv_nsec; - if (nsec < 0) { - sec--; - nsec += 1000000000; - } - return sec * 1000000 + nsec / 1000; -} - -void TimerProbe::print(const timespec& r, const timespec& p, - const timespec& t) const -{ - uint32_t es = ElapsedTime(gRealBase, mRealStart); - uint32_t er = ElapsedTime(mRealStart, r); - uint32_t ep = ElapsedTime(mPStart, p); - uint32_t et = ElapsedTime(mTStart, t); - if (mIndent > 0) { - Bucket& bucket = gBuckets.editItemAt(mBucket); - if (bucket.mStart == 0) - bucket.mStart = es; - bucket.mReal += er; - bucket.mProcess += ep; - bucket.mThread += et; - bucket.mCount++; - return; - } - int index = 0; - int buckets = gBuckets.size(); - int count = 1; - const char* tag = mTag; - int indent = mIndent; - do { - LOGD("%-30.30s: (%3d) %-5.*s time=%-10.3f real=%7dus process=%7dus (%3d%%) thread=%7dus (%3d%%)\n", - tag, count, indent > 5 ? 5 : indent, "+++++", es / 1000000.0, - er, ep, ep * 100 / er, et, et * 100 / er); - if (index >= buckets) - break; - Bucket& bucket = gBuckets.editItemAt(index); - count = bucket.mCount; - es = bucket.mStart; - er = bucket.mReal; - ep = bucket.mProcess; - et = bucket.mThread; - tag = bucket.mTag; - indent = bucket.mIndent; - *bucket.mSlotPtr = 0; - } while (++index); // always true - gBuckets.clear(); -} - -}; // namespace android - -#endif diff --git a/libs/utils/Timers.cpp b/libs/utils/Timers.cpp index 2abc811..784f035 100644 --- a/libs/utils/Timers.cpp +++ b/libs/utils/Timers.cpp @@ -18,7 +18,6 @@ // Timer functions. // #include <utils/Timers.h> -#include <utils/ported.h> // may need usleep #include <utils/Log.h> #include <stdlib.h> @@ -54,130 +53,6 @@ nsecs_t systemTime(int clock) #endif } -//#define MONITOR_USLEEP - -/* - * Sleep long enough that we'll wake up "interval" milliseconds after - * the previous snooze. - * - * The "nextTick" argument is updated on each call, and should be passed - * in every time. Set its fields to zero on the first call. - * - * Returns the #of intervals we have overslept, which will be zero if we're - * on time. [Currently just returns 0 or 1.] - */ -int sleepForInterval(long interval, struct timeval* pNextTick) -{ - struct timeval now; - long long timeBeforeNext; - long sleepTime = 0; - bool overSlept = false; - //int usleepBias = 0; - -#ifdef USLEEP_BIAS - /* - * Linux likes to add 9000ms or so. - * [not using this for now] - */ - //usleepBias = USLEEP_BIAS; -#endif - - gettimeofday(&now, NULL); - - if (pNextTick->tv_sec == 0) { - /* special-case for first time through */ - *pNextTick = now; - sleepTime = interval; - android::DurationTimer::addToTimeval(pNextTick, interval); - } else { - /* - * Compute how much time there is before the next tick. If this - * value is negative, we've run over. If we've run over a little - * bit we can shorten the next frame to keep the pace steady, but - * if we've dramatically overshot we need to re-sync. - */ - timeBeforeNext = android::DurationTimer::subtractTimevals(pNextTick, &now); - //printf("TOP: now=%ld.%ld next=%ld.%ld diff=%ld\n", - // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, - // (long) timeBeforeNext); - if (timeBeforeNext < -interval) { - /* way over */ - overSlept = true; - sleepTime = 0; - *pNextTick = now; - } else if (timeBeforeNext <= 0) { - /* slightly over, keep the pace steady */ - overSlept = true; - sleepTime = 0; - } else if (timeBeforeNext <= interval) { - /* right on schedule */ - sleepTime = timeBeforeNext; - } else if (timeBeforeNext > interval && timeBeforeNext <= 2*interval) { - /* sleep call returned early; do a longer sleep this time */ - sleepTime = timeBeforeNext; - } else if (timeBeforeNext > interval) { - /* we went back in time -- somebody updated system clock? */ - /* (could also be a *seriously* broken usleep()) */ - LOG(LOG_DEBUG, "", - " Impossible: timeBeforeNext = %ld\n", (long)timeBeforeNext); - sleepTime = 0; - *pNextTick = now; - } - android::DurationTimer::addToTimeval(pNextTick, interval); - } - //printf(" Before sleep: now=%ld.%ld next=%ld.%ld sleepTime=%ld\n", - // now.tv_sec, now.tv_usec, pNextTick->tv_sec, pNextTick->tv_usec, - // sleepTime); - - /* - * Sleep for the designated period of time. - * - * Linux tends to sleep for longer than requested, often by 17-18ms. - * MinGW tends to sleep for less than requested, by as much as 14ms, - * but occasionally oversleeps for 40+ms (looks like some external - * factors plus round-off on a 64Hz clock). Cygwin is pretty steady. - * - * If you start the MinGW version, and then launch the Cygwin version, - * the MinGW clock becomes more erratic. Not entirely sure why. - * - * (There's a lot of stuff here; it's really just a usleep() call with - * a bunch of instrumentation.) - */ - if (sleepTime > 0) { -#if defined(MONITOR_USLEEP) - struct timeval before, after; - long long actual; - - gettimeofday(&before, NULL); - usleep((long) sleepTime); - gettimeofday(&after, NULL); - - /* check usleep() accuracy; default Linux threads are pretty sloppy */ - actual = android::DurationTimer::subtractTimevals(&after, &before); - if ((long) actual < sleepTime - 14000 /*(sleepTime/10)*/ || - (long) actual > sleepTime + 20000 /*(sleepTime/10)*/) - { - LOG(LOG_DEBUG, "", " Odd usleep: req=%ld, actual=%ld\n", sleepTime, - (long) actual); - } -#else -#ifdef HAVE_WIN32_THREADS - Sleep( sleepTime/1000 ); -#else - usleep((long) sleepTime); -#endif -#endif - } - - //printf("slept %d\n", sleepTime); - - if (overSlept) - return 1; // close enough - else - return 0; -} - - /* * =========================================================================== * DurationTimer diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp deleted file mode 100644 index 96f9fc4..0000000 --- a/libs/utils/ZipEntry.cpp +++ /dev/null @@ -1,696 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Access to entries in a Zip archive. -// - -#define LOG_TAG "zip" - -#include <utils/ZipEntry.h> -#include <utils/Log.h> - -#include <stdio.h> -#include <string.h> -#include <assert.h> - -using namespace android; - -/* - * Initialize a new ZipEntry structure from a FILE* positioned at a - * CentralDirectoryEntry. - * - * On exit, the file pointer will be at the start of the next CDE or - * at the EOCD. - */ -status_t ZipEntry::initFromCDE(FILE* fp) -{ - status_t result; - long posn; - bool hasDD; - - //LOGV("initFromCDE ---\n"); - - /* read the CDE */ - result = mCDE.read(fp); - if (result != NO_ERROR) { - LOGD("mCDE.read failed\n"); - return result; - } - - //mCDE.dump(); - - /* using the info in the CDE, go load up the LFH */ - posn = ftell(fp); - if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) { - LOGD("local header seek failed (%ld)\n", - mCDE.mLocalHeaderRelOffset); - return UNKNOWN_ERROR; - } - - result = mLFH.read(fp); - if (result != NO_ERROR) { - LOGD("mLFH.read failed\n"); - return result; - } - - if (fseek(fp, posn, SEEK_SET) != 0) - return UNKNOWN_ERROR; - - //mLFH.dump(); - - /* - * We *might* need to read the Data Descriptor at this point and - * integrate it into the LFH. If this bit is set, the CRC-32, - * compressed size, and uncompressed size will be zero. In practice - * these seem to be rare. - */ - hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0; - if (hasDD) { - // do something clever - //LOGD("+++ has data descriptor\n"); - } - - /* - * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr" - * flag is set, because the LFH is incomplete. (Not a problem, since we - * prefer the CDE values.) - */ - if (!hasDD && !compareHeaders()) { - LOGW("WARNING: header mismatch\n"); - // keep going? - } - - /* - * If the mVersionToExtract is greater than 20, we may have an - * issue unpacking the record -- could be encrypted, compressed - * with something we don't support, or use Zip64 extensions. We - * can defer worrying about that to when we're extracting data. - */ - - return NO_ERROR; -} - -/* - * Initialize a new entry. Pass in the file name and an optional comment. - * - * Initializes the CDE and the LFH. - */ -void ZipEntry::initNew(const char* fileName, const char* comment) -{ - assert(fileName != NULL && *fileName != '\0'); // name required - - /* most fields are properly initialized by constructor */ - mCDE.mVersionMadeBy = kDefaultMadeBy; - mCDE.mVersionToExtract = kDefaultVersion; - mCDE.mCompressionMethod = kCompressStored; - mCDE.mFileNameLength = strlen(fileName); - if (comment != NULL) - mCDE.mFileCommentLength = strlen(comment); - mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does - - if (mCDE.mFileNameLength > 0) { - mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; - strcpy((char*) mCDE.mFileName, fileName); - } - if (mCDE.mFileCommentLength > 0) { - /* TODO: stop assuming null-terminated ASCII here? */ - mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; - strcpy((char*) mCDE.mFileComment, comment); - } - - copyCDEtoLFH(); -} - -/* - * Initialize a new entry, starting with the ZipEntry from a different - * archive. - * - * Initializes the CDE and the LFH. - */ -status_t ZipEntry::initFromExternal(const ZipFile* pZipFile, - const ZipEntry* pEntry) -{ - /* - * Copy everything in the CDE over, then fix up the hairy bits. - */ - memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE)); - - if (mCDE.mFileNameLength > 0) { - mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1]; - if (mCDE.mFileName == NULL) - return NO_MEMORY; - strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName); - } - if (mCDE.mFileCommentLength > 0) { - mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1]; - if (mCDE.mFileComment == NULL) - return NO_MEMORY; - strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment); - } - if (mCDE.mExtraFieldLength > 0) { - /* we null-terminate this, though it may not be a string */ - mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1]; - if (mCDE.mExtraField == NULL) - return NO_MEMORY; - memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField, - mCDE.mExtraFieldLength+1); - } - - /* construct the LFH from the CDE */ - copyCDEtoLFH(); - - /* - * The LFH "extra" field is independent of the CDE "extra", so we - * handle it here. - */ - assert(mLFH.mExtraField == NULL); - mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength; - if (mLFH.mExtraFieldLength > 0) { - mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1]; - if (mLFH.mExtraField == NULL) - return NO_MEMORY; - memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField, - mLFH.mExtraFieldLength+1); - } - - return NO_ERROR; -} - -/* - * Insert pad bytes in the LFH by tweaking the "extra" field. This will - * potentially confuse something that put "extra" data in here earlier, - * but I can't find an actual problem. - */ -status_t ZipEntry::addPadding(int padding) -{ - if (padding <= 0) - return INVALID_OPERATION; - - //LOGI("HEY: adding %d pad bytes to existing %d in %s\n", - // padding, mLFH.mExtraFieldLength, mCDE.mFileName); - - if (mLFH.mExtraFieldLength > 0) { - /* extend existing field */ - unsigned char* newExtra; - - newExtra = new unsigned char[mLFH.mExtraFieldLength + padding]; - if (newExtra == NULL) - return NO_MEMORY; - memset(newExtra + mLFH.mExtraFieldLength, 0, padding); - memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength); - - delete[] mLFH.mExtraField; - mLFH.mExtraField = newExtra; - mLFH.mExtraFieldLength += padding; - } else { - /* create new field */ - mLFH.mExtraField = new unsigned char[padding]; - memset(mLFH.mExtraField, 0, padding); - mLFH.mExtraFieldLength = padding; - } - - return NO_ERROR; -} - -/* - * Set the fields in the LFH equal to the corresponding fields in the CDE. - * - * This does not touch the LFH "extra" field. - */ -void ZipEntry::copyCDEtoLFH(void) -{ - mLFH.mVersionToExtract = mCDE.mVersionToExtract; - mLFH.mGPBitFlag = mCDE.mGPBitFlag; - mLFH.mCompressionMethod = mCDE.mCompressionMethod; - mLFH.mLastModFileTime = mCDE.mLastModFileTime; - mLFH.mLastModFileDate = mCDE.mLastModFileDate; - mLFH.mCRC32 = mCDE.mCRC32; - mLFH.mCompressedSize = mCDE.mCompressedSize; - mLFH.mUncompressedSize = mCDE.mUncompressedSize; - mLFH.mFileNameLength = mCDE.mFileNameLength; - // the "extra field" is independent - - delete[] mLFH.mFileName; - if (mLFH.mFileNameLength > 0) { - mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1]; - strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName); - } else { - mLFH.mFileName = NULL; - } -} - -/* - * Set some information about a file after we add it. - */ -void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32, - int compressionMethod) -{ - mCDE.mCompressionMethod = compressionMethod; - mCDE.mCRC32 = crc32; - mCDE.mCompressedSize = compLen; - mCDE.mUncompressedSize = uncompLen; - mCDE.mCompressionMethod = compressionMethod; - if (compressionMethod == kCompressDeflated) { - mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used - } - copyCDEtoLFH(); -} - -/* - * See if the data in mCDE and mLFH match up. This is mostly useful for - * debugging these classes, but it can be used to identify damaged - * archives. - * - * Returns "false" if they differ. - */ -bool ZipEntry::compareHeaders(void) const -{ - if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) { - LOGV("cmp: VersionToExtract\n"); - return false; - } - if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) { - LOGV("cmp: GPBitFlag\n"); - return false; - } - if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) { - LOGV("cmp: CompressionMethod\n"); - return false; - } - if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) { - LOGV("cmp: LastModFileTime\n"); - return false; - } - if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) { - LOGV("cmp: LastModFileDate\n"); - return false; - } - if (mCDE.mCRC32 != mLFH.mCRC32) { - LOGV("cmp: CRC32\n"); - return false; - } - if (mCDE.mCompressedSize != mLFH.mCompressedSize) { - LOGV("cmp: CompressedSize\n"); - return false; - } - if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) { - LOGV("cmp: UncompressedSize\n"); - return false; - } - if (mCDE.mFileNameLength != mLFH.mFileNameLength) { - LOGV("cmp: FileNameLength\n"); - return false; - } -#if 0 // this seems to be used for padding, not real data - if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) { - LOGV("cmp: ExtraFieldLength\n"); - return false; - } -#endif - if (mCDE.mFileName != NULL) { - if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) { - LOGV("cmp: FileName\n"); - return false; - } - } - - return true; -} - - -/* - * Convert the DOS date/time stamp into a UNIX time stamp. - */ -time_t ZipEntry::getModWhen(void) const -{ - struct tm parts; - - parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1; - parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5; - parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11; - parts.tm_mday = (mCDE.mLastModFileDate & 0x001f); - parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1; - parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80; - parts.tm_wday = parts.tm_yday = 0; - parts.tm_isdst = -1; // DST info "not available" - - return mktime(&parts); -} - -/* - * Set the CDE/LFH timestamp from UNIX time. - */ -void ZipEntry::setModWhen(time_t when) -{ -#ifdef HAVE_LOCALTIME_R - struct tm tmResult; -#endif - time_t even; - unsigned short zdate, ztime; - - struct tm* ptm; - - /* round up to an even number of seconds */ - even = (time_t)(((unsigned long)(when) + 1) & (~1)); - - /* expand */ -#ifdef HAVE_LOCALTIME_R - ptm = localtime_r(&even, &tmResult); -#else - ptm = localtime(&even); -#endif - - int year; - year = ptm->tm_year; - if (year < 80) - year = 80; - - zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; - ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; - - mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime; - mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate; -} - - -/* - * =========================================================================== - * ZipEntry::LocalFileHeader - * =========================================================================== - */ - -/* - * Read a local file header. - * - * On entry, "fp" points to the signature at the start of the header. - * On exit, "fp" points to the start of data. - */ -status_t ZipEntry::LocalFileHeader::read(FILE* fp) -{ - status_t result = NO_ERROR; - unsigned char buf[kLFHLen]; - - assert(mFileName == NULL); - assert(mExtraField == NULL); - - if (fread(buf, 1, kLFHLen, fp) != kLFHLen) { - result = UNKNOWN_ERROR; - goto bail; - } - - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { - LOGD("whoops: didn't find expected signature\n"); - result = UNKNOWN_ERROR; - goto bail; - } - - mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]); - mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]); - mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]); - mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]); - mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]); - mCRC32 = ZipEntry::getLongLE(&buf[0x0e]); - mCompressedSize = ZipEntry::getLongLE(&buf[0x12]); - mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]); - mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]); - mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]); - - // TODO: validate sizes - - /* grab filename */ - if (mFileNameLength != 0) { - mFileName = new unsigned char[mFileNameLength+1]; - if (mFileName == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mFileName[mFileNameLength] = '\0'; - } - - /* grab extra field */ - if (mExtraFieldLength != 0) { - mExtraField = new unsigned char[mExtraFieldLength+1]; - if (mExtraField == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mExtraField[mExtraFieldLength] = '\0'; - } - -bail: - return result; -} - -/* - * Write a local file header. - */ -status_t ZipEntry::LocalFileHeader::write(FILE* fp) -{ - unsigned char buf[kLFHLen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mVersionToExtract); - ZipEntry::putShortLE(&buf[0x06], mGPBitFlag); - ZipEntry::putShortLE(&buf[0x08], mCompressionMethod); - ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime); - ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate); - ZipEntry::putLongLE(&buf[0x0e], mCRC32); - ZipEntry::putLongLE(&buf[0x12], mCompressedSize); - ZipEntry::putLongLE(&buf[0x16], mUncompressedSize); - ZipEntry::putShortLE(&buf[0x1a], mFileNameLength); - ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength); - - if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen) - return UNKNOWN_ERROR; - - /* write filename */ - if (mFileNameLength != 0) { - if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) - return UNKNOWN_ERROR; - } - - /* write "extra field" */ - if (mExtraFieldLength != 0) { - if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - - -/* - * Dump the contents of a LocalFileHeader object. - */ -void ZipEntry::LocalFileHeader::dump(void) const -{ - LOGD(" LocalFileHeader contents:\n"); - LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n", - mVersionToExtract, mGPBitFlag, mCompressionMethod); - LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", - mLastModFileTime, mLastModFileDate, mCRC32); - LOGD(" compressedSize=%lu uncompressedSize=%lu\n", - mCompressedSize, mUncompressedSize); - LOGD(" filenameLen=%u extraLen=%u\n", - mFileNameLength, mExtraFieldLength); - if (mFileName != NULL) - LOGD(" filename: '%s'\n", mFileName); -} - - -/* - * =========================================================================== - * ZipEntry::CentralDirEntry - * =========================================================================== - */ - -/* - * Read the central dir entry that appears next in the file. - * - * On entry, "fp" should be positioned on the signature bytes for the - * entry. On exit, "fp" will point at the signature word for the next - * entry or for the EOCD. - */ -status_t ZipEntry::CentralDirEntry::read(FILE* fp) -{ - status_t result = NO_ERROR; - unsigned char buf[kCDELen]; - - /* no re-use */ - assert(mFileName == NULL); - assert(mExtraField == NULL); - assert(mFileComment == NULL); - - if (fread(buf, 1, kCDELen, fp) != kCDELen) { - result = UNKNOWN_ERROR; - goto bail; - } - - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) { - LOGD("Whoops: didn't find expected signature\n"); - result = UNKNOWN_ERROR; - goto bail; - } - - mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]); - mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]); - mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]); - mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]); - mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]); - mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]); - mCRC32 = ZipEntry::getLongLE(&buf[0x10]); - mCompressedSize = ZipEntry::getLongLE(&buf[0x14]); - mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]); - mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]); - mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]); - mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]); - mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]); - mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]); - mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]); - mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]); - - // TODO: validate sizes and offsets - - /* grab filename */ - if (mFileNameLength != 0) { - mFileName = new unsigned char[mFileNameLength+1]; - if (mFileName == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mFileName[mFileNameLength] = '\0'; - } - - /* read "extra field" */ - if (mExtraFieldLength != 0) { - mExtraField = new unsigned char[mExtraFieldLength+1]; - if (mExtraField == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) { - result = UNKNOWN_ERROR; - goto bail; - } - mExtraField[mExtraFieldLength] = '\0'; - } - - - /* grab comment, if any */ - if (mFileCommentLength != 0) { - mFileComment = new unsigned char[mFileCommentLength+1]; - if (mFileComment == NULL) { - result = NO_MEMORY; - goto bail; - } - if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) - { - result = UNKNOWN_ERROR; - goto bail; - } - mFileComment[mFileCommentLength] = '\0'; - } - -bail: - return result; -} - -/* - * Write a central dir entry. - */ -status_t ZipEntry::CentralDirEntry::write(FILE* fp) -{ - unsigned char buf[kCDELen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy); - ZipEntry::putShortLE(&buf[0x06], mVersionToExtract); - ZipEntry::putShortLE(&buf[0x08], mGPBitFlag); - ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod); - ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime); - ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate); - ZipEntry::putLongLE(&buf[0x10], mCRC32); - ZipEntry::putLongLE(&buf[0x14], mCompressedSize); - ZipEntry::putLongLE(&buf[0x18], mUncompressedSize); - ZipEntry::putShortLE(&buf[0x1c], mFileNameLength); - ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength); - ZipEntry::putShortLE(&buf[0x20], mFileCommentLength); - ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart); - ZipEntry::putShortLE(&buf[0x24], mInternalAttrs); - ZipEntry::putLongLE(&buf[0x26], mExternalAttrs); - ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset); - - if (fwrite(buf, 1, kCDELen, fp) != kCDELen) - return UNKNOWN_ERROR; - - /* write filename */ - if (mFileNameLength != 0) { - if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength) - return UNKNOWN_ERROR; - } - - /* write "extra field" */ - if (mExtraFieldLength != 0) { - if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) - return UNKNOWN_ERROR; - } - - /* write comment */ - if (mFileCommentLength != 0) { - if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - -/* - * Dump the contents of a CentralDirEntry object. - */ -void ZipEntry::CentralDirEntry::dump(void) const -{ - LOGD(" CentralDirEntry contents:\n"); - LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n", - mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod); - LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n", - mLastModFileTime, mLastModFileDate, mCRC32); - LOGD(" compressedSize=%lu uncompressedSize=%lu\n", - mCompressedSize, mUncompressedSize); - LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n", - mFileNameLength, mExtraFieldLength, mFileCommentLength); - LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n", - mDiskNumberStart, mInternalAttrs, mExternalAttrs, - mLocalHeaderRelOffset); - - if (mFileName != NULL) - LOGD(" filename: '%s'\n", mFileName); - if (mFileComment != NULL) - LOGD(" comment: '%s'\n", mFileComment); -} - diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp deleted file mode 100644 index 6f27d17..0000000 --- a/libs/utils/ZipFile.cpp +++ /dev/null @@ -1,1296 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Access to Zip archives. -// - -#define LOG_TAG "zip" - -#include <utils/ZipFile.h> -#include <utils/ZipUtils.h> -#include <utils/Log.h> - -#include <zlib.h> -#define DEF_MEM_LEVEL 8 // normally in zutil.h? - -#include <memory.h> -#include <sys/stat.h> -#include <errno.h> -#include <assert.h> - -using namespace android; - -/* - * Some environments require the "b", some choke on it. - */ -#define FILE_OPEN_RO "rb" -#define FILE_OPEN_RW "r+b" -#define FILE_OPEN_RW_CREATE "w+b" - -/* should live somewhere else? */ -static status_t errnoToStatus(int err) -{ - if (err == ENOENT) - return NAME_NOT_FOUND; - else if (err == EACCES) - return PERMISSION_DENIED; - else - return UNKNOWN_ERROR; -} - -/* - * Open a file and parse its guts. - */ -status_t ZipFile::open(const char* zipFileName, int flags) -{ - bool newArchive = false; - - assert(mZipFp == NULL); // no reopen - - if ((flags & kOpenTruncate)) - flags |= kOpenCreate; // trunc implies create - - if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite)) - return INVALID_OPERATION; // not both - if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite))) - return INVALID_OPERATION; // not neither - if ((flags & kOpenCreate) && !(flags & kOpenReadWrite)) - return INVALID_OPERATION; // create requires write - - if (flags & kOpenTruncate) { - newArchive = true; - } else { - newArchive = (access(zipFileName, F_OK) != 0); - if (!(flags & kOpenCreate) && newArchive) { - /* not creating, must already exist */ - LOGD("File %s does not exist", zipFileName); - return NAME_NOT_FOUND; - } - } - - /* open the file */ - const char* openflags; - if (flags & kOpenReadWrite) { - if (newArchive) - openflags = FILE_OPEN_RW_CREATE; - else - openflags = FILE_OPEN_RW; - } else { - openflags = FILE_OPEN_RO; - } - mZipFp = fopen(zipFileName, openflags); - if (mZipFp == NULL) { - int err = errno; - LOGD("fopen failed: %d\n", err); - return errnoToStatus(err); - } - - status_t result; - if (!newArchive) { - /* - * Load the central directory. If that fails, then this probably - * isn't a Zip archive. - */ - result = readCentralDir(); - } else { - /* - * Newly-created. The EndOfCentralDir constructor actually - * sets everything to be the way we want it (all zeroes). We - * set mNeedCDRewrite so that we create *something* if the - * caller doesn't add any files. (We could also just unlink - * the file if it's brand new and nothing was added, but that's - * probably doing more than we really should -- the user might - * have a need for empty zip files.) - */ - mNeedCDRewrite = true; - result = NO_ERROR; - } - - if (flags & kOpenReadOnly) - mReadOnly = true; - else - assert(!mReadOnly); - - return result; -} - -/* - * Return the Nth entry in the archive. - */ -ZipEntry* ZipFile::getEntryByIndex(int idx) const -{ - if (idx < 0 || idx >= (int) mEntries.size()) - return NULL; - - return mEntries[idx]; -} - -/* - * Find an entry by name. - */ -ZipEntry* ZipFile::getEntryByName(const char* fileName) const -{ - /* - * Do a stupid linear string-compare search. - * - * There are various ways to speed this up, especially since it's rare - * to intermingle changes to the archive with "get by name" calls. We - * don't want to sort the mEntries vector itself, however, because - * it's used to recreate the Central Directory. - * - * (Hash table works, parallel list of pointers in sorted order is good.) - */ - int idx; - - for (idx = mEntries.size()-1; idx >= 0; idx--) { - ZipEntry* pEntry = mEntries[idx]; - if (!pEntry->getDeleted() && - strcmp(fileName, pEntry->getFileName()) == 0) - { - return pEntry; - } - } - - return NULL; -} - -/* - * Empty the mEntries vector. - */ -void ZipFile::discardEntries(void) -{ - int count = mEntries.size(); - - while (--count >= 0) - delete mEntries[count]; - - mEntries.clear(); -} - - -/* - * Find the central directory and read the contents. - * - * The fun thing about ZIP archives is that they may or may not be - * readable from start to end. In some cases, notably for archives - * that were written to stdout, the only length information is in the - * central directory at the end of the file. - * - * Of course, the central directory can be followed by a variable-length - * comment field, so we have to scan through it backwards. The comment - * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff - * itself, plus apparently sometimes people throw random junk on the end - * just for the fun of it. - * - * This is all a little wobbly. If the wrong value ends up in the EOCD - * area, we're hosed. This appears to be the way that everbody handles - * it though, so we're in pretty good company if this fails. - */ -status_t ZipFile::readCentralDir(void) -{ - status_t result = NO_ERROR; - unsigned char* buf = NULL; - off_t fileLength, seekStart; - long readAmount; - int i; - - fseek(mZipFp, 0, SEEK_END); - fileLength = ftell(mZipFp); - rewind(mZipFp); - - /* too small to be a ZIP archive? */ - if (fileLength < EndOfCentralDir::kEOCDLen) { - LOGD("Length is %ld -- too small\n", (long)fileLength); - result = INVALID_OPERATION; - goto bail; - } - - buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch]; - if (buf == NULL) { - LOGD("Failure allocating %d bytes for EOCD search", - EndOfCentralDir::kMaxEOCDSearch); - result = NO_MEMORY; - goto bail; - } - - if (fileLength > EndOfCentralDir::kMaxEOCDSearch) { - seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch; - readAmount = EndOfCentralDir::kMaxEOCDSearch; - } else { - seekStart = 0; - readAmount = (long) fileLength; - } - if (fseek(mZipFp, seekStart, SEEK_SET) != 0) { - LOGD("Failure seeking to end of zip at %ld", (long) seekStart); - result = UNKNOWN_ERROR; - goto bail; - } - - /* read the last part of the file into the buffer */ - if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) { - LOGD("short file? wanted %ld\n", readAmount); - result = UNKNOWN_ERROR; - goto bail; - } - - /* find the end-of-central-dir magic */ - for (i = readAmount - 4; i >= 0; i--) { - if (buf[i] == 0x50 && - ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature) - { - LOGV("+++ Found EOCD at buf+%d\n", i); - break; - } - } - if (i < 0) { - LOGD("EOCD not found, not Zip\n"); - result = INVALID_OPERATION; - goto bail; - } - - /* extract eocd values */ - result = mEOCD.readBuf(buf + i, readAmount - i); - if (result != NO_ERROR) { - LOGD("Failure reading %ld bytes of EOCD values", readAmount - i); - goto bail; - } - //mEOCD.dump(); - - if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 || - mEOCD.mNumEntries != mEOCD.mTotalNumEntries) - { - LOGD("Archive spanning not supported\n"); - result = INVALID_OPERATION; - goto bail; - } - - /* - * So far so good. "mCentralDirSize" is the size in bytes of the - * central directory, so we can just seek back that far to find it. - * We can also seek forward mCentralDirOffset bytes from the - * start of the file. - * - * We're not guaranteed to have the rest of the central dir in the - * buffer, nor are we guaranteed that the central dir will have any - * sort of convenient size. We need to skip to the start of it and - * read the header, then the other goodies. - * - * The only thing we really need right now is the file comment, which - * we're hoping to preserve. - */ - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - LOGD("Failure seeking to central dir offset %ld\n", - mEOCD.mCentralDirOffset); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * Loop through and read the central dir entries. - */ - LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries); - int entry; - for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) { - ZipEntry* pEntry = new ZipEntry; - - result = pEntry->initFromCDE(mZipFp); - if (result != NO_ERROR) { - LOGD("initFromCDE failed\n"); - delete pEntry; - goto bail; - } - - mEntries.add(pEntry); - } - - - /* - * If all went well, we should now be back at the EOCD. - */ - { - unsigned char checkBuf[4]; - if (fread(checkBuf, 1, 4, mZipFp) != 4) { - LOGD("EOCD check read failed\n"); - result = INVALID_OPERATION; - goto bail; - } - if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) { - LOGD("EOCD read check failed\n"); - result = UNKNOWN_ERROR; - goto bail; - } - LOGV("+++ EOCD read check passed\n"); - } - -bail: - delete[] buf; - return result; -} - - -/* - * Add a new file to the archive. - * - * This requires creating and populating a ZipEntry structure, and copying - * the data into the file at the appropriate position. The "appropriate - * position" is the current location of the central directory, which we - * casually overwrite (we can put it back later). - * - * If we were concerned about safety, we would want to make all changes - * in a temp file and then overwrite the original after everything was - * safely written. Not really a concern for us. - */ -status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size, - const char* storageName, int sourceType, int compressionMethod, - ZipEntry** ppEntry) -{ - ZipEntry* pEntry = NULL; - status_t result = NO_ERROR; - long lfhPosn, startPosn, endPosn, uncompressedLen; - FILE* inputFp = NULL; - unsigned long crc; - time_t modWhen; - - if (mReadOnly) - return INVALID_OPERATION; - - assert(compressionMethod == ZipEntry::kCompressDeflated || - compressionMethod == ZipEntry::kCompressStored); - - /* make sure we're in a reasonable state */ - assert(mZipFp != NULL); - assert(mEntries.size() == mEOCD.mTotalNumEntries); - - /* make sure it doesn't already exist */ - if (getEntryByName(storageName) != NULL) - return ALREADY_EXISTS; - - if (!data) { - inputFp = fopen(fileName, FILE_OPEN_RO); - if (inputFp == NULL) - return errnoToStatus(errno); - } - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - - pEntry = new ZipEntry; - pEntry->initNew(storageName, NULL); - - /* - * From here on out, failures are more interesting. - */ - mNeedCDRewrite = true; - - /* - * Write the LFH, even though it's still mostly blank. We need it - * as a place-holder. In theory the LFH isn't necessary, but in - * practice some utilities demand it. - */ - lfhPosn = ftell(mZipFp); - pEntry->mLFH.write(mZipFp); - startPosn = ftell(mZipFp); - - /* - * Copy the data in, possibly compressing it as we go. - */ - if (sourceType == ZipEntry::kCompressStored) { - if (compressionMethod == ZipEntry::kCompressDeflated) { - bool failed = false; - result = compressFpToFp(mZipFp, inputFp, data, size, &crc); - if (result != NO_ERROR) { - LOGD("compression failed, storing\n"); - failed = true; - } else { - /* - * Make sure it has compressed "enough". This probably ought - * to be set through an API call, but I don't expect our - * criteria to change over time. - */ - long src = inputFp ? ftell(inputFp) : size; - long dst = ftell(mZipFp) - startPosn; - if (dst + (dst / 10) > src) { - LOGD("insufficient compression (src=%ld dst=%ld), storing\n", - src, dst); - failed = true; - } - } - - if (failed) { - compressionMethod = ZipEntry::kCompressStored; - if (inputFp) rewind(inputFp); - fseek(mZipFp, startPosn, SEEK_SET); - /* fall through to kCompressStored case */ - } - } - /* handle "no compression" request, or failed compression from above */ - if (compressionMethod == ZipEntry::kCompressStored) { - if (inputFp) { - result = copyFpToFp(mZipFp, inputFp, &crc); - } else { - result = copyDataToFp(mZipFp, data, size, &crc); - } - if (result != NO_ERROR) { - // don't need to truncate; happens in CDE rewrite - LOGD("failed copying data in\n"); - goto bail; - } - } - - // currently seeked to end of file - uncompressedLen = inputFp ? ftell(inputFp) : size; - } else if (sourceType == ZipEntry::kCompressDeflated) { - /* we should support uncompressed-from-compressed, but it's not - * important right now */ - assert(compressionMethod == ZipEntry::kCompressDeflated); - - bool scanResult; - int method; - long compressedLen; - - scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen, - &compressedLen, &crc); - if (!scanResult || method != ZipEntry::kCompressDeflated) { - LOGD("this isn't a deflated gzip file?"); - result = UNKNOWN_ERROR; - goto bail; - } - - result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL); - if (result != NO_ERROR) { - LOGD("failed copying gzip data in\n"); - goto bail; - } - } else { - assert(false); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * We could write the "Data Descriptor", but there doesn't seem to - * be any point since we're going to go back and write the LFH. - * - * Update file offsets. - */ - endPosn = ftell(mZipFp); // seeked to end of compressed data - - /* - * Success! Fill out new values. - */ - pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc, - compressionMethod); - modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp)); - pEntry->setModWhen(modWhen); - pEntry->setLFHOffset(lfhPosn); - mEOCD.mNumEntries++; - mEOCD.mTotalNumEntries++; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - mEOCD.mCentralDirOffset = endPosn; - - /* - * Go back and write the LFH. - */ - if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - pEntry->mLFH.write(mZipFp); - - /* - * Add pEntry to the list. - */ - mEntries.add(pEntry); - if (ppEntry != NULL) - *ppEntry = pEntry; - pEntry = NULL; - -bail: - if (inputFp != NULL) - fclose(inputFp); - delete pEntry; - return result; -} - -/* - * Add an entry by copying it from another zip file. If "padding" is - * nonzero, the specified number of bytes will be added to the "extra" - * field in the header. - * - * If "ppEntry" is non-NULL, a pointer to the new entry will be returned. - */ -status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry, - int padding, ZipEntry** ppEntry) -{ - ZipEntry* pEntry = NULL; - status_t result; - long lfhPosn, endPosn; - - if (mReadOnly) - return INVALID_OPERATION; - - /* make sure we're in a reasonable state */ - assert(mZipFp != NULL); - assert(mEntries.size() == mEOCD.mTotalNumEntries); - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) { - result = UNKNOWN_ERROR; - goto bail; - } - - pEntry = new ZipEntry; - if (pEntry == NULL) { - result = NO_MEMORY; - goto bail; - } - - result = pEntry->initFromExternal(pSourceZip, pSourceEntry); - if (result != NO_ERROR) - goto bail; - if (padding != 0) { - result = pEntry->addPadding(padding); - if (result != NO_ERROR) - goto bail; - } - - /* - * From here on out, failures are more interesting. - */ - mNeedCDRewrite = true; - - /* - * Write the LFH. Since we're not recompressing the data, we already - * have all of the fields filled out. - */ - lfhPosn = ftell(mZipFp); - pEntry->mLFH.write(mZipFp); - - /* - * Copy the data over. - * - * If the "has data descriptor" flag is set, we want to copy the DD - * fields as well. This is a fixed-size area immediately following - * the data. - */ - if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0) - { - result = UNKNOWN_ERROR; - goto bail; - } - - off_t copyLen; - copyLen = pSourceEntry->getCompressedLen(); - if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0) - copyLen += ZipEntry::kDataDescriptorLen; - - if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL) - != NO_ERROR) - { - LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName); - result = UNKNOWN_ERROR; - goto bail; - } - - /* - * Update file offsets. - */ - endPosn = ftell(mZipFp); - - /* - * Success! Fill out new values. - */ - pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset - mEOCD.mNumEntries++; - mEOCD.mTotalNumEntries++; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - mEOCD.mCentralDirOffset = endPosn; - - /* - * Add pEntry to the list. - */ - mEntries.add(pEntry); - if (ppEntry != NULL) - *ppEntry = pEntry; - pEntry = NULL; - - result = NO_ERROR; - -bail: - delete pEntry; - return result; -} - -/* - * Copy all of the bytes in "src" to "dst". - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the data. - */ -status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32) -{ - unsigned char tmpBuf[32768]; - size_t count; - - *pCRC32 = crc32(0L, Z_NULL, 0); - - while (1) { - count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp); - if (ferror(srcFp) || ferror(dstFp)) - return errnoToStatus(errno); - if (count == 0) - break; - - *pCRC32 = crc32(*pCRC32, tmpBuf, count); - - if (fwrite(tmpBuf, 1, count, dstFp) != count) { - LOGD("fwrite %d bytes failed\n", (int) count); - return UNKNOWN_ERROR; - } - } - - return NO_ERROR; -} - -/* - * Copy all of the bytes in "src" to "dst". - * - * On exit, "dstFp" will be seeked immediately past the data. - */ -status_t ZipFile::copyDataToFp(FILE* dstFp, - const void* data, size_t size, unsigned long* pCRC32) -{ - size_t count; - - *pCRC32 = crc32(0L, Z_NULL, 0); - if (size > 0) { - *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size); - if (fwrite(data, 1, size, dstFp) != size) { - LOGD("fwrite %d bytes failed\n", (int) size); - return UNKNOWN_ERROR; - } - } - - return NO_ERROR; -} - -/* - * Copy some of the bytes in "src" to "dst". - * - * If "pCRC32" is NULL, the CRC will not be computed. - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the data just written. - */ -status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length, - unsigned long* pCRC32) -{ - unsigned char tmpBuf[32768]; - size_t count; - - if (pCRC32 != NULL) - *pCRC32 = crc32(0L, Z_NULL, 0); - - while (length) { - long readSize; - - readSize = sizeof(tmpBuf); - if (readSize > length) - readSize = length; - - count = fread(tmpBuf, 1, readSize, srcFp); - if ((long) count != readSize) { // error or unexpected EOF - LOGD("fread %d bytes failed\n", (int) readSize); - return UNKNOWN_ERROR; - } - - if (pCRC32 != NULL) - *pCRC32 = crc32(*pCRC32, tmpBuf, count); - - if (fwrite(tmpBuf, 1, count, dstFp) != count) { - LOGD("fwrite %d bytes failed\n", (int) count); - return UNKNOWN_ERROR; - } - - length -= readSize; - } - - return NO_ERROR; -} - -/* - * Compress all of the data in "srcFp" and write it to "dstFp". - * - * On exit, "srcFp" will be seeked to the end of the file, and "dstFp" - * will be seeked immediately past the compressed data. - */ -status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp, - const void* data, size_t size, unsigned long* pCRC32) -{ - status_t result = NO_ERROR; - const size_t kBufSize = 32768; - unsigned char* inBuf = NULL; - unsigned char* outBuf = NULL; - z_stream zstream; - bool atEof = false; // no feof() aviailable yet - unsigned long crc; - int zerr; - - /* - * Create an input buffer and an output buffer. - */ - inBuf = new unsigned char[kBufSize]; - outBuf = new unsigned char[kBufSize]; - if (inBuf == NULL || outBuf == NULL) { - result = NO_MEMORY; - goto bail; - } - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - zstream.data_type = Z_UNKNOWN; - - zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, - Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (zerr != Z_OK) { - result = UNKNOWN_ERROR; - if (zerr == Z_VERSION_ERROR) { - LOGE("Installed zlib is not compatible with linked version (%s)\n", - ZLIB_VERSION); - } else { - LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr); - } - goto bail; - } - - crc = crc32(0L, Z_NULL, 0); - - /* - * Loop while we have data. - */ - do { - size_t getSize; - int flush; - - /* only read if the input buffer is empty */ - if (zstream.avail_in == 0 && !atEof) { - LOGV("+++ reading %d bytes\n", (int)kBufSize); - if (data) { - getSize = size > kBufSize ? kBufSize : size; - memcpy(inBuf, data, getSize); - data = ((const char*)data) + getSize; - size -= getSize; - } else { - getSize = fread(inBuf, 1, kBufSize, srcFp); - if (ferror(srcFp)) { - LOGD("deflate read failed (errno=%d)\n", errno); - goto z_bail; - } - } - if (getSize < kBufSize) { - LOGV("+++ got %d bytes, EOF reached\n", - (int)getSize); - atEof = true; - } - - crc = crc32(crc, inBuf, getSize); - - zstream.next_in = inBuf; - zstream.avail_in = getSize; - } - - if (atEof) - flush = Z_FINISH; /* tell zlib that we're done */ - else - flush = Z_NO_FLUSH; /* more to come! */ - - zerr = deflate(&zstream, flush); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGD("zlib deflate call failed (zerr=%d)\n", zerr); - result = UNKNOWN_ERROR; - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize)) - { - LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf)); - if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) != - (size_t)(zstream.next_out - outBuf)) - { - LOGD("write %d failed in deflate\n", - (int) (zstream.next_out - outBuf)); - goto z_bail; - } - - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - *pCRC32 = crc; - -z_bail: - deflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] inBuf; - delete[] outBuf; - - return result; -} - -/* - * Mark an entry as deleted. - * - * We will eventually need to crunch the file down, but if several files - * are being removed (perhaps as part of an "update" process) we can make - * things considerably faster by deferring the removal to "flush" time. - */ -status_t ZipFile::remove(ZipEntry* pEntry) -{ - /* - * Should verify that pEntry is actually part of this archive, and - * not some stray ZipEntry from a different file. - */ - - /* mark entry as deleted, and mark archive as dirty */ - pEntry->setDeleted(); - mNeedCDRewrite = true; - return NO_ERROR; -} - -/* - * Flush any pending writes. - * - * In particular, this will crunch out deleted entries, and write the - * Central Directory and EOCD if we have stomped on them. - */ -status_t ZipFile::flush(void) -{ - status_t result = NO_ERROR; - long eocdPosn; - int i, count; - - if (mReadOnly) - return INVALID_OPERATION; - if (!mNeedCDRewrite) - return NO_ERROR; - - assert(mZipFp != NULL); - - result = crunchArchive(); - if (result != NO_ERROR) - return result; - - if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) - return UNKNOWN_ERROR; - - count = mEntries.size(); - for (i = 0; i < count; i++) { - ZipEntry* pEntry = mEntries[i]; - pEntry->mCDE.write(mZipFp); - } - - eocdPosn = ftell(mZipFp); - mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset; - - mEOCD.write(mZipFp); - - /* - * If we had some stuff bloat up during compression and get replaced - * with plain files, or if we deleted some entries, there's a lot - * of wasted space at the end of the file. Remove it now. - */ - if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) { - LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno)); - // not fatal - } - - /* should we clear the "newly added" flag in all entries now? */ - - mNeedCDRewrite = false; - return NO_ERROR; -} - -/* - * Crunch deleted files out of an archive by shifting the later files down. - * - * Because we're not using a temp file, we do the operation inside the - * current file. - */ -status_t ZipFile::crunchArchive(void) -{ - status_t result = NO_ERROR; - int i, count; - long delCount, adjust; - -#if 0 - printf("CONTENTS:\n"); - for (i = 0; i < (int) mEntries.size(); i++) { - printf(" %d: lfhOff=%ld del=%d\n", - i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted()); - } - printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset); -#endif - - /* - * Roll through the set of files, shifting them as appropriate. We - * could probably get a slight performance improvement by sliding - * multiple files down at once (because we could use larger reads - * when operating on batches of small files), but it's not that useful. - */ - count = mEntries.size(); - delCount = adjust = 0; - for (i = 0; i < count; i++) { - ZipEntry* pEntry = mEntries[i]; - long span; - - if (pEntry->getLFHOffset() != 0) { - long nextOffset; - - /* Get the length of this entry by finding the offset - * of the next entry. Directory entries don't have - * file offsets, so we need to find the next non-directory - * entry. - */ - nextOffset = 0; - for (int ii = i+1; nextOffset == 0 && ii < count; ii++) - nextOffset = mEntries[ii]->getLFHOffset(); - if (nextOffset == 0) - nextOffset = mEOCD.mCentralDirOffset; - span = nextOffset - pEntry->getLFHOffset(); - - assert(span >= ZipEntry::LocalFileHeader::kLFHLen); - } else { - /* This is a directory entry. It doesn't have - * any actual file contents, so there's no need to - * move anything. - */ - span = 0; - } - - //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n", - // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count); - - if (pEntry->getDeleted()) { - adjust += span; - delCount++; - - delete pEntry; - mEntries.removeAt(i); - - /* adjust loop control */ - count--; - i--; - } else if (span != 0 && adjust > 0) { - /* shuffle this entry back */ - //printf("+++ Shuffling '%s' back %ld\n", - // pEntry->getFileName(), adjust); - result = filemove(mZipFp, pEntry->getLFHOffset() - adjust, - pEntry->getLFHOffset(), span); - if (result != NO_ERROR) { - /* this is why you use a temp file */ - LOGE("error during crunch - archive is toast\n"); - return result; - } - - pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust); - } - } - - /* - * Fix EOCD info. We have to wait until the end to do some of this - * because we use mCentralDirOffset to determine "span" for the - * last entry. - */ - mEOCD.mCentralDirOffset -= adjust; - mEOCD.mNumEntries -= delCount; - mEOCD.mTotalNumEntries -= delCount; - mEOCD.mCentralDirSize = 0; // mark invalid; set by flush() - - assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries); - assert(mEOCD.mNumEntries == count); - - return result; -} - -/* - * Works like memmove(), but on pieces of a file. - */ -status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n) -{ - if (dst == src || n <= 0) - return NO_ERROR; - - unsigned char readBuf[32768]; - - if (dst < src) { - /* shift stuff toward start of file; must read from start */ - while (n != 0) { - size_t getSize = sizeof(readBuf); - if (getSize > n) - getSize = n; - - if (fseek(fp, (long) src, SEEK_SET) != 0) { - LOGD("filemove src seek %ld failed\n", (long) src); - return UNKNOWN_ERROR; - } - - if (fread(readBuf, 1, getSize, fp) != getSize) { - LOGD("filemove read %ld off=%ld failed\n", - (long) getSize, (long) src); - return UNKNOWN_ERROR; - } - - if (fseek(fp, (long) dst, SEEK_SET) != 0) { - LOGD("filemove dst seek %ld failed\n", (long) dst); - return UNKNOWN_ERROR; - } - - if (fwrite(readBuf, 1, getSize, fp) != getSize) { - LOGD("filemove write %ld off=%ld failed\n", - (long) getSize, (long) dst); - return UNKNOWN_ERROR; - } - - src += getSize; - dst += getSize; - n -= getSize; - } - } else { - /* shift stuff toward end of file; must read from end */ - assert(false); // write this someday, maybe - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - - -/* - * Get the modification time from a file descriptor. - */ -time_t ZipFile::getModTime(int fd) -{ - struct stat sb; - - if (fstat(fd, &sb) < 0) { - LOGD("HEY: fstat on fd %d failed\n", fd); - return (time_t) -1; - } - - return sb.st_mtime; -} - - -#if 0 /* this is a bad idea */ -/* - * Get a copy of the Zip file descriptor. - * - * We don't allow this if the file was opened read-write because we tend - * to leave the file contents in an uncertain state between calls to - * flush(). The duplicated file descriptor should only be valid for reads. - */ -int ZipFile::getZipFd(void) const -{ - if (!mReadOnly) - return INVALID_OPERATION; - assert(mZipFp != NULL); - - int fd; - fd = dup(fileno(mZipFp)); - if (fd < 0) { - LOGD("didn't work, errno=%d\n", errno); - } - - return fd; -} -#endif - - -#if 0 -/* - * Expand data. - */ -bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const -{ - return false; -} -#endif - -// free the memory when you're done -void* ZipFile::uncompress(const ZipEntry* entry) -{ - size_t unlen = entry->getUncompressedLen(); - size_t clen = entry->getCompressedLen(); - - void* buf = malloc(unlen); - if (buf == NULL) { - return NULL; - } - - fseek(mZipFp, 0, SEEK_SET); - - off_t offset = entry->getFileOffset(); - if (fseek(mZipFp, offset, SEEK_SET) != 0) { - goto bail; - } - - switch (entry->getCompressionMethod()) - { - case ZipEntry::kCompressStored: { - ssize_t amt = fread(buf, 1, unlen, mZipFp); - if (amt != (ssize_t)unlen) { - goto bail; - } -#if 0 - printf("data...\n"); - const unsigned char* p = (unsigned char*)buf; - const unsigned char* end = p+unlen; - for (int i=0; i<32 && p < end; i++) { - printf("0x%08x ", (int)(offset+(i*0x10))); - for (int j=0; j<0x10 && p < end; j++) { - printf(" %02x", *p); - p++; - } - printf("\n"); - } -#endif - - } - break; - case ZipEntry::kCompressDeflated: { - if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) { - goto bail; - } - } - break; - default: - goto bail; - } - return buf; - -bail: - free(buf); - return NULL; -} - - -/* - * =========================================================================== - * ZipFile::EndOfCentralDir - * =========================================================================== - */ - -/* - * Read the end-of-central-dir fields. - * - * "buf" should be positioned at the EOCD signature, and should contain - * the entire EOCD area including the comment. - */ -status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len) -{ - /* don't allow re-use */ - assert(mComment == NULL); - - if (len < kEOCDLen) { - /* looks like ZIP file got truncated */ - LOGD(" Zip EOCD: expected >= %d bytes, found %d\n", - kEOCDLen, len); - return INVALID_OPERATION; - } - - /* this should probably be an assert() */ - if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) - return UNKNOWN_ERROR; - - mDiskNumber = ZipEntry::getShortLE(&buf[0x04]); - mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]); - mNumEntries = ZipEntry::getShortLE(&buf[0x08]); - mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]); - mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]); - mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]); - mCommentLen = ZipEntry::getShortLE(&buf[0x14]); - - // TODO: validate mCentralDirOffset - - if (mCommentLen > 0) { - if (kEOCDLen + mCommentLen > len) { - LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n", - kEOCDLen, mCommentLen, len); - return UNKNOWN_ERROR; - } - mComment = new unsigned char[mCommentLen]; - memcpy(mComment, buf + kEOCDLen, mCommentLen); - } - - return NO_ERROR; -} - -/* - * Write an end-of-central-directory section. - */ -status_t ZipFile::EndOfCentralDir::write(FILE* fp) -{ - unsigned char buf[kEOCDLen]; - - ZipEntry::putLongLE(&buf[0x00], kSignature); - ZipEntry::putShortLE(&buf[0x04], mDiskNumber); - ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir); - ZipEntry::putShortLE(&buf[0x08], mNumEntries); - ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries); - ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize); - ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset); - ZipEntry::putShortLE(&buf[0x14], mCommentLen); - - if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen) - return UNKNOWN_ERROR; - if (mCommentLen > 0) { - assert(mComment != NULL); - if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen) - return UNKNOWN_ERROR; - } - - return NO_ERROR; -} - -/* - * Dump the contents of an EndOfCentralDir object. - */ -void ZipFile::EndOfCentralDir::dump(void) const -{ - LOGD(" EndOfCentralDir contents:\n"); - LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n", - mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries); - LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n", - mCentralDirSize, mCentralDirOffset, mCommentLen); -} - diff --git a/libs/utils/futex_synchro.c b/libs/utils/futex_synchro.c deleted file mode 100644 index ab48c69..0000000 --- a/libs/utils/futex_synchro.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <limits.h> - -#include <sys/time.h> -#include <sched.h> - -#include <errno.h> - -#include <private/utils/futex_synchro.h> - - -// This futex glue code is need on desktop linux, but is already part of bionic. -#if !defined(HAVE_FUTEX_WRAPPERS) - -#include <unistd.h> -#include <sys/syscall.h> -typedef unsigned int u32; -#define asmlinkage -#define __user -#include <linux/futex.h> -#include <utils/Atomic.h> - - -int futex (int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) -{ - int err = syscall(SYS_futex, uaddr, op, val, timeout, uaddr2, val3); - return err == 0 ? 0 : -errno; -} - -int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout) -{ - return futex((int*)ftx, FUTEX_WAIT, val, timeout, NULL, 0); -} - -int __futex_wake(volatile void *ftx, int count) -{ - return futex((int*)ftx, FUTEX_WAKE, count, NULL, NULL, 0); -} - -int __atomic_cmpxchg(int old, int _new, volatile int *ptr) -{ - return android_atomic_cmpxchg(old, _new, ptr); -} - -int __atomic_swap(int _new, volatile int *ptr) -{ - return android_atomic_swap(_new, ptr); -} - -int __atomic_dec(volatile int *ptr) -{ - return android_atomic_dec(ptr); -} - -#else // !defined(__arm__) - -int __futex_wait(volatile void *ftx, int val, const struct timespec *timeout); -int __futex_wake(volatile void *ftx, int count); - -int __atomic_cmpxchg(int old, int _new, volatile int *ptr); -int __atomic_swap(int _new, volatile int *ptr); -int __atomic_dec(volatile int *ptr); - -#endif // !defined(HAVE_FUTEX_WRAPPERS) - - -// lock states -// -// 0: unlocked -// 1: locked, no waiters -// 2: locked, maybe waiters - -void futex_mutex_init(futex_mutex_t *m) -{ - m->value = 0; -} - -int futex_mutex_lock(futex_mutex_t *m, unsigned msec) -{ - if(__atomic_cmpxchg(0, 1, &m->value) == 0) { - return 0; - } - if(msec == FUTEX_WAIT_INFINITE) { - while(__atomic_swap(2, &m->value) != 0) { - __futex_wait(&m->value, 2, 0); - } - } else { - struct timespec ts; - ts.tv_sec = msec / 1000; - ts.tv_nsec = (msec % 1000) * 1000000; - while(__atomic_swap(2, &m->value) != 0) { - if(__futex_wait(&m->value, 2, &ts) == -ETIMEDOUT) { - return -1; - } - } - } - return 0; -} - -int futex_mutex_trylock(futex_mutex_t *m) -{ - if(__atomic_cmpxchg(0, 1, &m->value) == 0) { - return 0; - } - return -1; -} - -void futex_mutex_unlock(futex_mutex_t *m) -{ - if(__atomic_dec(&m->value) != 1) { - m->value = 0; - __futex_wake(&m->value, 1); - } -} - -/* XXX *technically* there is a race condition that could allow - * XXX a signal to be missed. If thread A is preempted in _wait() - * XXX after unlocking the mutex and before waiting, and if other - * XXX threads call signal or broadcast UINT_MAX times (exactly), - * XXX before thread A is scheduled again and calls futex_wait(), - * XXX then the signal will be lost. - */ - -void futex_cond_init(futex_cond_t *c) -{ - c->value = 0; -} - -int futex_cond_wait(futex_cond_t *c, futex_mutex_t *m, unsigned msec) -{ - if(msec == FUTEX_WAIT_INFINITE){ - int oldvalue = c->value; - futex_mutex_unlock(m); - __futex_wait(&c->value, oldvalue, 0); - futex_mutex_lock(m, FUTEX_WAIT_INFINITE); - return 0; - } else { - int oldvalue = c->value; - struct timespec ts; - ts.tv_sec = msec / 1000; - ts.tv_nsec = (msec % 1000) * 1000000; - futex_mutex_unlock(m); - const int err = __futex_wait(&c->value, oldvalue, &ts); - futex_mutex_lock(m, FUTEX_WAIT_INFINITE); - return err; - } -} - -void futex_cond_signal(futex_cond_t *c) -{ - __atomic_dec(&c->value); - __futex_wake(&c->value, 1); -} - -void futex_cond_broadcast(futex_cond_t *c) -{ - __atomic_dec(&c->value); - __futex_wake(&c->value, INT_MAX); -} - diff --git a/libs/utils/ported.cpp b/libs/utils/ported.cpp deleted file mode 100644 index 656e46f..0000000 --- a/libs/utils/ported.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Ports of standard functions that don't exist on a specific platform. -// -// Note these are NOT in the "android" namespace. -// -#include <utils/ported.h> - -#if defined(NEED_GETTIMEOFDAY) || defined(NEED_USLEEP) -# include <sys/time.h> -# include <windows.h> -#endif - - -#if defined(NEED_GETTIMEOFDAY) -/* - * Replacement gettimeofday() for Windows environments (primarily MinGW). - * - * Ignores "tz". - */ -int gettimeofday(struct timeval* ptv, struct timezone* tz) -{ - long long nsTime; // time in 100ns units since Jan 1 1601 - FILETIME ft; - - if (tz != NULL) { - // oh well - } - - ::GetSystemTimeAsFileTime(&ft); - nsTime = (long long) ft.dwHighDateTime << 32 | - (long long) ft.dwLowDateTime; - // convert to time in usec since Jan 1 1970 - ptv->tv_usec = (long) ((nsTime / 10LL) % 1000000LL); - ptv->tv_sec = (long) ((nsTime - 116444736000000000LL) / 10000000LL); - - return 0; -} -#endif - -#if defined(NEED_USLEEP) -// -// Replacement usleep for Windows environments (primarily MinGW). -// -void usleep(unsigned long usec) -{ - // Win32 API function Sleep() takes milliseconds - ::Sleep((usec + 500) / 1000); -} -#endif - -#if 0 //defined(NEED_PIPE) -// -// Replacement pipe() command for MinGW -// -// The _O_NOINHERIT flag sets bInheritHandle to FALSE in the -// SecurityAttributes argument to CreatePipe(). This means the handles -// aren't inherited when a new process is created. The examples I've seen -// use it, possibly because there's a lot of junk going on behind the -// scenes. (I'm assuming "process" and "thread" are different here, so -// we should be okay spinning up a thread.) The recommended practice is -// to dup() the descriptor you want the child to have. -// -// It appears that unnamed pipes can't do non-blocking ("overlapped") I/O. -// You can't use select() either, since that only works on sockets. The -// Windows API calls that are useful here all operate on a HANDLE, not -// an integer file descriptor, and I don't think you can get there from -// here. The "named pipe" stuff is insane. -// -int pipe(int filedes[2]) -{ - return _pipe(filedes, 0, _O_BINARY | _O_NOINHERIT); -} -#endif - -#if defined(NEED_SETENV) -/* - * MinGW lacks these. For now, just stub them out so the code compiles. - */ -int setenv(const char* name, const char* value, int overwrite) -{ - return 0; -} -void unsetenv(const char* name) -{ -} -char* getenv(const char* name) -{ - return NULL; -} -#endif |