diff options
80 files changed, 5607 insertions, 2974 deletions
diff --git a/cmds/bugreport/bugreport.c b/cmds/bugreport/bugreport.c index 4a0b511470..11e9057fec 100644 --- a/cmds/bugreport/bugreport.c +++ b/cmds/bugreport/bugreport.c @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) { property_set("ctl.start", "dumpstate"); /* socket will not be available until service starts */ - for (i = 0; i < 10; i++) { + for (i = 0; i < 20; i++) { s = socket_local_client("dumpstate", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM); diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index 220af478ac..7fb5b129b7 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -174,9 +174,7 @@ static void dumpstate() { dump_file("LAST PANIC CONSOLE", "/data/dontpanic/apanic_console"); dump_file("LAST PANIC THREADS", "/data/dontpanic/apanic_threads"); - run_command("SYSTEM SETTINGS", 20, SU_PATH, "root", "sqlite3", - "/data/data/com.android.providers.settings/databases/settings.db", - "pragma user_version; select * from system; select * from secure; select * from global;", NULL); + for_each_userid(do_dump_settings, NULL); /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */ run_command("NETWORK INTERFACES", 10, SU_PATH, "root", "netcfg", NULL); diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 67bbd7e2ad..d820495793 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -26,6 +26,7 @@ typedef void (for_each_pid_func)(int, const char *); typedef void (for_each_tid_func)(int, int, const char *); +typedef void (for_each_userid_func)(int); /* prints the contents of a file */ int dump_file(const char *title, const char* path); @@ -51,6 +52,9 @@ void for_each_pid(for_each_pid_func func, const char *header); /* for each thread in the system, run the specified function */ void for_each_tid(for_each_tid_func func, const char *header); +/* for each user id in the system, run the specified function */ +void for_each_userid(for_each_userid_func func, const char *header); + /* Displays a blocked processes in-kernel wait channel */ void show_wchan(int pid, int tid, const char *name); @@ -60,6 +64,9 @@ void do_showmap(int pid, const char *name); /* Gets the dmesg output for the kernel */ void do_dmesg(); +/* Dumps settings for a given user id */ +void do_dump_settings(int userid); + /* Play a sound via Stagefright */ void play_sound(const char* path); diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c index fe716acfe2..7f84f98243 100644 --- a/cmds/dumpstate/utils.c +++ b/cmds/dumpstate/utils.c @@ -51,6 +51,29 @@ static const char* native_processes_to_dump[] = { NULL, }; +void for_each_userid(void (*func)(int), const char *header) { + DIR *d; + struct dirent *de; + + if (header) printf("\n------ %s ------\n", header); + func(0); + + if (!(d = opendir("/data/system/users"))) { + printf("Failed to open /data/system/users (%s)\n", strerror(errno)); + return; + } + + while ((de = readdir(d))) { + int userid; + if (de->d_type != DT_DIR || !(userid = atoi(de->d_name))) { + continue; + } + func(userid); + } + + closedir(d); +} + static void __for_each_pid(void (*helper)(int, const char *, void *), const char *header, void *arg) { DIR *d; struct dirent *de; @@ -175,6 +198,22 @@ out_close: return; } +void do_dump_settings(int userid) { + char title[255]; + char dbpath[255]; + char sql[255]; + sprintf(title, "SYSTEM SETTINGS (user %d)", userid); + if (userid == 0) { + strcpy(dbpath, "/data/data/com.android.providers.settings/databases/settings.db"); + strcpy(sql, "pragma user_version; select * from system; select * from secure; select * from global;"); + } else { + sprintf(dbpath, "/data/system/users/%d/settings.db", userid); + strcpy(sql, "pragma user_version; select * from system; select * from secure;"); + } + run_command(title, 20, SU_PATH, "root", "sqlite3", dbpath, sql, NULL); + return; +} + void do_dmesg() { printf("------ KERNEL LOG (dmesg) ------\n"); /* Get size of kernel buffer */ diff --git a/cmds/flatland/Main.cpp b/cmds/flatland/Main.cpp index d6ac3d2593..e80dbb1505 100644 --- a/cmds/flatland/Main.cpp +++ b/cmds/flatland/Main.cpp @@ -73,7 +73,7 @@ static const BenchmarkDesc benchmarks[] = { }, }, - { "3:2 Single Static Window", + { "4:3 Single Static Window", 2048, 1536, { 1536 }, { { // Window @@ -117,7 +117,7 @@ static const BenchmarkDesc benchmarks[] = { }, }, - { "3:2 App -> Home Transition", + { "4:3 App -> Home Transition", 2048, 1536, { 1536 }, { { // Wallpaper @@ -173,7 +173,7 @@ static const BenchmarkDesc benchmarks[] = { }, }, - { "3:2 SurfaceView -> Home Transition", + { "4:3 SurfaceView -> Home Transition", 2048, 1536, { 1536 }, { { // Wallpaper diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index 79ce6eda5b..cacbea091e 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -32,6 +32,8 @@ static struct { { AID_MEDIA, "media.player" }, { AID_MEDIA, "media.camera" }, { AID_MEDIA, "media.audio_policy" }, + { AID_AUDIO, "audio" }, + { AID_INPUT, "input" }, { AID_DRM, "drm.drmManager" }, { AID_NFC, "nfc" }, { AID_BLUETOOTH, "bluetooth" }, diff --git a/include/batteryservice/BatteryService.h b/include/batteryservice/BatteryService.h index 829061a346..7ae13425ed 100644 --- a/include/batteryservice/BatteryService.h +++ b/include/batteryservice/BatteryService.h @@ -43,6 +43,13 @@ enum { BATTERY_HEALTH_COLD = 7, // equals BatteryManager.BATTERY_HEALTH_COLD constant }; +// must be kept in sync with definitions in BatteryProperty.java +enum { + BATTERY_PROP_CHARGE_COUNTER = 1, // equals BatteryProperty.BATTERY_PROP_CHARGE_COUNTER constant + BATTERY_PROP_CURRENT_NOW = 2, // equals BatteryProperty.BATTERY_PROP_CURRENT_NOW constant + BATTERY_PROP_CURRENT_AVG = 3, // equals BatteryProperty.BATTERY_PROP_CURRENT_AVG constant +}; + struct BatteryProperties { bool chargerAcOnline; bool chargerUsbOnline; @@ -52,8 +59,6 @@ struct BatteryProperties { bool batteryPresent; int batteryLevel; int batteryVoltage; - int batteryCurrentNow; - int batteryChargeCounter; int batteryTemperature; String8 batteryTechnology; @@ -61,6 +66,13 @@ struct BatteryProperties { status_t readFromParcel(Parcel* parcel); }; +struct BatteryProperty { + int valueInt; + + status_t writeToParcel(Parcel* parcel) const; + status_t readFromParcel(Parcel* parcel); +}; + }; // namespace android #endif // ANDROID_BATTERYSERVICE_H diff --git a/include/batteryservice/IBatteryPropertiesRegistrar.h b/include/batteryservice/IBatteryPropertiesRegistrar.h index 8d28b1d35f..eca075d7ef 100644 --- a/include/batteryservice/IBatteryPropertiesRegistrar.h +++ b/include/batteryservice/IBatteryPropertiesRegistrar.h @@ -26,6 +26,7 @@ namespace android { enum { REGISTER_LISTENER = IBinder::FIRST_CALL_TRANSACTION, UNREGISTER_LISTENER, + GET_PROPERTY, }; class IBatteryPropertiesRegistrar : public IInterface { @@ -34,6 +35,7 @@ public: virtual void registerListener(const sp<IBatteryPropertiesListener>& listener) = 0; virtual void unregisterListener(const sp<IBatteryPropertiesListener>& listener) = 0; + virtual status_t getProperty(int id, struct BatteryProperty *val) = 0; }; class BnBatteryPropertiesRegistrar : public BnInterface<IBatteryPropertiesRegistrar> { diff --git a/include/binder/IBatteryStats.h b/include/binder/IBatteryStats.h new file mode 100644 index 0000000000..f4a8aa33cb --- /dev/null +++ b/include/binder/IBatteryStats.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 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_IBATTERYSTATS_H +#define ANDROID_IBATTERYSTATS_H + +#include <binder/IInterface.h> + +namespace android { + +// ---------------------------------------------------------------------- + +class IBatteryStats : public IInterface +{ +public: + DECLARE_META_INTERFACE(BatteryStats); + + virtual void noteStartSensor(int uid, int sensor) = 0; + virtual void noteStopSensor(int uid, int sensor) = 0; + + enum { + NOTE_START_SENSOR_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + NOTE_STOP_SENSOR_TRANSACTION, + }; +}; + +// ---------------------------------------------------------------------- + +class BnBatteryStats : public BnInterface<IBatteryStats> +{ +public: + virtual status_t onTransact( uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags = 0); +}; + +// ---------------------------------------------------------------------- + +}; // namespace android + +#endif // ANDROID_IBATTERYSTATS_H diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h index 8b849513b0..43b654334b 100644 --- a/include/binder/IBinder.h +++ b/include/binder/IBinder.h @@ -81,14 +81,6 @@ public: Parcel* reply, uint32_t flags = 0) = 0; - /** - * This method allows you to add data that is transported through - * IPC along with your IBinder pointer. When implementing a Binder - * object, override it to write your desired data in to @a outData. - * You can then call getConstantData() on your IBinder to retrieve - * that data, from any process. You MUST return the number of bytes - * written in to the parcel (including padding). - */ class DeathRecipient : public virtual RefBase { public: diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h index ed2e7df2c3..e9966fec64 100644 --- a/include/binder/Parcel.h +++ b/include/binder/Parcel.h @@ -128,6 +128,11 @@ public: // will be closed once the parcel is destroyed. status_t writeDupFileDescriptor(int fd); + // Writes a raw fd and optional comm channel fd to the parcel as a ParcelFileDescriptor. + // A dup's of the fds are made, which will be closed once the parcel is destroyed. + // Null values are passed as -1. + status_t writeParcelFileDescriptor(int fd, int commChannel = -1); + // Writes a blob to the parcel. // If the blob is small, then it is stored in-place, otherwise it is // transferred by way of an anonymous shared memory region. @@ -187,6 +192,11 @@ public: // in the parcel, which you do not own -- use dup() to get your own copy. int readFileDescriptor() const; + // Reads a ParcelFileDescriptor from the parcel. Returns the raw fd as + // the result, and the optional comm channel fd in outCommChannel. + // Null values are returned as -1. + int readParcelFileDescriptor(int& outCommChannel) const; + // Reads a blob from the parcel. // The caller should call release() on the blob after reading its contents. status_t readBlob(size_t len, ReadableBlob* outBlob) const; diff --git a/include/gui/BufferItemConsumer.h b/include/gui/BufferItemConsumer.h index 52edf17fe4..2c58ca5e2f 100644 --- a/include/gui/BufferItemConsumer.h +++ b/include/gui/BufferItemConsumer.h @@ -44,6 +44,7 @@ class BufferItemConsumer: public ConsumerBase typedef BufferQueue::BufferItem BufferItem; + enum { MIN_UNDEQUEUED_BUFFERS = -1 }; enum { INVALID_BUFFER_SLOT = BufferQueue::INVALID_BUFFER_SLOT }; enum { NO_BUFFER_AVAILABLE = BufferQueue::NO_BUFFER_AVAILABLE }; @@ -54,7 +55,7 @@ class BufferItemConsumer: public ConsumerBase // controlledByApp tells whether this consumer is controlled by the // application. BufferItemConsumer(const sp<BufferQueue>& bq, uint32_t consumerUsage, - int bufferCount = BufferQueue::MIN_UNDEQUEUED_BUFFERS, + int bufferCount = MIN_UNDEQUEUED_BUFFERS, bool controlledByApp = false); virtual ~BufferItemConsumer(); diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h index 408956b6e7..15dc64524d 100644 --- a/include/gui/BufferQueue.h +++ b/include/gui/BufferQueue.h @@ -41,11 +41,16 @@ class BufferQueue : public BnGraphicBufferProducer, public BnGraphicBufferConsumer, private IBinder::DeathRecipient { public: - enum { MIN_UNDEQUEUED_BUFFERS = 2 }; + // BufferQueue will keep track of at most this value of buffers. + // Attempts at runtime to increase the number of buffers past this will fail. enum { NUM_BUFFER_SLOTS = 32 }; - enum { NO_CONNECTED_API = 0 }; - enum { INVALID_BUFFER_SLOT = -1 }; - enum { STALE_BUFFER_SLOT = 1, NO_BUFFER_AVAILABLE, PRESENT_LATER }; + // Used as a placeholder slot# when the value isn't pointing to an existing buffer. + enum { INVALID_BUFFER_SLOT = IGraphicBufferConsumer::BufferItem::INVALID_BUFFER_SLOT }; + // Alias to <IGraphicBufferConsumer.h> -- please scope from there in future code! + enum { + NO_BUFFER_AVAILABLE = IGraphicBufferConsumer::NO_BUFFER_AVAILABLE, + PRESENT_LATER = IGraphicBufferConsumer::PRESENT_LATER, + }; // When in async mode we reserve two slots in order to guarantee that the // producer and consumer can run asynchronously. @@ -75,7 +80,6 @@ public: wp<ConsumerListener> mConsumerListener; }; - // BufferQueue manages a pool of gralloc memory slots to be used by // producers and consumers. allocator is used to allocate all the // needed gralloc buffers. @@ -103,7 +107,7 @@ public: // // This will fail if the producer has dequeued any buffers, or if // bufferCount is invalid. bufferCount must generally be a value - // between the minimum undequeued buffer count and NUM_BUFFER_SLOTS + // between the minimum undequeued buffer count (exclusive) and NUM_BUFFER_SLOTS // (inclusive). It may also be set to zero (the default) to indicate // that the producer does not wish to set a value. The minimum value // can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, @@ -212,7 +216,7 @@ public: */ // acquireBuffer attempts to acquire ownership of the next pending buffer in - // the BufferQueue. If no buffer is pending then it returns -EINVAL. If a + // the BufferQueue. If no buffer is pending then it returns NO_BUFFER_AVAILABLE. If a // buffer is successfully acquired, the information about the buffer is // returned in BufferItem. If the buffer returned had previously been // acquired then the BufferItem::mGraphicBuffer field of buffer is set to @@ -224,7 +228,7 @@ public: // future, the buffer won't be acquired, and PRESENT_LATER will be // returned. The presentation time is in nanoseconds, and the time base // is CLOCK_MONOTONIC. - virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen); + virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen); // releaseBuffer releases a buffer slot from the consumer back to the // BufferQueue. This may be done while the buffer's contents are still @@ -312,8 +316,13 @@ public: // dump our state in a String virtual void dump(String8& result, const char* prefix) const; - private: + // The default API number used to indicate no producer client is connected. + enum { NO_CONNECTED_API = 0 }; + + // Aliases for using enums from <IGraphicBufferConsumer.h> + enum { STALE_BUFFER_SLOT = IGraphicBufferConsumer::STALE_BUFFER_SLOT }; + // freeBufferLocked frees the GraphicBuffer and sync resources for the // given slot. void freeBufferLocked(int index); diff --git a/include/gui/IGraphicBufferConsumer.h b/include/gui/IGraphicBufferConsumer.h index 0e35f1371e..9a6645cf21 100644 --- a/include/gui/IGraphicBufferConsumer.h +++ b/include/gui/IGraphicBufferConsumer.h @@ -48,6 +48,7 @@ public: status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); public: + // The default value of mBuf, used to indicate this doesn't correspond to a slot. enum { INVALID_BUFFER_SLOT = -1 }; BufferItem(); @@ -63,13 +64,17 @@ public: Rect mCrop; // mTransform is the current transform flags for this buffer slot. + // refer to NATIVE_WINDOW_TRANSFORM_* in <window.h> uint32_t mTransform; // mScalingMode is the current scaling mode for this buffer slot. + // refer to NATIVE_WINDOW_SCALING_* in <window.h> uint32_t mScalingMode; // mTimestamp is the current timestamp for this buffer slot. This gets - // to set by queueBuffer each time this slot is queued. + // to set by queueBuffer each time this slot is queued. This value + // is guaranteed to be monotonically increasing for each newly + // acquired buffer. int64_t mTimestamp; // mIsAutoTimestamp indicates whether mTimestamp was generated @@ -79,7 +84,7 @@ public: // mFrameNumber is the number of the queued frame for this slot. uint64_t mFrameNumber; - // mBuf is the slot index of this buffer + // mBuf is the slot index of this buffer (default INVALID_BUFFER_SLOT). int mBuf; // mIsDroppable whether this buffer was queued with the @@ -97,21 +102,42 @@ public: bool mTransformToDisplayInverse; }; + enum { + // Returned by releaseBuffer, after which the consumer must + // free any references to the just-released buffer that it might have. + STALE_BUFFER_SLOT = 1, + // Returned by dequeueBuffer if there are no pending buffers available. + NO_BUFFER_AVAILABLE, + // Returned by dequeueBuffer if it's too early for the buffer to be acquired. + PRESENT_LATER, + }; // acquireBuffer attempts to acquire ownership of the next pending buffer in - // the BufferQueue. If no buffer is pending then it returns -EINVAL. If a - // buffer is successfully acquired, the information about the buffer is - // returned in BufferItem. If the buffer returned had previously been + // the BufferQueue. If no buffer is pending then it returns + // NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the + // information about the buffer is returned in BufferItem. + // + // If the buffer returned had previously been // acquired then the BufferItem::mGraphicBuffer field of buffer is set to // NULL and it is assumed that the consumer still holds a reference to the // buffer. // - // If presentWhen is nonzero, it indicates the time when the buffer will + // If presentWhen is non-zero, it indicates the time when the buffer will // be displayed on screen. If the buffer's timestamp is farther in the // future, the buffer won't be acquired, and PRESENT_LATER will be // returned. The presentation time is in nanoseconds, and the time base // is CLOCK_MONOTONIC. - virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) = 0; + // + // Return of NO_ERROR means the operation completed as normal. + // + // Return of a positive value means the operation could not be completed + // at this time, but the user should try again later: + // * NO_BUFFER_AVAILABLE - no buffer is pending (nothing queued by producer) + // * PRESENT_LATER - the buffer's timestamp is farther in the future + // + // Return of a negative value means an error has occurred: + // * INVALID_OPERATION - too many buffers have been acquired + virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) = 0; // releaseBuffer releases a buffer slot from the consumer back to the // BufferQueue. This may be done while the buffer's contents are still @@ -125,6 +151,18 @@ public: // // Note that the dependencies on EGL will be removed once we switch to using // the Android HW Sync HAL. + // + // Return of NO_ERROR means the operation completed as normal. + // + // Return of a positive value means the operation could not be completed + // at this time, but the user should try again later: + // * STALE_BUFFER_SLOT - see above (second paragraph) + // + // Return of a negative value means an error has occurred: + // * BAD_VALUE - one of the following could've happened: + // * the buffer slot was invalid + // * the fence was NULL + // * the buffer slot specified is not in the acquired state virtual status_t releaseBuffer(int buf, uint64_t frameNumber, EGLDisplay display, EGLSyncKHR fence, const sp<Fence>& releaseFence) = 0; @@ -137,24 +175,38 @@ public: // the application. // // consumer may not be NULL. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned + // * BAD_VALUE - a NULL consumer was provided virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) = 0; // consumerDisconnect disconnects a consumer from the BufferQueue. All // buffers will be freed and the BufferQueue is placed in the "abandoned" // state, causing most interactions with the BufferQueue by the producer to // fail. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - no consumer is currently connected virtual status_t consumerDisconnect() = 0; - // getReleasedBuffers sets the value pointed to by slotMask to a bit mask - // indicating which buffer slots have been released by the BufferQueue - // but have not yet been released by the consumer. + // getReleasedBuffers sets the value pointed to by slotMask to a bit set. + // Each bit index with a 1 corresponds to a released buffer slot with that + // index value. In particular, a released buffer is one that has + // been released by the BufferQueue but have not yet been released by the consumer. // // This should be called from the onBuffersReleased() callback. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned. virtual status_t getReleasedBuffers(uint32_t* slotMask) = 0; // setDefaultBufferSize is used to set the size of buffers returned by // dequeueBuffer when a width and height of zero is requested. Default // is 1x1. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - either w or h was zero virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0; // setDefaultMaxBufferCount sets the default value for the maximum buffer @@ -163,6 +215,9 @@ public: // take effect if the producer sets the count back to zero. // // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - bufferCount was out of range (see above). virtual status_t setDefaultMaxBufferCount(int bufferCount) = 0; // disableAsyncBuffer disables the extra buffer used in async mode @@ -170,11 +225,20 @@ public: // flag) and has dequeueBuffer() return WOULD_BLOCK instead. // // This can only be called before consumerConnect(). + // + // Return of a value other than NO_ERROR means an error has occurred: + // * INVALID_OPERATION - attempting to call this after consumerConnect. virtual status_t disableAsyncBuffer() = 0; // setMaxAcquiredBufferCount sets the maximum number of buffers that can // be acquired by the consumer at one time (default 1). This call will // fail if a producer is connected to the BufferQueue. + // + // maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - maxAcquiredBuffers was out of range (see above). + // * INVALID_OPERATION - attempting to call this after a producer connected. virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0; // setConsumerName sets the name used in logging @@ -184,16 +248,22 @@ public: // GraphicBuffers of a defaultFormat if no format is specified // in dequeueBuffer. Formats are enumerated in graphics.h; the // initial default is HAL_PIXEL_FORMAT_RGBA_8888. + // + // Return of a value other than NO_ERROR means an unknown error has occurred. virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) = 0; // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer. // These are merged with the bits passed to dequeueBuffer. The values are // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0. + // + // Return of a value other than NO_ERROR means an unknown error has occurred. virtual status_t setConsumerUsageBits(uint32_t usage) = 0; // setTransformHint bakes in rotation to buffers so overlays can be used. // The values are enumerated in window.h, e.g. // NATIVE_WINDOW_TRANSFORM_ROT_90. The default is 0 (no transform). + // + // Return of a value other than NO_ERROR means an unknown error has occurred. virtual status_t setTransformHint(uint32_t hint) = 0; // dump state into a string diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h index 342ba08111..70025308c5 100644 --- a/include/gui/IGraphicBufferProducer.h +++ b/include/gui/IGraphicBufferProducer.h @@ -54,7 +54,11 @@ public: DECLARE_META_INTERFACE(GraphicBufferProducer); enum { + // A flag returned by dequeueBuffer when the client needs to call + // requestBuffer immediately thereafter. BUFFER_NEEDS_REALLOCATION = 0x1, + // A flag returned by dequeueBuffer when all mirrored slots should be + // released by the client. This flag should always be processed first. RELEASE_ALL_BUFFERS = 0x2, }; @@ -63,51 +67,144 @@ public: // buffer to the given slot index, and the client is expected to mirror the // slot->buffer mapping so that it's not necessary to transfer a // GraphicBuffer for every dequeue operation. + // + // The slot must be in the range of [0, NUM_BUFFER_SLOTS). + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned. + // * BAD_VALUE - one of the two conditions occurred: + // * slot was out of range (see above) + // * buffer specified by the slot is not dequeued virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) = 0; // setBufferCount sets the number of buffer slots available. Calling this // will also cause all buffer slots to be emptied. The caller should empty // its mirrored copy of the buffer slots when calling this method. + // + // This function should not be called when there are any dequeued buffer + // slots, doing so will result in a BAD_VALUE error returned. + // + // The buffer count should be at most NUM_BUFFER_SLOTS (inclusive), but at least + // the minimum undequeued buffer count (exclusive). The minimum value + // can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS). + // In particular the range is (minUndequeudBuffers, NUM_BUFFER_SLOTS]. + // + // The buffer count may also be set to 0 (the default), to indicate that + // the producer does not wish to set a value. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned. + // * BAD_VALUE - one of the below conditions occurred: + // * bufferCount was out of range (see above) + // * client has one or more buffers dequeued virtual status_t setBufferCount(int bufferCount) = 0; // dequeueBuffer requests a new buffer slot for the client to use. Ownership // of the slot is transfered to the client, meaning that the server will not - // use the contents of the buffer associated with that slot. The slot index - // returned may or may not contain a buffer. If the slot is empty the client - // should call requestBuffer to assign a new buffer to that slot. The client - // is expected to either call cancelBuffer on the dequeued slot or to fill - // in the contents of its associated buffer contents and call queueBuffer. - // If dequeueBuffer return BUFFER_NEEDS_REALLOCATION, the client is + // use the contents of the buffer associated with that slot. + // + // The slot index returned may or may not contain a buffer (client-side). + // If the slot is empty the client should call requestBuffer to assign a new + // buffer to that slot. + // + // Once the client is done filling this buffer, it is expected to transfer + // buffer ownership back to the server with either cancelBuffer on + // the dequeued slot or to fill in the contents of its associated buffer + // contents and call queueBuffer. + // + // If dequeueBuffer returns the BUFFER_NEEDS_REALLOCATION flag, the client is // expected to call requestBuffer immediately. // + // If dequeueBuffer returns the RELEASE_ALL_BUFFERS flag, the client is + // expected to release all of the mirrored slot->buffer mappings. + // // The fence parameter will be updated to hold the fence associated with // the buffer. The contents of the buffer must not be overwritten until the - // fence signals. If the fence is NULL, the buffer may be written + // fence signals. If the fence is Fence::NO_FENCE, the buffer may be written // immediately. // - // The async parameter sets whether we're in asynchrnous mode for this - // deququeBuffer() call. - virtual status_t dequeueBuffer(int *slot, sp<Fence>* fence, bool async, + // The async parameter sets whether we're in asynchronous mode for this + // dequeueBuffer() call. + // + // The width and height parameters must be no greater than the minimum of + // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv). + // An error due to invalid dimensions might not be reported until + // updateTexImage() is called. If width and height are both zero, the + // default values specified by setDefaultBufferSize() are used instead. + // + // The pixel formats are enumerated in <graphics.h>, e.g. + // HAL_PIXEL_FORMAT_RGBA_8888. If the format is 0, the default format + // will be used. + // + // The usage argument specifies gralloc buffer usage flags. The values + // are enumerated in <gralloc.h>, e.g. GRALLOC_USAGE_HW_RENDER. These + // will be merged with the usage flags specified by + // IGraphicBufferConsumer::setConsumerUsageBits. + // + // This call will block until a buffer is available to be dequeued. If + // both the producer and consumer are controlled by the app, then this call + // can never block and will return WOULD_BLOCK if no buffer is available. + // + // A non-negative value with flags set (see above) will be returned upon + // success. + // + // Return of a negative means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned. + // * BAD_VALUE - one of the below conditions occurred: + // * both in async mode and buffer count was less than the + // max numbers of buffers that can be allocated at once + // * attempting dequeue more than one buffer at a time + // without setting the buffer count with setBufferCount() + // * -EBUSY - attempting to dequeue too many buffers at a time + // * WOULD_BLOCK - no buffer is currently available, and blocking is disabled + // since both the producer/consumer are controlled by app + // * NO_MEMORY - out of memory, cannot allocate the graphics buffer. + // + // All other negative values are an unknown error returned downstream + // from the graphics allocator (typically errno). + virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, bool async, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) = 0; // queueBuffer indicates that the client has finished filling in the // contents of the buffer associated with slot and transfers ownership of - // that slot back to the server. It is not valid to call queueBuffer on a - // slot that is not owned by the client or one for which a buffer associated - // via requestBuffer. In addition, a timestamp must be provided by the - // client for this buffer. The timestamp is measured in nanoseconds, and - // must be monotonically increasing. Its other properties (zero point, etc) + // that slot back to the server. + // + // It is not valid to call queueBuffer on a slot that is not owned + // by the client or one for which a buffer associated via requestBuffer + // (an attempt to do so will fail with a return value of BAD_VALUE). + // + // In addition, the input must be described by the client (as documented + // below). Any other properties (zero point, etc) // are client-dependent, and should be documented by the client. // - // The async parameter sets whether we're queuing a buffer in asynchronous mode. + // The slot must be in the range of [0, NUM_BUFFER_SLOTS). + // + // Upon success, the output will be filled with meaningful values + // (refer to the documentation below). // - // outWidth, outHeight and outTransform are filled with the default width - // and height of the window and current transform applied to buffers, - // respectively. + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned. + // * BAD_VALUE - one of the below conditions occurred: + // * fence was NULL + // * scaling mode was unknown + // * both in async mode and buffer count was less than the + // max numbers of buffers that can be allocated at once + // * slot index was out of range (see above). + // * the slot was not in the dequeued state + // * the slot was enqueued without requesting a buffer + // * crop rect is out of bounds of the buffer dimensions struct QueueBufferInput : public Flattenable<QueueBufferInput> { friend class Flattenable<QueueBufferInput>; inline QueueBufferInput(const Parcel& parcel); + // timestamp - a monotonically increasing value in nanoseconds + // isAutoTimestamp - if the timestamp was synthesized at queue time + // crop - a crop rectangle that's used as a hint to the consumer + // scalingMode - a set of flags from NATIVE_WINDOW_SCALING_* in <window.h> + // transform - a set of flags from NATIVE_WINDOW_TRANSFORM_* in <window.h> + // async - if the buffer is queued in asynchronous mode + // fence - a fence that the consumer must wait on before reading the buffer, + // set this to Fence::NO_FENCE if the buffer is ready immediately inline QueueBufferInput(int64_t timestamp, bool isAutoTimestamp, const Rect& crop, int scalingMode, uint32_t transform, bool async, const sp<Fence>& fence) @@ -143,8 +240,13 @@ public: }; // QueueBufferOutput must be a POD structure - struct QueueBufferOutput { + struct __attribute__ ((__packed__)) QueueBufferOutput { inline QueueBufferOutput() { } + // outWidth - filled with default width applied to the buffer + // outHeight - filled with default height applied to the buffer + // outTransformHint - filled with default transform applied to the buffer + // outNumPendingBuffers - num buffers queued that haven't yet been acquired + // (counting the currently queued buffer) inline void deflate(uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransformHint, @@ -174,24 +276,54 @@ public: // cancelBuffer indicates that the client does not wish to fill in the // buffer associated with slot and transfers ownership of the slot back to // the server. + // + // The buffer is not queued for use by the consumer. + // + // The buffer will not be overwritten until the fence signals. The fence + // will usually be the one obtained from dequeueBuffer. virtual void cancelBuffer(int slot, const sp<Fence>& fence) = 0; // query retrieves some information for this surface - // 'what' tokens allowed are that of android_natives.h + // 'what' tokens allowed are that of NATIVE_WINDOW_* in <window.h> + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - the buffer queue has been abandoned. + // * BAD_VALUE - what was out of range virtual int query(int what, int* value) = 0; // connect attempts to connect a client API to the IGraphicBufferProducer. // This must be called before any other IGraphicBufferProducer methods are - // called except for getAllocator. + // called except for getAllocator. A consumer must be already connected. // // This method will fail if the connect was previously called on the // IGraphicBufferProducer and no corresponding disconnect call was made. // - // outWidth, outHeight and outTransform are filled with the default width - // and height of the window and current transform applied to buffers, - // respectively. The token needs to be any binder object that lives in the + // The token needs to be any opaque binder object that lives in the // producer process -- it is solely used for obtaining a death notification // when the producer is killed. + // + // The api should be one of the NATIVE_WINDOW_API_* values in <window.h> + // + // The producerControlledByApp should be set to true if the producer is hosted + // by an untrusted process (typically app_process-forked processes). If both + // the producer and the consumer are app-controlled then all buffer queues + // will operate in async mode regardless of the async flag. + // + // Upon success, the output will be filled with meaningful data + // (refer to QueueBufferOutput documentation above). + // + // Return of a value other than NO_ERROR means an error has occurred: + // * NO_INIT - one of the following occurred: + // * the buffer queue was abandoned + // * no consumer has yet connected + // * BAD_VALUE - one of the following has occurred: + // * the producer is already connected + // * api was out of range (see above). + // * output was NULL. + // * DEAD_OBJECT - the token is hosted by an already-dead process + // + // Additional negative errors may be returned by the internals, they + // should be treated as opaque fatal unrecoverable errors. virtual status_t connect(const sp<IBinder>& token, int api, bool producerControlledByApp, QueueBufferOutput* output) = 0; @@ -203,6 +335,17 @@ public: // // This method will fail if the the IGraphicBufferProducer is not currently // connected to the specified client API. + // + // The api should be one of the NATIVE_WINDOW_API_* values in <window.h> + // + // Disconnecting from an abandoned IGraphicBufferProducer is legal and + // is considered a no-op. + // + // Return of a value other than NO_ERROR means an error has occurred: + // * BAD_VALUE - one of the following has occurred: + // * the api specified does not match the one that was connected + // * api was out of range (see above). + // * DEAD_OBJECT - the token is hosted by an already-dead process virtual status_t disconnect(int api) = 0; }; diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h new file mode 100644 index 0000000000..79ff12a664 --- /dev/null +++ b/include/input/IInputFlinger.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 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 _LIBINPUT_IINPUT_FLINGER_H +#define _LIBINPUT_IINPUT_FLINGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <binder/IInterface.h> + +namespace android { + +/* + * This class defines the Binder IPC interface for accessing various + * InputFlinger features. + */ +class IInputFlinger : public IInterface { +public: + DECLARE_META_INTERFACE(InputFlinger); + + virtual status_t doSomething() = 0; +}; + + +/** + * Binder implementation. + */ +class BnInputFlinger : public BnInterface<IInputFlinger> { +public: + enum { + DO_SOMETHING_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, + }; + + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags = 0); +}; + +} // namespace android + +#endif // _LIBINPUT_IINPUT_FLINGER_H diff --git a/include/input/Input.h b/include/input/Input.h index e77807634d..37f3b727eb 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -65,6 +65,34 @@ enum { AINPUT_SOURCE_SWITCH = 0x80000000, }; +enum { + /** + * Constants for LEDs. Hidden from the API since we don't actually expose a way to interact + * with LEDs to developers + * + * NOTE: If you add LEDs here, you must also add them to KeycodeLabels.h + */ + + ALED_NUM_LOCK = 0x00, + ALED_CAPS_LOCK = 0x01, + ALED_SCROLL_LOCK = 0x02, + ALED_COMPOSE = 0x03, + ALED_KANA = 0x04, + ALED_SLEEP = 0x05, + ALED_SUSPEND = 0x06, + ALED_MUTE = 0x07, + ALED_MISC = 0x08, + ALED_MAIL = 0x09, + ALED_CHARGING = 0x0a, + ALED_CONTROLLER_1 = 0x10, + ALED_CONTROLLER_2 = 0x11, + ALED_CONTROLLER_3 = 0x12, + ALED_CONTROLLER_4 = 0x13, +}; + +/* Maximum number of controller LEDs we support */ +#define MAX_CONTROLLER_LEDS 4 + /* * SystemUiVisibility constants from View. */ diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 1419b451f8..adf9fb9170 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -46,6 +46,11 @@ struct InputDeviceIdentifier { // Ideally, the way this value is computed should not change between Android releases // because that would invalidate persistent settings that rely on it. String8 descriptor; + + // A value added to uniquely identify a device in the absence of a unique id. This + // is intended to be a minimum way to distinguish from other active devices and may + // reuse values that are not associated with an input anymore. + uint16_t nonce; }; /* diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index eec11cf13a..1e8de7173b 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -67,6 +67,8 @@ public: status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const; status_t findScanCodesForKey(int32_t keyCode, Vector<int32_t>* outScanCodes) const; + status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const; + status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const; status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; @@ -79,9 +81,16 @@ private: uint32_t flags; }; + struct Led { + int32_t ledCode; + }; + + KeyedVector<int32_t, Key> mKeysByScanCode; KeyedVector<int32_t, Key> mKeysByUsageCode; KeyedVector<int32_t, AxisInfo> mAxes; + KeyedVector<int32_t, Led> mLedsByScanCode; + KeyedVector<int32_t, Led> mLedsByUsageCode; KeyLayoutMap(); @@ -99,6 +108,7 @@ private: private: status_t parseKey(); status_t parseAxis(); + status_t parseLed(); }; }; diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index 846cb0c831..25b2f07cff 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -94,18 +94,24 @@ extern int32_t getKeyCodeByLabel(const char* label); extern uint32_t getKeyFlagByLabel(const char* label); /** - * Gets a axis by its short form label, eg. "X". + * Gets an axis by its short form label, eg. "X". * Returns -1 if unknown. */ extern int32_t getAxisByLabel(const char* label); /** - * Gets a axis label by its id. + * Gets an axis label by its id. * Returns NULL if unknown. */ extern const char* getAxisLabel(int32_t axisId); /** + * Gets an LED by its short form label, eg. "CAPS_LOCK". + * Returns -1 if unknown. + */ +extern int32_t getLedByLabel(const char* label); + +/** * Updates a meta state field when a key is pressed or released. */ extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState); diff --git a/include/input/KeycodeLabels.h b/include/input/KeycodeLabels.h index c64c5d8daa..19582e9205 100644 --- a/include/input/KeycodeLabels.h +++ b/include/input/KeycodeLabels.h @@ -319,4 +319,26 @@ static const KeycodeLabel AXES[] = { { NULL, -1 } }; +static const KeycodeLabel LEDS[] = { + { "NUM_LOCK", 0x00 }, + { "CAPS_LOCK", 0x01 }, + { "SCROLL_LOCK", 0x02 }, + { "COMPOSE", 0x03 }, + { "KANA", 0x04 }, + { "SLEEP", 0x05 }, + { "SUSPEND", 0x06 }, + { "MUTE", 0x07 }, + { "MISC", 0x08 }, + { "MAIL", 0x09 }, + { "CHARGING", 0x0a }, + { "CONTROLLER_1", 0x10 }, + { "CONTROLLER_2", 0x11 }, + { "CONTROLLER_3", 0x12 }, + { "CONTROLLER_4", 0x13 }, + + // NOTE: If you add new LEDs here, you must also add them to Input.h + + { NULL, -1 } +}; + #endif // _LIBINPUT_KEYCODE_LABELS_H diff --git a/include/media/hardware/HDCPAPI.h b/include/media/hardware/HDCPAPI.h index d4abb3f883..3a53e9fc90 100644 --- a/include/media/hardware/HDCPAPI.h +++ b/include/media/hardware/HDCPAPI.h @@ -88,6 +88,11 @@ struct HDCPModule { // Request to shutdown the active HDCP session. virtual status_t shutdownAsync() = 0; + // Returns the capability bitmask of this HDCP session. + virtual uint32_t getCaps() { + return HDCP_CAPS_ENCRYPT; + } + // ENCRYPTION only: // Encrypt data according to the HDCP spec. "size" bytes of data are // available at "inData" (virtual address), "size" may not be a multiple diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h index bf4bf034ff..5584fb1727 100644 --- a/include/private/gui/LayerState.h +++ b/include/private/gui/LayerState.h @@ -30,11 +30,15 @@ namespace android { class Parcel; class ISurfaceComposerClient; +/* + * Used to communicate layer information between SurfaceFlinger and its clients. + */ struct layer_state_t { enum { - eLayerHidden = 0x01, + eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java + eLayerOpaque = 0x02, // SURFACE_OPAQUE }; enum { @@ -47,6 +51,7 @@ struct layer_state_t { eVisibilityChanged = 0x00000040, eLayerStackChanged = 0x00000080, eCropChanged = 0x00000100, + eOpacityChanged = 0x00000200, }; layer_state_t() diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h index 627cfb695c..7e469453fd 100644 --- a/include/ui/PixelFormat.h +++ b/include/ui/PixelFormat.h @@ -56,13 +56,15 @@ enum { // real pixel formats supported for rendering ----------------------------- - PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA - PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0 - PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB - PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB - PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA - PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB - PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB + PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA + PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0 + PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB + PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB + PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA + PIXEL_FORMAT_RGBA_5551 = 6, // 16-bit ARGB + PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB + PIXEL_FORMAT_sRGB_A_8888 = HAL_PIXEL_FORMAT_sRGB_A_8888, // 4x8-bit sRGB + A + PIXEL_FORMAT_sRGB_X_8888 = HAL_PIXEL_FORMAT_sRGB_X_8888, // 4x8-bit sRGB, no A }; typedef int32_t PixelFormat; diff --git a/libs/binder/Android.mk b/libs/binder/Android.mk index 673fc8238f..d8ae0aa3fe 100644 --- a/libs/binder/Android.mk +++ b/libs/binder/Android.mk @@ -21,6 +21,7 @@ sources := \ Debug.cpp \ IAppOpsCallback.cpp \ IAppOpsService.cpp \ + IBatteryStats.cpp \ IInterface.cpp \ IMemory.cpp \ IPCThreadState.cpp \ diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp new file mode 100644 index 0000000000..6469b084d9 --- /dev/null +++ b/libs/binder/IBatteryStats.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/IBatteryStats.h> + +#include <utils/Debug.h> +#include <utils/Log.h> +#include <binder/Parcel.h> +#include <utils/String8.h> + +#include <private/binder/Static.h> + +namespace android { + +// ---------------------------------------------------------------------- + +class BpBatteryStats : public BpInterface<IBatteryStats> +{ +public: + BpBatteryStats(const sp<IBinder>& impl) + : BpInterface<IBatteryStats>(impl) + { + } + + virtual void noteStartSensor(int uid, int sensor) { + Parcel data, reply; + data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); + data.writeInt32(uid); + data.writeInt32(sensor); + remote()->transact(NOTE_START_SENSOR_TRANSACTION, data, &reply); + } + + virtual void noteStopSensor(int uid, int sensor) { + Parcel data, reply; + data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor()); + data.writeInt32(uid); + data.writeInt32(sensor); + remote()->transact(NOTE_STOP_SENSOR_TRANSACTION, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(BatteryStats, "com.android.internal.app.IBatteryStats"); + +// ---------------------------------------------------------------------- + +status_t BnBatteryStats::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + switch(code) { + case NOTE_START_SENSOR_TRANSACTION: { + CHECK_INTERFACE(IBatteryStats, data, reply); + int uid = data.readInt32(); + int sensor = data.readInt32(); + noteStartSensor(uid, sensor); + reply->writeNoException(); + return NO_ERROR; + } break; + case NOTE_STOP_SENSOR_TRANSACTION: { + CHECK_INTERFACE(IBatteryStats, data, reply); + int uid = data.readInt32(); + int sensor = data.readInt32(); + noteStopSensor(uid, sensor); + reply->writeNoException(); + return NO_ERROR; + } break; + default: + return BBinder::onTransact(code, data, reply, flags); + } +} + +}; // namespace android diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index ee453b657d..7de2d8d758 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -765,6 +765,32 @@ status_t Parcel::writeDupFileDescriptor(int fd) return err; } +// WARNING: This method must stay in sync with +// Parcelable.Creator<ParcelFileDescriptor> CREATOR +// in frameworks/base/core/java/android/os/ParcelFileDescriptor.java +status_t Parcel::writeParcelFileDescriptor(int fd, int commChannel) { + status_t status; + + if (fd < 0) { + status = writeInt32(0); // ParcelFileDescriptor is null + if (status) return status; + } else { + status = writeInt32(1); // ParcelFileDescriptor is not null + if (status) return status; + status = writeDupFileDescriptor(fd); + if (status) return status; + if (commChannel < 0) { + status = writeInt32(0); // commChannel is null + if (status) return status; + } else { + status = writeInt32(1); // commChannel is not null + if (status) return status; + status = writeDupFileDescriptor(commChannel); + } + } + return status; +} + status_t Parcel::writeBlob(size_t len, WritableBlob* outBlob) { status_t status; @@ -1179,6 +1205,23 @@ int Parcel::readFileDescriptor() const return BAD_TYPE; } +// WARNING: This method must stay in sync with writeToParcel() +// in frameworks/base/core/java/android/os/ParcelFileDescriptor.java +int Parcel::readParcelFileDescriptor(int& outCommChannel) const { + int fd; + outCommChannel = -1; + + if (readInt32() == 0) { + fd = -1; + } else { + fd = readFileDescriptor(); + if (fd >= 0 && readInt32() != 0) { + outCommChannel = readFileDescriptor(); + } + } + return fd; +} + status_t Parcel::readBlob(size_t len, ReadableBlob* outBlob) const { int32_t useAshmem; diff --git a/libs/gui/BufferItemConsumer.cpp b/libs/gui/BufferItemConsumer.cpp index 350887a1f7..74a65ed8dd 100644 --- a/libs/gui/BufferItemConsumer.cpp +++ b/libs/gui/BufferItemConsumer.cpp @@ -33,6 +33,13 @@ BufferItemConsumer::BufferItemConsumer(const sp<BufferQueue>& bq, uint32_t consumerUsage, int bufferCount, bool controlledByApp) : ConsumerBase(bq, controlledByApp) { + if (bufferCount == MIN_UNDEQUEUED_BUFFERS) { + status_t res; + res = bq->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &bufferCount); + LOG_ALWAYS_FATAL_IF(res != OK || bufferCount < 0, + "Failed to query min buffer count"); + } + mConsumer->setConsumerUsageBits(consumerUsage); mConsumer->setMaxAcquiredBufferCount(bufferCount); } diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index 2aecb67788..2fa0433ce8 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -31,7 +31,6 @@ #include <utils/Log.h> #include <utils/Trace.h> -#include <utils/CallStack.h> // Macros for including the BufferQueue name in log messages #define ST_LOGV(x, ...) ALOGV("[%s] "x, mConsumerName.string(), ##__VA_ARGS__) @@ -196,6 +195,11 @@ int BufferQueue::query(int what, int* outValue) ATRACE_CALL(); Mutex::Autolock lock(mMutex); + if (outValue == NULL) { + ST_LOGE("query: outValue was NULL"); + return BAD_VALUE; + } + if (mAbandoned) { ST_LOGE("query: BufferQueue has been abandoned!"); return NO_INIT; @@ -655,10 +659,15 @@ retry: return NO_INIT; } + if (output == NULL) { + ST_LOGE("connect: output was NULL"); + return BAD_VALUE; + } + if (mConnectedApi != NO_CONNECTED_API) { ST_LOGE("connect: already connected (cur=%d, req=%d)", mConnectedApi, api); - return -EINVAL; + return BAD_VALUE; } // If we disconnect and reconnect quickly, we can be in a state where our slots are @@ -694,7 +703,7 @@ retry: } break; default: - err = -EINVAL; + err = BAD_VALUE; break; } @@ -704,7 +713,7 @@ retry: return err; } -void BufferQueue::binderDied(const wp<IBinder>& who) { +void BufferQueue::binderDied(const wp<IBinder>& who __attribute__((unused))) { // If we're here, it means that a producer we were connected to died. // We're GUARANTEED that we still are connected to it because it has no other way // to get disconnected -- or -- we wouldn't be here because we're removing this diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp index c4ec8578a8..c5900aa5b1 100644 --- a/libs/gui/ConsumerBase.cpp +++ b/libs/gui/ConsumerBase.cpp @@ -85,7 +85,7 @@ ConsumerBase::~ConsumerBase() { "consumer is not abandoned!", mName.string()); } -void ConsumerBase::onLastStrongRef(const void* id) { +void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) { abandon(); } @@ -243,7 +243,7 @@ status_t ConsumerBase::releaseBufferLocked( slot, mSlots[slot].mFrameNumber); status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, display, eglFence, mSlots[slot].mFence); - if (err == BufferQueue::STALE_BUFFER_SLOT) { + if (err == IGraphicBufferConsumer::STALE_BUFFER_SLOT) { freeBufferLocked(slot); } diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp index 9574b61762..876c89568d 100644 --- a/libs/gui/IGraphicBufferConsumer.cpp +++ b/libs/gui/IGraphicBufferConsumer.cpp @@ -223,7 +223,7 @@ public: } virtual status_t releaseBuffer(int buf, uint64_t frameNumber, - EGLDisplay display, EGLSyncKHR fence, + EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)), const sp<Fence>& releaseFence) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor()); diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 27dbc4eeea..975d00575a 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -251,7 +251,7 @@ int Surface::getSlotFromBufferLocked( return BAD_VALUE; } -int Surface::lockBuffer_DEPRECATED(android_native_buffer_t* buffer) { +int Surface::lockBuffer_DEPRECATED(android_native_buffer_t* buffer __attribute__((unused))) { ALOGV("Surface::lockBuffer"); Mutex::Autolock lock(mMutex); return OK; @@ -482,7 +482,7 @@ int Surface::dispatchLock(va_list args) { return lock(outBuffer, inOutDirtyBounds); } -int Surface::dispatchUnlockAndPost(va_list args) { +int Surface::dispatchUnlockAndPost(va_list args __attribute__((unused))) { return unlockAndPost(); } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index aafc4d255f..2246f5f9bb 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -309,7 +309,12 @@ status_t Composer::setFlags(const sp<SurfaceComposerClient>& client, layer_state_t* s = getLayerStateLocked(client, id); if (!s) return BAD_INDEX; - s->what |= layer_state_t::eVisibilityChanged; + if (mask & layer_state_t::eLayerOpaque) { + s->what |= layer_state_t::eOpacityChanged; + } + if (mask & layer_state_t::eLayerHidden) { + s->what |= layer_state_t::eVisibilityChanged; + } s->flags &= ~mask; s->flags |= (flags & mask); s->mask |= mask; diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 16e533cd2e..de182eec45 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -23,7 +23,6 @@ #include <android/native_window.h> -#include <utils/CallStack.h> #include <utils/Errors.h> #include <utils/Log.h> #include <utils/threads.h> diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk index 21bd875e34..2eeb5c7bbb 100644 --- a/libs/gui/tests/Android.mk +++ b/libs/gui/tests/Android.mk @@ -9,9 +9,19 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ BufferQueue_test.cpp \ CpuConsumer_test.cpp \ + FillBuffer.cpp \ + GLTest.cpp \ + IGraphicBufferProducer_test.cpp \ + MultiTextureConsumer_test.cpp \ + SRGB_test.cpp \ SurfaceTextureClient_test.cpp \ - SurfaceTexture_test.cpp \ + SurfaceTextureFBO_test.cpp \ + SurfaceTextureGLThreadToGL_test.cpp \ + SurfaceTextureGLToGL_test.cpp \ + SurfaceTextureGL_test.cpp \ + SurfaceTextureMultiContextGL_test.cpp \ Surface_test.cpp \ + TextureRenderer.cpp \ LOCAL_SHARED_LIBRARIES := \ libEGL \ diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 03c1a29a79..06f9a9297a 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -52,6 +52,12 @@ protected: testInfo->name()); } + void GetMinUndequeuedBufferCount(int* bufferCount) { + ASSERT_NE((void*)NULL, bufferCount); + ASSERT_EQ(OK, mBQ->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, bufferCount)); + ASSERT_LE(0, *bufferCount); // non-negative + } + sp<BufferQueue> mBQ; }; @@ -97,20 +103,28 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) sp<DummyConsumer> dc(new DummyConsumer); mBQ->consumerConnect(dc, false); - ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(0)); - ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(-3)); - ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount( + int minBufferCount; + ASSERT_NO_FATAL_FAILURE(GetMinUndequeuedBufferCount(&minBufferCount)); + EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(minBufferCount - 1)); + + EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(0)); + EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(-3)); + EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount( BufferQueue::MAX_MAX_ACQUIRED_BUFFERS+1)); - ASSERT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(100)); + EXPECT_EQ(BAD_VALUE, mBQ->setMaxAcquiredBufferCount(100)); } TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { sp<DummyConsumer> dc(new DummyConsumer); mBQ->consumerConnect(dc, false); - ASSERT_EQ(OK, mBQ->setMaxAcquiredBufferCount(1)); - ASSERT_EQ(OK, mBQ->setMaxAcquiredBufferCount(2)); - ASSERT_EQ(OK, mBQ->setMaxAcquiredBufferCount( + int minBufferCount; + ASSERT_NO_FATAL_FAILURE(GetMinUndequeuedBufferCount(&minBufferCount)); + + EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount(1)); + EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount(2)); + EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount(minBufferCount)); + EXPECT_EQ(OK, mBQ->setMaxAcquiredBufferCount( BufferQueue::MAX_MAX_ACQUIRED_BUFFERS)); } diff --git a/libs/gui/tests/DisconnectWaiter.h b/libs/gui/tests/DisconnectWaiter.h new file mode 100644 index 0000000000..3a30a1937e --- /dev/null +++ b/libs/gui/tests/DisconnectWaiter.h @@ -0,0 +1,78 @@ +/* + * Copyright 2013 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_DISCONNECT_WAITER_H +#define ANDROID_DISCONNECT_WAITER_H + +#include <gui/IConsumerListener.h> + +#include <utils/Condition.h> +#include <utils/Mutex.h> + +namespace android { + +// Note that GLConsumer will lose the notifications +// onBuffersReleased and onFrameAvailable as there is currently +// no way to forward the events. This DisconnectWaiter will not let the +// disconnect finish until finishDisconnect() is called. It will +// also block until a disconnect is called +class DisconnectWaiter : public BnConsumerListener { +public: + DisconnectWaiter () : + mWaitForDisconnect(false), + mPendingFrames(0) { + } + + void waitForFrame() { + Mutex::Autolock lock(mMutex); + while (mPendingFrames == 0) { + mFrameCondition.wait(mMutex); + } + mPendingFrames--; + } + + virtual void onFrameAvailable() { + Mutex::Autolock lock(mMutex); + mPendingFrames++; + mFrameCondition.signal(); + } + + virtual void onBuffersReleased() { + Mutex::Autolock lock(mMutex); + while (!mWaitForDisconnect) { + mDisconnectCondition.wait(mMutex); + } + } + + void finishDisconnect() { + Mutex::Autolock lock(mMutex); + mWaitForDisconnect = true; + mDisconnectCondition.signal(); + } + +private: + Mutex mMutex; + + bool mWaitForDisconnect; + Condition mDisconnectCondition; + + int mPendingFrames; + Condition mFrameCondition; +}; + +} // namespace android + +#endif diff --git a/libs/gui/tests/FillBuffer.cpp b/libs/gui/tests/FillBuffer.cpp new file mode 100644 index 0000000000..079962c8b9 --- /dev/null +++ b/libs/gui/tests/FillBuffer.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2013 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 "FillBuffer.h" + +#include <ui/GraphicBuffer.h> + +#include <gtest/gtest.h> + +namespace android { + +void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) { + const int blockWidth = w > 16 ? w / 16 : 1; + const int blockHeight = h > 16 ? h / 16 : 1; + const int yuvTexOffsetY = 0; + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * h; + int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; + int yuvTexStrideU = yuvTexStrideV; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + int parityX = (x / blockWidth) & 1; + int parityY = (y / blockHeight) & 1; + unsigned char intensity = (parityX ^ parityY) ? 63 : 191; + buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; + if (x < w / 2 && y < h / 2) { + buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; + if (x * 2 < w / 2 && y * 2 < h / 2) { + buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = + buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = + buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = + buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = + intensity; + } + } + } + } +} + +void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, + const android_native_rect_t& rect) { + const int yuvTexOffsetY = 0; + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * h; + int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; + int yuvTexStrideU = yuvTexStrideV; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + bool inside = rect.left <= x && x < rect.right && + rect.top <= y && y < rect.bottom; + buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64; + if (x < w / 2 && y < h / 2) { + bool inside = rect.left <= 2*x && 2*x < rect.right && + rect.top <= 2*y && 2*y < rect.bottom; + buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16; + buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = + inside ? 16 : 255; + } + } + } +} + +void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { + const size_t PIXEL_SIZE = 4; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + off_t offset = (y * stride + x) * PIXEL_SIZE; + for (int c = 0; c < 4; c++) { + int parityX = (x / (1 << (c+2))) & 1; + int parityY = (y / (1 << (c+2))) & 1; + buf[offset + c] = (parityX ^ parityY) ? 231 : 35; + } + } + } +} + +void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) { + android_native_buffer_t* anb; + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), + &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + + uint8_t* img = NULL; + ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, + (void**)(&img))); + fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride()); + ASSERT_EQ(NO_ERROR, buf->unlock()); + ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf->getNativeBuffer(), + -1)); +} +} // namespace android diff --git a/libs/gui/tests/FillBuffer.h b/libs/gui/tests/FillBuffer.h new file mode 100644 index 0000000000..b584179318 --- /dev/null +++ b/libs/gui/tests/FillBuffer.h @@ -0,0 +1,43 @@ +/* + * Copyright 2013 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_FILL_BUFFER_H +#define ANDROID_FILL_BUFFER_H + +#include <system/window.h> +#include <utils/StrongPointer.h> + +namespace android { + +// Fill a YV12 buffer with a multi-colored checkerboard pattern +void fillYV12Buffer(uint8_t* buf, int w, int h, int stride); + +// Fill a YV12 buffer with red outside a given rectangle and green inside it. +void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, + const android_native_rect_t& rect); + +void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride); + +// Produce a single RGBA8 frame by filling a buffer with a checkerboard pattern +// using the CPU. This assumes that the ANativeWindow is already configured to +// allow this to be done (e.g. the format is set to RGBA8). +// +// Calls to this function should be wrapped in an ASSERT_NO_FATAL_FAILURE(). +void produceOneRGBA8Frame(const sp<ANativeWindow>& anw); + +} // namespace android + +#endif diff --git a/libs/gui/tests/FrameWaiter.h b/libs/gui/tests/FrameWaiter.h new file mode 100644 index 0000000000..bdedba67ad --- /dev/null +++ b/libs/gui/tests/FrameWaiter.h @@ -0,0 +1,52 @@ +/* + * Copyright 2013 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_FRAME_WAITER_H +#define ANDROID_FRAME_WAITER_H + +#include <gui/GLConsumer.h> + +namespace android { + +class FrameWaiter : public GLConsumer::FrameAvailableListener { +public: + FrameWaiter(): + mPendingFrames(0) { + } + + void waitForFrame() { + Mutex::Autolock lock(mMutex); + while (mPendingFrames == 0) { + mCondition.wait(mMutex); + } + mPendingFrames--; + } + + virtual void onFrameAvailable() { + Mutex::Autolock lock(mMutex); + mPendingFrames++; + mCondition.signal(); + } + +private: + int mPendingFrames; + Mutex mMutex; + Condition mCondition; +}; + +} // namespace android + +#endif diff --git a/libs/gui/tests/GLTest.cpp b/libs/gui/tests/GLTest.cpp new file mode 100644 index 0000000000..1739d9c7ca --- /dev/null +++ b/libs/gui/tests/GLTest.cpp @@ -0,0 +1,339 @@ +/* + * Copyright 2013 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 "GLTest.h" + +#include <gui/Surface.h> + +#include <GLES2/gl2.h> + +namespace android { + +static int abs(int value) { + return value > 0 ? value : -value; +} + +void GLTest::SetUp() { + const ::testing::TestInfo* const testInfo = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); + + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); + + EGLint majorVersion; + EGLint minorVersion; + EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + RecordProperty("EglVersionMajor", majorVersion); + RecordProperty("EglVersionMinor", minorVersion); + + EGLint numConfigs = 0; + EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig, 1, + &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS"); + if (displaySecsEnv != NULL) { + mDisplaySecs = atoi(displaySecsEnv); + if (mDisplaySecs < 0) { + mDisplaySecs = 0; + } + } else { + mDisplaySecs = 0; + } + + if (mDisplaySecs > 0) { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + mSurfaceControl = mComposerClient->createSurface( + String8("Test Surface"), getSurfaceWidth(), getSurfaceHeight(), + PIXEL_FORMAT_RGB_888, 0); + + ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl->isValid()); + + SurfaceComposerClient::openGlobalTransaction(); + ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); + ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); + SurfaceComposerClient::closeGlobalTransaction(); + + sp<ANativeWindow> window = mSurfaceControl->getSurface(); + mEglSurface = createWindowSurface(mEglDisplay, mGlConfig, window); + } else { + EGLint pbufferAttribs[] = { + EGL_WIDTH, getSurfaceWidth(), + EGL_HEIGHT, getSurfaceHeight(), + EGL_NONE }; + + mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig, + pbufferAttribs); + } + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mEglSurface); + + mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, + getContextAttribs()); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mEglContext); + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EGLint w, h; + EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + RecordProperty("EglSurfaceWidth", w); + RecordProperty("EglSurfaceHeight", h); + + glViewport(0, 0, w, h); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); +} + +void GLTest::TearDown() { + // Display the result + if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) { + eglSwapBuffers(mEglDisplay, mEglSurface); + sleep(mDisplaySecs); + } + + if (mComposerClient != NULL) { + mComposerClient->dispose(); + } + if (mEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mEglContext); + } + if (mEglSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEglDisplay, mEglSurface); + } + if (mEglDisplay != EGL_NO_DISPLAY) { + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + eglTerminate(mEglDisplay); + } + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + const ::testing::TestInfo* const testInfo = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGV("End test: %s.%s", testInfo->test_case_name(), testInfo->name()); +} + +EGLint const* GLTest::getConfigAttribs() { + static const EGLint sDefaultConfigAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + EGL_NONE }; + + return sDefaultConfigAttribs; +} + +EGLint const* GLTest::getContextAttribs() { + static const EGLint sDefaultContextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE }; + + return sDefaultContextAttribs; +} + +EGLint GLTest::getSurfaceWidth() { + return 512; +} + +EGLint GLTest::getSurfaceHeight() { + return 512; +} + +EGLSurface GLTest::createWindowSurface(EGLDisplay display, EGLConfig config, + sp<ANativeWindow>& window) const { + return eglCreateWindowSurface(display, config, window.get(), NULL); +} + +::testing::AssertionResult GLTest::checkPixel(int x, int y, + int r, int g, int b, int a, int tolerance) { + GLubyte pixel[4]; + String8 msg; + glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + GLenum err = glGetError(); + if (err != GL_NO_ERROR) { + msg += String8::format("error reading pixel: %#x", err); + while ((err = glGetError()) != GL_NO_ERROR) { + msg += String8::format(", %#x", err); + } + return ::testing::AssertionFailure(::testing::Message(msg.string())); + } + if (r >= 0 && abs(r - int(pixel[0])) > tolerance) { + msg += String8::format("r(%d isn't %d)", pixel[0], r); + } + if (g >= 0 && abs(g - int(pixel[1])) > tolerance) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("g(%d isn't %d)", pixel[1], g); + } + if (b >= 0 && abs(b - int(pixel[2])) > tolerance) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("b(%d isn't %d)", pixel[2], b); + } + if (a >= 0 && abs(a - int(pixel[3])) > tolerance) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("a(%d isn't %d)", pixel[3], a); + } + if (!msg.isEmpty()) { + return ::testing::AssertionFailure(::testing::Message(msg.string())); + } else { + return ::testing::AssertionSuccess(); + } +} + +::testing::AssertionResult GLTest::assertRectEq(const Rect &r1, const Rect &r2, + int tolerance) { + String8 msg; + + if (abs(r1.left - r2.left) > tolerance) { + msg += String8::format("left(%d isn't %d)", r1.left, r2.left); + } + if (abs(r1.top - r2.top) > tolerance) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("top(%d isn't %d)", r1.top, r2.top); + } + if (abs(r1.right - r2.right) > tolerance) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("right(%d isn't %d)", r1.right, r2.right); + } + if (abs(r1.bottom - r2.bottom) > tolerance) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("bottom(%d isn't %d)", r1.bottom, r2.bottom); + } + if (!msg.isEmpty()) { + msg += String8::format(" R1: [%d %d %d %d] R2: [%d %d %d %d]", + r1.left, r1.top, r1.right, r1.bottom, + r2.left, r2.top, r2.right, r2.bottom); + fprintf(stderr, "assertRectEq: %s\n", msg.string()); + return ::testing::AssertionFailure(::testing::Message(msg.string())); + } else { + return ::testing::AssertionSuccess(); + } +} + +void GLTest::loadShader(GLenum shaderType, const char* pSource, + GLuint* outShader) { + GLuint shader = glCreateShader(shaderType); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (shader) { + glShaderSource(shader, 1, &pSource, NULL); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glCompileShader(shader); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (!compiled) { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (infoLen) { + char* buf = (char*) malloc(infoLen); + if (buf) { + glGetShaderInfoLog(shader, infoLen, NULL, buf); + printf("Shader compile log:\n%s\n", buf); + free(buf); + FAIL(); + } + } else { + char* buf = (char*) malloc(0x1000); + if (buf) { + glGetShaderInfoLog(shader, 0x1000, NULL, buf); + printf("Shader compile log:\n%s\n", buf); + free(buf); + FAIL(); + } + } + glDeleteShader(shader); + shader = 0; + } + } + ASSERT_TRUE(shader != 0); + *outShader = shader; +} + +void GLTest::createProgram(const char* pVertexSource, + const char* pFragmentSource, GLuint* outPgm) { + GLuint vertexShader, fragmentShader; + { + SCOPED_TRACE("compiling vertex shader"); + ASSERT_NO_FATAL_FAILURE(loadShader(GL_VERTEX_SHADER, pVertexSource, + &vertexShader)); + } + { + SCOPED_TRACE("compiling fragment shader"); + ASSERT_NO_FATAL_FAILURE(loadShader(GL_FRAGMENT_SHADER, pFragmentSource, + &fragmentShader)); + } + + GLuint program = glCreateProgram(); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (program) { + glAttachShader(program, vertexShader); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glAttachShader(program, fragmentShader); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glLinkProgram(program); + GLint linkStatus = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + if (linkStatus != GL_TRUE) { + GLint bufLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); + if (bufLength) { + char* buf = (char*) malloc(bufLength); + if (buf) { + glGetProgramInfoLog(program, bufLength, NULL, buf); + printf("Program link log:\n%s\n", buf); + free(buf); + FAIL(); + } + } + glDeleteProgram(program); + program = 0; + } + } + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + ASSERT_TRUE(program != 0); + *outPgm = program; +} + +} // namespace android diff --git a/libs/gui/tests/GLTest.h b/libs/gui/tests/GLTest.h new file mode 100644 index 0000000000..d3c4a951f3 --- /dev/null +++ b/libs/gui/tests/GLTest.h @@ -0,0 +1,70 @@ +/* + * Copyright 2013 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_GL_TEST_H +#define ANDROID_GL_TEST_H + +#include <gtest/gtest.h> + +#include <gui/SurfaceComposerClient.h> + +#include <EGL/egl.h> +#include <GLES/gl.h> + +namespace android { + +class GLTest : public ::testing::Test { +public: + static void loadShader(GLenum shaderType, const char* pSource, + GLuint* outShader); + static void createProgram(const char* pVertexSource, + const char* pFragmentSource, GLuint* outPgm); + +protected: + GLTest() : + mEglDisplay(EGL_NO_DISPLAY), + mEglSurface(EGL_NO_SURFACE), + mEglContext(EGL_NO_CONTEXT) { + } + + virtual void SetUp(); + virtual void TearDown(); + + virtual EGLint const* getConfigAttribs(); + virtual EGLint const* getContextAttribs(); + virtual EGLint getSurfaceWidth(); + virtual EGLint getSurfaceHeight(); + virtual EGLSurface createWindowSurface(EGLDisplay display, EGLConfig config, + sp<ANativeWindow>& window) const; + + ::testing::AssertionResult checkPixel(int x, int y, + int r, int g, int b, int a, int tolerance = 2); + ::testing::AssertionResult assertRectEq(const Rect &r1, const Rect &r2, + int tolerance = 1); + + int mDisplaySecs; + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mSurfaceControl; + + EGLDisplay mEglDisplay; + EGLSurface mEglSurface; + EGLContext mEglContext; + EGLConfig mGlConfig; +}; + +} // namespace android + +#endif diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp new file mode 100644 index 0000000000..d16177b14c --- /dev/null +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -0,0 +1,568 @@ +/* + * Copyright (C) 2013 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 "IGraphicBufferProducer_test" +//#define LOG_NDEBUG 0 + +#include <gtest/gtest.h> + +#include <utils/String8.h> +#include <utils/threads.h> + +#include <ui/GraphicBuffer.h> +#include <ui/FramebufferNativeWindow.h> + +#include <gui/BufferQueue.h> + +#include <vector> + +#define ASSERT_OK(x) ASSERT_EQ(OK, (x)) +#define EXPECT_OK(x) EXPECT_EQ(OK, (x)) + +#define TEST_TOKEN ((IBinder*)(NULL)) +#define TEST_API NATIVE_WINDOW_API_CPU +#define TEST_API_OTHER NATIVE_WINDOW_API_EGL // valid API that's not TEST_API +#define TEST_CONTROLLED_BY_APP false +#define TEST_PRODUCER_USAGE_BITS (0) + +// TODO: Make these public constants in a header +enum { + // Default dimensions before setDefaultBufferSize is called + DEFAULT_WIDTH = 1, + DEFAULT_HEIGHT = 1, + + // Default format before setDefaultBufferFormat is called + DEFAULT_FORMAT = HAL_PIXEL_FORMAT_RGBA_8888, + + // Default transform hint before setTransformHint is called + DEFAULT_TRANSFORM_HINT = 0, +}; + +namespace android { + +namespace { +// Parameters for a generic "valid" input for queueBuffer. +const int64_t QUEUE_BUFFER_INPUT_TIMESTAMP = 1384888611; +const bool QUEUE_BUFFER_INPUT_IS_AUTO_TIMESTAMP = false; +const Rect QUEUE_BUFFER_INPUT_RECT = Rect(DEFAULT_WIDTH, DEFAULT_HEIGHT); +const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0; +const int QUEUE_BUFFER_INPUT_TRANSFORM = 0; +const bool QUEUE_BUFFER_INPUT_ASYNC = false; +const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE; +}; // namespace anonymous + +struct DummyConsumer : public BnConsumerListener { + virtual void onFrameAvailable() {} + virtual void onBuffersReleased() {} +}; + +class IGraphicBufferProducerTest : public ::testing::Test { +protected: + + IGraphicBufferProducerTest() {} + + virtual void SetUp() { + const ::testing::TestInfo* const testInfo = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGV("Begin test: %s.%s", testInfo->test_case_name(), + testInfo->name()); + + mBQ = new BufferQueue(); + mDC = new DummyConsumer; + + mProducer = mBQ; + mConsumer = mBQ; + + // Test check: Can't connect producer if no consumer yet + ASSERT_EQ(NO_INIT, TryConnectProducer()); + + // Must connect consumer before producer connects will succeed. + ASSERT_OK(mConsumer->consumerConnect(mDC, /*controlledByApp*/false)); + } + + virtual void TearDown() { + const ::testing::TestInfo* const testInfo = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGV("End test: %s.%s", testInfo->test_case_name(), + testInfo->name()); + } + + status_t TryConnectProducer() { + IGraphicBufferProducer::QueueBufferOutput output; + return mProducer->connect(TEST_TOKEN, + TEST_API, + TEST_CONTROLLED_BY_APP, + &output); + // TODO: use params to vary token, api, producercontrolledbyapp, etc + } + + // Connect to a producer in a 'correct' fashion. + // Precondition: Consumer is connected. + void ConnectProducer() { + ASSERT_OK(TryConnectProducer()); + } + + // Create a generic "valid" input for queueBuffer + // -- uses the default buffer format, width, etc. + static IGraphicBufferProducer::QueueBufferInput CreateBufferInput() { + return QueueBufferInputBuilder().build(); + } + + // Builder pattern to slightly vary *almost* correct input + // -- avoids copying and pasting + struct QueueBufferInputBuilder { + QueueBufferInputBuilder() { + timestamp = QUEUE_BUFFER_INPUT_TIMESTAMP; + isAutoTimestamp = QUEUE_BUFFER_INPUT_IS_AUTO_TIMESTAMP; + crop = QUEUE_BUFFER_INPUT_RECT; + scalingMode = QUEUE_BUFFER_INPUT_SCALING_MODE; + transform = QUEUE_BUFFER_INPUT_TRANSFORM; + async = QUEUE_BUFFER_INPUT_ASYNC; + fence = QUEUE_BUFFER_INPUT_FENCE; + } + + IGraphicBufferProducer::QueueBufferInput build() { + return IGraphicBufferProducer::QueueBufferInput( + timestamp, + isAutoTimestamp, + crop, + scalingMode, + transform, + async, + fence); + } + + QueueBufferInputBuilder& setTimestamp(int64_t timestamp) { + this->timestamp = timestamp; + return *this; + } + + QueueBufferInputBuilder& setIsAutoTimestamp(bool isAutoTimestamp) { + this->isAutoTimestamp = isAutoTimestamp; + return *this; + } + + QueueBufferInputBuilder& setCrop(Rect crop) { + this->crop = crop; + return *this; + } + + QueueBufferInputBuilder& setScalingMode(int scalingMode) { + this->scalingMode = scalingMode; + return *this; + } + + QueueBufferInputBuilder& setTransform(uint32_t transform) { + this->transform = transform; + return *this; + } + + QueueBufferInputBuilder& setAsync(bool async) { + this->async = async; + return *this; + } + + QueueBufferInputBuilder& setFence(sp<Fence> fence) { + this->fence = fence; + return *this; + } + + private: + int64_t timestamp; + bool isAutoTimestamp; + Rect crop; + int scalingMode; + uint32_t transform; + int async; + sp<Fence> fence; + }; // struct QueueBufferInputBuilder + + // To easily store dequeueBuffer results into containers + struct DequeueBufferResult { + int slot; + sp<Fence> fence; + }; + + status_t dequeueBuffer(bool async, uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) { + return mProducer->dequeueBuffer(&result->slot, &result->fence, async, w, h, format, usage); + } + +private: // hide from test body + sp<BufferQueue> mBQ; + sp<DummyConsumer> mDC; + +protected: // accessible from test body + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; +}; + +TEST_F(IGraphicBufferProducerTest, ConnectFirst_ReturnsError) { + IGraphicBufferProducer::QueueBufferOutput output; + + // NULL output returns BAD_VALUE + EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, + TEST_API, + TEST_CONTROLLED_BY_APP, + /*output*/NULL)); + + // Invalid API returns bad value + EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, + /*api*/0xDEADBEEF, + TEST_CONTROLLED_BY_APP, + &output)); + + // TODO: get a token from a dead process somehow +} + +TEST_F(IGraphicBufferProducerTest, ConnectAgain_ReturnsError) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + // Can't connect when there is already a producer connected + IGraphicBufferProducer::QueueBufferOutput output; + EXPECT_EQ(BAD_VALUE, mProducer->connect(TEST_TOKEN, + TEST_API, + TEST_CONTROLLED_BY_APP, + &output)); + + ASSERT_OK(mConsumer->consumerDisconnect()); + // Can't connect when IGBP is abandoned + EXPECT_EQ(NO_INIT, mProducer->connect(TEST_TOKEN, + TEST_API, + TEST_CONTROLLED_BY_APP, + &output)); +} + +TEST_F(IGraphicBufferProducerTest, Disconnect_Succeeds) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + ASSERT_OK(mProducer->disconnect(TEST_API)); +} + + +TEST_F(IGraphicBufferProducerTest, Disconnect_ReturnsError) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + // Must disconnect with same API number + ASSERT_EQ(BAD_VALUE, mProducer->disconnect(TEST_API_OTHER)); + // API must not be out of range + ASSERT_EQ(BAD_VALUE, mProducer->disconnect(/*api*/0xDEADBEEF)); + + // TODO: somehow kill mProducer so that this returns DEAD_OBJECT +} + +TEST_F(IGraphicBufferProducerTest, Query_Succeeds) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + // TODO: Make these constants in header + const int DEFAULT_CONSUMER_USAGE_BITS = 0; + + int value = -1; + EXPECT_OK(mProducer->query(NATIVE_WINDOW_WIDTH, &value)); + EXPECT_EQ(DEFAULT_WIDTH, value); + + EXPECT_OK(mProducer->query(NATIVE_WINDOW_HEIGHT, &value)); + EXPECT_EQ(DEFAULT_HEIGHT, value); + + EXPECT_OK(mProducer->query(NATIVE_WINDOW_FORMAT, &value)); + EXPECT_EQ(DEFAULT_FORMAT, value); + + EXPECT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &value)); + EXPECT_LE(0, value); + EXPECT_GE(BufferQueue::NUM_BUFFER_SLOTS, value); + + EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, &value)); + EXPECT_FALSE(value); // Can't run behind when we haven't touched the queue + + EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value)); + EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, value); + +} + +TEST_F(IGraphicBufferProducerTest, Query_ReturnsError) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + // One past the end of the last 'query' enum value. Update this if we add more enums. + const int NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE = NATIVE_WINDOW_CONSUMER_USAGE_BITS + 1; + + int value; + // What was out of range + EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/-1, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(/*what*/0xDEADBEEF, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, &value)); + + // Some enums from window.h are 'invalid' + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_CONCRETE_TYPE, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_WIDTH, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_DEFAULT_HEIGHT, &value)); + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value)); + // TODO: Consider documented the above enums as unsupported or make a new enum for IGBP + + // Value was NULL + EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/NULL)); + + ASSERT_OK(mConsumer->consumerDisconnect()); + + // BQ was abandoned + EXPECT_EQ(NO_INIT, mProducer->query(NATIVE_WINDOW_FORMAT, &value)); + + // TODO: other things in window.h that are supported by Surface::query + // but not by BufferQueue::query +} + +// TODO: queue under more complicated situations not involving just a single buffer +TEST_F(IGraphicBufferProducerTest, Queue_Succeeds) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + int dequeuedSlot = -1; + sp<Fence> dequeuedFence; + + // XX: OK to assume first call returns this flag or not? Not really documented. + ASSERT_EQ(OK | IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, + QUEUE_BUFFER_INPUT_ASYNC, + DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS)); + + EXPECT_LE(0, dequeuedSlot); + EXPECT_GT(BufferQueue::NUM_BUFFER_SLOTS, dequeuedSlot); + + // Request the buffer (pre-requisite for queueing) + sp<GraphicBuffer> dequeuedBuffer; + ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer)); + + // A generic "valid" input + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + + // Queue the buffer back into the BQ + ASSERT_OK(mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { + uint32_t width; + uint32_t height; + uint32_t transformHint; + uint32_t numPendingBuffers; + + output.deflate(&width, &height, &transformHint, &numPendingBuffers); + + EXPECT_EQ(DEFAULT_WIDTH, width); + EXPECT_EQ(DEFAULT_HEIGHT, height); + EXPECT_EQ(DEFAULT_TRANSFORM_HINT, transformHint); + EXPECT_EQ(1, numPendingBuffers); // since queueBuffer was called exactly once + } + + // Buffer was not in the dequeued state + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); +} + +TEST_F(IGraphicBufferProducerTest, Queue_ReturnsError) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + // Invalid slot number + { + // A generic "valid" input + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/-1, input, &output)); + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0xDEADBEEF, input, &output)); + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueue::NUM_BUFFER_SLOTS, + input, &output)); + } + + // Slot was not in the dequeued state (all slots start out in Free state) + { + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0, input, &output)); + } + + // Put the slot into the "dequeued" state for the rest of the test + int dequeuedSlot = -1; + sp<Fence> dequeuedFence; + + ASSERT_EQ(OK | IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, + QUEUE_BUFFER_INPUT_ASYNC, + DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS)); + + // Slot was enqueued without requesting a buffer + { + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + } + + // Request the buffer so that the rest of the tests don't fail on earlier checks. + sp<GraphicBuffer> dequeuedBuffer; + ASSERT_OK(mProducer->requestBuffer(dequeuedSlot, &dequeuedBuffer)); + + // Fence was NULL + { + sp<Fence> nullFence = NULL; + + IGraphicBufferProducer::QueueBufferInput input = + QueueBufferInputBuilder().setFence(nullFence).build(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + } + + // Scaling mode was unknown + { + IGraphicBufferProducer::QueueBufferInput input = + QueueBufferInputBuilder().setScalingMode(-1).build(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build(); + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + } + + // Crop rect is out of bounds of the buffer dimensions + { + IGraphicBufferProducer::QueueBufferInput input = + QueueBufferInputBuilder().setCrop(Rect(DEFAULT_WIDTH + 1, DEFAULT_HEIGHT + 1)) + .build(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + } + + // Abandon the buffer queue so that the last test fails + ASSERT_OK(mConsumer->consumerDisconnect()); + + // The buffer queue has been abandoned. + { + IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); + IGraphicBufferProducer::QueueBufferOutput output; + + EXPECT_EQ(NO_INIT, mProducer->queueBuffer(dequeuedSlot, input, &output)); + } +} + +TEST_F(IGraphicBufferProducerTest, CancelBuffer_DoesntCrash) { + ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + + int dequeuedSlot = -1; + sp<Fence> dequeuedFence; + + ASSERT_EQ(OK | IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, + QUEUE_BUFFER_INPUT_ASYNC, + DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS)); + + // No return code, but at least test that it doesn't blow up... + // TODO: add a return code + mProducer->cancelBuffer(dequeuedSlot, dequeuedFence); +} + +TEST_F(IGraphicBufferProducerTest, SetBufferCount_Succeeds) { + + // The producer does not wish to set a buffer count + EXPECT_OK(mProducer->setBufferCount(0)) << "bufferCount: " << 0; + // TODO: how to test "0" buffer count? + + int minBuffers; + ASSERT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minBuffers)); + + // The MIN_UNDEQUEUED_BUFFERS limit is exclusive, so need to increment by at least 1 + minBuffers++; + + ASSERT_OK(mProducer->setBufferCount(minBuffers)) << "bufferCount: " << minBuffers; + + std::vector<DequeueBufferResult> dequeueList; + + // Should now be able to dequeue up to minBuffers times + for (int i = 0; i < minBuffers; ++i) { + DequeueBufferResult result; + + EXPECT_LE(OK, + dequeueBuffer(QUEUE_BUFFER_INPUT_ASYNC, + DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS, &result)) + << "iteration: " << i << ", slot: " << result.slot; + + dequeueList.push_back(result); + } + + // Cancel every buffer, so we can set buffer count again + for (int i = 0; i < minBuffers; ++i) { + DequeueBufferResult& result = dequeueList[i]; + mProducer->cancelBuffer(result.slot, result.fence); + } + + ASSERT_OK(mProducer->setBufferCount(BufferQueue::NUM_BUFFER_SLOTS)); + + // Should now be able to dequeue up to NUM_BUFFER_SLOTS times + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; ++i) { + int dequeuedSlot = -1; + sp<Fence> dequeuedFence; + + EXPECT_LE(OK, + mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, + QUEUE_BUFFER_INPUT_ASYNC, + DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS)) + << "iteration: " << i << ", slot: " << dequeuedSlot; + } +} + +TEST_F(IGraphicBufferProducerTest, SetBufferCount_Fails) { + int minBuffers; + ASSERT_OK(mProducer->query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minBuffers)); + + // The MIN_UNDEQUEUED_BUFFERS limit is exclusive, so need to increment by at least 1 + minBuffers++; + + // Buffer count was out of range + EXPECT_EQ(BAD_VALUE, mProducer->setBufferCount(-1)) << "bufferCount: " << -1; + EXPECT_EQ(BAD_VALUE, mProducer->setBufferCount(minBuffers - 1)) << "bufferCount: " << minBuffers - 1; + EXPECT_EQ(BAD_VALUE, mProducer->setBufferCount(BufferQueue::NUM_BUFFER_SLOTS + 1)) + << "bufferCount: " << BufferQueue::NUM_BUFFER_SLOTS + 1; + + // Pre-requisite to fail out a valid setBufferCount call + { + int dequeuedSlot = -1; + sp<Fence> dequeuedFence; + + ASSERT_LE(OK, + mProducer->dequeueBuffer(&dequeuedSlot, &dequeuedFence, + QUEUE_BUFFER_INPUT_ASYNC, + DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, + TEST_PRODUCER_USAGE_BITS)) + << "slot: " << dequeuedSlot; + } + + // Client has one or more buffers dequeued + EXPECT_EQ(BAD_VALUE, mProducer->setBufferCount(minBuffers)) << "bufferCount: " << minBuffers; + + // Abandon buffer queue + ASSERT_OK(mConsumer->consumerDisconnect()); + + // Fail because the buffer queue was abandoned + EXPECT_EQ(NO_INIT, mProducer->setBufferCount(minBuffers)) << "bufferCount: " << minBuffers; + +} + +} // namespace android diff --git a/libs/gui/tests/MultiTextureConsumer_test.cpp b/libs/gui/tests/MultiTextureConsumer_test.cpp new file mode 100644 index 0000000000..853c25caed --- /dev/null +++ b/libs/gui/tests/MultiTextureConsumer_test.cpp @@ -0,0 +1,123 @@ +/* + * Copyright 2013 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 "MultiTextureConsumer_test" +//#define LOG_NDEBUG 0 + +#include "GLTest.h" + +#include <gui/GLConsumer.h> +#include <gui/Surface.h> + +#include <android/native_window.h> + +#include <GLES/glext.h> + +namespace android { + +class MultiTextureConsumerTest : public GLTest { +protected: + enum { TEX_ID = 123 }; + + virtual void SetUp() { + GLTest::SetUp(); + sp<BufferQueue> bq = new BufferQueue(); + mGlConsumer = new GLConsumer(bq, TEX_ID); + mSurface = new Surface(bq); + mANW = mSurface.get(); + + } + virtual void TearDown() { + GLTest::TearDown(); + } + virtual EGLint const* getContextAttribs() { + return NULL; + } + virtual EGLint const* getConfigAttribs() { + static EGLint sDefaultConfigAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE }; + + return sDefaultConfigAttribs; + } + sp<GLConsumer> mGlConsumer; + sp<Surface> mSurface; + ANativeWindow* mANW; +}; + +TEST_F(MultiTextureConsumerTest, EGLImageTargetWorks) { + ANativeWindow_Buffer buffer; + + ASSERT_EQ(native_window_set_usage(mANW, GRALLOC_USAGE_SW_WRITE_OFTEN), NO_ERROR); + ASSERT_EQ(native_window_set_buffers_format(mANW, HAL_PIXEL_FORMAT_RGBA_8888), NO_ERROR); + + glShadeModel(GL_FLAT); + glDisable(GL_DITHER); + glDisable(GL_CULL_FACE); + glViewport(0, 0, getSurfaceWidth(), getSurfaceHeight()); + glOrthof(0, getSurfaceWidth(), 0, getSurfaceHeight(), 0, 1); + glEnableClientState(GL_VERTEX_ARRAY); + glColor4f(1, 1, 1, 1); + + glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID); + glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + uint32_t texel = 0x80808080; + glBindTexture(GL_TEXTURE_2D, TEX_ID+1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texel); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, TEX_ID+1); + glEnable(GL_TEXTURE_2D); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID); + glEnable(GL_TEXTURE_EXTERNAL_OES); + glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + glClear(GL_COLOR_BUFFER_BIT); + for (int i=0 ; i<8 ; i++) { + mSurface->lock(&buffer, NULL); + memset(buffer.bits, (i&7) * 0x20, buffer.stride * buffer.height * 4); + mSurface->unlockAndPost(); + + mGlConsumer->updateTexImage(); + + GLfloat vertices[][2] = { {i*16.0f, 0}, {(i+1)*16.0f, 0}, {(i+1)*16.0f, 16.0f}, {i*16.0f, 16.0f} }; + glVertexPointer(2, GL_FLOAT, 0, vertices); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + } + + for (int i=0 ; i<8 ; i++) { + EXPECT_TRUE(checkPixel(i*16 + 8, 8, i*16, i*16, i*16, i*16, 0)); + } +} + +} // namespace android diff --git a/libs/gui/tests/SRGB_test.cpp b/libs/gui/tests/SRGB_test.cpp new file mode 100644 index 0000000000..1077c9d8e9 --- /dev/null +++ b/libs/gui/tests/SRGB_test.cpp @@ -0,0 +1,476 @@ +/* + * Copyright 2013 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 "SRGB_test" +//#define LOG_NDEBUG 0 + +#include "GLTest.h" + +#include <gui/CpuConsumer.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <GLES3/gl3.h> + +#include <android/native_window.h> + +#include <gtest/gtest.h> + +namespace android { + +class SRGBTest : public ::testing::Test { +protected: + // Class constants + enum { + DISPLAY_WIDTH = 512, + DISPLAY_HEIGHT = 512, + PIXEL_SIZE = 4, // bytes or components + DISPLAY_SIZE = DISPLAY_WIDTH * DISPLAY_HEIGHT * PIXEL_SIZE, + ALPHA_VALUE = 223, // should be in [0, 255] + TOLERANCE = 1, + }; + static const char SHOW_DEBUG_STRING[]; + + SRGBTest() : + mInputSurface(), mCpuConsumer(), mLockedBuffer(), + mEglDisplay(EGL_NO_DISPLAY), mEglConfig(), + mEglContext(EGL_NO_CONTEXT), mEglSurface(EGL_NO_SURFACE), + mComposerClient(), mSurfaceControl(), mOutputSurface() { + } + + virtual ~SRGBTest() { + if (mEglDisplay != EGL_NO_DISPLAY) { + if (mEglSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEglDisplay, mEglSurface); + } + if (mEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mEglContext); + } + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + eglTerminate(mEglDisplay); + } + } + + virtual void SetUp() { + mBufferQueue = new BufferQueue(); + ASSERT_EQ(NO_ERROR, mBufferQueue->setDefaultBufferSize( + DISPLAY_WIDTH, DISPLAY_HEIGHT)); + mCpuConsumer = new CpuConsumer(mBufferQueue, 1); + String8 name("CpuConsumer_for_SRGBTest"); + mCpuConsumer->setName(name); + mInputSurface = new Surface(mBufferQueue); + + ASSERT_NO_FATAL_FAILURE(createEGLSurface(mInputSurface.get())); + ASSERT_NO_FATAL_FAILURE(createDebugSurface()); + } + + virtual void TearDown() { + ASSERT_NO_FATAL_FAILURE(copyToDebugSurface()); + ASSERT_TRUE(mLockedBuffer.data != NULL); + ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer)); + } + + static float linearToSRGB(float l) { + if (l <= 0.0031308f) { + return l * 12.92f; + } else { + return 1.055f * pow(l, (1 / 2.4f)) - 0.055f; + } + } + + static float srgbToLinear(float s) { + if (s <= 0.04045) { + return s / 12.92f; + } else { + return pow(((s + 0.055f) / 1.055f), 2.4f); + } + } + + static uint8_t srgbToLinear(uint8_t u) { + float f = u / 255.0f; + return static_cast<uint8_t>(srgbToLinear(f) * 255.0f + 0.5f); + } + + void fillTexture(bool writeAsSRGB) { + uint8_t* textureData = new uint8_t[DISPLAY_SIZE]; + + for (int y = 0; y < DISPLAY_HEIGHT; ++y) { + for (int x = 0; x < DISPLAY_WIDTH; ++x) { + float realValue = static_cast<float>(x) / (DISPLAY_WIDTH - 1); + realValue *= ALPHA_VALUE / 255.0f; // Premultiply by alpha + if (writeAsSRGB) { + realValue = linearToSRGB(realValue); + } + + int offset = (y * DISPLAY_WIDTH + x) * PIXEL_SIZE; + for (int c = 0; c < 3; ++c) { + uint8_t intValue = static_cast<uint8_t>( + realValue * 255.0f + 0.5f); + textureData[offset + c] = intValue; + } + textureData[offset + 3] = ALPHA_VALUE; + } + } + + glTexImage2D(GL_TEXTURE_2D, 0, writeAsSRGB ? GL_SRGB8_ALPHA8 : GL_RGBA8, + DISPLAY_WIDTH, DISPLAY_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, + textureData); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + + delete[] textureData; + } + + void initShaders() { + static const char vertexSource[] = + "attribute vec4 vPosition;\n" + "varying vec2 texCoords;\n" + "void main() {\n" + " texCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n" + " gl_Position = vPosition;\n" + "}\n"; + + static const char fragmentSource[] = + "precision mediump float;\n" + "uniform sampler2D texSampler;\n" + "varying vec2 texCoords;\n" + "void main() {\n" + " gl_FragColor = texture2D(texSampler, texCoords);\n" + "}\n"; + + GLuint program; + { + SCOPED_TRACE("Creating shader program"); + ASSERT_NO_FATAL_FAILURE(GLTest::createProgram( + vertexSource, fragmentSource, &program)); + } + + GLint positionHandle = glGetAttribLocation(program, "vPosition"); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + ASSERT_NE(-1, positionHandle); + + GLint samplerHandle = glGetUniformLocation(program, "texSampler"); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + ASSERT_NE(-1, samplerHandle); + + static const GLfloat vertices[] = { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f, + 1.0f, 1.0f, + }; + + glVertexAttribPointer(positionHandle, 2, GL_FLOAT, GL_FALSE, 0, vertices); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + glEnableVertexAttribArray(positionHandle); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + + glUseProgram(program); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + glUniform1i(samplerHandle, 0); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + + GLuint textureHandle; + glGenTextures(1, &textureHandle); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + glBindTexture(GL_TEXTURE_2D, textureHandle); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + } + + void drawTexture(bool asSRGB, GLint x, GLint y, GLsizei width, + GLsizei height) { + ASSERT_NO_FATAL_FAILURE(fillTexture(asSRGB)); + glViewport(x, y, width, height); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + ASSERT_EQ(GL_NO_ERROR, glGetError()); + } + + void checkLockedBuffer(PixelFormat format) { + ASSERT_EQ(mLockedBuffer.format, format); + ASSERT_EQ(mLockedBuffer.width, DISPLAY_WIDTH); + ASSERT_EQ(mLockedBuffer.height, DISPLAY_HEIGHT); + } + + static bool withinTolerance(int a, int b) { + int diff = a - b; + return diff >= 0 ? diff <= TOLERANCE : -diff <= TOLERANCE; + } + + // Primary producer and consumer + sp<BufferQueue> mBufferQueue; + sp<Surface> mInputSurface; + sp<CpuConsumer> mCpuConsumer; + CpuConsumer::LockedBuffer mLockedBuffer; + + EGLDisplay mEglDisplay; + EGLConfig mEglConfig; + EGLContext mEglContext; + EGLSurface mEglSurface; + + // Auxiliary display output + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mSurfaceControl; + sp<Surface> mOutputSurface; + +private: + void createEGLSurface(Surface* inputSurface) { + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); + + EXPECT_TRUE(eglInitialize(mEglDisplay, NULL, NULL)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + static const EGLint configAttribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_NONE }; + + EGLint numConfigs = 0; + EXPECT_TRUE(eglChooseConfig(mEglDisplay, configAttribs, &mEglConfig, 1, + &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_GT(numConfigs, 0); + + static const EGLint contextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 3, + EGL_NONE } ; + + mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, + contextAttribs); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mEglContext); + + mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, + inputSurface, NULL); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mEglSurface); + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + } + + void createDebugSurface() { + if (getenv(SHOW_DEBUG_STRING) == NULL) return; + + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + mSurfaceControl = mComposerClient->createSurface( + String8("SRGBTest Surface"), DISPLAY_WIDTH, DISPLAY_HEIGHT, + PIXEL_FORMAT_RGBA_8888); + + ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl->isValid()); + + SurfaceComposerClient::openGlobalTransaction(); + ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); + ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); + SurfaceComposerClient::closeGlobalTransaction(); + + ANativeWindow_Buffer outBuffer; + ARect inOutDirtyBounds; + mOutputSurface = mSurfaceControl->getSurface(); + mOutputSurface->lock(&outBuffer, &inOutDirtyBounds); + uint8_t* bytePointer = reinterpret_cast<uint8_t*>(outBuffer.bits); + for (int y = 0; y < outBuffer.height; ++y) { + int rowOffset = y * outBuffer.stride; // pixels + for (int x = 0; x < outBuffer.width; ++x) { + int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes + for (int c = 0; c < PIXEL_SIZE; ++c) { + int offset = colOffset + c; + bytePointer[offset] = ((c + 1) * 56) - 1; + } + } + } + mOutputSurface->unlockAndPost(); + } + + void copyToDebugSurface() { + if (!mOutputSurface.get()) return; + + size_t bufferSize = mLockedBuffer.height * mLockedBuffer.stride * + PIXEL_SIZE; + + ANativeWindow_Buffer outBuffer; + ARect outBufferBounds; + mOutputSurface->lock(&outBuffer, &outBufferBounds); + ASSERT_EQ(mLockedBuffer.width, outBuffer.width); + ASSERT_EQ(mLockedBuffer.height, outBuffer.height); + ASSERT_EQ(mLockedBuffer.stride, outBuffer.stride); + + if (mLockedBuffer.format == outBuffer.format) { + memcpy(outBuffer.bits, mLockedBuffer.data, bufferSize); + } else { + ASSERT_EQ(mLockedBuffer.format, PIXEL_FORMAT_sRGB_A_8888); + ASSERT_EQ(outBuffer.format, PIXEL_FORMAT_RGBA_8888); + uint8_t* outPointer = reinterpret_cast<uint8_t*>(outBuffer.bits); + for (int y = 0; y < outBuffer.height; ++y) { + int rowOffset = y * outBuffer.stride; // pixels + for (int x = 0; x < outBuffer.width; ++x) { + int colOffset = (rowOffset + x) * PIXEL_SIZE; // bytes + + // RGB are converted + for (int c = 0; c < (PIXEL_SIZE - 1); ++c) { + outPointer[colOffset + c] = srgbToLinear( + mLockedBuffer.data[colOffset + c]); + } + + // Alpha isn't converted + outPointer[colOffset + 3] = + mLockedBuffer.data[colOffset + 3]; + } + } + } + mOutputSurface->unlockAndPost(); + + int sleepSeconds = atoi(getenv(SHOW_DEBUG_STRING)); + sleep(sleepSeconds); + } +}; + +const char SRGBTest::SHOW_DEBUG_STRING[] = "DEBUG_OUTPUT_SECONDS"; + +TEST_F(SRGBTest, GLRenderFromSRGBTexture) { + ASSERT_NO_FATAL_FAILURE(initShaders()); + + // The RGB texture is displayed in the top half + ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, DISPLAY_HEIGHT / 2, + DISPLAY_WIDTH, DISPLAY_HEIGHT / 2)); + + // The SRGB texture is displayed in the bottom half + ASSERT_NO_FATAL_FAILURE(drawTexture(true, 0, 0, + DISPLAY_WIDTH, DISPLAY_HEIGHT / 2)); + + eglSwapBuffers(mEglDisplay, mEglSurface); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Lock + ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer)); + ASSERT_NO_FATAL_FAILURE(checkLockedBuffer(PIXEL_FORMAT_RGBA_8888)); + + // Compare a pixel in the middle of each texture + int midSRGBOffset = (DISPLAY_HEIGHT / 4) * mLockedBuffer.stride * + PIXEL_SIZE; + int midRGBOffset = midSRGBOffset * 3; + midRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE; + midSRGBOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE; + for (int c = 0; c < PIXEL_SIZE; ++c) { + int expectedValue = mLockedBuffer.data[midRGBOffset + c]; + int actualValue = mLockedBuffer.data[midSRGBOffset + c]; + ASSERT_PRED2(withinTolerance, expectedValue, actualValue); + } + + // mLockedBuffer is unlocked in TearDown so we can copy data from it to + // the debug surface if necessary +} + +TEST_F(SRGBTest, RenderToSRGBSurface) { + ASSERT_NO_FATAL_FAILURE(initShaders()); + + // By default, the first buffer we write into will be RGB + + // Render an RGB texture across the whole surface + ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0, + DISPLAY_WIDTH, DISPLAY_HEIGHT)); + eglSwapBuffers(mEglDisplay, mEglSurface); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Lock + ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer)); + ASSERT_NO_FATAL_FAILURE(checkLockedBuffer(PIXEL_FORMAT_RGBA_8888)); + + // Save the values of the middle pixel for later comparison against SRGB + uint8_t values[PIXEL_SIZE] = {}; + int middleOffset = (DISPLAY_HEIGHT / 2) * mLockedBuffer.stride * + PIXEL_SIZE; + middleOffset += (DISPLAY_WIDTH / 2) * PIXEL_SIZE; + for (int c = 0; c < PIXEL_SIZE; ++c) { + values[c] = mLockedBuffer.data[middleOffset + c]; + } + + // Unlock + ASSERT_EQ(NO_ERROR, mCpuConsumer->unlockBuffer(mLockedBuffer)); + + // Switch to SRGB window surface +#define EGL_GL_COLORSPACE_KHR EGL_VG_COLORSPACE +#define EGL_GL_COLORSPACE_SRGB_KHR EGL_VG_COLORSPACE_sRGB + + static const int srgbAttribs[] = { + EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, + EGL_NONE, + }; + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EXPECT_TRUE(eglDestroySurface(mEglDisplay, mEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, + mInputSurface.get(), srgbAttribs); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mEglSurface); + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Render the texture again + ASSERT_NO_FATAL_FAILURE(drawTexture(false, 0, 0, + DISPLAY_WIDTH, DISPLAY_HEIGHT)); + eglSwapBuffers(mEglDisplay, mEglSurface); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Lock + ASSERT_EQ(NO_ERROR, mCpuConsumer->lockNextBuffer(&mLockedBuffer)); + + // Make sure we actually got the SRGB buffer on the consumer side + ASSERT_NO_FATAL_FAILURE(checkLockedBuffer(PIXEL_FORMAT_sRGB_A_8888)); + + // Verify that the stored value is the same, accounting for RGB/SRGB + for (int c = 0; c < PIXEL_SIZE; ++c) { + // The alpha value should be equivalent before linear->SRGB + float rgbAsSRGB = (c == 3) ? values[c] / 255.0f : + linearToSRGB(values[c] / 255.0f); + int expectedValue = rgbAsSRGB * 255.0f + 0.5f; + int actualValue = mLockedBuffer.data[middleOffset + c]; + ASSERT_PRED2(withinTolerance, expectedValue, actualValue); + } + + // mLockedBuffer is unlocked in TearDown so we can copy data from it to + // the debug surface if necessary +} + +} // namespace android diff --git a/libs/gui/tests/SurfaceTextureFBO.h b/libs/gui/tests/SurfaceTextureFBO.h new file mode 100644 index 0000000000..7f1ae84c48 --- /dev/null +++ b/libs/gui/tests/SurfaceTextureFBO.h @@ -0,0 +1,75 @@ +/* + * Copyright 2013 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_SURFACE_TEXTURE_FBO_H +#define ANDROID_SURFACE_TEXTURE_FBO_H + +#include "SurfaceTextureGL.h" + +#include <GLES2/gl2.h> + +namespace android { + +class SurfaceTextureFBOTest : public SurfaceTextureGLTest { +protected: + virtual void SetUp() { + SurfaceTextureGLTest::SetUp(); + + glGenFramebuffers(1, &mFbo); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + glGenTextures(1, &mFboTex); + glBindTexture(GL_TEXTURE_2D, mFboTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(), + getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, mFboTex, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + } + + virtual void TearDown() { + SurfaceTextureGLTest::TearDown(); + + glDeleteTextures(1, &mFboTex); + glDeleteFramebuffers(1, &mFbo); + } + + GLuint mFbo; + GLuint mFboTex; +}; + +void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, + uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + const size_t PIXEL_SIZE = 4; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + off_t offset = (y * stride + x) * PIXEL_SIZE; + buf[offset + 0] = r; + buf[offset + 1] = g; + buf[offset + 2] = b; + buf[offset + 3] = a; + } + } +} + +} // namespace android + +#endif diff --git a/libs/gui/tests/SurfaceTextureFBO_test.cpp b/libs/gui/tests/SurfaceTextureFBO_test.cpp new file mode 100644 index 0000000000..b165ae6609 --- /dev/null +++ b/libs/gui/tests/SurfaceTextureFBO_test.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2013 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 "SurfaceTextureFBO_test" +//#define LOG_NDEBUG 0 + +#include "SurfaceTextureFBO.h" + +namespace android { + +// This test is intended to verify that proper synchronization is done when +// rendering into an FBO. +TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { + const int texWidth = 64; + const int texHeight = 64; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + android_native_buffer_t* anb; + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + + // Fill the buffer with green + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255, + 0, 255); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), + -1)); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + drawTexture(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + for (int i = 0; i < 4; i++) { + SCOPED_TRACE(String8::format("frame %d", i).string()); + + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); + ASSERT_TRUE(anb != NULL); + + buf = new GraphicBuffer(anb, false); + + // Fill the buffer with red + ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, + (void**)(&img))); + fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 255, 0, + 0, 255); + ASSERT_EQ(NO_ERROR, buf->unlock()); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), + buf->getNativeBuffer(), -1)); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + drawTexture(); + + EXPECT_TRUE(checkPixel( 24, 39, 255, 0, 0, 255)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + + EXPECT_TRUE(checkPixel( 24, 39, 0, 255, 0, 255)); +} + +} // namespace android diff --git a/libs/gui/tests/SurfaceTextureGL.h b/libs/gui/tests/SurfaceTextureGL.h new file mode 100644 index 0000000000..ac112c4462 --- /dev/null +++ b/libs/gui/tests/SurfaceTextureGL.h @@ -0,0 +1,73 @@ +/* + * Copyright 2013 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_SURFACE_TEXTURE_GL_H +#define ANDROID_SURFACE_TEXTURE_GL_H + +#include "GLTest.h" + +#include "FrameWaiter.h" +#include "TextureRenderer.h" + +#include <gui/GLConsumer.h> +#include <gui/Surface.h> + +namespace android { + +class FrameWaiter; +class GLConsumer; +class TextureRenderer; + +class SurfaceTextureGLTest : public GLTest { +protected: + enum { TEX_ID = 123 }; + + void SetUp() { + GLTest::SetUp(); + sp<BufferQueue> bq = new BufferQueue(); + mBQ = bq; + mST = new GLConsumer(bq, TEX_ID); + mSTC = new Surface(bq); + mANW = mSTC; + mTextureRenderer = new TextureRenderer(TEX_ID, mST); + ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp()); + mFW = new FrameWaiter; + mST->setFrameAvailableListener(mFW); + } + + void TearDown() { + mTextureRenderer.clear(); + mANW.clear(); + mSTC.clear(); + mST.clear(); + GLTest::TearDown(); + } + + void drawTexture() { + mTextureRenderer->drawTexture(); + } + + sp<BufferQueue> mBQ; + sp<GLConsumer> mST; + sp<Surface> mSTC; + sp<ANativeWindow> mANW; + sp<TextureRenderer> mTextureRenderer; + sp<FrameWaiter> mFW; +}; + +} // namespace android + +#endif diff --git a/libs/gui/tests/SurfaceTextureGLThreadToGL.h b/libs/gui/tests/SurfaceTextureGLThreadToGL.h new file mode 100644 index 0000000000..6410516d27 --- /dev/null +++ b/libs/gui/tests/SurfaceTextureGLThreadToGL.h @@ -0,0 +1,183 @@ +/* + * Copyright 2013 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_SURFACE_TEXTURE_GL_THREAD_TO_GL_H +#define ANDROID_SURFACE_TEXTURE_GL_THREAD_TO_GL_H + +#include "SurfaceTextureGLToGL.h" + +namespace android { + +/* + * This test fixture is for testing GL -> GL texture streaming from one thread + * to another. It contains functionality to create a producer thread that will + * perform GL rendering to an ANativeWindow that feeds frames to a + * GLConsumer. Additionally it supports interlocking the producer and + * consumer threads so that a specific sequence of calls can be + * deterministically created by the test. + * + * The intended usage is as follows: + * + * TEST_F(...) { + * class PT : public ProducerThread { + * virtual void render() { + * ... + * swapBuffers(); + * } + * }; + * + * runProducerThread(new PT()); + * + * // The order of these calls will vary from test to test and may include + * // multiple frames and additional operations (e.g. GL rendering from the + * // texture). + * fc->waitForFrame(); + * mST->updateTexImage(); + * fc->finishFrame(); + * } + * + */ +class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest { +protected: + + // ProducerThread is an abstract base class to simplify the creation of + // OpenGL ES frame producer threads. + class ProducerThread : public Thread { + public: + virtual ~ProducerThread() { + } + + void setEglObjects(EGLDisplay producerEglDisplay, + EGLSurface producerEglSurface, + EGLContext producerEglContext) { + mProducerEglDisplay = producerEglDisplay; + mProducerEglSurface = producerEglSurface; + mProducerEglContext = producerEglContext; + } + + virtual bool threadLoop() { + eglMakeCurrent(mProducerEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext); + render(); + eglMakeCurrent(mProducerEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + return false; + } + + protected: + virtual void render() = 0; + + void swapBuffers() { + eglSwapBuffers(mProducerEglDisplay, mProducerEglSurface); + } + + EGLDisplay mProducerEglDisplay; + EGLSurface mProducerEglSurface; + EGLContext mProducerEglContext; + }; + + // FrameCondition is a utility class for interlocking between the producer + // and consumer threads. The FrameCondition object should be created and + // destroyed in the consumer thread only. The consumer thread should set + // the FrameCondition as the FrameAvailableListener of the GLConsumer, + // and should call both waitForFrame and finishFrame once for each expected + // frame. + // + // This interlocking relies on the fact that onFrameAvailable gets called + // synchronously from GLConsumer::queueBuffer. + class FrameCondition : public GLConsumer::FrameAvailableListener { + public: + FrameCondition(): + mFrameAvailable(false), + mFrameFinished(false) { + } + + // waitForFrame waits for the next frame to arrive. This should be + // called from the consumer thread once for every frame expected by the + // test. + void waitForFrame() { + Mutex::Autolock lock(mMutex); + ALOGV("+waitForFrame"); + while (!mFrameAvailable) { + mFrameAvailableCondition.wait(mMutex); + } + mFrameAvailable = false; + ALOGV("-waitForFrame"); + } + + // Allow the producer to return from its swapBuffers call and continue + // on to produce the next frame. This should be called by the consumer + // thread once for every frame expected by the test. + void finishFrame() { + Mutex::Autolock lock(mMutex); + ALOGV("+finishFrame"); + mFrameFinished = true; + mFrameFinishCondition.signal(); + ALOGV("-finishFrame"); + } + + // This should be called by GLConsumer on the producer thread. + virtual void onFrameAvailable() { + Mutex::Autolock lock(mMutex); + ALOGV("+onFrameAvailable"); + mFrameAvailable = true; + mFrameAvailableCondition.signal(); + while (!mFrameFinished) { + mFrameFinishCondition.wait(mMutex); + } + mFrameFinished = false; + ALOGV("-onFrameAvailable"); + } + + protected: + bool mFrameAvailable; + bool mFrameFinished; + + Mutex mMutex; + Condition mFrameAvailableCondition; + Condition mFrameFinishCondition; + }; + + virtual void SetUp() { + SurfaceTextureGLToGLTest::SetUp(); + mFC = new FrameCondition(); + mST->setFrameAvailableListener(mFC); + } + + virtual void TearDown() { + if (mProducerThread != NULL) { + mProducerThread->requestExitAndWait(); + } + mProducerThread.clear(); + mFC.clear(); + SurfaceTextureGLToGLTest::TearDown(); + } + + void runProducerThread(const sp<ProducerThread> producerThread) { + ASSERT_TRUE(mProducerThread == NULL); + mProducerThread = producerThread; + producerThread->setEglObjects(mEglDisplay, mProducerEglSurface, + mProducerEglContext); + producerThread->run(); + } + + sp<ProducerThread> mProducerThread; + sp<FrameCondition> mFC; +}; + +} // namespace android + +#endif diff --git a/libs/gui/tests/SurfaceTextureGLThreadToGL_test.cpp b/libs/gui/tests/SurfaceTextureGLThreadToGL_test.cpp new file mode 100644 index 0000000000..9776733efd --- /dev/null +++ b/libs/gui/tests/SurfaceTextureGLThreadToGL_test.cpp @@ -0,0 +1,186 @@ +/* + * Copyright 2013 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 "SurfaceTextureGLThreadToGL_test" +//#define LOG_NDEBUG 0 + +#include "SurfaceTextureGLThreadToGL.h" + +namespace android { + +TEST_F(SurfaceTextureGLThreadToGLTest, + UpdateTexImageBeforeFrameFinishedCompletes) { + class PT : public ProducerThread { + virtual void render() { + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + swapBuffers(); + } + }; + + runProducerThread(new PT()); + + mFC->waitForFrame(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + mFC->finishFrame(); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! +} + +TEST_F(SurfaceTextureGLThreadToGLTest, + UpdateTexImageAfterFrameFinishedCompletes) { + class PT : public ProducerThread { + virtual void render() { + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + swapBuffers(); + } + }; + + runProducerThread(new PT()); + + mFC->waitForFrame(); + mFC->finishFrame(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! +} + +TEST_F(SurfaceTextureGLThreadToGLTest, + RepeatedUpdateTexImageBeforeFrameFinishedCompletes) { + enum { NUM_ITERATIONS = 1024 }; + + class PT : public ProducerThread { + virtual void render() { + for (int i = 0; i < NUM_ITERATIONS; i++) { + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + ALOGV("+swapBuffers"); + swapBuffers(); + ALOGV("-swapBuffers"); + } + } + }; + + runProducerThread(new PT()); + + for (int i = 0; i < NUM_ITERATIONS; i++) { + mFC->waitForFrame(); + ALOGV("+updateTexImage"); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ALOGV("-updateTexImage"); + mFC->finishFrame(); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! + } +} + +TEST_F(SurfaceTextureGLThreadToGLTest, + RepeatedUpdateTexImageAfterFrameFinishedCompletes) { + enum { NUM_ITERATIONS = 1024 }; + + class PT : public ProducerThread { + virtual void render() { + for (int i = 0; i < NUM_ITERATIONS; i++) { + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + ALOGV("+swapBuffers"); + swapBuffers(); + ALOGV("-swapBuffers"); + } + } + }; + + runProducerThread(new PT()); + + for (int i = 0; i < NUM_ITERATIONS; i++) { + mFC->waitForFrame(); + mFC->finishFrame(); + ALOGV("+updateTexImage"); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ALOGV("-updateTexImage"); + + // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! + } +} + +// XXX: This test is disabled because it is currently hanging on some devices. +TEST_F(SurfaceTextureGLThreadToGLTest, + DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) { + enum { NUM_ITERATIONS = 64 }; + + class PT : public ProducerThread { + virtual void render() { + for (int i = 0; i < NUM_ITERATIONS; i++) { + glClearColor(0.0f, 1.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + ALOGV("+swapBuffers"); + swapBuffers(); + ALOGV("-swapBuffers"); + } + } + }; + + ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2)); + + runProducerThread(new PT()); + + // Allow three frames to be rendered and queued before starting the + // rendering in this thread. For the latter two frames we don't call + // updateTexImage so the next dequeue from the producer thread will block + // waiting for a frame to become available. + mFC->waitForFrame(); + mFC->finishFrame(); + + // We must call updateTexImage to consume the first frame so that the + // SurfaceTexture is able to reduce the buffer count to 2. This is because + // the GL driver may dequeue a buffer when the EGLSurface is created, and + // that happens before we call setDefaultMaxBufferCount. It's possible that the + // driver does not dequeue a buffer at EGLSurface creation time, so we + // cannot rely on this to cause the second dequeueBuffer call to block. + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + mFC->waitForFrame(); + mFC->finishFrame(); + mFC->waitForFrame(); + mFC->finishFrame(); + + // Sleep for 100ms to allow the producer thread's dequeueBuffer call to + // block waiting for a buffer to become available. + usleep(100000); + + // Render and present a number of images. This thread should not be blocked + // by the fact that the producer thread is blocking in dequeue. + for (int i = 0; i < NUM_ITERATIONS; i++) { + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mEglSurface); + } + + // Consume the two pending buffers to unblock the producer thread. + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + // Consume the remaining buffers from the producer thread. + for (int i = 0; i < NUM_ITERATIONS-3; i++) { + mFC->waitForFrame(); + mFC->finishFrame(); + ALOGV("+updateTexImage"); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ALOGV("-updateTexImage"); + } +} + +} // namespace android diff --git a/libs/gui/tests/SurfaceTextureGLToGL.h b/libs/gui/tests/SurfaceTextureGLToGL.h new file mode 100644 index 0000000000..5a2eff3951 --- /dev/null +++ b/libs/gui/tests/SurfaceTextureGLToGL.h @@ -0,0 +1,65 @@ +/* + * Copyright 2013 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_SURFACE_TEXTURE_GL_TO_GL_H +#define ANDROID_SURFACE_TEXTURE_GL_TO_GL_H + +#include "SurfaceTextureGL.h" + +namespace android { + +/* + * This test fixture is for testing GL -> GL texture streaming. It creates an + * EGLSurface and an EGLContext for the image producer to use. + */ +class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest { +protected: + SurfaceTextureGLToGLTest(): + mProducerEglSurface(EGL_NO_SURFACE), + mProducerEglContext(EGL_NO_CONTEXT) { + } + + virtual void SetUp() { + SurfaceTextureGLTest::SetUp(); + + mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, + mANW.get(), NULL); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface); + + mProducerEglContext = eglCreateContext(mEglDisplay, mGlConfig, + EGL_NO_CONTEXT, getContextAttribs()); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext); + } + + virtual void TearDown() { + if (mProducerEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mProducerEglContext); + } + if (mProducerEglSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEglDisplay, mProducerEglSurface); + } + SurfaceTextureGLTest::TearDown(); + } + + EGLSurface mProducerEglSurface; + EGLContext mProducerEglContext; +}; + +} // namespace android + +#endif diff --git a/libs/gui/tests/SurfaceTextureGLToGL_test.cpp b/libs/gui/tests/SurfaceTextureGLToGL_test.cpp new file mode 100644 index 0000000000..f4c796131b --- /dev/null +++ b/libs/gui/tests/SurfaceTextureGLToGL_test.cpp @@ -0,0 +1,502 @@ +/* + * Copyright 2013 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 "SurfaceTextureGLToGL_test" +//#define LOG_NDEBUG 0 + +#include "SurfaceTextureGLToGL.h" + +namespace android { + +TEST_F(SurfaceTextureGLToGLTest, TransformHintGetsRespected) { + const uint32_t texWidth = 32; + const uint32_t texHeight = 64; + + mST->setDefaultBufferSize(texWidth, texHeight); + mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); + + // This test requires 3 buffers to avoid deadlock because we're + // both producer and consumer, and only using one thread. + mST->setDefaultMaxBufferCount(3); + + // Do the producer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Start a buffer with our chosen size and transform hint moving + // through the system. + glClear(GL_COLOR_BUFFER_BIT); // give the driver something to do + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + mST->updateTexImage(); // consume it + // Swap again. + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + mST->updateTexImage(); + + // The current buffer should either show the effects of the transform + // hint (in the form of an inverse transform), or show that the + // transform hint has been ignored. + sp<GraphicBuffer> buf = mST->getCurrentBuffer(); + if (mST->getCurrentTransform() == NATIVE_WINDOW_TRANSFORM_ROT_270) { + ASSERT_EQ(texWidth, buf->getHeight()); + ASSERT_EQ(texHeight, buf->getWidth()); + } else { + ASSERT_EQ(texWidth, buf->getWidth()); + ASSERT_EQ(texHeight, buf->getHeight()); + } + + // Reset the transform hint and confirm that it takes. + mST->setTransformHint(0); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + mST->updateTexImage(); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + mST->updateTexImage(); + + buf = mST->getCurrentBuffer(); + ASSERT_EQ((uint32_t) 0, mST->getCurrentTransform()); + ASSERT_EQ(texWidth, buf->getWidth()); + ASSERT_EQ(texHeight, buf->getHeight()); +} + +TEST_F(SurfaceTextureGLToGLTest, TexturingFromGLFilledRGBABufferPow2) { + const int texWidth = 64; + const int texHeight = 64; + + mST->setDefaultBufferSize(texWidth, texHeight); + + // This test requires 3 buffers to complete run on a single thread. + mST->setDefaultMaxBufferCount(3); + + // Do the producer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // This is needed to ensure we pick up a buffer of the correct size. + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + + glClearColor(0.6, 0.6, 0.6, 0.6); + glClear(GL_COLOR_BUFFER_BIT); + + glEnable(GL_SCISSOR_TEST); + glScissor(4, 4, 4, 4); + glClearColor(1.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glScissor(24, 48, 4, 4); + glClearColor(0.0, 1.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glScissor(37, 17, 4, 4); + glClearColor(0.0, 0.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + + // Do the consumer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + glDisable(GL_SCISSOR_TEST); + + // Skip the first frame, which was empty + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, texWidth, texHeight); + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153)); + + EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255)); + EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255)); + EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255)); + EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); +} + +TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) { + sp<GraphicBuffer> buffers[2]; + + // This test requires async mode to run on a single thread. + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + for (int i = 0; i < 2; i++) { + // Produce a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + + // Consume a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mFW->waitForFrame(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + buffers[i] = mST->getCurrentBuffer(); + } + + // Destroy the GL texture object to release its ref on buffers[2]. + GLuint texID = TEX_ID; + glDeleteTextures(1, &texID); + + // Destroy the EGLSurface + EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mProducerEglSurface = EGL_NO_SURFACE; + + // This test should have the only reference to buffer 0. + EXPECT_EQ(1, buffers[0]->getStrongCount()); + + // The GLConsumer should hold a single reference to buffer 1 in its + // mCurrentBuffer member. All of the references in the slots should have + // been released. + EXPECT_EQ(2, buffers[1]->getStrongCount()); +} + +TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) { + sp<GraphicBuffer> buffers[3]; + + // This test requires async mode to run on a single thread. + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + for (int i = 0; i < 3; i++) { + // Produce a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Consume a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mFW->waitForFrame(); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + buffers[i] = mST->getCurrentBuffer(); + } + + // Abandon the GLConsumer, releasing the ref that the GLConsumer has + // on buffers[2]. + mST->abandon(); + + // Destroy the GL texture object to release its ref on buffers[2]. + GLuint texID = TEX_ID; + glDeleteTextures(1, &texID); + + // Destroy the EGLSurface. + EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mProducerEglSurface = EGL_NO_SURFACE; + + EXPECT_EQ(1, buffers[0]->getStrongCount()); + EXPECT_EQ(1, buffers[1]->getStrongCount()); + + // Depending on how lazily the GL driver dequeues buffers, we may end up + // with either two or three total buffers. If there are three, make sure + // the last one was properly down-ref'd. + if (buffers[2] != buffers[0]) { + EXPECT_EQ(1, buffers[2]->getStrongCount()); + } +} + +TEST_F(SurfaceTextureGLToGLTest, EglMakeCurrentBeforeConsumerDeathUnrefsBuffers) { + sp<GraphicBuffer> buffer; + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + + // Produce a frame + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Destroy the EGLSurface. + EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mProducerEglSurface = EGL_NO_SURFACE; + mSTC.clear(); + mANW.clear(); + mTextureRenderer.clear(); + + // Consume a frame + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + buffer = mST->getCurrentBuffer(); + + // Destroy the GL texture object to release its ref + GLuint texID = TEX_ID; + glDeleteTextures(1, &texID); + + // make un-current, all references to buffer should be gone + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT)); + + // Destroy consumer + mST.clear(); + + EXPECT_EQ(1, buffer->getStrongCount()); +} + +TEST_F(SurfaceTextureGLToGLTest, EglMakeCurrentAfterConsumerDeathUnrefsBuffers) { + sp<GraphicBuffer> buffer; + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + + // Produce a frame + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Destroy the EGLSurface. + EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mProducerEglSurface = EGL_NO_SURFACE; + mSTC.clear(); + mANW.clear(); + mTextureRenderer.clear(); + + // Consume a frame + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + buffer = mST->getCurrentBuffer(); + + // Destroy the GL texture object to release its ref + GLuint texID = TEX_ID; + glDeleteTextures(1, &texID); + + // Destroy consumer + mST.clear(); + + // make un-current, all references to buffer should be gone + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT)); + + EXPECT_EQ(1, buffer->getStrongCount()); +} + +TEST_F(SurfaceTextureGLToGLTest, TexturingFromUserSizedGLFilledBuffer) { + enum { texWidth = 64 }; + enum { texHeight = 64 }; + + // This test requires 3 buffers to complete run on a single thread. + mST->setDefaultMaxBufferCount(3); + + // Set the user buffer size. + native_window_set_buffers_user_dimensions(mANW.get(), texWidth, texHeight); + + // Do the producer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // This is needed to ensure we pick up a buffer of the correct size. + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + + glClearColor(0.6, 0.6, 0.6, 0.6); + glClear(GL_COLOR_BUFFER_BIT); + + glEnable(GL_SCISSOR_TEST); + glScissor(4, 4, 1, 1); + glClearColor(1.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + + // Do the consumer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + glDisable(GL_SCISSOR_TEST); + + // Skip the first frame, which was empty + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, texWidth, texHeight); + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153)); + + EXPECT_TRUE(checkPixel( 4, 4, 255, 0, 0, 255)); + EXPECT_TRUE(checkPixel( 5, 5, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 3, 3, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(45, 52, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(12, 36, 153, 153, 153, 153)); +} + +TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedUserSizedGLFilledBuffer) { + enum { texWidth = 64 }; + enum { texHeight = 16 }; + + // This test requires 3 buffers to complete run on a single thread. + mST->setDefaultMaxBufferCount(3); + + // Set the transform hint. + mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); + + // Set the user buffer size. + native_window_set_buffers_user_dimensions(mANW.get(), texWidth, texHeight); + + // Do the producer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // This is needed to ensure we pick up a buffer of the correct size and the + // new rotation hint. + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + + glClearColor(0.6, 0.6, 0.6, 0.6); + glClear(GL_COLOR_BUFFER_BIT); + + glEnable(GL_SCISSOR_TEST); + glScissor(24, 4, 1, 1); + glClearColor(1.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + + // Do the consumer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + glDisable(GL_SCISSOR_TEST); + + // Skip the first frame, which was empty + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, texWidth, texHeight); + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 15, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 0, 15, 153, 153, 153, 153)); + + EXPECT_TRUE(checkPixel(24, 4, 255, 0, 0, 255)); + EXPECT_TRUE(checkPixel(25, 5, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(23, 3, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(45, 13, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(12, 8, 153, 153, 153, 153)); +} + +TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedGLFilledBuffer) { + enum { texWidth = 64 }; + enum { texHeight = 16 }; + + // This test requires 3 buffers to complete run on a single thread. + mST->setDefaultMaxBufferCount(3); + + // Set the transform hint. + mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); + + // Set the default buffer size. + mST->setDefaultBufferSize(texWidth, texHeight); + + // Do the producer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // This is needed to ensure we pick up a buffer of the correct size and the + // new rotation hint. + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + + glClearColor(0.6, 0.6, 0.6, 0.6); + glClear(GL_COLOR_BUFFER_BIT); + + glEnable(GL_SCISSOR_TEST); + glScissor(24, 4, 1, 1); + glClearColor(1.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + eglSwapBuffers(mEglDisplay, mProducerEglSurface); + + // Do the consumer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + glDisable(GL_SCISSOR_TEST); + + // Skip the first frame, which was empty + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, texWidth, texHeight); + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 15, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 0, 15, 153, 153, 153, 153)); + + EXPECT_TRUE(checkPixel(24, 4, 255, 0, 0, 255)); + EXPECT_TRUE(checkPixel(25, 5, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(23, 3, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(45, 13, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(12, 8, 153, 153, 153, 153)); +} + +} // namespace android diff --git a/libs/gui/tests/SurfaceTextureGL_test.cpp b/libs/gui/tests/SurfaceTextureGL_test.cpp new file mode 100644 index 0000000000..25b2319e6c --- /dev/null +++ b/libs/gui/tests/SurfaceTextureGL_test.cpp @@ -0,0 +1,703 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "SurfaceTextureGL_test" +//#define LOG_NDEBUG 0 + +#include "SurfaceTextureGL.h" + +#include "DisconnectWaiter.h" +#include "FillBuffer.h" + +namespace android { + +TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { + const int texWidth = 64; + const int texHeight = 66; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + ANativeWindowBuffer* anb; + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + + // Fill the buffer with the a checkerboard pattern + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), + -1)); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, texWidth, texHeight); + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 255, 127, 255, 255, 3)); + EXPECT_TRUE(checkPixel(63, 0, 0, 133, 0, 255, 3)); + EXPECT_TRUE(checkPixel(63, 65, 0, 133, 0, 255, 3)); + EXPECT_TRUE(checkPixel( 0, 65, 255, 127, 255, 255, 3)); + + EXPECT_TRUE(checkPixel(22, 44, 255, 127, 255, 255, 3)); + EXPECT_TRUE(checkPixel(45, 52, 255, 127, 255, 255, 3)); + EXPECT_TRUE(checkPixel(52, 51, 98, 255, 73, 255, 3)); + EXPECT_TRUE(checkPixel( 7, 31, 155, 0, 118, 255, 3)); + EXPECT_TRUE(checkPixel(31, 9, 107, 24, 87, 255, 3)); + EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255, 3)); + EXPECT_TRUE(checkPixel(36, 22, 155, 29, 0, 255, 3)); +} + +TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) { + const int texWidth = 64; + const int texHeight = 64; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + ANativeWindowBuffer* anb; + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + + // Fill the buffer with the a checkerboard pattern + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), + -1)); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, texWidth, texHeight); + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 0, 133, 0, 255)); + EXPECT_TRUE(checkPixel(63, 0, 255, 127, 255, 255)); + EXPECT_TRUE(checkPixel(63, 63, 0, 133, 0, 255)); + EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255)); + + EXPECT_TRUE(checkPixel(22, 19, 100, 255, 74, 255)); + EXPECT_TRUE(checkPixel(45, 11, 100, 255, 74, 255)); + EXPECT_TRUE(checkPixel(52, 12, 155, 0, 181, 255)); + EXPECT_TRUE(checkPixel( 7, 32, 150, 237, 170, 255)); + EXPECT_TRUE(checkPixel(31, 54, 0, 71, 117, 255)); + EXPECT_TRUE(checkPixel(29, 28, 0, 133, 0, 255)); + EXPECT_TRUE(checkPixel(36, 41, 100, 232, 255, 255)); +} + +TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { + const int texWidth = 64; + const int texHeight = 66; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + android_native_rect_t crops[] = { + {4, 6, 22, 36}, + {0, 6, 22, 36}, + {4, 0, 22, 36}, + {4, 6, texWidth, 36}, + {4, 6, 22, texHeight}, + }; + + for (int i = 0; i < 5; i++) { + const android_native_rect_t& crop(crops[i]); + SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", + crop.left, crop.top, crop.right, crop.bottom).string()); + + ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop)); + + ANativeWindowBuffer* anb; + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), + buf->getNativeBuffer(), -1)); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, 64, 64); + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(63, 0, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(63, 63, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel( 0, 63, 82, 255, 35, 255)); + + EXPECT_TRUE(checkPixel(25, 14, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(35, 31, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(57, 6, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel( 5, 42, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(32, 33, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(16, 26, 82, 255, 35, 255)); + EXPECT_TRUE(checkPixel(46, 51, 82, 255, 35, 255)); + } +} + +// This test is intended to catch synchronization bugs between the CPU-written +// and GPU-read buffers. +TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { + enum { texWidth = 16 }; + enum { texHeight = 16 }; + enum { numFrames = 1024 }; + + ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_WRITE_OFTEN)); + + struct TestPixel { + int x; + int y; + }; + const TestPixel testPixels[] = { + { 4, 11 }, + { 12, 14 }, + { 7, 2 }, + }; + enum {numTestPixels = sizeof(testPixels) / sizeof(testPixels[0])}; + + class ProducerThread : public Thread { + public: + ProducerThread(const sp<ANativeWindow>& anw, + const TestPixel* testPixels): + mANW(anw), + mTestPixels(testPixels) { + } + + virtual ~ProducerThread() { + } + + virtual bool threadLoop() { + for (int i = 0; i < numFrames; i++) { + ANativeWindowBuffer* anb; + if (native_window_dequeue_buffer_and_wait(mANW.get(), + &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + + const int yuvTexOffsetY = 0; + int stride = buf->getStride(); + int yuvTexStrideY = stride; + int yuvTexOffsetV = yuvTexStrideY * texHeight; + int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; + int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2; + int yuvTexStrideU = yuvTexStrideV; + + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + + // Gray out all the test pixels first, so we're more likely to + // see a failure if GL is still texturing from the buffer we + // just dequeued. + for (int j = 0; j < numTestPixels; j++) { + int x = mTestPixels[j].x; + int y = mTestPixels[j].y; + uint8_t value = 128; + img[y*stride + x] = value; + } + + // Fill the buffer with gray. + for (int y = 0; y < texHeight; y++) { + for (int x = 0; x < texWidth; x++) { + img[yuvTexOffsetY + y*yuvTexStrideY + x] = 128; + img[yuvTexOffsetU + (y/2)*yuvTexStrideU + x/2] = 128; + img[yuvTexOffsetV + (y/2)*yuvTexStrideV + x/2] = 128; + } + } + + // Set the test pixels to either white or black. + for (int j = 0; j < numTestPixels; j++) { + int x = mTestPixels[j].x; + int y = mTestPixels[j].y; + uint8_t value = 0; + if (j == (i % numTestPixels)) { + value = 255; + } + img[y*stride + x] = value; + } + + buf->unlock(); + if (mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), -1) + != NO_ERROR) { + return false; + } + } + return false; + } + + sp<ANativeWindow> mANW; + const TestPixel* mTestPixels; + }; + + sp<Thread> pt(new ProducerThread(mANW, testPixels)); + pt->run(); + + glViewport(0, 0, texWidth, texHeight); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + // We wait for the first two frames up front so that the producer will be + // likely to dequeue the buffer that's currently being textured from. + mFW->waitForFrame(); + mFW->waitForFrame(); + + for (int i = 0; i < numFrames; i++) { + SCOPED_TRACE(String8::format("frame %d", i).string()); + + // We must wait for each frame to come in because if we ever do an + // updateTexImage call that doesn't consume a newly available buffer + // then the producer and consumer will get out of sync, which will cause + // a deadlock. + if (i > 1) { + mFW->waitForFrame(); + } + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + drawTexture(); + + for (int j = 0; j < numTestPixels; j++) { + int x = testPixels[j].x; + int y = testPixels[j].y; + uint8_t value = 0; + if (j == (i % numTestPixels)) { + // We must y-invert the texture coords + EXPECT_TRUE(checkPixel(x, texHeight-y-1, 255, 255, 255, 255)); + } else { + // We must y-invert the texture coords + EXPECT_TRUE(checkPixel(x, texHeight-y-1, 0, 0, 0, 255)); + } + } + } + + pt->requestExitAndWait(); +} + +TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferNpot) { + const int texWidth = 64; + const int texHeight = 66; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, texWidth, texHeight); + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); + EXPECT_TRUE(checkPixel(63, 0, 231, 231, 231, 231)); + EXPECT_TRUE(checkPixel(63, 65, 231, 231, 231, 231)); + EXPECT_TRUE(checkPixel( 0, 65, 35, 35, 35, 35)); + + EXPECT_TRUE(checkPixel(15, 10, 35, 231, 231, 231)); + EXPECT_TRUE(checkPixel(23, 65, 231, 35, 231, 35)); + EXPECT_TRUE(checkPixel(19, 40, 35, 231, 35, 35)); + EXPECT_TRUE(checkPixel(38, 30, 231, 35, 35, 35)); + EXPECT_TRUE(checkPixel(42, 54, 35, 35, 35, 231)); + EXPECT_TRUE(checkPixel(37, 34, 35, 231, 231, 231)); + EXPECT_TRUE(checkPixel(31, 8, 231, 35, 35, 231)); + EXPECT_TRUE(checkPixel(37, 47, 231, 35, 231, 231)); + EXPECT_TRUE(checkPixel(25, 38, 35, 35, 35, 35)); + EXPECT_TRUE(checkPixel(49, 6, 35, 231, 35, 35)); + EXPECT_TRUE(checkPixel(54, 50, 35, 231, 231, 231)); + EXPECT_TRUE(checkPixel(27, 26, 231, 231, 231, 231)); + EXPECT_TRUE(checkPixel(10, 6, 35, 35, 231, 231)); + EXPECT_TRUE(checkPixel(29, 4, 35, 35, 35, 231)); + EXPECT_TRUE(checkPixel(55, 28, 35, 35, 231, 35)); + EXPECT_TRUE(checkPixel(58, 55, 35, 35, 231, 231)); +} + +TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) { + const int texWidth = 64; + const int texHeight = 64; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, texWidth, texHeight); + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 231, 231, 231, 231)); + EXPECT_TRUE(checkPixel(63, 0, 35, 35, 35, 35)); + EXPECT_TRUE(checkPixel(63, 63, 231, 231, 231, 231)); + EXPECT_TRUE(checkPixel( 0, 63, 35, 35, 35, 35)); + + EXPECT_TRUE(checkPixel(12, 46, 231, 231, 231, 35)); + EXPECT_TRUE(checkPixel(16, 1, 231, 231, 35, 231)); + EXPECT_TRUE(checkPixel(21, 12, 231, 35, 35, 231)); + EXPECT_TRUE(checkPixel(26, 51, 231, 35, 231, 35)); + EXPECT_TRUE(checkPixel( 5, 32, 35, 231, 231, 35)); + EXPECT_TRUE(checkPixel(13, 8, 35, 231, 231, 231)); + EXPECT_TRUE(checkPixel(46, 3, 35, 35, 231, 35)); + EXPECT_TRUE(checkPixel(30, 33, 35, 35, 35, 35)); + EXPECT_TRUE(checkPixel( 6, 52, 231, 231, 35, 35)); + EXPECT_TRUE(checkPixel(55, 33, 35, 231, 35, 231)); + EXPECT_TRUE(checkPixel(16, 29, 35, 35, 231, 231)); + EXPECT_TRUE(checkPixel( 1, 30, 35, 35, 35, 231)); + EXPECT_TRUE(checkPixel(41, 37, 35, 35, 231, 231)); + EXPECT_TRUE(checkPixel(46, 29, 231, 231, 35, 35)); + EXPECT_TRUE(checkPixel(15, 25, 35, 231, 35, 231)); + EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35)); +} + +// Tests if GLConsumer and BufferQueue are robust enough +// to handle a special case where updateTexImage is called +// in the middle of disconnect. This ordering is enforced +// by blocking in the disconnect callback. +TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { + + class ProducerThread : public Thread { + public: + ProducerThread(const sp<ANativeWindow>& anw): + mANW(anw) { + } + + virtual ~ProducerThread() { + } + + virtual bool threadLoop() { + ANativeWindowBuffer* anb; + + native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_EGL); + + for (int numFrames =0 ; numFrames < 2; numFrames ++) { + + if (native_window_dequeue_buffer_and_wait(mANW.get(), + &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb, -1) + != NO_ERROR) { + return false; + } + } + + native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL); + + return false; + } + + private: + sp<ANativeWindow> mANW; + }; + + sp<DisconnectWaiter> dw(new DisconnectWaiter()); + mBQ->consumerConnect(dw, false); + + + sp<Thread> pt(new ProducerThread(mANW)); + pt->run(); + + // eat a frame so GLConsumer will own an at least one slot + dw->waitForFrame(); + EXPECT_EQ(OK,mST->updateTexImage()); + + dw->waitForFrame(); + // Could fail here as GLConsumer thinks it still owns the slot + // but bufferQueue has released all slots + EXPECT_EQ(OK,mST->updateTexImage()); + + dw->finishDisconnect(); +} + + +// This test ensures that the GLConsumer clears the mCurrentTexture +// when it is disconnected and reconnected. Otherwise it will +// attempt to release a buffer that it does not owned +TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { + ASSERT_EQ(OK, native_window_api_connect(mANW.get(), + NATIVE_WINDOW_API_EGL)); + + ANativeWindowBuffer *anb; + + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); + + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); + + EXPECT_EQ(OK,mST->updateTexImage()); + EXPECT_EQ(OK,mST->updateTexImage()); + + ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(), + NATIVE_WINDOW_API_EGL)); + ASSERT_EQ(OK, native_window_api_connect(mANW.get(), + NATIVE_WINDOW_API_EGL)); + + EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); + + // Will fail here if mCurrentTexture is not cleared properly + mFW->waitForFrame(); + EXPECT_EQ(OK,mST->updateTexImage()); + + ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(), + NATIVE_WINDOW_API_EGL)); +} + +TEST_F(SurfaceTextureGLTest, ScaleToWindowMode) { + ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(), + NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)); + + // The producer image size + ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 512, 512)); + + // The consumer image size (16 x 9) ratio + mST->setDefaultBufferSize(1280, 720); + + ASSERT_EQ(OK, native_window_api_connect(mANW.get(), + NATIVE_WINDOW_API_CPU)); + + ANativeWindowBuffer *anb; + + android_native_rect_t odd = {23, 78, 123, 477}; + ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &odd)); + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); + mFW->waitForFrame(); + EXPECT_EQ(OK, mST->updateTexImage()); + Rect r = mST->getCurrentCrop(); + assertRectEq(Rect(23, 78, 123, 477), r); + + ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(), + NATIVE_WINDOW_API_CPU)); +} + +// This test ensures the scaling mode does the right thing +// ie NATIVE_WINDOW_SCALING_MODE_CROP should crop +// the image such that it has the same aspect ratio as the +// default buffer size +TEST_F(SurfaceTextureGLTest, CroppedScalingMode) { + ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(), + NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)); + + // The producer image size + ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 512, 512)); + + // The consumer image size (16 x 9) ratio + mST->setDefaultBufferSize(1280, 720); + + native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU); + + ANativeWindowBuffer *anb; + + // The crop is in the shape of (320, 180) === 16 x 9 + android_native_rect_t standard = {10, 20, 330, 200}; + ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &standard)); + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); + mFW->waitForFrame(); + EXPECT_EQ(OK, mST->updateTexImage()); + Rect r = mST->getCurrentCrop(); + // crop should be the same as crop (same aspect ratio) + assertRectEq(Rect(10, 20, 330, 200), r); + + // make this wider then desired aspect 239 x 100 (2.39:1) + android_native_rect_t wide = {20, 30, 259, 130}; + ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &wide)); + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); + mFW->waitForFrame(); + EXPECT_EQ(OK, mST->updateTexImage()); + r = mST->getCurrentCrop(); + // crop should be the same height, but have cropped left and right borders + // offset is 30.6 px L+, R- + assertRectEq(Rect(51, 30, 228, 130), r); + + // This image is taller then desired aspect 400 x 300 (4:3) + android_native_rect_t narrow = {0, 0, 400, 300}; + ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &narrow)); + EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); + EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); + mFW->waitForFrame(); + EXPECT_EQ(OK, mST->updateTexImage()); + r = mST->getCurrentCrop(); + // crop should be the same width, but have cropped top and bottom borders + // offset is 37.5 px + assertRectEq(Rect(0, 37, 400, 262), r); + + native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); +} + +TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { + class ProducerThread : public Thread { + public: + ProducerThread(const sp<ANativeWindow>& anw): + mANW(anw), + mDequeueError(NO_ERROR) { + } + + virtual ~ProducerThread() { + } + + virtual bool threadLoop() { + Mutex::Autolock lock(mMutex); + ANativeWindowBuffer* anb; + + // Frame 1 + if (native_window_dequeue_buffer_and_wait(mANW.get(), + &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb, -1) + != NO_ERROR) { + return false; + } + + // Frame 2 + if (native_window_dequeue_buffer_and_wait(mANW.get(), + &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb, -1) + != NO_ERROR) { + return false; + } + + // Frame 3 - error expected + mDequeueError = native_window_dequeue_buffer_and_wait(mANW.get(), + &anb); + return false; + } + + status_t getDequeueError() { + Mutex::Autolock lock(mMutex); + return mDequeueError; + } + + private: + sp<ANativeWindow> mANW; + status_t mDequeueError; + Mutex mMutex; + }; + + ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2)); + + sp<Thread> pt(new ProducerThread(mANW)); + pt->run(); + + mFW->waitForFrame(); + mFW->waitForFrame(); + + // Sleep for 100ms to allow the producer thread's dequeueBuffer call to + // block waiting for a buffer to become available. + usleep(100000); + + mST->abandon(); + + pt->requestExitAndWait(); + ASSERT_EQ(NO_INIT, + reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError()); +} + +TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) { + int texHeight = 16; + ANativeWindowBuffer* anb; + + GLint maxTextureSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + + // make sure it works with small textures + mST->setDefaultBufferSize(16, texHeight); + EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); + EXPECT_EQ(16, anb->width); + EXPECT_EQ(texHeight, anb->height); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); + EXPECT_EQ(NO_ERROR, mST->updateTexImage()); + + // make sure it works with GL_MAX_TEXTURE_SIZE + mST->setDefaultBufferSize(maxTextureSize, texHeight); + EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); + EXPECT_EQ(maxTextureSize, anb->width); + EXPECT_EQ(texHeight, anb->height); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); + EXPECT_EQ(NO_ERROR, mST->updateTexImage()); + + // make sure it fails with GL_MAX_TEXTURE_SIZE+1 + mST->setDefaultBufferSize(maxTextureSize+1, texHeight); + EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), + &anb)); + EXPECT_EQ(maxTextureSize+1, anb->width); + EXPECT_EQ(texHeight, anb->height); + EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); + ASSERT_NE(NO_ERROR, mST->updateTexImage()); +} + +} // namespace android diff --git a/libs/gui/tests/SurfaceTextureMultiContextGL.h b/libs/gui/tests/SurfaceTextureMultiContextGL.h new file mode 100644 index 0000000000..7934bbccfd --- /dev/null +++ b/libs/gui/tests/SurfaceTextureMultiContextGL.h @@ -0,0 +1,84 @@ +/* + * Copyright 2013 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_SURFACE_TEXTURE_MULTI_CONTEXT_GL_H +#define ANDROID_SURFACE_TEXTURE_MULTI_CONTEXT_GL_H + +#include "SurfaceTextureGL.h" + +namespace android { + +class SurfaceTextureMultiContextGLTest : public SurfaceTextureGLTest { +protected: + enum { SECOND_TEX_ID = 123 }; + enum { THIRD_TEX_ID = 456 }; + + SurfaceTextureMultiContextGLTest(): + mSecondEglContext(EGL_NO_CONTEXT) { + } + + virtual void SetUp() { + SurfaceTextureGLTest::SetUp(); + + // Set up the secondary context and texture renderer. + mSecondEglContext = eglCreateContext(mEglDisplay, mGlConfig, + EGL_NO_CONTEXT, getContextAttribs()); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mSecondEglContext); + + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mSecondEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mSecondTextureRenderer = new TextureRenderer(SECOND_TEX_ID, mST); + ASSERT_NO_FATAL_FAILURE(mSecondTextureRenderer->SetUp()); + + // Set up the tertiary context and texture renderer. + mThirdEglContext = eglCreateContext(mEglDisplay, mGlConfig, + EGL_NO_CONTEXT, getContextAttribs()); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mThirdEglContext); + + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mThirdEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mThirdTextureRenderer = new TextureRenderer(THIRD_TEX_ID, mST); + ASSERT_NO_FATAL_FAILURE(mThirdTextureRenderer->SetUp()); + + // Switch back to the primary context to start the tests. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + } + + virtual void TearDown() { + if (mThirdEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mThirdEglContext); + } + if (mSecondEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mSecondEglContext); + } + SurfaceTextureGLTest::TearDown(); + } + + EGLContext mSecondEglContext; + sp<TextureRenderer> mSecondTextureRenderer; + + EGLContext mThirdEglContext; + sp<TextureRenderer> mThirdTextureRenderer; +}; + +} + +#endif diff --git a/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp b/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp new file mode 100644 index 0000000000..115a47d0f3 --- /dev/null +++ b/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp @@ -0,0 +1,389 @@ +/* + * Copyright 2013 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 "SurfaceTextureMultiContextGL_test" +//#define LOG_NDEBUG 0 + +#include "SurfaceTextureMultiContextGL.h" + +#include "FillBuffer.h" + +#include <GLES/glext.h> + +namespace android { + +TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Attempt to latch the texture on the secondary context. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mSecondEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_EQ(INVALID_OPERATION, mST->updateTexImage()); +} + +TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextSucceeds) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Detach from the primary context. + ASSERT_EQ(OK, mST->detachFromContext()); + + // Check that the GL texture was deleted. + EXPECT_EQ(GL_FALSE, glIsTexture(TEX_ID)); +} + +TEST_F(SurfaceTextureMultiContextGLTest, + DetachFromContextSucceedsAfterProducerDisconnect) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Detach from the primary context. + native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); + ASSERT_EQ(OK, mST->detachFromContext()); + + // Check that the GL texture was deleted. + EXPECT_EQ(GL_FALSE, glIsTexture(TEX_ID)); +} + +TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenAbandoned) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Attempt to detach from the primary context. + mST->abandon(); + ASSERT_EQ(NO_INIT, mST->detachFromContext()); +} + +TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenDetached) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Detach from the primary context. + ASSERT_EQ(OK, mST->detachFromContext()); + + // Attempt to detach from the primary context again. + ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); +} + +TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoDisplay) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Make there be no current display. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Attempt to detach from the primary context. + ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); +} + +TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoContext) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Make current context be incorrect. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mSecondEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Attempt to detach from the primary context. + ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); +} + +TEST_F(SurfaceTextureMultiContextGLTest, UpdateTexImageFailsWhenDetached) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Detach from the primary context. + ASSERT_EQ(OK, mST->detachFromContext()); + + // Attempt to latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(INVALID_OPERATION, mST->updateTexImage()); +} + +TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceeds) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Detach from the primary context. + ASSERT_EQ(OK, mST->detachFromContext()); + + // Attach to the secondary context. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mSecondEglContext)); + ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); + + // Verify that the texture object was created and bound. + GLint texBinding = -1; + glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); + EXPECT_EQ(SECOND_TEX_ID, texBinding); + + // Try to use the texture from the secondary context. + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, 1, 1); + mSecondTextureRenderer->drawTexture(); + ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); +} + +TEST_F(SurfaceTextureMultiContextGLTest, + AttachToContextSucceedsAfterProducerDisconnect) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Detach from the primary context. + native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); + ASSERT_EQ(OK, mST->detachFromContext()); + + // Attach to the secondary context. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mSecondEglContext)); + ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); + + // Verify that the texture object was created and bound. + GLint texBinding = -1; + glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); + EXPECT_EQ(SECOND_TEX_ID, texBinding); + + // Try to use the texture from the secondary context. + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, 1, 1); + mSecondTextureRenderer->drawTexture(); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); +} + +TEST_F(SurfaceTextureMultiContextGLTest, + AttachToContextSucceedsBeforeUpdateTexImage) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Detach from the primary context. + native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); + ASSERT_EQ(OK, mST->detachFromContext()); + + // Attach to the secondary context. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mSecondEglContext)); + ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); + + // Verify that the texture object was created and bound. + GLint texBinding = -1; + glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); + EXPECT_EQ(SECOND_TEX_ID, texBinding); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Try to use the texture from the secondary context. + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, 1, 1); + mSecondTextureRenderer->drawTexture(); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); +} + +TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAbandoned) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Detach from the primary context. + ASSERT_EQ(OK, mST->detachFromContext()); + + // Attempt to attach to the secondary context. + mST->abandon(); + + // Attempt to attach to the primary context. + ASSERT_EQ(NO_INIT, mST->attachToContext(SECOND_TEX_ID)); +} + +TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAttached) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Attempt to attach to the primary context. + ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); +} + +TEST_F(SurfaceTextureMultiContextGLTest, + AttachToContextFailsWhenAttachedBeforeUpdateTexImage) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Attempt to attach to the primary context. + ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); +} + +TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWithNoDisplay) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Detach from the primary context. + ASSERT_EQ(OK, mST->detachFromContext()); + + // Make there be no current display. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + // Attempt to attach with no context current. + ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); +} + +TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceedsTwice) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Latch the texture contents on the primary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Detach from the primary context. + ASSERT_EQ(OK, mST->detachFromContext()); + + // Attach to the secondary context. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mSecondEglContext)); + ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); + + // Detach from the secondary context. + ASSERT_EQ(OK, mST->detachFromContext()); + + // Attach to the tertiary context. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mThirdEglContext)); + ASSERT_EQ(OK, mST->attachToContext(THIRD_TEX_ID)); + + // Verify that the texture object was created and bound. + GLint texBinding = -1; + glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); + EXPECT_EQ(THIRD_TEX_ID, texBinding); + + // Try to use the texture from the tertiary context. + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, 1, 1); + mThirdTextureRenderer->drawTexture(); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); +} + +TEST_F(SurfaceTextureMultiContextGLTest, + AttachToContextSucceedsTwiceBeforeUpdateTexImage) { + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Detach from the primary context. + ASSERT_EQ(OK, mST->detachFromContext()); + + // Attach to the secondary context. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mSecondEglContext)); + ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); + + // Detach from the secondary context. + ASSERT_EQ(OK, mST->detachFromContext()); + + // Attach to the tertiary context. + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mThirdEglContext)); + ASSERT_EQ(OK, mST->attachToContext(THIRD_TEX_ID)); + + // Verify that the texture object was created and bound. + GLint texBinding = -1; + glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); + EXPECT_EQ(THIRD_TEX_ID, texBinding); + + // Latch the texture contents on the tertiary context. + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // Try to use the texture from the tertiary context. + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, 1, 1); + mThirdTextureRenderer->drawTexture(); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); +} + +TEST_F(SurfaceTextureMultiContextGLTest, + UpdateTexImageSucceedsForBufferConsumedBeforeDetach) { + ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); + + // produce two frames and consume them both on the primary context + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); + + // produce one more frame + ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); + + // Detach from the primary context and attach to the secondary context + ASSERT_EQ(OK, mST->detachFromContext()); + ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mSecondEglContext)); + ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); + + // Consume final frame on secondary context + mFW->waitForFrame(); + ASSERT_EQ(OK, mST->updateTexImage()); +} + +} // namespace android diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp deleted file mode 100644 index e4fba1561d..0000000000 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ /dev/null @@ -1,2816 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "SurfaceTexture_test" -//#define LOG_NDEBUG 0 - -#include <gtest/gtest.h> -#include <gui/GLConsumer.h> -#include <ui/GraphicBuffer.h> -#include <utils/String8.h> -#include <utils/threads.h> - -#include <gui/ISurfaceComposer.h> -#include <gui/Surface.h> -#include <gui/SurfaceComposerClient.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES/gl.h> -#include <GLES/glext.h> -#include <GLES2/gl2.h> -#include <GLES2/gl2ext.h> - -#include <ui/FramebufferNativeWindow.h> -#include <android/native_window.h> - -namespace android { - -class GLTest : public ::testing::Test { -protected: - - GLTest(): - mEglDisplay(EGL_NO_DISPLAY), - mEglSurface(EGL_NO_SURFACE), - mEglContext(EGL_NO_CONTEXT) { - } - - virtual void SetUp() { - const ::testing::TestInfo* const testInfo = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGV("Begin test: %s.%s", testInfo->test_case_name(), - testInfo->name()); - - mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); - - EGLint majorVersion; - EGLint minorVersion; - EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - RecordProperty("EglVersionMajor", majorVersion); - RecordProperty("EglVersionMajor", minorVersion); - - EGLint numConfigs = 0; - EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig, - 1, &numConfigs)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS"); - if (displaySecsEnv != NULL) { - mDisplaySecs = atoi(displaySecsEnv); - if (mDisplaySecs < 0) { - mDisplaySecs = 0; - } - } else { - mDisplaySecs = 0; - } - - if (mDisplaySecs > 0) { - mComposerClient = new SurfaceComposerClient; - ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); - - mSurfaceControl = mComposerClient->createSurface( - String8("Test Surface"), - getSurfaceWidth(), getSurfaceHeight(), - PIXEL_FORMAT_RGB_888, 0); - - ASSERT_TRUE(mSurfaceControl != NULL); - ASSERT_TRUE(mSurfaceControl->isValid()); - - SurfaceComposerClient::openGlobalTransaction(); - ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); - ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); - SurfaceComposerClient::closeGlobalTransaction(); - - sp<ANativeWindow> window = mSurfaceControl->getSurface(); - mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, - window.get(), NULL); - } else { - EGLint pbufferAttribs[] = { - EGL_WIDTH, getSurfaceWidth(), - EGL_HEIGHT, getSurfaceHeight(), - EGL_NONE }; - - mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig, - pbufferAttribs); - } - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_SURFACE, mEglSurface); - - mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, - getContextAttribs()); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_CONTEXT, mEglContext); - - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - EGLint w, h; - EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - RecordProperty("EglSurfaceWidth", w); - RecordProperty("EglSurfaceHeight", h); - - glViewport(0, 0, w, h); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - } - - virtual void TearDown() { - // Display the result - if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) { - eglSwapBuffers(mEglDisplay, mEglSurface); - sleep(mDisplaySecs); - } - - if (mComposerClient != NULL) { - mComposerClient->dispose(); - } - if (mEglContext != EGL_NO_CONTEXT) { - eglDestroyContext(mEglDisplay, mEglContext); - } - if (mEglSurface != EGL_NO_SURFACE) { - eglDestroySurface(mEglDisplay, mEglSurface); - } - if (mEglDisplay != EGL_NO_DISPLAY) { - eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT); - eglTerminate(mEglDisplay); - } - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - const ::testing::TestInfo* const testInfo = - ::testing::UnitTest::GetInstance()->current_test_info(); - ALOGV("End test: %s.%s", testInfo->test_case_name(), - testInfo->name()); - } - - virtual EGLint const* getConfigAttribs() { - static EGLint sDefaultConfigAttribs[] = { - EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 16, - EGL_STENCIL_SIZE, 8, - EGL_NONE }; - - return sDefaultConfigAttribs; - } - - virtual EGLint const* getContextAttribs() { - static EGLint sDefaultContextAttribs[] = { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE }; - - return sDefaultContextAttribs; - } - - virtual EGLint getSurfaceWidth() { - return 512; - } - - virtual EGLint getSurfaceHeight() { - return 512; - } - - ::testing::AssertionResult checkPixel(int x, int y, int r, - int g, int b, int a, int tolerance=2) { - GLubyte pixel[4]; - String8 msg; - glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); - GLenum err = glGetError(); - if (err != GL_NO_ERROR) { - msg += String8::format("error reading pixel: %#x", err); - while ((err = glGetError()) != GL_NO_ERROR) { - msg += String8::format(", %#x", err); - } - return ::testing::AssertionFailure( - ::testing::Message(msg.string())); - } - if (r >= 0 && abs(r - int(pixel[0])) > tolerance) { - msg += String8::format("r(%d isn't %d)", pixel[0], r); - } - if (g >= 0 && abs(g - int(pixel[1])) > tolerance) { - if (!msg.isEmpty()) { - msg += " "; - } - msg += String8::format("g(%d isn't %d)", pixel[1], g); - } - if (b >= 0 && abs(b - int(pixel[2])) > tolerance) { - if (!msg.isEmpty()) { - msg += " "; - } - msg += String8::format("b(%d isn't %d)", pixel[2], b); - } - if (a >= 0 && abs(a - int(pixel[3])) > tolerance) { - if (!msg.isEmpty()) { - msg += " "; - } - msg += String8::format("a(%d isn't %d)", pixel[3], a); - } - if (!msg.isEmpty()) { - return ::testing::AssertionFailure( - ::testing::Message(msg.string())); - } else { - return ::testing::AssertionSuccess(); - } - } - - ::testing::AssertionResult assertRectEq(const Rect &r1, - const Rect &r2, int tolerance=1) { - - String8 msg; - - if (abs(r1.left - r2.left) > tolerance) { - msg += String8::format("left(%d isn't %d)", r1.left, r2.left); - } - if (abs(r1.top - r2.top) > tolerance) { - if (!msg.isEmpty()) { - msg += " "; - } - msg += String8::format("top(%d isn't %d)", r1.top, r2.top); - } - if (abs(r1.right - r2.right) > tolerance) { - if (!msg.isEmpty()) { - msg += " "; - } - msg += String8::format("right(%d isn't %d)", r1.right, r2.right); - } - if (abs(r1.bottom - r2.bottom) > tolerance) { - if (!msg.isEmpty()) { - msg += " "; - } - msg += String8::format("bottom(%d isn't %d)", r1.bottom, r2.bottom); - } - if (!msg.isEmpty()) { - msg += String8::format(" R1: [%d %d %d %d] R2: [%d %d %d %d]", - r1.left, r1.top, r1.right, r1.bottom, - r2.left, r2.top, r2.right, r2.bottom); - fprintf(stderr, "assertRectEq: %s\n", msg.string()); - return ::testing::AssertionFailure( - ::testing::Message(msg.string())); - } else { - return ::testing::AssertionSuccess(); - } - } - - int mDisplaySecs; - sp<SurfaceComposerClient> mComposerClient; - sp<SurfaceControl> mSurfaceControl; - - EGLDisplay mEglDisplay; - EGLSurface mEglSurface; - EGLContext mEglContext; - EGLConfig mGlConfig; -}; - -static void loadShader(GLenum shaderType, const char* pSource, - GLuint* outShader) { - GLuint shader = glCreateShader(shaderType); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - if (shader) { - glShaderSource(shader, 1, &pSource, NULL); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glCompileShader(shader); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - GLint compiled = 0; - glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - if (!compiled) { - GLint infoLen = 0; - glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - if (infoLen) { - char* buf = (char*) malloc(infoLen); - if (buf) { - glGetShaderInfoLog(shader, infoLen, NULL, buf); - printf("Shader compile log:\n%s\n", buf); - free(buf); - FAIL(); - } - } else { - char* buf = (char*) malloc(0x1000); - if (buf) { - glGetShaderInfoLog(shader, 0x1000, NULL, buf); - printf("Shader compile log:\n%s\n", buf); - free(buf); - FAIL(); - } - } - glDeleteShader(shader); - shader = 0; - } - } - ASSERT_TRUE(shader != 0); - *outShader = shader; -} - -static void createProgram(const char* pVertexSource, - const char* pFragmentSource, GLuint* outPgm) { - GLuint vertexShader, fragmentShader; - { - SCOPED_TRACE("compiling vertex shader"); - ASSERT_NO_FATAL_FAILURE(loadShader(GL_VERTEX_SHADER, pVertexSource, - &vertexShader)); - } - { - SCOPED_TRACE("compiling fragment shader"); - ASSERT_NO_FATAL_FAILURE(loadShader(GL_FRAGMENT_SHADER, pFragmentSource, - &fragmentShader)); - } - - GLuint program = glCreateProgram(); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - if (program) { - glAttachShader(program, vertexShader); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glAttachShader(program, fragmentShader); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glLinkProgram(program); - GLint linkStatus = GL_FALSE; - glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); - if (linkStatus != GL_TRUE) { - GLint bufLength = 0; - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); - if (bufLength) { - char* buf = (char*) malloc(bufLength); - if (buf) { - glGetProgramInfoLog(program, bufLength, NULL, buf); - printf("Program link log:\n%s\n", buf); - free(buf); - FAIL(); - } - } - glDeleteProgram(program); - program = 0; - } - } - glDeleteShader(vertexShader); - glDeleteShader(fragmentShader); - ASSERT_TRUE(program != 0); - *outPgm = program; -} - -static int abs(int value) { - return value > 0 ? value : -value; -} - - -// XXX: Code above this point should live elsewhere - -class MultiTextureConsumerTest : public GLTest { -protected: - enum { TEX_ID = 123 }; - - virtual void SetUp() { - GLTest::SetUp(); - sp<BufferQueue> bq = new BufferQueue(); - mGlConsumer = new GLConsumer(bq, TEX_ID); - mSurface = new Surface(bq); - mANW = mSurface.get(); - - } - virtual void TearDown() { - GLTest::TearDown(); - } - virtual EGLint const* getContextAttribs() { - return NULL; - } - virtual EGLint const* getConfigAttribs() { - static EGLint sDefaultConfigAttribs[] = { - EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_NONE }; - - return sDefaultConfigAttribs; - } - sp<GLConsumer> mGlConsumer; - sp<Surface> mSurface; - ANativeWindow* mANW; -}; - - -TEST_F(MultiTextureConsumerTest, EGLImageTargetWorks) { - ANativeWindow_Buffer buffer; - - ASSERT_EQ(native_window_set_usage(mANW, GRALLOC_USAGE_SW_WRITE_OFTEN), NO_ERROR); - ASSERT_EQ(native_window_set_buffers_format(mANW, HAL_PIXEL_FORMAT_RGBA_8888), NO_ERROR); - - glShadeModel(GL_FLAT); - glDisable(GL_DITHER); - glDisable(GL_CULL_FACE); - glViewport(0, 0, getSurfaceWidth(), getSurfaceHeight()); - glOrthof(0, getSurfaceWidth(), 0, getSurfaceHeight(), 0, 1); - glEnableClientState(GL_VERTEX_ARRAY); - glColor4f(1, 1, 1, 1); - - glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID); - glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - uint32_t texel = 0x80808080; - glBindTexture(GL_TEXTURE_2D, TEX_ID+1); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &texel); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, TEX_ID+1); - glEnable(GL_TEXTURE_2D); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID); - glEnable(GL_TEXTURE_EXTERNAL_OES); - glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - glClear(GL_COLOR_BUFFER_BIT); - for (int i=0 ; i<8 ; i++) { - mSurface->lock(&buffer, NULL); - memset(buffer.bits, (i&7) * 0x20, buffer.stride * buffer.height * 4); - mSurface->unlockAndPost(); - - mGlConsumer->updateTexImage(); - - GLfloat vertices[][2] = { {i*16.0f, 0}, {(i+1)*16.0f, 0}, {(i+1)*16.0f, 16.0f}, {i*16.0f, 16.0f} }; - glVertexPointer(2, GL_FLOAT, 0, vertices); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - } - - for (int i=0 ; i<8 ; i++) { - EXPECT_TRUE(checkPixel(i*16 + 8, 8, i*16, i*16, i*16, i*16, 0)); - } -} - - - -class SurfaceTextureGLTest : public GLTest { -protected: - enum { TEX_ID = 123 }; - - virtual void SetUp() { - GLTest::SetUp(); - sp<BufferQueue> bq = new BufferQueue(); - mBQ = bq; - mST = new GLConsumer(bq, TEX_ID); - mSTC = new Surface(bq); - mANW = mSTC; - mTextureRenderer = new TextureRenderer(TEX_ID, mST); - ASSERT_NO_FATAL_FAILURE(mTextureRenderer->SetUp()); - mFW = new FrameWaiter; - mST->setFrameAvailableListener(mFW); - } - - virtual void TearDown() { - mANW.clear(); - mSTC.clear(); - mST.clear(); - GLTest::TearDown(); - } - - void drawTexture() { - mTextureRenderer->drawTexture(); - } - - class TextureRenderer: public RefBase { - public: - TextureRenderer(GLuint texName, const sp<GLConsumer>& st): - mTexName(texName), - mST(st) { - } - - void SetUp() { - const char vsrc[] = - "attribute vec4 vPosition;\n" - "varying vec2 texCoords;\n" - "uniform mat4 texMatrix;\n" - "void main() {\n" - " vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n" - " texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n" - " gl_Position = vPosition;\n" - "}\n"; - - const char fsrc[] = - "#extension GL_OES_EGL_image_external : require\n" - "precision mediump float;\n" - "uniform samplerExternalOES texSampler;\n" - "varying vec2 texCoords;\n" - "void main() {\n" - " gl_FragColor = texture2D(texSampler, texCoords);\n" - "}\n"; - - { - SCOPED_TRACE("creating shader program"); - ASSERT_NO_FATAL_FAILURE(createProgram(vsrc, fsrc, &mPgm)); - } - - mPositionHandle = glGetAttribLocation(mPgm, "vPosition"); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - ASSERT_NE(-1, mPositionHandle); - mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler"); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - ASSERT_NE(-1, mTexSamplerHandle); - mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix"); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - ASSERT_NE(-1, mTexMatrixHandle); - } - - // drawTexture draws the GLConsumer over the entire GL viewport. - void drawTexture() { - static const GLfloat triangleVertices[] = { - -1.0f, 1.0f, - -1.0f, -1.0f, - 1.0f, -1.0f, - 1.0f, 1.0f, - }; - - glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, - triangleVertices); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glEnableVertexAttribArray(mPositionHandle); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - - glUseProgram(mPgm); - glUniform1i(mTexSamplerHandle, 0); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - - // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as - // they're setting the defautls for that target, but when hacking - // things to use GL_TEXTURE_2D they are needed to achieve the same - // behavior. - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, - GL_LINEAR); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, - GL_LINEAR); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, - GL_CLAMP_TO_EDGE); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, - GL_CLAMP_TO_EDGE); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - - GLfloat texMatrix[16]; - mST->getTransformMatrix(texMatrix); - glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - } - - GLuint mTexName; - sp<GLConsumer> mST; - GLuint mPgm; - GLint mPositionHandle; - GLint mTexSamplerHandle; - GLint mTexMatrixHandle; - }; - - class FrameWaiter : public GLConsumer::FrameAvailableListener { - public: - FrameWaiter(): - mPendingFrames(0) { - } - - void waitForFrame() { - Mutex::Autolock lock(mMutex); - while (mPendingFrames == 0) { - mCondition.wait(mMutex); - } - mPendingFrames--; - } - - virtual void onFrameAvailable() { - Mutex::Autolock lock(mMutex); - mPendingFrames++; - mCondition.signal(); - } - - int mPendingFrames; - Mutex mMutex; - Condition mCondition; - }; - - // Note that GLConsumer will lose the notifications - // onBuffersReleased and onFrameAvailable as there is currently - // no way to forward the events. This DisconnectWaiter will not let the - // disconnect finish until finishDisconnect() is called. It will - // also block until a disconnect is called - class DisconnectWaiter : public BnConsumerListener { - public: - DisconnectWaiter () : - mWaitForDisconnect(false), - mPendingFrames(0) { - } - - void waitForFrame() { - Mutex::Autolock lock(mMutex); - while (mPendingFrames == 0) { - mFrameCondition.wait(mMutex); - } - mPendingFrames--; - } - - virtual void onFrameAvailable() { - Mutex::Autolock lock(mMutex); - mPendingFrames++; - mFrameCondition.signal(); - } - - virtual void onBuffersReleased() { - Mutex::Autolock lock(mMutex); - while (!mWaitForDisconnect) { - mDisconnectCondition.wait(mMutex); - } - } - - void finishDisconnect() { - Mutex::Autolock lock(mMutex); - mWaitForDisconnect = true; - mDisconnectCondition.signal(); - } - - private: - Mutex mMutex; - - bool mWaitForDisconnect; - Condition mDisconnectCondition; - - int mPendingFrames; - Condition mFrameCondition; - }; - - sp<BufferQueue> mBQ; - sp<GLConsumer> mST; - sp<Surface> mSTC; - sp<ANativeWindow> mANW; - sp<TextureRenderer> mTextureRenderer; - sp<FrameWaiter> mFW; -}; - -// Fill a YV12 buffer with a multi-colored checkerboard pattern -void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) { - const int blockWidth = w > 16 ? w / 16 : 1; - const int blockHeight = h > 16 ? h / 16 : 1; - const int yuvTexOffsetY = 0; - int yuvTexStrideY = stride; - int yuvTexOffsetV = yuvTexStrideY * h; - int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; - int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; - int yuvTexStrideU = yuvTexStrideV; - for (int x = 0; x < w; x++) { - for (int y = 0; y < h; y++) { - int parityX = (x / blockWidth) & 1; - int parityY = (y / blockHeight) & 1; - unsigned char intensity = (parityX ^ parityY) ? 63 : 191; - buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = intensity; - if (x < w / 2 && y < h / 2) { - buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = intensity; - if (x * 2 < w / 2 && y * 2 < h / 2) { - buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 0] = - buf[yuvTexOffsetV + (y*2 * yuvTexStrideV) + x*2 + 1] = - buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 0] = - buf[yuvTexOffsetV + ((y*2+1) * yuvTexStrideV) + x*2 + 1] = - intensity; - } - } - } - } -} - -// Fill a YV12 buffer with red outside a given rectangle and green inside it. -void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, - const android_native_rect_t& rect) { - const int yuvTexOffsetY = 0; - int yuvTexStrideY = stride; - int yuvTexOffsetV = yuvTexStrideY * h; - int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; - int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * h/2; - int yuvTexStrideU = yuvTexStrideV; - for (int x = 0; x < w; x++) { - for (int y = 0; y < h; y++) { - bool inside = rect.left <= x && x < rect.right && - rect.top <= y && y < rect.bottom; - buf[yuvTexOffsetY + (y * yuvTexStrideY) + x] = inside ? 240 : 64; - if (x < w / 2 && y < h / 2) { - bool inside = rect.left <= 2*x && 2*x < rect.right && - rect.top <= 2*y && 2*y < rect.bottom; - buf[yuvTexOffsetU + (y * yuvTexStrideU) + x] = 16; - buf[yuvTexOffsetV + (y * yuvTexStrideV) + x] = - inside ? 16 : 255; - } - } - } -} - -void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { - const size_t PIXEL_SIZE = 4; - for (int x = 0; x < w; x++) { - for (int y = 0; y < h; y++) { - off_t offset = (y * stride + x) * PIXEL_SIZE; - for (int c = 0; c < 4; c++) { - int parityX = (x / (1 << (c+2))) & 1; - int parityY = (y / (1 << (c+2))) & 1; - buf[offset + c] = (parityX ^ parityY) ? 231 : 35; - } - } - } -} - -void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r, - uint8_t g, uint8_t b, uint8_t a) { - const size_t PIXEL_SIZE = 4; - for (int y = 0; y < h; y++) { - for (int x = 0; x < h; x++) { - off_t offset = (y * stride + x) * PIXEL_SIZE; - buf[offset + 0] = r; - buf[offset + 1] = g; - buf[offset + 2] = b; - buf[offset + 3] = a; - } - } -} - -// Produce a single RGBA8 frame by filling a buffer with a checkerboard pattern -// using the CPU. This assumes that the ANativeWindow is already configured to -// allow this to be done (e.g. the format is set to RGBA8). -// -// Calls to this function should be wrapped in an ASSERT_NO_FATAL_FAILURE(). -void produceOneRGBA8Frame(const sp<ANativeWindow>& anw) { - android_native_buffer_t* anb; - ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(), - &anb)); - ASSERT_TRUE(anb != NULL); - - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - - uint8_t* img = NULL; - ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, - (void**)(&img))); - fillRGBA8Buffer(img, buf->getWidth(), buf->getHeight(), buf->getStride()); - ASSERT_EQ(NO_ERROR, buf->unlock()); - ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf->getNativeBuffer(), - -1)); -} - -TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { - const int texWidth = 64; - const int texHeight = 66; - - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); - - ANativeWindowBuffer* anb; - ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), - &anb)); - ASSERT_TRUE(anb != NULL); - - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - - // Fill the buffer with the a checkerboard pattern - uint8_t* img = NULL; - buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); - fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); - buf->unlock(); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), - -1)); - - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, texWidth, texHeight); - drawTexture(); - - EXPECT_TRUE(checkPixel( 0, 0, 255, 127, 255, 255, 3)); - EXPECT_TRUE(checkPixel(63, 0, 0, 133, 0, 255, 3)); - EXPECT_TRUE(checkPixel(63, 65, 0, 133, 0, 255, 3)); - EXPECT_TRUE(checkPixel( 0, 65, 255, 127, 255, 255, 3)); - - EXPECT_TRUE(checkPixel(22, 44, 255, 127, 255, 255, 3)); - EXPECT_TRUE(checkPixel(45, 52, 255, 127, 255, 255, 3)); - EXPECT_TRUE(checkPixel(52, 51, 98, 255, 73, 255, 3)); - EXPECT_TRUE(checkPixel( 7, 31, 155, 0, 118, 255, 3)); - EXPECT_TRUE(checkPixel(31, 9, 107, 24, 87, 255, 3)); - EXPECT_TRUE(checkPixel(29, 35, 255, 127, 255, 255, 3)); - EXPECT_TRUE(checkPixel(36, 22, 155, 29, 0, 255, 3)); -} - -TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferPow2) { - const int texWidth = 64; - const int texHeight = 64; - - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); - - ANativeWindowBuffer* anb; - ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), - &anb)); - ASSERT_TRUE(anb != NULL); - - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - - // Fill the buffer with the a checkerboard pattern - uint8_t* img = NULL; - buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); - fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); - buf->unlock(); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), - -1)); - - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, texWidth, texHeight); - drawTexture(); - - EXPECT_TRUE(checkPixel( 0, 0, 0, 133, 0, 255)); - EXPECT_TRUE(checkPixel(63, 0, 255, 127, 255, 255)); - EXPECT_TRUE(checkPixel(63, 63, 0, 133, 0, 255)); - EXPECT_TRUE(checkPixel( 0, 63, 255, 127, 255, 255)); - - EXPECT_TRUE(checkPixel(22, 19, 100, 255, 74, 255)); - EXPECT_TRUE(checkPixel(45, 11, 100, 255, 74, 255)); - EXPECT_TRUE(checkPixel(52, 12, 155, 0, 181, 255)); - EXPECT_TRUE(checkPixel( 7, 32, 150, 237, 170, 255)); - EXPECT_TRUE(checkPixel(31, 54, 0, 71, 117, 255)); - EXPECT_TRUE(checkPixel(29, 28, 0, 133, 0, 255)); - EXPECT_TRUE(checkPixel(36, 41, 100, 232, 255, 255)); -} - -TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { - const int texWidth = 64; - const int texHeight = 66; - - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); - - android_native_rect_t crops[] = { - {4, 6, 22, 36}, - {0, 6, 22, 36}, - {4, 0, 22, 36}, - {4, 6, texWidth, 36}, - {4, 6, 22, texHeight}, - }; - - for (int i = 0; i < 5; i++) { - const android_native_rect_t& crop(crops[i]); - SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", - crop.left, crop.top, crop.right, crop.bottom).string()); - - ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop)); - - ANativeWindowBuffer* anb; - ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), - &anb)); - ASSERT_TRUE(anb != NULL); - - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - - uint8_t* img = NULL; - buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); - fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop); - buf->unlock(); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), - buf->getNativeBuffer(), -1)); - - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, 64, 64); - drawTexture(); - - EXPECT_TRUE(checkPixel( 0, 0, 82, 255, 35, 255)); - EXPECT_TRUE(checkPixel(63, 0, 82, 255, 35, 255)); - EXPECT_TRUE(checkPixel(63, 63, 82, 255, 35, 255)); - EXPECT_TRUE(checkPixel( 0, 63, 82, 255, 35, 255)); - - EXPECT_TRUE(checkPixel(25, 14, 82, 255, 35, 255)); - EXPECT_TRUE(checkPixel(35, 31, 82, 255, 35, 255)); - EXPECT_TRUE(checkPixel(57, 6, 82, 255, 35, 255)); - EXPECT_TRUE(checkPixel( 5, 42, 82, 255, 35, 255)); - EXPECT_TRUE(checkPixel(32, 33, 82, 255, 35, 255)); - EXPECT_TRUE(checkPixel(16, 26, 82, 255, 35, 255)); - EXPECT_TRUE(checkPixel(46, 51, 82, 255, 35, 255)); - } -} - -// This test is intended to catch synchronization bugs between the CPU-written -// and GPU-read buffers. -TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { - enum { texWidth = 16 }; - enum { texHeight = 16 }; - enum { numFrames = 1024 }; - - ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_WRITE_OFTEN)); - - struct TestPixel { - int x; - int y; - }; - const TestPixel testPixels[] = { - { 4, 11 }, - { 12, 14 }, - { 7, 2 }, - }; - enum {numTestPixels = sizeof(testPixels) / sizeof(testPixels[0])}; - - class ProducerThread : public Thread { - public: - ProducerThread(const sp<ANativeWindow>& anw, - const TestPixel* testPixels): - mANW(anw), - mTestPixels(testPixels) { - } - - virtual ~ProducerThread() { - } - - virtual bool threadLoop() { - for (int i = 0; i < numFrames; i++) { - ANativeWindowBuffer* anb; - if (native_window_dequeue_buffer_and_wait(mANW.get(), - &anb) != NO_ERROR) { - return false; - } - if (anb == NULL) { - return false; - } - - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - - const int yuvTexOffsetY = 0; - int stride = buf->getStride(); - int yuvTexStrideY = stride; - int yuvTexOffsetV = yuvTexStrideY * texHeight; - int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf; - int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2; - int yuvTexStrideU = yuvTexStrideV; - - uint8_t* img = NULL; - buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); - - // Gray out all the test pixels first, so we're more likely to - // see a failure if GL is still texturing from the buffer we - // just dequeued. - for (int j = 0; j < numTestPixels; j++) { - int x = mTestPixels[j].x; - int y = mTestPixels[j].y; - uint8_t value = 128; - img[y*stride + x] = value; - } - - // Fill the buffer with gray. - for (int y = 0; y < texHeight; y++) { - for (int x = 0; x < texWidth; x++) { - img[yuvTexOffsetY + y*yuvTexStrideY + x] = 128; - img[yuvTexOffsetU + (y/2)*yuvTexStrideU + x/2] = 128; - img[yuvTexOffsetV + (y/2)*yuvTexStrideV + x/2] = 128; - } - } - - // Set the test pixels to either white or black. - for (int j = 0; j < numTestPixels; j++) { - int x = mTestPixels[j].x; - int y = mTestPixels[j].y; - uint8_t value = 0; - if (j == (i % numTestPixels)) { - value = 255; - } - img[y*stride + x] = value; - } - - buf->unlock(); - if (mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), -1) - != NO_ERROR) { - return false; - } - } - return false; - } - - sp<ANativeWindow> mANW; - const TestPixel* mTestPixels; - }; - - sp<Thread> pt(new ProducerThread(mANW, testPixels)); - pt->run(); - - glViewport(0, 0, texWidth, texHeight); - - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - - // We wait for the first two frames up front so that the producer will be - // likely to dequeue the buffer that's currently being textured from. - mFW->waitForFrame(); - mFW->waitForFrame(); - - for (int i = 0; i < numFrames; i++) { - SCOPED_TRACE(String8::format("frame %d", i).string()); - - // We must wait for each frame to come in because if we ever do an - // updateTexImage call that doesn't consume a newly available buffer - // then the producer and consumer will get out of sync, which will cause - // a deadlock. - if (i > 1) { - mFW->waitForFrame(); - } - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - drawTexture(); - - for (int j = 0; j < numTestPixels; j++) { - int x = testPixels[j].x; - int y = testPixels[j].y; - uint8_t value = 0; - if (j == (i % numTestPixels)) { - // We must y-invert the texture coords - EXPECT_TRUE(checkPixel(x, texHeight-y-1, 255, 255, 255, 255)); - } else { - // We must y-invert the texture coords - EXPECT_TRUE(checkPixel(x, texHeight-y-1, 0, 0, 0, 255)); - } - } - } - - pt->requestExitAndWait(); -} - -TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferNpot) { - const int texWidth = 64; - const int texHeight = 66; - - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); - - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, texWidth, texHeight); - drawTexture(); - - EXPECT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); - EXPECT_TRUE(checkPixel(63, 0, 231, 231, 231, 231)); - EXPECT_TRUE(checkPixel(63, 65, 231, 231, 231, 231)); - EXPECT_TRUE(checkPixel( 0, 65, 35, 35, 35, 35)); - - EXPECT_TRUE(checkPixel(15, 10, 35, 231, 231, 231)); - EXPECT_TRUE(checkPixel(23, 65, 231, 35, 231, 35)); - EXPECT_TRUE(checkPixel(19, 40, 35, 231, 35, 35)); - EXPECT_TRUE(checkPixel(38, 30, 231, 35, 35, 35)); - EXPECT_TRUE(checkPixel(42, 54, 35, 35, 35, 231)); - EXPECT_TRUE(checkPixel(37, 34, 35, 231, 231, 231)); - EXPECT_TRUE(checkPixel(31, 8, 231, 35, 35, 231)); - EXPECT_TRUE(checkPixel(37, 47, 231, 35, 231, 231)); - EXPECT_TRUE(checkPixel(25, 38, 35, 35, 35, 35)); - EXPECT_TRUE(checkPixel(49, 6, 35, 231, 35, 35)); - EXPECT_TRUE(checkPixel(54, 50, 35, 231, 231, 231)); - EXPECT_TRUE(checkPixel(27, 26, 231, 231, 231, 231)); - EXPECT_TRUE(checkPixel(10, 6, 35, 35, 231, 231)); - EXPECT_TRUE(checkPixel(29, 4, 35, 35, 35, 231)); - EXPECT_TRUE(checkPixel(55, 28, 35, 35, 231, 35)); - EXPECT_TRUE(checkPixel(58, 55, 35, 35, 231, 231)); -} - -TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) { - const int texWidth = 64; - const int texHeight = 64; - - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); - - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, texWidth, texHeight); - drawTexture(); - - EXPECT_TRUE(checkPixel( 0, 0, 231, 231, 231, 231)); - EXPECT_TRUE(checkPixel(63, 0, 35, 35, 35, 35)); - EXPECT_TRUE(checkPixel(63, 63, 231, 231, 231, 231)); - EXPECT_TRUE(checkPixel( 0, 63, 35, 35, 35, 35)); - - EXPECT_TRUE(checkPixel(12, 46, 231, 231, 231, 35)); - EXPECT_TRUE(checkPixel(16, 1, 231, 231, 35, 231)); - EXPECT_TRUE(checkPixel(21, 12, 231, 35, 35, 231)); - EXPECT_TRUE(checkPixel(26, 51, 231, 35, 231, 35)); - EXPECT_TRUE(checkPixel( 5, 32, 35, 231, 231, 35)); - EXPECT_TRUE(checkPixel(13, 8, 35, 231, 231, 231)); - EXPECT_TRUE(checkPixel(46, 3, 35, 35, 231, 35)); - EXPECT_TRUE(checkPixel(30, 33, 35, 35, 35, 35)); - EXPECT_TRUE(checkPixel( 6, 52, 231, 231, 35, 35)); - EXPECT_TRUE(checkPixel(55, 33, 35, 231, 35, 231)); - EXPECT_TRUE(checkPixel(16, 29, 35, 35, 231, 231)); - EXPECT_TRUE(checkPixel( 1, 30, 35, 35, 35, 231)); - EXPECT_TRUE(checkPixel(41, 37, 35, 35, 231, 231)); - EXPECT_TRUE(checkPixel(46, 29, 231, 231, 35, 35)); - EXPECT_TRUE(checkPixel(15, 25, 35, 231, 35, 231)); - EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35)); -} - -// Tests if GLConsumer and BufferQueue are robust enough -// to handle a special case where updateTexImage is called -// in the middle of disconnect. This ordering is enforced -// by blocking in the disconnect callback. -TEST_F(SurfaceTextureGLTest, DisconnectStressTest) { - - class ProducerThread : public Thread { - public: - ProducerThread(const sp<ANativeWindow>& anw): - mANW(anw) { - } - - virtual ~ProducerThread() { - } - - virtual bool threadLoop() { - ANativeWindowBuffer* anb; - - native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_EGL); - - for (int numFrames =0 ; numFrames < 2; numFrames ++) { - - if (native_window_dequeue_buffer_and_wait(mANW.get(), - &anb) != NO_ERROR) { - return false; - } - if (anb == NULL) { - return false; - } - if (mANW->queueBuffer(mANW.get(), anb, -1) - != NO_ERROR) { - return false; - } - } - - native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL); - - return false; - } - - private: - sp<ANativeWindow> mANW; - }; - - sp<DisconnectWaiter> dw(new DisconnectWaiter()); - mBQ->consumerConnect(dw, false); - - - sp<Thread> pt(new ProducerThread(mANW)); - pt->run(); - - // eat a frame so GLConsumer will own an at least one slot - dw->waitForFrame(); - EXPECT_EQ(OK,mST->updateTexImage()); - - dw->waitForFrame(); - // Could fail here as GLConsumer thinks it still owns the slot - // but bufferQueue has released all slots - EXPECT_EQ(OK,mST->updateTexImage()); - - dw->finishDisconnect(); -} - - -// This test ensures that the GLConsumer clears the mCurrentTexture -// when it is disconnected and reconnected. Otherwise it will -// attempt to release a buffer that it does not owned -TEST_F(SurfaceTextureGLTest, DisconnectClearsCurrentTexture) { - ASSERT_EQ(OK, native_window_api_connect(mANW.get(), - NATIVE_WINDOW_API_EGL)); - - ANativeWindowBuffer *anb; - - EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); - - EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); - - EXPECT_EQ(OK,mST->updateTexImage()); - EXPECT_EQ(OK,mST->updateTexImage()); - - ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(), - NATIVE_WINDOW_API_EGL)); - ASSERT_EQ(OK, native_window_api_connect(mANW.get(), - NATIVE_WINDOW_API_EGL)); - - EXPECT_EQ(OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); - - // Will fail here if mCurrentTexture is not cleared properly - mFW->waitForFrame(); - EXPECT_EQ(OK,mST->updateTexImage()); - - ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(), - NATIVE_WINDOW_API_EGL)); -} - -TEST_F(SurfaceTextureGLTest, ScaleToWindowMode) { - ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(), - NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW)); - - // The producer image size - ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 512, 512)); - - // The consumer image size (16 x 9) ratio - mST->setDefaultBufferSize(1280, 720); - - ASSERT_EQ(OK, native_window_api_connect(mANW.get(), - NATIVE_WINDOW_API_CPU)); - - ANativeWindowBuffer *anb; - - android_native_rect_t odd = {23, 78, 123, 477}; - ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &odd)); - EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); - mFW->waitForFrame(); - EXPECT_EQ(OK, mST->updateTexImage()); - Rect r = mST->getCurrentCrop(); - assertRectEq(Rect(23, 78, 123, 477), r); - - ASSERT_EQ(OK, native_window_api_disconnect(mANW.get(), - NATIVE_WINDOW_API_CPU)); -} - -// This test ensures the scaling mode does the right thing -// ie NATIVE_WINDOW_SCALING_MODE_CROP should crop -// the image such that it has the same aspect ratio as the -// default buffer size -TEST_F(SurfaceTextureGLTest, CroppedScalingMode) { - ASSERT_EQ(OK, native_window_set_scaling_mode(mANW.get(), - NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)); - - // The producer image size - ASSERT_EQ(OK, native_window_set_buffers_dimensions(mANW.get(), 512, 512)); - - // The consumer image size (16 x 9) ratio - mST->setDefaultBufferSize(1280, 720); - - native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU); - - ANativeWindowBuffer *anb; - - // The crop is in the shape of (320, 180) === 16 x 9 - android_native_rect_t standard = {10, 20, 330, 200}; - ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &standard)); - EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); - mFW->waitForFrame(); - EXPECT_EQ(OK, mST->updateTexImage()); - Rect r = mST->getCurrentCrop(); - // crop should be the same as crop (same aspect ratio) - assertRectEq(Rect(10, 20, 330, 200), r); - - // make this wider then desired aspect 239 x 100 (2.39:1) - android_native_rect_t wide = {20, 30, 259, 130}; - ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &wide)); - EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); - mFW->waitForFrame(); - EXPECT_EQ(OK, mST->updateTexImage()); - r = mST->getCurrentCrop(); - // crop should be the same height, but have cropped left and right borders - // offset is 30.6 px L+, R- - assertRectEq(Rect(51, 30, 228, 130), r); - - // This image is taller then desired aspect 400 x 300 (4:3) - android_native_rect_t narrow = {0, 0, 400, 300}; - ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &narrow)); - EXPECT_EQ (OK, native_window_dequeue_buffer_and_wait(mANW.get(), &anb)); - EXPECT_EQ(OK, mANW->queueBuffer(mANW.get(), anb, -1)); - mFW->waitForFrame(); - EXPECT_EQ(OK, mST->updateTexImage()); - r = mST->getCurrentCrop(); - // crop should be the same width, but have cropped top and bottom borders - // offset is 37.5 px - assertRectEq(Rect(0, 37, 400, 262), r); - - native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); -} - -TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { - class ProducerThread : public Thread { - public: - ProducerThread(const sp<ANativeWindow>& anw): - mANW(anw), - mDequeueError(NO_ERROR) { - } - - virtual ~ProducerThread() { - } - - virtual bool threadLoop() { - Mutex::Autolock lock(mMutex); - ANativeWindowBuffer* anb; - - // Frame 1 - if (native_window_dequeue_buffer_and_wait(mANW.get(), - &anb) != NO_ERROR) { - return false; - } - if (anb == NULL) { - return false; - } - if (mANW->queueBuffer(mANW.get(), anb, -1) - != NO_ERROR) { - return false; - } - - // Frame 2 - if (native_window_dequeue_buffer_and_wait(mANW.get(), - &anb) != NO_ERROR) { - return false; - } - if (anb == NULL) { - return false; - } - if (mANW->queueBuffer(mANW.get(), anb, -1) - != NO_ERROR) { - return false; - } - - // Frame 3 - error expected - mDequeueError = native_window_dequeue_buffer_and_wait(mANW.get(), - &anb); - return false; - } - - status_t getDequeueError() { - Mutex::Autolock lock(mMutex); - return mDequeueError; - } - - private: - sp<ANativeWindow> mANW; - status_t mDequeueError; - Mutex mMutex; - }; - - ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2)); - - sp<Thread> pt(new ProducerThread(mANW)); - pt->run(); - - mFW->waitForFrame(); - mFW->waitForFrame(); - - // Sleep for 100ms to allow the producer thread's dequeueBuffer call to - // block waiting for a buffer to become available. - usleep(100000); - - mST->abandon(); - - pt->requestExitAndWait(); - ASSERT_EQ(NO_INIT, - reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError()); -} - -TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) { - int texHeight = 16; - ANativeWindowBuffer* anb; - - GLint maxTextureSize; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); - - // make sure it works with small textures - mST->setDefaultBufferSize(16, texHeight); - EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), - &anb)); - EXPECT_EQ(16, anb->width); - EXPECT_EQ(texHeight, anb->height); - EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); - EXPECT_EQ(NO_ERROR, mST->updateTexImage()); - - // make sure it works with GL_MAX_TEXTURE_SIZE - mST->setDefaultBufferSize(maxTextureSize, texHeight); - EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), - &anb)); - EXPECT_EQ(maxTextureSize, anb->width); - EXPECT_EQ(texHeight, anb->height); - EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); - EXPECT_EQ(NO_ERROR, mST->updateTexImage()); - - // make sure it fails with GL_MAX_TEXTURE_SIZE+1 - mST->setDefaultBufferSize(maxTextureSize+1, texHeight); - EXPECT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), - &anb)); - EXPECT_EQ(maxTextureSize+1, anb->width); - EXPECT_EQ(texHeight, anb->height); - EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb, -1)); - ASSERT_NE(NO_ERROR, mST->updateTexImage()); -} - -/* - * This test fixture is for testing GL -> GL texture streaming. It creates an - * EGLSurface and an EGLContext for the image producer to use. - */ -class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest { -protected: - SurfaceTextureGLToGLTest(): - mProducerEglSurface(EGL_NO_SURFACE), - mProducerEglContext(EGL_NO_CONTEXT) { - } - - virtual void SetUp() { - SurfaceTextureGLTest::SetUp(); - - mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, - mANW.get(), NULL); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface); - - mProducerEglContext = eglCreateContext(mEglDisplay, mGlConfig, - EGL_NO_CONTEXT, getContextAttribs()); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext); - } - - virtual void TearDown() { - if (mProducerEglContext != EGL_NO_CONTEXT) { - eglDestroyContext(mEglDisplay, mProducerEglContext); - } - if (mProducerEglSurface != EGL_NO_SURFACE) { - eglDestroySurface(mEglDisplay, mProducerEglSurface); - } - SurfaceTextureGLTest::TearDown(); - } - - EGLSurface mProducerEglSurface; - EGLContext mProducerEglContext; -}; - -TEST_F(SurfaceTextureGLToGLTest, TransformHintGetsRespected) { - const uint32_t texWidth = 32; - const uint32_t texHeight = 64; - - mST->setDefaultBufferSize(texWidth, texHeight); - mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); - - // This test requires 3 buffers to avoid deadlock because we're - // both producer and consumer, and only using one thread. - mST->setDefaultMaxBufferCount(3); - - // Do the producer side of things - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Start a buffer with our chosen size and transform hint moving - // through the system. - glClear(GL_COLOR_BUFFER_BIT); // give the driver something to do - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - mST->updateTexImage(); // consume it - // Swap again. - glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - mST->updateTexImage(); - - // The current buffer should either show the effects of the transform - // hint (in the form of an inverse transform), or show that the - // transform hint has been ignored. - sp<GraphicBuffer> buf = mST->getCurrentBuffer(); - if (mST->getCurrentTransform() == NATIVE_WINDOW_TRANSFORM_ROT_270) { - ASSERT_EQ(texWidth, buf->getHeight()); - ASSERT_EQ(texHeight, buf->getWidth()); - } else { - ASSERT_EQ(texWidth, buf->getWidth()); - ASSERT_EQ(texHeight, buf->getHeight()); - } - - // Reset the transform hint and confirm that it takes. - mST->setTransformHint(0); - glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - mST->updateTexImage(); - glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - mST->updateTexImage(); - - buf = mST->getCurrentBuffer(); - ASSERT_EQ((uint32_t) 0, mST->getCurrentTransform()); - ASSERT_EQ(texWidth, buf->getWidth()); - ASSERT_EQ(texHeight, buf->getHeight()); -} - -TEST_F(SurfaceTextureGLToGLTest, TexturingFromGLFilledRGBABufferPow2) { - const int texWidth = 64; - const int texHeight = 64; - - mST->setDefaultBufferSize(texWidth, texHeight); - - // This test requires 3 buffers to complete run on a single thread. - mST->setDefaultMaxBufferCount(3); - - // Do the producer side of things - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // This is needed to ensure we pick up a buffer of the correct size. - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - - glClearColor(0.6, 0.6, 0.6, 0.6); - glClear(GL_COLOR_BUFFER_BIT); - - glEnable(GL_SCISSOR_TEST); - glScissor(4, 4, 4, 4); - glClearColor(1.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - - glScissor(24, 48, 4, 4); - glClearColor(0.0, 1.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - - glScissor(37, 17, 4, 4); - glClearColor(0.0, 0.0, 1.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - - // Do the consumer side of things - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - glDisable(GL_SCISSOR_TEST); - - // Skip the first frame, which was empty - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, texWidth, texHeight); - drawTexture(); - - EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153)); - - EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255)); - EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255)); - EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255)); - EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); -} - -TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) { - sp<GraphicBuffer> buffers[2]; - - // This test requires async mode to run on a single thread. - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - for (int i = 0; i < 2; i++) { - // Produce a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - - // Consume a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - mFW->waitForFrame(); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - buffers[i] = mST->getCurrentBuffer(); - } - - // Destroy the GL texture object to release its ref on buffers[2]. - GLuint texID = TEX_ID; - glDeleteTextures(1, &texID); - - // Destroy the EGLSurface - EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - mProducerEglSurface = EGL_NO_SURFACE; - - // This test should have the only reference to buffer 0. - EXPECT_EQ(1, buffers[0]->getStrongCount()); - - // The GLConsumer should hold a single reference to buffer 1 in its - // mCurrentBuffer member. All of the references in the slots should have - // been released. - EXPECT_EQ(2, buffers[1]->getStrongCount()); -} - -TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) { - sp<GraphicBuffer> buffers[3]; - - // This test requires async mode to run on a single thread. - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - for (int i = 0; i < 3; i++) { - // Produce a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - glClear(GL_COLOR_BUFFER_BIT); - EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Consume a frame - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - mFW->waitForFrame(); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - buffers[i] = mST->getCurrentBuffer(); - } - - // Abandon the GLConsumer, releasing the ref that the GLConsumer has - // on buffers[2]. - mST->abandon(); - - // Destroy the GL texture object to release its ref on buffers[2]. - GLuint texID = TEX_ID; - glDeleteTextures(1, &texID); - - // Destroy the EGLSurface. - EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - mProducerEglSurface = EGL_NO_SURFACE; - - EXPECT_EQ(1, buffers[0]->getStrongCount()); - EXPECT_EQ(1, buffers[1]->getStrongCount()); - - // Depending on how lazily the GL driver dequeues buffers, we may end up - // with either two or three total buffers. If there are three, make sure - // the last one was properly down-ref'd. - if (buffers[2] != buffers[0]) { - EXPECT_EQ(1, buffers[2]->getStrongCount()); - } -} - -TEST_F(SurfaceTextureGLToGLTest, EglMakeCurrentBeforeConsumerDeathUnrefsBuffers) { - sp<GraphicBuffer> buffer; - - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - - // Produce a frame - glClear(GL_COLOR_BUFFER_BIT); - EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Destroy the EGLSurface. - EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - mProducerEglSurface = EGL_NO_SURFACE; - mSTC.clear(); - mANW.clear(); - mTextureRenderer.clear(); - - // Consume a frame - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - buffer = mST->getCurrentBuffer(); - - // Destroy the GL texture object to release its ref - GLuint texID = TEX_ID; - glDeleteTextures(1, &texID); - - // make un-current, all references to buffer should be gone - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, - EGL_NO_SURFACE, EGL_NO_CONTEXT)); - - // Destroy consumer - mST.clear(); - - EXPECT_EQ(1, buffer->getStrongCount()); -} - -TEST_F(SurfaceTextureGLToGLTest, EglMakeCurrentAfterConsumerDeathUnrefsBuffers) { - sp<GraphicBuffer> buffer; - - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - - // Produce a frame - glClear(GL_COLOR_BUFFER_BIT); - EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Destroy the EGLSurface. - EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - mProducerEglSurface = EGL_NO_SURFACE; - mSTC.clear(); - mANW.clear(); - mTextureRenderer.clear(); - - // Consume a frame - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - buffer = mST->getCurrentBuffer(); - - // Destroy the GL texture object to release its ref - GLuint texID = TEX_ID; - glDeleteTextures(1, &texID); - - // Destroy consumer - mST.clear(); - - // make un-current, all references to buffer should be gone - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, - EGL_NO_SURFACE, EGL_NO_CONTEXT)); - - EXPECT_EQ(1, buffer->getStrongCount()); -} - -TEST_F(SurfaceTextureGLToGLTest, TexturingFromUserSizedGLFilledBuffer) { - enum { texWidth = 64 }; - enum { texHeight = 64 }; - - // This test requires 3 buffers to complete run on a single thread. - mST->setDefaultMaxBufferCount(3); - - // Set the user buffer size. - native_window_set_buffers_user_dimensions(mANW.get(), texWidth, texHeight); - - // Do the producer side of things - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // This is needed to ensure we pick up a buffer of the correct size. - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - - glClearColor(0.6, 0.6, 0.6, 0.6); - glClear(GL_COLOR_BUFFER_BIT); - - glEnable(GL_SCISSOR_TEST); - glScissor(4, 4, 1, 1); - glClearColor(1.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - - // Do the consumer side of things - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - glDisable(GL_SCISSOR_TEST); - - // Skip the first frame, which was empty - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, texWidth, texHeight); - drawTexture(); - - EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153)); - - EXPECT_TRUE(checkPixel( 4, 4, 255, 0, 0, 255)); - EXPECT_TRUE(checkPixel( 5, 5, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel( 3, 3, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(45, 52, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(12, 36, 153, 153, 153, 153)); -} - -TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedUserSizedGLFilledBuffer) { - enum { texWidth = 64 }; - enum { texHeight = 16 }; - - // This test requires 3 buffers to complete run on a single thread. - mST->setDefaultMaxBufferCount(3); - - // Set the transform hint. - mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); - - // Set the user buffer size. - native_window_set_buffers_user_dimensions(mANW.get(), texWidth, texHeight); - - // Do the producer side of things - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // This is needed to ensure we pick up a buffer of the correct size and the - // new rotation hint. - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - - glClearColor(0.6, 0.6, 0.6, 0.6); - glClear(GL_COLOR_BUFFER_BIT); - - glEnable(GL_SCISSOR_TEST); - glScissor(24, 4, 1, 1); - glClearColor(1.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - - // Do the consumer side of things - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - glDisable(GL_SCISSOR_TEST); - - // Skip the first frame, which was empty - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, texWidth, texHeight); - drawTexture(); - - EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(63, 15, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel( 0, 15, 153, 153, 153, 153)); - - EXPECT_TRUE(checkPixel(24, 4, 255, 0, 0, 255)); - EXPECT_TRUE(checkPixel(25, 5, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(23, 3, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(45, 13, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(12, 8, 153, 153, 153, 153)); -} - -TEST_F(SurfaceTextureGLToGLTest, TexturingFromPreRotatedGLFilledBuffer) { - enum { texWidth = 64 }; - enum { texHeight = 16 }; - - // This test requires 3 buffers to complete run on a single thread. - mST->setDefaultMaxBufferCount(3); - - // Set the transform hint. - mST->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_90); - - // Set the default buffer size. - mST->setDefaultBufferSize(texWidth, texHeight); - - // Do the producer side of things - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // This is needed to ensure we pick up a buffer of the correct size and the - // new rotation hint. - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - - glClearColor(0.6, 0.6, 0.6, 0.6); - glClear(GL_COLOR_BUFFER_BIT); - - glEnable(GL_SCISSOR_TEST); - glScissor(24, 4, 1, 1); - glClearColor(1.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - - eglSwapBuffers(mEglDisplay, mProducerEglSurface); - - // Do the consumer side of things - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - glDisable(GL_SCISSOR_TEST); - - // Skip the first frame, which was empty - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, texWidth, texHeight); - drawTexture(); - - EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(63, 15, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel( 0, 15, 153, 153, 153, 153)); - - EXPECT_TRUE(checkPixel(24, 4, 255, 0, 0, 255)); - EXPECT_TRUE(checkPixel(25, 5, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(23, 3, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(45, 13, 153, 153, 153, 153)); - EXPECT_TRUE(checkPixel(12, 8, 153, 153, 153, 153)); -} - -/* - * This test fixture is for testing GL -> GL texture streaming from one thread - * to another. It contains functionality to create a producer thread that will - * perform GL rendering to an ANativeWindow that feeds frames to a - * GLConsumer. Additionally it supports interlocking the producer and - * consumer threads so that a specific sequence of calls can be - * deterministically created by the test. - * - * The intended usage is as follows: - * - * TEST_F(...) { - * class PT : public ProducerThread { - * virtual void render() { - * ... - * swapBuffers(); - * } - * }; - * - * runProducerThread(new PT()); - * - * // The order of these calls will vary from test to test and may include - * // multiple frames and additional operations (e.g. GL rendering from the - * // texture). - * fc->waitForFrame(); - * mST->updateTexImage(); - * fc->finishFrame(); - * } - * - */ -class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest { -protected: - - // ProducerThread is an abstract base class to simplify the creation of - // OpenGL ES frame producer threads. - class ProducerThread : public Thread { - public: - virtual ~ProducerThread() { - } - - void setEglObjects(EGLDisplay producerEglDisplay, - EGLSurface producerEglSurface, - EGLContext producerEglContext) { - mProducerEglDisplay = producerEglDisplay; - mProducerEglSurface = producerEglSurface; - mProducerEglContext = producerEglContext; - } - - virtual bool threadLoop() { - eglMakeCurrent(mProducerEglDisplay, mProducerEglSurface, - mProducerEglSurface, mProducerEglContext); - render(); - eglMakeCurrent(mProducerEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT); - return false; - } - - protected: - virtual void render() = 0; - - void swapBuffers() { - eglSwapBuffers(mProducerEglDisplay, mProducerEglSurface); - } - - EGLDisplay mProducerEglDisplay; - EGLSurface mProducerEglSurface; - EGLContext mProducerEglContext; - }; - - // FrameCondition is a utility class for interlocking between the producer - // and consumer threads. The FrameCondition object should be created and - // destroyed in the consumer thread only. The consumer thread should set - // the FrameCondition as the FrameAvailableListener of the GLConsumer, - // and should call both waitForFrame and finishFrame once for each expected - // frame. - // - // This interlocking relies on the fact that onFrameAvailable gets called - // synchronously from GLConsumer::queueBuffer. - class FrameCondition : public GLConsumer::FrameAvailableListener { - public: - FrameCondition(): - mFrameAvailable(false), - mFrameFinished(false) { - } - - // waitForFrame waits for the next frame to arrive. This should be - // called from the consumer thread once for every frame expected by the - // test. - void waitForFrame() { - Mutex::Autolock lock(mMutex); - ALOGV("+waitForFrame"); - while (!mFrameAvailable) { - mFrameAvailableCondition.wait(mMutex); - } - mFrameAvailable = false; - ALOGV("-waitForFrame"); - } - - // Allow the producer to return from its swapBuffers call and continue - // on to produce the next frame. This should be called by the consumer - // thread once for every frame expected by the test. - void finishFrame() { - Mutex::Autolock lock(mMutex); - ALOGV("+finishFrame"); - mFrameFinished = true; - mFrameFinishCondition.signal(); - ALOGV("-finishFrame"); - } - - // This should be called by GLConsumer on the producer thread. - virtual void onFrameAvailable() { - Mutex::Autolock lock(mMutex); - ALOGV("+onFrameAvailable"); - mFrameAvailable = true; - mFrameAvailableCondition.signal(); - while (!mFrameFinished) { - mFrameFinishCondition.wait(mMutex); - } - mFrameFinished = false; - ALOGV("-onFrameAvailable"); - } - - protected: - bool mFrameAvailable; - bool mFrameFinished; - - Mutex mMutex; - Condition mFrameAvailableCondition; - Condition mFrameFinishCondition; - }; - - virtual void SetUp() { - SurfaceTextureGLToGLTest::SetUp(); - mFC = new FrameCondition(); - mST->setFrameAvailableListener(mFC); - } - - virtual void TearDown() { - if (mProducerThread != NULL) { - mProducerThread->requestExitAndWait(); - } - mProducerThread.clear(); - mFC.clear(); - SurfaceTextureGLToGLTest::TearDown(); - } - - void runProducerThread(const sp<ProducerThread> producerThread) { - ASSERT_TRUE(mProducerThread == NULL); - mProducerThread = producerThread; - producerThread->setEglObjects(mEglDisplay, mProducerEglSurface, - mProducerEglContext); - producerThread->run(); - } - - sp<ProducerThread> mProducerThread; - sp<FrameCondition> mFC; -}; - -TEST_F(SurfaceTextureGLThreadToGLTest, - UpdateTexImageBeforeFrameFinishedCompletes) { - class PT : public ProducerThread { - virtual void render() { - glClearColor(0.0f, 1.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - swapBuffers(); - } - }; - - runProducerThread(new PT()); - - mFC->waitForFrame(); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - mFC->finishFrame(); - - // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! -} - -TEST_F(SurfaceTextureGLThreadToGLTest, - UpdateTexImageAfterFrameFinishedCompletes) { - class PT : public ProducerThread { - virtual void render() { - glClearColor(0.0f, 1.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - swapBuffers(); - } - }; - - runProducerThread(new PT()); - - mFC->waitForFrame(); - mFC->finishFrame(); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! -} - -TEST_F(SurfaceTextureGLThreadToGLTest, - RepeatedUpdateTexImageBeforeFrameFinishedCompletes) { - enum { NUM_ITERATIONS = 1024 }; - - class PT : public ProducerThread { - virtual void render() { - for (int i = 0; i < NUM_ITERATIONS; i++) { - glClearColor(0.0f, 1.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - ALOGV("+swapBuffers"); - swapBuffers(); - ALOGV("-swapBuffers"); - } - } - }; - - runProducerThread(new PT()); - - for (int i = 0; i < NUM_ITERATIONS; i++) { - mFC->waitForFrame(); - ALOGV("+updateTexImage"); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - ALOGV("-updateTexImage"); - mFC->finishFrame(); - - // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! - } -} - -TEST_F(SurfaceTextureGLThreadToGLTest, - RepeatedUpdateTexImageAfterFrameFinishedCompletes) { - enum { NUM_ITERATIONS = 1024 }; - - class PT : public ProducerThread { - virtual void render() { - for (int i = 0; i < NUM_ITERATIONS; i++) { - glClearColor(0.0f, 1.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - ALOGV("+swapBuffers"); - swapBuffers(); - ALOGV("-swapBuffers"); - } - } - }; - - runProducerThread(new PT()); - - for (int i = 0; i < NUM_ITERATIONS; i++) { - mFC->waitForFrame(); - mFC->finishFrame(); - ALOGV("+updateTexImage"); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - ALOGV("-updateTexImage"); - - // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported! - } -} - -// XXX: This test is disabled because it is currently hanging on some devices. -TEST_F(SurfaceTextureGLThreadToGLTest, - DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) { - enum { NUM_ITERATIONS = 64 }; - - class PT : public ProducerThread { - virtual void render() { - for (int i = 0; i < NUM_ITERATIONS; i++) { - glClearColor(0.0f, 1.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - ALOGV("+swapBuffers"); - swapBuffers(); - ALOGV("-swapBuffers"); - } - } - }; - - ASSERT_EQ(OK, mST->setDefaultMaxBufferCount(2)); - - runProducerThread(new PT()); - - // Allow three frames to be rendered and queued before starting the - // rendering in this thread. For the latter two frames we don't call - // updateTexImage so the next dequeue from the producer thread will block - // waiting for a frame to become available. - mFC->waitForFrame(); - mFC->finishFrame(); - - // We must call updateTexImage to consume the first frame so that the - // SurfaceTexture is able to reduce the buffer count to 2. This is because - // the GL driver may dequeue a buffer when the EGLSurface is created, and - // that happens before we call setDefaultMaxBufferCount. It's possible that the - // driver does not dequeue a buffer at EGLSurface creation time, so we - // cannot rely on this to cause the second dequeueBuffer call to block. - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - mFC->waitForFrame(); - mFC->finishFrame(); - mFC->waitForFrame(); - mFC->finishFrame(); - - // Sleep for 100ms to allow the producer thread's dequeueBuffer call to - // block waiting for a buffer to become available. - usleep(100000); - - // Render and present a number of images. This thread should not be blocked - // by the fact that the producer thread is blocking in dequeue. - for (int i = 0; i < NUM_ITERATIONS; i++) { - glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(mEglDisplay, mEglSurface); - } - - // Consume the two pending buffers to unblock the producer thread. - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - // Consume the remaining buffers from the producer thread. - for (int i = 0; i < NUM_ITERATIONS-3; i++) { - mFC->waitForFrame(); - mFC->finishFrame(); - ALOGV("+updateTexImage"); - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - ALOGV("-updateTexImage"); - } -} - -class SurfaceTextureFBOTest : public SurfaceTextureGLTest { -protected: - - virtual void SetUp() { - SurfaceTextureGLTest::SetUp(); - - glGenFramebuffers(1, &mFbo); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - - glGenTextures(1, &mFboTex); - glBindTexture(GL_TEXTURE_2D, mFboTex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(), - getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glBindTexture(GL_TEXTURE_2D, 0); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - - glBindFramebuffer(GL_FRAMEBUFFER, mFbo); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, mFboTex, 0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - } - - virtual void TearDown() { - SurfaceTextureGLTest::TearDown(); - - glDeleteTextures(1, &mFboTex); - glDeleteFramebuffers(1, &mFbo); - } - - GLuint mFbo; - GLuint mFboTex; -}; - -// This test is intended to verify that proper synchronization is done when -// rendering into an FBO. -TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { - const int texWidth = 64; - const int texHeight = 64; - - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); - - android_native_buffer_t* anb; - ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), - &anb)); - ASSERT_TRUE(anb != NULL); - - sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); - - // Fill the buffer with green - uint8_t* img = NULL; - buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); - fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255, - 0, 255); - buf->unlock(); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer(), - -1)); - - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - glBindFramebuffer(GL_FRAMEBUFFER, mFbo); - drawTexture(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - - for (int i = 0; i < 4; i++) { - SCOPED_TRACE(String8::format("frame %d", i).string()); - - ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(mANW.get(), - &anb)); - ASSERT_TRUE(anb != NULL); - - buf = new GraphicBuffer(anb, false); - - // Fill the buffer with red - ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, - (void**)(&img))); - fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 255, 0, - 0, 255); - ASSERT_EQ(NO_ERROR, buf->unlock()); - ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), - buf->getNativeBuffer(), -1)); - - ASSERT_EQ(NO_ERROR, mST->updateTexImage()); - - drawTexture(); - - EXPECT_TRUE(checkPixel( 24, 39, 255, 0, 0, 255)); - } - - glBindFramebuffer(GL_FRAMEBUFFER, mFbo); - - EXPECT_TRUE(checkPixel( 24, 39, 0, 255, 0, 255)); -} - -class SurfaceTextureMultiContextGLTest : public SurfaceTextureGLTest { -protected: - enum { SECOND_TEX_ID = 123 }; - enum { THIRD_TEX_ID = 456 }; - - SurfaceTextureMultiContextGLTest(): - mSecondEglContext(EGL_NO_CONTEXT) { - } - - virtual void SetUp() { - SurfaceTextureGLTest::SetUp(); - - // Set up the secondary context and texture renderer. - mSecondEglContext = eglCreateContext(mEglDisplay, mGlConfig, - EGL_NO_CONTEXT, getContextAttribs()); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_CONTEXT, mSecondEglContext); - - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mSecondEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - mSecondTextureRenderer = new TextureRenderer(SECOND_TEX_ID, mST); - ASSERT_NO_FATAL_FAILURE(mSecondTextureRenderer->SetUp()); - - // Set up the tertiary context and texture renderer. - mThirdEglContext = eglCreateContext(mEglDisplay, mGlConfig, - EGL_NO_CONTEXT, getContextAttribs()); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_CONTEXT, mThirdEglContext); - - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mThirdEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - mThirdTextureRenderer = new TextureRenderer(THIRD_TEX_ID, mST); - ASSERT_NO_FATAL_FAILURE(mThirdTextureRenderer->SetUp()); - - // Switch back to the primary context to start the tests. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mEglContext)); - } - - virtual void TearDown() { - if (mThirdEglContext != EGL_NO_CONTEXT) { - eglDestroyContext(mEglDisplay, mThirdEglContext); - } - if (mSecondEglContext != EGL_NO_CONTEXT) { - eglDestroyContext(mEglDisplay, mSecondEglContext); - } - SurfaceTextureGLTest::TearDown(); - } - - EGLContext mSecondEglContext; - sp<TextureRenderer> mSecondTextureRenderer; - - EGLContext mThirdEglContext; - sp<TextureRenderer> mThirdTextureRenderer; -}; - -TEST_F(SurfaceTextureMultiContextGLTest, UpdateFromMultipleContextsFails) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Attempt to latch the texture on the secondary context. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mSecondEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_EQ(INVALID_OPERATION, mST->updateTexImage()); -} - -TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextSucceeds) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Detach from the primary context. - ASSERT_EQ(OK, mST->detachFromContext()); - - // Check that the GL texture was deleted. - EXPECT_EQ(GL_FALSE, glIsTexture(TEX_ID)); -} - -TEST_F(SurfaceTextureMultiContextGLTest, - DetachFromContextSucceedsAfterProducerDisconnect) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Detach from the primary context. - native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); - ASSERT_EQ(OK, mST->detachFromContext()); - - // Check that the GL texture was deleted. - EXPECT_EQ(GL_FALSE, glIsTexture(TEX_ID)); -} - -TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenAbandoned) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Attempt to detach from the primary context. - mST->abandon(); - ASSERT_EQ(NO_INIT, mST->detachFromContext()); -} - -TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWhenDetached) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Detach from the primary context. - ASSERT_EQ(OK, mST->detachFromContext()); - - // Attempt to detach from the primary context again. - ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); -} - -TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoDisplay) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Make there be no current display. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Attempt to detach from the primary context. - ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); -} - -TEST_F(SurfaceTextureMultiContextGLTest, DetachFromContextFailsWithNoContext) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Make current context be incorrect. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mSecondEglContext)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Attempt to detach from the primary context. - ASSERT_EQ(INVALID_OPERATION, mST->detachFromContext()); -} - -TEST_F(SurfaceTextureMultiContextGLTest, UpdateTexImageFailsWhenDetached) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Detach from the primary context. - ASSERT_EQ(OK, mST->detachFromContext()); - - // Attempt to latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(INVALID_OPERATION, mST->updateTexImage()); -} - -TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceeds) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Detach from the primary context. - ASSERT_EQ(OK, mST->detachFromContext()); - - // Attach to the secondary context. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mSecondEglContext)); - ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); - - // Verify that the texture object was created and bound. - GLint texBinding = -1; - glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); - EXPECT_EQ(SECOND_TEX_ID, texBinding); - - // Try to use the texture from the secondary context. - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - glViewport(0, 0, 1, 1); - mSecondTextureRenderer->drawTexture(); - ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); -} - -TEST_F(SurfaceTextureMultiContextGLTest, - AttachToContextSucceedsAfterProducerDisconnect) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Detach from the primary context. - native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); - ASSERT_EQ(OK, mST->detachFromContext()); - - // Attach to the secondary context. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mSecondEglContext)); - ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); - - // Verify that the texture object was created and bound. - GLint texBinding = -1; - glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); - EXPECT_EQ(SECOND_TEX_ID, texBinding); - - // Try to use the texture from the secondary context. - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - glViewport(0, 0, 1, 1); - mSecondTextureRenderer->drawTexture(); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); -} - -TEST_F(SurfaceTextureMultiContextGLTest, - AttachToContextSucceedsBeforeUpdateTexImage) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Detach from the primary context. - native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU); - ASSERT_EQ(OK, mST->detachFromContext()); - - // Attach to the secondary context. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mSecondEglContext)); - ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); - - // Verify that the texture object was created and bound. - GLint texBinding = -1; - glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); - EXPECT_EQ(SECOND_TEX_ID, texBinding); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Try to use the texture from the secondary context. - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - glViewport(0, 0, 1, 1); - mSecondTextureRenderer->drawTexture(); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); -} - -TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAbandoned) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Detach from the primary context. - ASSERT_EQ(OK, mST->detachFromContext()); - - // Attempt to attach to the secondary context. - mST->abandon(); - - // Attempt to attach to the primary context. - ASSERT_EQ(NO_INIT, mST->attachToContext(SECOND_TEX_ID)); -} - -TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWhenAttached) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Attempt to attach to the primary context. - ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); -} - -TEST_F(SurfaceTextureMultiContextGLTest, - AttachToContextFailsWhenAttachedBeforeUpdateTexImage) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Attempt to attach to the primary context. - ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); -} - -TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextFailsWithNoDisplay) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Detach from the primary context. - ASSERT_EQ(OK, mST->detachFromContext()); - - // Make there be no current display. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, - EGL_NO_CONTEXT)); - ASSERT_EQ(EGL_SUCCESS, eglGetError()); - - // Attempt to attach with no context current. - ASSERT_EQ(INVALID_OPERATION, mST->attachToContext(SECOND_TEX_ID)); -} - -TEST_F(SurfaceTextureMultiContextGLTest, AttachToContextSucceedsTwice) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Latch the texture contents on the primary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Detach from the primary context. - ASSERT_EQ(OK, mST->detachFromContext()); - - // Attach to the secondary context. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mSecondEglContext)); - ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); - - // Detach from the secondary context. - ASSERT_EQ(OK, mST->detachFromContext()); - - // Attach to the tertiary context. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mThirdEglContext)); - ASSERT_EQ(OK, mST->attachToContext(THIRD_TEX_ID)); - - // Verify that the texture object was created and bound. - GLint texBinding = -1; - glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); - EXPECT_EQ(THIRD_TEX_ID, texBinding); - - // Try to use the texture from the tertiary context. - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - glViewport(0, 0, 1, 1); - mThirdTextureRenderer->drawTexture(); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); -} - -TEST_F(SurfaceTextureMultiContextGLTest, - AttachToContextSucceedsTwiceBeforeUpdateTexImage) { - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Detach from the primary context. - ASSERT_EQ(OK, mST->detachFromContext()); - - // Attach to the secondary context. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mSecondEglContext)); - ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); - - // Detach from the secondary context. - ASSERT_EQ(OK, mST->detachFromContext()); - - // Attach to the tertiary context. - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mThirdEglContext)); - ASSERT_EQ(OK, mST->attachToContext(THIRD_TEX_ID)); - - // Verify that the texture object was created and bound. - GLint texBinding = -1; - glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texBinding); - EXPECT_EQ(THIRD_TEX_ID, texBinding); - - // Latch the texture contents on the tertiary context. - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // Try to use the texture from the tertiary context. - glClearColor(0.2, 0.2, 0.2, 0.2); - glClear(GL_COLOR_BUFFER_BIT); - glViewport(0, 0, 1, 1); - mThirdTextureRenderer->drawTexture(); - ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); - ASSERT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); -} - -TEST_F(SurfaceTextureMultiContextGLTest, - UpdateTexImageSucceedsForBufferConsumedBeforeDetach) { - ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2)); - - // produce two frames and consume them both on the primary context - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); - - // produce one more frame - ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW)); - - // Detach from the primary context and attach to the secondary context - ASSERT_EQ(OK, mST->detachFromContext()); - ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, - mSecondEglContext)); - ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID)); - - // Consume final frame on secondary context - mFW->waitForFrame(); - ASSERT_EQ(OK, mST->updateTexImage()); -} - -} // namespace android diff --git a/libs/gui/tests/TextureRenderer.cpp b/libs/gui/tests/TextureRenderer.cpp new file mode 100644 index 0000000000..90951b3b20 --- /dev/null +++ b/libs/gui/tests/TextureRenderer.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2013 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 "TextureRenderer.h" + +#include "GLTest.h" + +#include <gui/GLConsumer.h> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <gtest/gtest.h> + +namespace android { + +TextureRenderer::TextureRenderer(GLuint texName, + const sp<GLConsumer>& st) : mTexName(texName), mST(st) { +} + +void TextureRenderer::SetUp() { + const char vsrc[] = + "attribute vec4 vPosition;\n" + "varying vec2 texCoords;\n" + "uniform mat4 texMatrix;\n" + "void main() {\n" + " vec2 vTexCoords = 0.5 * (vPosition.xy + vec2(1.0, 1.0));\n" + " texCoords = (texMatrix * vec4(vTexCoords, 0.0, 1.0)).xy;\n" + " gl_Position = vPosition;\n" + "}\n"; + + const char fsrc[] = + "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + "uniform samplerExternalOES texSampler;\n" + "varying vec2 texCoords;\n" + "void main() {\n" + " gl_FragColor = texture2D(texSampler, texCoords);\n" + "}\n"; + + { + SCOPED_TRACE("creating shader program"); + ASSERT_NO_FATAL_FAILURE(GLTest::createProgram(vsrc, fsrc, &mPgm)); + } + + mPositionHandle = glGetAttribLocation(mPgm, "vPosition"); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + ASSERT_NE(-1, mPositionHandle); + mTexSamplerHandle = glGetUniformLocation(mPgm, "texSampler"); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + ASSERT_NE(-1, mTexSamplerHandle); + mTexMatrixHandle = glGetUniformLocation(mPgm, "texMatrix"); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + ASSERT_NE(-1, mTexMatrixHandle); +} + +// drawTexture draws the GLConsumer over the entire GL viewport. +void TextureRenderer::drawTexture() { + static const GLfloat triangleVertices[] = { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f, + 1.0f, 1.0f, + }; + + glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, + triangleVertices); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glEnableVertexAttribArray(mPositionHandle); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + glUseProgram(mPgm); + glUniform1i(mTexSamplerHandle, 0); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as + // they're setting the defautls for that target, but when hacking + // things to use GL_TEXTURE_2D they are needed to achieve the same + // behavior. + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, + GL_LINEAR); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, + GL_LINEAR); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + GLfloat texMatrix[16]; + mST->getTransformMatrix(texMatrix); + glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); +} + +} // namespace android diff --git a/libs/gui/tests/TextureRenderer.h b/libs/gui/tests/TextureRenderer.h new file mode 100644 index 0000000000..37b2b47b41 --- /dev/null +++ b/libs/gui/tests/TextureRenderer.h @@ -0,0 +1,46 @@ +/* + * Copyright 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_TEXTURE_RENDERER_H +#define ANDROID_TEXTURE_RENDERER_H + +#include <GLES/gl.h> + +#include <utils/RefBase.h> + +namespace android { + +class GLConsumer; + +class TextureRenderer : public RefBase { +public: + TextureRenderer(GLuint texName, const sp<GLConsumer>& st); + + void SetUp(); + void drawTexture(); + +private: + GLuint mTexName; + sp<GLConsumer> mST; + GLuint mPgm; + GLint mPositionHandle; + GLint mTexSamplerHandle; + GLint mTexMatrixHandle; +}; + +} // namespace android + +#endif diff --git a/libs/input/Android.mk b/libs/input/Android.mk index f1921a4e0d..944ac7f653 100644 --- a/libs/input/Android.mk +++ b/libs/input/Android.mk @@ -27,6 +27,7 @@ commonSources := \ deviceSources := \ $(commonSources) \ + IInputFlinger.cpp \ InputTransport.cpp \ VelocityControl.cpp \ VelocityTracker.cpp diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp new file mode 100644 index 0000000000..e00973149c --- /dev/null +++ b/libs/input/IInputFlinger.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2013 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 <binder/Parcel.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +#include <input/IInputFlinger.h> + + +namespace android { + +class BpInputFlinger : public BpInterface<IInputFlinger> { +public: + BpInputFlinger(const sp<IBinder>& impl) : + BpInterface<IInputFlinger>(impl) { } + + virtual status_t doSomething() { + Parcel data, reply; + data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); + remote()->transact(BnInputFlinger::DO_SOMETHING_TRANSACTION, data, &reply); + return reply.readInt32(); + } +}; + +IMPLEMENT_META_INTERFACE(InputFlinger, "android.input.IInputFlinger"); + + +status_t BnInputFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch(code) { + case DO_SOMETHING_TRANSACTION: { + CHECK_INTERFACE(IInputFlinger, data, reply); + reply->writeInt32(0); + break; + } + default: + return BBinder::onTransact(code, data, reply, flags); + } + return NO_ERROR; +} + +}; diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 2f5494b70a..0800a31d75 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -150,6 +150,40 @@ status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { return NO_ERROR; } +status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const { + const size_t N = mLedsByScanCode.size(); + for (size_t i = 0; i < N; i++) { + if (mLedsByScanCode.valueAt(i).ledCode == ledCode) { + *outScanCode = mLedsByScanCode.keyAt(i); +#if DEBUG_MAPPING + ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode); +#endif + return NO_ERROR; + } + } +#if DEBUG_MAPPING + ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); +#endif + return NAME_NOT_FOUND; +} + +status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const { + const size_t N = mLedsByUsageCode.size(); + for (size_t i = 0; i < N; i++) { + if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) { + *outUsageCode = mLedsByUsageCode.keyAt(i); +#if DEBUG_MAPPING + ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode); +#endif + return NO_ERROR; + } + } +#if DEBUG_MAPPING + ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode); +#endif + return NAME_NOT_FOUND; +} + // --- KeyLayoutMap::Parser --- @@ -179,6 +213,10 @@ status_t KeyLayoutMap::Parser::parse() { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseAxis(); if (status) return status; + } else if (keywordToken == "led") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseLed(); + if (status) return status; } else { ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); @@ -215,8 +253,7 @@ status_t KeyLayoutMap::Parser::parseKey() { mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } - KeyedVector<int32_t, Key>& map = - mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + KeyedVector<int32_t, Key>& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; if (map.indexOfKey(code) >= 0) { ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); @@ -364,4 +401,46 @@ status_t KeyLayoutMap::Parser::parseAxis() { return NO_ERROR; } +status_t KeyLayoutMap::Parser::parseLed() { + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + bool mapUsage = false; + if (codeToken == "usage") { + mapUsage = true; + mTokenizer->skipDelimiters(WHITESPACE); + codeToken = mTokenizer->nextToken(WHITESPACE); + } + char* end; + int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); + if (*end) { + ALOGE("%s: Expected led %s number, got '%s'.", mTokenizer->getLocation().string(), + mapUsage ? "usage" : "scan code", codeToken.string()); + return BAD_VALUE; + } + + KeyedVector<int32_t, Led>& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; + if (map.indexOfKey(code) >= 0) { + ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(), + mapUsage ? "usage" : "scan code", codeToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 ledCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t ledCode = getLedByLabel(ledCodeToken.string()); + if (ledCode < 0) { + ALOGE("%s: Expected LED code label, got '%s'.", mTokenizer->getLocation().string(), + ledCodeToken.string()); + return BAD_VALUE; + } + +#if DEBUG_PARSER + ALOGD("Parsed led %s: code=%d, ledCode=%d.", + mapUsage ? "usage" : "scan code", code, ledCode); +#endif + + Led led; + led.ledCode = ledCode; + map.add(code, led); + return NO_ERROR; +} }; diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index b6551ee760..7d4ac92167 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -203,6 +203,10 @@ const char* getAxisLabel(int32_t axisId) { return lookupLabelByValue(axisId, AXES); } +int32_t getLedByLabel(const char* label) { + return int32_t(lookupValueByLabel(label, LEDS)); +} + static int32_t setEphemeralMetaState(int32_t mask, bool down, int32_t oldMetaState) { int32_t newMetaState; if (down) { diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp index d2d103ab43..5ce7fba54e 100644 --- a/libs/ui/PixelFormat.cpp +++ b/libs/ui/PixelFormat.cpp @@ -26,6 +26,8 @@ ssize_t bytesPerPixel(PixelFormat format) { case PIXEL_FORMAT_RGBA_8888: case PIXEL_FORMAT_RGBX_8888: case PIXEL_FORMAT_BGRA_8888: + case PIXEL_FORMAT_sRGB_A_8888: + case PIXEL_FORMAT_sRGB_X_8888: return 4; case PIXEL_FORMAT_RGB_888: return 3; diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index d96b54f3c2..b6ffdb2c67 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -408,9 +408,11 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, if (dp) { EGLDisplay iDpy = dp->disp.dpy; - if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != OK) { - ALOGE("EGLNativeWindowType %p already connected to another API", - window); + int result = native_window_api_connect(window, NATIVE_WINDOW_API_EGL); + if (result != OK) { + ALOGE("eglCreateWindowSurface: native_window_api_connect (win=%p) " + "failed (%#x) (already connected to another API?)", + window, result); return setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); } diff --git a/services/batteryservice/Android.mk b/services/batteryservice/Android.mk index 0a29c36d5d..9354b99ae8 100644 --- a/services/batteryservice/Android.mk +++ b/services/batteryservice/Android.mk @@ -3,6 +3,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ BatteryProperties.cpp \ + BatteryProperty.cpp \ IBatteryPropertiesListener.cpp \ IBatteryPropertiesRegistrar.cpp diff --git a/services/batteryservice/BatteryProperties.cpp b/services/batteryservice/BatteryProperties.cpp index e4a42edcdf..ab636a9f8b 100644 --- a/services/batteryservice/BatteryProperties.cpp +++ b/services/batteryservice/BatteryProperties.cpp @@ -38,8 +38,6 @@ status_t BatteryProperties::readFromParcel(Parcel* p) { batteryPresent = p->readInt32() == 1 ? true : false; batteryLevel = p->readInt32(); batteryVoltage = p->readInt32(); - batteryCurrentNow = p->readInt32(); - batteryChargeCounter = p->readInt32(); batteryTemperature = p->readInt32(); batteryTechnology = String8((p->readString16()).string()); return OK; @@ -54,8 +52,6 @@ status_t BatteryProperties::writeToParcel(Parcel* p) const { p->writeInt32(batteryPresent ? 1 : 0); p->writeInt32(batteryLevel); p->writeInt32(batteryVoltage); - p->writeInt32(batteryCurrentNow); - p->writeInt32(batteryChargeCounter); p->writeInt32(batteryTemperature); p->writeString16(String16(batteryTechnology)); return OK; diff --git a/services/batteryservice/BatteryProperty.cpp b/services/batteryservice/BatteryProperty.cpp new file mode 100644 index 0000000000..6cbc896d48 --- /dev/null +++ b/services/batteryservice/BatteryProperty.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2013 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 <batteryservice/BatteryService.h> +#include <binder/Parcel.h> +#include <utils/Errors.h> + +namespace android { + +/* + * Parcel read/write code must be kept in sync with + * frameworks/base/core/java/android/os/BatteryProperty.java + */ + +status_t BatteryProperty::readFromParcel(Parcel* p) { + valueInt = p->readInt32(); + return OK; +} + +status_t BatteryProperty::writeToParcel(Parcel* p) const { + p->writeInt32(valueInt); + return OK; +} + +}; // namespace android diff --git a/services/batteryservice/IBatteryPropertiesRegistrar.cpp b/services/batteryservice/IBatteryPropertiesRegistrar.cpp index 6c2d2a5bc9..6647122d1f 100644 --- a/services/batteryservice/IBatteryPropertiesRegistrar.cpp +++ b/services/batteryservice/IBatteryPropertiesRegistrar.cpp @@ -44,6 +44,18 @@ public: data.writeStrongBinder(listener->asBinder()); remote()->transact(UNREGISTER_LISTENER, data, NULL); } + + status_t getProperty(int id, struct BatteryProperty *val) { + Parcel data, reply; + data.writeInterfaceToken(IBatteryPropertiesRegistrar::getInterfaceDescriptor()); + data.writeInt32(id); + remote()->transact(GET_PROPERTY, data, &reply); + status_t ret = reply.readInt32(); + int parcelpresent = reply.readInt32(); + if (parcelpresent) + val->readFromParcel(&reply); + return ret; + } }; IMPLEMENT_META_INTERFACE(BatteryPropertiesRegistrar, "android.os.IBatteryPropertiesRegistrar"); @@ -69,6 +81,18 @@ status_t BnBatteryPropertiesRegistrar::onTransact(uint32_t code, unregisterListener(listener); return OK; } + + case GET_PROPERTY: { + CHECK_INTERFACE(IBatteryPropertiesRegistrar, data, reply); + int id = data.readInt32(); + struct BatteryProperty val; + status_t result = getProperty(id, &val); + reply->writeNoException(); + reply->writeInt32(result); + reply->writeInt32(1); + val.writeToParcel(reply); + return OK; + } } return BBinder::onTransact(code, data, reply, flags); }; diff --git a/services/inputflinger/Android.mk b/services/inputflinger/Android.mk new file mode 100644 index 0000000000..e32d38a918 --- /dev/null +++ b/services/inputflinger/Android.mk @@ -0,0 +1,50 @@ +# Copyright (C) 2013 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:= \ + InputFlinger.cpp + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libcutils \ + libinput \ + liblog \ + libutils + +LOCAL_CFLAGS += -fvisibility=hidden + +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) + +LOCAL_MODULE := libinputflinger + +include $(BUILD_SHARED_LIBRARY) + +######################################################################## +# build input flinger executable +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + main.cpp + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libinputflinger \ + libutils + +LOCAL_MODULE := inputflinger + +include $(BUILD_EXECUTABLE) diff --git a/services/inputflinger/InputFlinger.cpp b/services/inputflinger/InputFlinger.cpp new file mode 100644 index 0000000000..9ea6ce5843 --- /dev/null +++ b/services/inputflinger/InputFlinger.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 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 "InputFlinger" + +#include "InputFlinger.h" + +#include <stdint.h> +#include <unistd.h> + +#include <sys/types.h> + +#include <binder/IPCThreadState.h> +#include <binder/PermissionCache.h> +#include <cutils/log.h> +#include <private/android_filesystem_config.h> + +namespace android { + +const String16 sAccessInputFlingerPermission("android.permission.ACCESS_INPUT_FLINGER"); +const String16 sDumpPermission("android.permission.DUMP"); + + +InputFlinger::InputFlinger() : + BnInputFlinger() { + ALOGI("InputFlinger is starting"); +} + +InputFlinger::~InputFlinger() { +} + +status_t InputFlinger::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch (code) { + case DO_SOMETHING_TRANSACTION: + const IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if (!PermissionCache::checkPermission(sAccessInputFlingerPermission, pid, uid)) { + ALOGE("Permission Denial: " + "can't access InputFlinger from pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + break; + } + + return BnInputFlinger::onTransact(code, data, reply, flags); +} + +status_t InputFlinger::dump(int fd, const Vector<String16>& args) { + String8 result; + const IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + if ((uid != AID_SHELL) + && !PermissionCache::checkPermission(sDumpPermission, pid, uid)) { + result.appendFormat("Permission Denial: " + "can't dump SurfaceFlinger from pid=%d, uid=%d\n", pid, uid); + } else { + dumpInternal(result); + } + write(fd, result.string(), result.size()); + return OK; +} + +void InputFlinger::dumpInternal(String8& result) { + result.append("INPUT FLINGER (dumpsys inputflinger)\n"); + result.append("... nothing here yet...\n"); +} + +status_t InputFlinger::doSomething() { + ALOGI("Did something..."); + return OK; +} + +}; // namespace android diff --git a/services/inputflinger/InputFlinger.h b/services/inputflinger/InputFlinger.h new file mode 100644 index 0000000000..731ab17e83 --- /dev/null +++ b/services/inputflinger/InputFlinger.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 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_INPUT_FLINGER_H +#define ANDROID_INPUT_FLINGER_H + +#include <stdint.h> +#include <sys/types.h> + +#include <cutils/compiler.h> +#include <input/IInputFlinger.h> +#include <utils/String8.h> +#include <utils/String16.h> + +namespace android { + +class InputFlinger : public BnInputFlinger { +public: + static char const* getServiceName() ANDROID_API { + return "inputflinger"; + } + + InputFlinger() ANDROID_API; + + // IBinder interface + virtual status_t onTransact(uint32_t code, + const Parcel& data, Parcel* reply, uint32_t flags); + virtual status_t dump(int fd, const Vector<String16>& args); + + // IInputFlinger interface + virtual status_t doSomething(); + +private: + virtual ~InputFlinger(); + + void dumpInternal(String8& result); +}; + +} // namespace android + +#endif // ANDROID_INPUT_FLINGER_H diff --git a/services/inputflinger/main.cpp b/services/inputflinger/main.cpp new file mode 100644 index 0000000000..3209a62a2e --- /dev/null +++ b/services/inputflinger/main.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/BinderService.h> +#include "InputFlinger.h" + +using namespace android; + +int main(int argc, char** argv) { + ProcessState::self()->setThreadPoolMaxThreadCount(4); + BinderService<InputFlinger>::publishAndJoinThreadPool(true); + return 0; +} diff --git a/services/sensorservice/BatteryService.cpp b/services/sensorservice/BatteryService.cpp index 38dc74996d..cb962a6349 100644 --- a/services/sensorservice/BatteryService.cpp +++ b/services/sensorservice/BatteryService.cpp @@ -34,32 +34,10 @@ BatteryService::BatteryService() { const sp<IServiceManager> sm(defaultServiceManager()); if (sm != NULL) { const String16 name("batterystats"); - mBatteryStatService = sm->getService(name); + mBatteryStatService = interface_cast<IBatteryStats>(sm->getService(name)); } } -status_t BatteryService::noteStartSensor(int uid, int handle) { - Parcel data, reply; - data.writeInterfaceToken(DESCRIPTOR); - data.writeInt32(uid); - data.writeInt32(handle); - status_t err = mBatteryStatService->transact( - TRANSACTION_noteStartSensor, data, &reply, 0); - err = reply.readExceptionCode(); - return err; -} - -status_t BatteryService::noteStopSensor(int uid, int handle) { - Parcel data, reply; - data.writeInterfaceToken(DESCRIPTOR); - data.writeInt32(uid); - data.writeInt32(handle); - status_t err = mBatteryStatService->transact( - TRANSACTION_noteStopSensor, data, &reply, 0); - err = reply.readExceptionCode(); - return err; -} - bool BatteryService::addSensor(uid_t uid, int handle) { Mutex::Autolock _l(mActivationsLock); Info key(uid, handle); @@ -86,7 +64,7 @@ void BatteryService::enableSensorImpl(uid_t uid, int handle) { if (mBatteryStatService != 0) { if (addSensor(uid, handle)) { int64_t identity = IPCThreadState::self()->clearCallingIdentity(); - noteStartSensor(uid, handle); + mBatteryStatService->noteStartSensor(uid, handle); IPCThreadState::self()->restoreCallingIdentity(identity); } } @@ -95,7 +73,7 @@ void BatteryService::disableSensorImpl(uid_t uid, int handle) { if (mBatteryStatService != 0) { if (removeSensor(uid, handle)) { int64_t identity = IPCThreadState::self()->clearCallingIdentity(); - noteStopSensor(uid, handle); + mBatteryStatService->noteStopSensor(uid, handle); IPCThreadState::self()->restoreCallingIdentity(identity); } } @@ -108,7 +86,7 @@ void BatteryService::cleanupImpl(uid_t uid) { for (ssize_t i=0 ; i<mActivations.size() ; i++) { const Info& info(mActivations[i]); if (info.uid == uid) { - noteStopSensor(info.uid, info.handle); + mBatteryStatService->noteStopSensor(info.uid, info.handle); mActivations.removeAt(i); i--; } @@ -117,8 +95,6 @@ void BatteryService::cleanupImpl(uid_t uid) { } } -const String16 BatteryService::DESCRIPTOR("com.android.internal.app.IBatteryStats"); - ANDROID_SINGLETON_STATIC_INSTANCE(BatteryService) // --------------------------------------------------------------------------- diff --git a/services/sensorservice/BatteryService.h b/services/sensorservice/BatteryService.h index 86cc884209..08ba857518 100644 --- a/services/sensorservice/BatteryService.h +++ b/services/sensorservice/BatteryService.h @@ -17,22 +17,18 @@ #include <stdint.h> #include <sys/types.h> +#include <binder/IBatteryStats.h> #include <utils/Singleton.h> namespace android { // --------------------------------------------------------------------------- class BatteryService : public Singleton<BatteryService> { - static const int TRANSACTION_noteStartSensor = IBinder::FIRST_CALL_TRANSACTION + 3; - static const int TRANSACTION_noteStopSensor = IBinder::FIRST_CALL_TRANSACTION + 4; - static const String16 DESCRIPTOR; friend class Singleton<BatteryService>; - sp<IBinder> mBatteryStatService; + sp<IBatteryStats> mBatteryStatService; BatteryService(); - status_t noteStartSensor(int uid, int handle); - status_t noteStopSensor(int uid, int handle); void enableSensorImpl(uid_t uid, int handle); void disableSensorImpl(uid_t uid, int handle); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 1b652c3540..474f633426 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -23,7 +23,6 @@ #include <sys/types.h> #include <math.h> -#include <utils/CallStack.h> #include <utils/Errors.h> #include <utils/misc.h> #include <utils/String8.h> diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 61af51faf5..fcc9d789df 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -64,7 +64,6 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, mName("unnamed"), mDebug(false), mFormat(PIXEL_FORMAT_NONE), - mOpaqueLayer(true), mTransactionFlags(0), mQueuedFrames(0), mCurrentTransform(0), @@ -86,7 +85,9 @@ Layer::Layer(SurfaceFlinger* flinger, const sp<Client>& client, uint32_t layerFlags = 0; if (flags & ISurfaceComposerClient::eHidden) - layerFlags = layer_state_t::eLayerHidden; + layerFlags |= layer_state_t::eLayerHidden; + if (flags & ISurfaceComposerClient::eOpaque) + layerFlags |= layer_state_t::eLayerOpaque; if (flags & ISurfaceComposerClient::eNonPremultiplied) mPremultipliedAlpha = false; @@ -189,7 +190,6 @@ status_t Layer::setBuffers( uint32_t w, uint32_t h, mSecure = (flags & ISurfaceComposerClient::eSecure) ? true : false; mProtectedByApp = (flags & ISurfaceComposerClient::eProtectedByApp) ? true : false; - mOpaqueLayer = (flags & ISurfaceComposerClient::eOpaque); mCurrentOpacity = getOpacityForFormat(format); mSurfaceFlingerConsumer->setDefaultBufferSize(w, h); @@ -352,7 +352,7 @@ void Layer::setGeometry( // this gives us only the "orientation" component of the transform const State& s(getDrawingState()); - if (!isOpaque() || s.alpha != 0xFF) { + if (!isOpaque(s) || s.alpha != 0xFF) { layer.setBlending(mPremultipliedAlpha ? HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE); @@ -596,7 +596,7 @@ void Layer::drawWithOpenGL( texCoords[3] = vec2(right, 1.0f - top); RenderEngine& engine(mFlinger->getRenderEngine()); - engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(), s.alpha); + engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), s.alpha); engine.drawMesh(mMesh); engine.disableBlending(); } @@ -656,7 +656,7 @@ void Layer::computeGeometry(const sp<const DisplayDevice>& hw, Mesh& mesh) const } } -bool Layer::isOpaque() const +bool Layer::isOpaque(const Layer::State& s) const { // if we don't have a buffer yet, we're translucent regardless of the // layer's opaque flag. @@ -666,7 +666,7 @@ bool Layer::isOpaque() const // if the layer has the opaque flag, then we're always opaque, // otherwise we use the current buffer's format. - return mOpaqueLayer || mCurrentOpacity; + return ((s.flags & layer_state_t::eLayerOpaque) != 0) || mCurrentOpacity; } bool Layer::isProtected() const @@ -954,7 +954,8 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) } // Capture the old state of the layer for comparisons later - const bool oldOpacity = isOpaque(); + const State& s(getDrawingState()); + const bool oldOpacity = isOpaque(s); sp<GraphicBuffer> oldActiveBuffer = mActiveBuffer; struct Reject : public SurfaceFlingerConsumer::BufferRejecter { @@ -1122,12 +1123,11 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) } mCurrentOpacity = getOpacityForFormat(mActiveBuffer->format); - if (oldOpacity != isOpaque()) { + if (oldOpacity != isOpaque(s)) { recomputeVisibleRegions = true; } // FIXME: postedRegion should be dirty & bounds - const Layer::State& s(getDrawingState()); Region dirtyRegion(Rect(s.active.w, s.active.h)); // transform the dirty region to window-manager space @@ -1188,7 +1188,7 @@ void Layer::dump(String8& result, Colorizer& colorizer) const s.layerStack, s.z, s.transform.tx(), s.transform.ty(), s.active.w, s.active.h, s.active.crop.left, s.active.crop.top, s.active.crop.right, s.active.crop.bottom, - isOpaque(), contentDirty, + isOpaque(s), contentDirty, s.alpha, s.flags, s.transform[0][0], s.transform[0][1], s.transform[1][0], s.transform[1][1], diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index ef4a7e918c..ea65ded528 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -149,8 +149,12 @@ public: /* * isOpaque - true if this surface is opaque + * + * This takes into account the buffer format (i.e. whether or not the + * pixel format includes an alpha channel) and the "opaque" flag set + * on the layer. It does not examine the current plane alpha value. */ - virtual bool isOpaque() const; + virtual bool isOpaque(const Layer::State& s) const; /* * isSecure - true if this surface is secure, that is if it prevents @@ -335,7 +339,6 @@ private: String8 mName; mutable bool mDebug; PixelFormat mFormat; - bool mOpaqueLayer; // these are protected by an external lock State mCurrentState; diff --git a/services/surfaceflinger/RenderEngine/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/ProgramCache.cpp index 09b0ddc4a4..d1305067b6 100644 --- a/services/surfaceflinger/RenderEngine/ProgramCache.cpp +++ b/services/surfaceflinger/RenderEngine/ProgramCache.cpp @@ -169,7 +169,8 @@ String8 ProgramCache::generateFragmentShader(const Key& needs) { fs << "gl_FragColor.rgb = gl_FragColor.rgb/gl_FragColor.a;"; } fs << "gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(2.2));"; - fs << "gl_FragColor = colorMatrix*gl_FragColor;"; + fs << "vec4 transformed = colorMatrix * vec4(gl_FragColor.rgb, 1);"; + fs << "gl_FragColor.rgb = transformed.rgb/transformed.a;"; fs << "gl_FragColor.rgb = pow(gl_FragColor.rgb, vec3(1.0 / 2.2));"; if (!needs.isOpaque() && needs.isPremultiplied()) { // and re-premultiply if needed after gamma correction diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index c00b034f7e..40ca8ebf92 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -152,7 +152,8 @@ SurfaceFlinger::SurfaceFlinger() mBootFinished(false), mPrimaryHWVsyncEnabled(false), mHWVsyncAvailable(false), - mDaltonize(false) + mDaltonize(false), + mHasColorMatrix(false) { ALOGI("SurfaceFlinger is starting"); @@ -902,7 +903,7 @@ void SurfaceFlinger::setUpHWComposer() { for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) { const sp<Layer>& layer(currentLayers[i]); layer->setGeometry(hw, *cur); - if (mDebugDisableHWC || mDebugRegion || mDaltonize) { + if (mDebugDisableHWC || mDebugRegion || mDaltonize || mHasColorMatrix) { cur->setSkip(true); } } @@ -1379,7 +1380,7 @@ void SurfaceFlinger::computeVisibleRegions( // handle hidden surfaces by setting the visible region to empty if (CC_LIKELY(layer->isVisible())) { - const bool translucent = !layer->isOpaque(); + const bool translucent = !layer->isOpaque(s); Rect bounds(s.transform.transform(layer->computeBounds())); visibleRegion.set(bounds); if (!visibleRegion.isEmpty()) { @@ -1524,11 +1525,15 @@ void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw, } } - if (CC_LIKELY(!mDaltonize)) { + if (CC_LIKELY(!mDaltonize && !mHasColorMatrix)) { doComposeSurfaces(hw, dirtyRegion); } else { RenderEngine& engine(getRenderEngine()); - engine.beginGroup(mDaltonizer()); + mat4 colorMatrix = mColorMatrix; + if (mDaltonize) { + colorMatrix = colorMatrix * mDaltonizer(); + } + engine.beginGroup(colorMatrix); doComposeSurfaces(hw, dirtyRegion); engine.endGroup(); } @@ -1624,7 +1629,7 @@ void SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const const Layer::State& state(layer->getDrawingState()); if ((cur->getHints() & HWC_HINT_CLEAR_FB) && i - && layer->isOpaque() && (state.alpha == 0xFF) + && layer->isOpaque(state) && (state.alpha == 0xFF) && hasGlesComposition) { // never clear the very first layer since we're // guaranteed the FB is already cleared @@ -1868,7 +1873,9 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (layer->setTransparentRegionHint(s.transparentRegion)) flags |= eTraversalNeeded; } - if (what & layer_state_t::eVisibilityChanged) { + if ((what & layer_state_t::eVisibilityChanged) || + (what & layer_state_t::eOpacityChanged)) { + // TODO: should we just use an eFlagsChanged for this? if (layer->setFlags(s.flags, s.mask)) flags |= eTraversalNeeded; } @@ -2415,7 +2422,8 @@ void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index, colorizer.reset(result); result.appendFormat(" h/w composer %s and %s\n", hwc.initCheck()==NO_ERROR ? "present" : "not present", - (mDebugDisableHWC || mDebugRegion || mDaltonize) ? "disabled" : "enabled"); + (mDebugDisableHWC || mDebugRegion || mDaltonize + || mHasColorMatrix) ? "disabled" : "enabled"); hwc.dump(result); /* @@ -2578,8 +2586,28 @@ status_t SurfaceFlinger::onTransact( mDaltonize = n > 0; invalidateHwcGeometry(); repaintEverything(); + return NO_ERROR; + } + case 1015: { + // apply a color matrix + n = data.readInt32(); + mHasColorMatrix = n ? 1 : 0; + if (n) { + // color matrix is sent as mat3 matrix followed by vec3 + // offset, then packed into a mat4 where the last row is + // the offset and extra values are 0 + for (size_t i = 0 ; i < 4; i++) { + for (size_t j = 0; j < 4; j++) { + mColorMatrix[i][j] = data.readFloat(); + } + } + } else { + mColorMatrix = mat4(); + } + invalidateHwcGeometry(); + repaintEverything(); + return NO_ERROR; } - return NO_ERROR; } } return err; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 80bb619d67..9db0ce150e 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -38,6 +38,7 @@ #include <binder/IMemory.h> #include <ui/PixelFormat.h> +#include <ui/mat4.h> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> @@ -472,6 +473,9 @@ private: Daltonizer mDaltonizer; bool mDaltonize; + + mat4 mColorMatrix; + bool mHasColorMatrix; }; }; // namespace android |