diff options
457 files changed, 26608 insertions, 43562 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..0d20b6487c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 4f7cdf3e5e..3fa5430d52 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -5,6 +5,7 @@ clang_format = true # Only turn on clang-format check for the following subfolders. clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp include/input/ + libs/binder/fuzzer/ libs/binder/ndk/ libs/graphicsenv/ libs/gui/ diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md index c818c05117..26dabbbcef 100644 --- a/cmds/dumpstate/README.md +++ b/cmds/dumpstate/README.md @@ -92,6 +92,12 @@ Then to restore the default version: adb shell setprop dumpstate.version default ``` +## To set Bugreport API workflow for bugreport + +``` +adb shell setprop settings_call_bugreport_api true +``` + ## Code style and formatting Use the style defined at the diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 67f12e8efe..b87582eab7 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -2070,12 +2070,12 @@ static void PrepareToWriteToFile() { } if (ds.options_->do_fb) { - ds.screenshot_path_ = ds.GetPath(".png"); + ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png"); } ds.tmp_path_ = ds.GetPath(".tmp"); ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt"); - std::string destination = ds.options_->bugreport_fd.get() != -1 + std::string destination = ds.CalledByApi() ? StringPrintf("[fd:%d]", ds.options_->bugreport_fd.get()) : ds.bugreport_internal_dir_.c_str(); MYLOGD( @@ -2089,7 +2089,7 @@ static void PrepareToWriteToFile() { ds.tmp_path_.c_str(), ds.screenshot_path_.c_str()); if (ds.options_->do_zip_file) { - ds.path_ = ds.GetPath(".zip"); + ds.path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.zip" : ".zip"); MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str()); create_parent_dirs(ds.path_.c_str()); ds.zip_file.reset(fopen(ds.path_.c_str(), "wb")); @@ -2124,7 +2124,7 @@ static void FinalizeFile() { MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str()); ds.name_ = name; if (!ds.screenshot_path_.empty()) { - std::string new_screenshot_path = ds.GetPath(".png"); + std::string new_screenshot_path = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png"); if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) { MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(), new_screenshot_path.c_str(), strerror(errno)); @@ -2142,7 +2142,7 @@ static void FinalizeFile() { } else { do_text_file = false; // If the user has changed the suffix, we need to change the zip file name. - std::string new_path = ds.GetPath(".zip"); + std::string new_path = ds.GetPath(ds.CalledByApi() ? "-tmp.zip" : ".zip"); if (ds.path_ != new_path) { MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str()); if (rename(ds.path_.c_str(), new_path.c_str())) { @@ -2376,9 +2376,6 @@ Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) // clang-format off case 'd': do_add_date = true; break; case 'z': do_zip_file = true; break; - // o=use_outfile not supported anymore. - // TODO(b/111441001): Remove when all callers have migrated. - case 'o': break; case 's': use_socket = true; break; case 'S': use_control_socket = true; break; case 'v': show_header_only = true; break; @@ -2529,6 +2526,8 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, return RunStatus::OK; } + MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n", + calling_uid, calling_package.c_str()); if (options_->bugreport_fd.get() != -1) { // If the output needs to be copied over to the caller's fd, get user consent. android::String16 package(calling_package.c_str()); @@ -2706,10 +2705,10 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, FinalizeFile(); } - // Share the final file with the caller if the user has consented. + // Share the final file with the caller if the user has consented or Shell is the caller. Dumpstate::RunStatus status = Dumpstate::RunStatus::OK; if (options_->bugreport_fd.get() != -1) { - status = CopyBugreportIfUserConsented(); + status = CopyBugreportIfUserConsented(calling_uid); if (status != Dumpstate::RunStatus::OK && status != Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) { // Do an early return if there were errors. We make an exception for consent @@ -2750,7 +2749,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } /* tell activity manager we're done */ - if (options_->do_broadcast) { + if (options_->do_broadcast && !CalledByApi()) { SendBugreportFinishedBroadcast(); // Note that listener_ is notified in Run(); } @@ -2779,6 +2778,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) { + if (calling_uid == AID_SHELL) { + return; + } consent_callback_ = new ConsentCallback(); const String16 incidentcompanion("incidentcompanion"); sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion)); @@ -2797,6 +2799,10 @@ bool Dumpstate::IsUserConsentDenied() const { ds.consent_callback_->getResult() == UserConsentResult::DENIED; } +bool Dumpstate::CalledByApi() const { + return ds.options_->bugreport_fd.get() != -1 ? true : false; +} + void Dumpstate::CleanupFiles() { android::os::UnlinkAndLogOnError(tmp_path_); android::os::UnlinkAndLogOnError(screenshot_path_); @@ -2809,10 +2815,15 @@ Dumpstate::RunStatus Dumpstate::HandleUserConsentDenied() { return USER_CONSENT_DENIED; } -Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() { +Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented(int32_t calling_uid) { // If the caller has asked to copy the bugreport over to their directory, we need explicit - // user consent. - UserConsentResult consent_result = consent_callback_->getResult(); + // user consent (unless the caller is Shell). + UserConsentResult consent_result; + if (calling_uid == AID_SHELL) { + consent_result = UserConsentResult::APPROVED; + } else { + consent_result = consent_callback_->getResult(); + } if (consent_result == UserConsentResult::UNAVAILABLE) { // User has not responded yet. uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs(); diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 82bf8219a2..430936ebfd 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -344,6 +344,11 @@ class Dumpstate { bool IsUserConsentDenied() const; /* + * Returns true if dumpstate is called by bugreporting API + */ + bool CalledByApi() const; + + /* * Structure to hold options that determine the behavior of dumpstate. */ struct DumpOptions { @@ -493,8 +498,9 @@ class Dumpstate { RunStatus HandleUserConsentDenied(); - // Copies bugreport artifacts over to the caller's directories provided there is user consent. - RunStatus CopyBugreportIfUserConsented(); + // Copies bugreport artifacts over to the caller's directories provided there is user consent or + // called by Shell. + RunStatus CopyBugreportIfUserConsented(int32_t calling_uid); // Used by GetInstance() only. explicit Dumpstate(const std::string& version = VERSION_CURRENT); diff --git a/cmds/dumpstate/dumpstate.rc b/cmds/dumpstate/dumpstate.rc index 14937b80b9..e491a4b614 100644 --- a/cmds/dumpstate/dumpstate.rc +++ b/cmds/dumpstate/dumpstate.rc @@ -11,8 +11,7 @@ service dumpstate /system/bin/dumpstate -s # dumpstatez generates a zipped bugreport but also uses a socket to print the file location once # it is finished. -service dumpstatez /system/bin/dumpstate -S -d -z \ - -o /data/user_de/0/com.android.shell/files/bugreports/bugreport +service dumpstatez /system/bin/dumpstate -S -d -z socket dumpstate stream 0660 shell log class main disabled diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp index 7e6f6f53e5..fbb01f5e99 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -201,9 +201,7 @@ class ZippedBugreportGenerationTest : public Test { (char*)"dumpstate", (char*)"-d", (char*)"-z", - (char*)"-B", - (char*)"-o", - (char*)dirname(android::base::GetExecutablePath().c_str()) + (char*)"-B" }; // clang-format on sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections)); diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 26e9984f11..d99bcc8d13 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -127,4 +127,6 @@ interface IInstalld { const int FLAG_USE_QUOTA = 0x1000; const int FLAG_FORCE = 0x2000; + + const int FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES = 0x20000; } diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto index c70bc3e5c1..792ff91dfa 100644 --- a/cmds/surfacereplayer/proto/src/trace.proto +++ b/cmds/surfacereplayer/proto/src/trace.proto @@ -1,5 +1,6 @@ syntax = "proto2"; option optimize_for = LITE_RUNTIME; +package android.surfaceflinger; message Trace { repeated Increment increment = 1; @@ -46,6 +47,10 @@ message SurfaceChange { SecureFlagChange secure_flag = 14; DeferredTransactionChange deferred_transaction = 15; CornerRadiusChange corner_radius = 16; + ReparentChange reparent = 17; + RelativeParentChange relative_parent = 18; + DetachChildrenChange detach_children = 19; + ReparentChildrenChange reparent_children = 20; } } @@ -177,3 +182,20 @@ message PowerModeUpdate { required int32 id = 1; required int32 mode = 2; } + +message ReparentChange { + required int32 parent_id = 1; +} + +message ReparentChildrenChange { + required int32 parent_id = 1; +} + +message RelativeParentChange { + required int32 relative_parent_id = 1; + required int32 z = 2; +} + +message DetachChildrenChange { + required bool detach_children = 1; +} diff --git a/cmds/surfacereplayer/replayer/Event.cpp b/cmds/surfacereplayer/replayer/Event.cpp index 390d3982ca..64db5f07b1 100644 --- a/cmds/surfacereplayer/replayer/Event.cpp +++ b/cmds/surfacereplayer/replayer/Event.cpp @@ -17,6 +17,7 @@ #include "Event.h" using namespace android; +using Increment = surfaceflinger::Increment; Event::Event(Increment::IncrementCase type) : mIncrementType(type) {} diff --git a/cmds/surfacereplayer/replayer/Event.h b/cmds/surfacereplayer/replayer/Event.h index 44b60f566a..09a7c248d5 100644 --- a/cmds/surfacereplayer/replayer/Event.h +++ b/cmds/surfacereplayer/replayer/Event.h @@ -24,6 +24,8 @@ namespace android { +using Increment = surfaceflinger::Increment; + class Event { public: Event(Increment::IncrementCase); diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index 34886a99e9..a4a9b6a6f4 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -412,6 +412,18 @@ status_t Replayer::doSurfaceTransaction( setDeferredTransaction(transaction, change.id(), change.deferred_transaction()); break; + case SurfaceChange::SurfaceChangeCase::kReparent: + setReparentChange(transaction, change.id(), change.reparent()); + break; + case SurfaceChange::SurfaceChangeCase::kReparentChildren: + setReparentChildrenChange(transaction, change.id(), change.reparent_children()); + break; + case SurfaceChange::SurfaceChangeCase::kRelativeParent: + setRelativeParentChange(transaction, change.id(), change.relative_parent()); + break; + case SurfaceChange::SurfaceChangeCase::kDetachChildren: + setDetachChildrenChange(transaction, change.id(), change.detach_children()); + break; default: status = 1; break; @@ -680,3 +692,35 @@ status_t Replayer::loadSurfaceComposerClient() { mComposerClient = new SurfaceComposerClient; return mComposerClient->initCheck(); } + +void Replayer::setReparentChange(SurfaceComposerClient::Transaction& t, + layer_id id, const ReparentChange& c) { + sp<IBinder> newParentHandle = nullptr; + if (mLayers.count(c.parent_id()) != 0 && mLayers[c.parent_id()] != nullptr) { + newParentHandle = mLayers[c.parent_id()]->getHandle(); + } + t.reparent(mLayers[id], newParentHandle); +} + +void Replayer::setRelativeParentChange(SurfaceComposerClient::Transaction& t, + layer_id id, const RelativeParentChange& c) { + if (mLayers.count(c.relative_parent_id()) == 0 || mLayers[c.relative_parent_id()] == nullptr) { + ALOGE("Layer %d not found in set relative parent transaction", c.relative_parent_id()); + return; + } + t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()]->getHandle(), c.z()); +} + +void Replayer::setDetachChildrenChange(SurfaceComposerClient::Transaction& t, + layer_id id, const DetachChildrenChange& c) { + t.detachChildren(mLayers[id]); +} + +void Replayer::setReparentChildrenChange(SurfaceComposerClient::Transaction& t, + layer_id id, const ReparentChildrenChange& c) { + if (mLayers.count(c.parent_id()) == 0 || mLayers[c.parent_id()] == nullptr) { + ALOGE("Layer %d not found in reparent children transaction", c.parent_id()); + return; + } + t.reparentChildren(mLayers[id], mLayers[c.parent_id()]->getHandle()); +} diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h index ad807ee950..3b94618acb 100644 --- a/cmds/surfacereplayer/replayer/Replayer.h +++ b/cmds/surfacereplayer/replayer/Replayer.h @@ -38,6 +38,8 @@ #include <unordered_map> #include <utility> +using namespace android::surfaceflinger; + namespace android { const auto DEFAULT_PATH = "/data/local/tmp/SurfaceTrace.dat"; @@ -108,6 +110,14 @@ class Replayer { layer_id id, const SecureFlagChange& sfc); void setDeferredTransaction(SurfaceComposerClient::Transaction& t, layer_id id, const DeferredTransactionChange& dtc); + void setReparentChange(SurfaceComposerClient::Transaction& t, + layer_id id, const ReparentChange& c); + void setRelativeParentChange(SurfaceComposerClient::Transaction& t, + layer_id id, const RelativeParentChange& c); + void setDetachChildrenChange(SurfaceComposerClient::Transaction& t, + layer_id id, const DetachChildrenChange& c); + void setReparentChildrenChange(SurfaceComposerClient::Transaction& t, + layer_id id, const ReparentChildrenChange& c); void setDisplaySurface(SurfaceComposerClient::Transaction& t, display_id id, const DispSurfaceChange& dsc); diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h index 479e9b8714..bbb3193dd1 100644 --- a/headers/media_plugin/media/openmax/OMX_IndexExt.h +++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h @@ -101,6 +101,7 @@ typedef enum OMX_INDEXEXTTYPE { OMX_IndexConfigOperatingRate, /**< reference: OMX_PARAM_U32TYPE in Q16 format for video and in Hz for audio */ OMX_IndexParamConsumerUsageBits, /**< reference: OMX_PARAM_U32TYPE */ OMX_IndexConfigLatency, /**< reference: OMX_PARAM_U32TYPE */ + OMX_IndexConfigLowLatency, /**< reference: OMX_CONFIG_BOOLEANTYPE */ OMX_IndexExtOtherEndUnused, /* Time configurations */ diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h index 435fcc8cca..dc37bbd1f7 100644 --- a/headers/media_plugin/media/openmax/OMX_VideoExt.h +++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h @@ -291,18 +291,19 @@ typedef struct OMX_VIDEO_RENDEREVENTTYPE { /** Dolby Vision Profile enum type */ typedef enum OMX_VIDEO_DOLBYVISIONPROFILETYPE { - OMX_VIDEO_DolbyVisionProfileUnknown = 0x0, - OMX_VIDEO_DolbyVisionProfileDvavPer = 0x1, - OMX_VIDEO_DolbyVisionProfileDvavPen = 0x2, - OMX_VIDEO_DolbyVisionProfileDvheDer = 0x4, - OMX_VIDEO_DolbyVisionProfileDvheDen = 0x8, - OMX_VIDEO_DolbyVisionProfileDvheDtr = 0x10, - OMX_VIDEO_DolbyVisionProfileDvheStn = 0x20, - OMX_VIDEO_DolbyVisionProfileDvheDth = 0x40, - OMX_VIDEO_DolbyVisionProfileDvheDtb = 0x80, - OMX_VIDEO_DolbyVisionProfileDvheSt = 0x100, - OMX_VIDEO_DolbyVisionProfileDvavSe = 0x200, - OMX_VIDEO_DolbyVisionProfileMax = 0x7FFFFFFF + OMX_VIDEO_DolbyVisionProfileUnknown = 0x0, + OMX_VIDEO_DolbyVisionProfileDvavPer = 0x1, + OMX_VIDEO_DolbyVisionProfileDvavPen = 0x2, + OMX_VIDEO_DolbyVisionProfileDvheDer = 0x4, + OMX_VIDEO_DolbyVisionProfileDvheDen = 0x8, + OMX_VIDEO_DolbyVisionProfileDvheDtr = 0x10, + OMX_VIDEO_DolbyVisionProfileDvheStn = 0x20, + OMX_VIDEO_DolbyVisionProfileDvheDth = 0x40, + OMX_VIDEO_DolbyVisionProfileDvheDtb = 0x80, + OMX_VIDEO_DolbyVisionProfileDvheSt = 0x100, + OMX_VIDEO_DolbyVisionProfileDvavSe = 0x200, + OMX_VIDEO_DolbyVisionProfileDvav110 = 0x400, + OMX_VIDEO_DolbyVisionProfileMax = 0x7FFFFFFF } OMX_VIDEO_DOLBYVISIONPROFILETYPE; /** Dolby Vision Level enum type */ diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 2def64dc90..01cf2f88ea 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -60,6 +60,22 @@ enum AndroidBitmapFormat { ANDROID_BITMAP_FORMAT_RGBA_4444 = 7, /** Alpha: 8 bits. */ ANDROID_BITMAP_FORMAT_A_8 = 8, + /** Each component is stored as a half float. **/ + ANDROID_BITMAP_FORMAT_RGBA_F16 = 9, +}; + +/** Bitmap alpha format */ +enum { + /** Pixel components are premultiplied by alpha. */ + ANDROID_BITMAP_FLAGS_ALPHA_PREMUL = 0, + /** Pixels are opaque. */ + ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE = 1, + /** Pixel components are independent of alpha. */ + ANDROID_BITMAP_FLAGS_ALPHA_UNPREMUL = 2, + /** Bit mask for AndroidBitmapFormat.flags to isolate the alpha. */ + ANDROID_BITMAP_FLAGS_ALPHA_MASK = 0x3, + /** Shift for AndroidBitmapFormat.flags to isolate the alpha. */ + ANDROID_BITMAP_FLAGS_ALPHA_SHIFT = 0, }; /** Bitmap info, see AndroidBitmap_getInfo(). */ @@ -72,8 +88,9 @@ typedef struct { uint32_t stride; /** The bitmap pixel format. See {@link AndroidBitmapFormat} */ int32_t format; - /** Unused. */ - uint32_t flags; // 0 for now + /** Two bits are used to encode alpha. Use ANDROID_BITMAP_FLAGS_ALPHA_MASK + * and ANDROID_BITMAP_FLAGS_ALPHA_SHIFT to retrieve them. */ + uint32_t flags; } AndroidBitmapInfo; /** diff --git a/include/android/choreographer.h b/include/android/choreographer.h index 44883cc498..1b589bca72 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -83,7 +83,7 @@ void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, * Power a callback to be run on the next frame. The data pointer provided will * be passed to the callback function when it's called. */ -void AChoreographer_postFrameCallback64(AChoreographer* chroreographer, +void AChoreographer_postFrameCallback64(AChoreographer* choreographer, AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29); /** diff --git a/include/android/surface_control.h b/include/android/surface_control.h index ef2ad9998c..abb8368069 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -130,7 +130,7 @@ int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_ /** * Returns a sync fence that signals when the transaction has been presented. * The recipient of the callback takes ownership of the fence and is responsible for closing - * it. + * it. If a device does not support present fences, a -1 will be returned. */ int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats) __INTRODUCED_IN(29); diff --git a/include/input/IInputFlinger.h b/include/input/IInputFlinger.h index 4365a3c4e3..d23e3b7767 100644 --- a/include/input/IInputFlinger.h +++ b/include/input/IInputFlinger.h @@ -37,7 +37,6 @@ public: virtual void setInputWindows(const std::vector<InputWindowInfo>& inputHandles, const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0; - virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0; virtual void registerInputChannel(const sp<InputChannel>& channel) = 0; virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0; }; @@ -51,8 +50,7 @@ public: enum { SET_INPUT_WINDOWS_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, REGISTER_INPUT_CHANNEL_TRANSACTION, - UNREGISTER_INPUT_CHANNEL_TRANSACTION, - TRANSFER_TOUCH_FOCUS + UNREGISTER_INPUT_CHANNEL_TRANSACTION }; virtual status_t onTransact(uint32_t code, const Parcel& data, diff --git a/include/input/Input.h b/include/input/Input.h index 805957a5ca..cbd1a412bf 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -24,12 +24,15 @@ */ #include <android/input.h> +#include <math.h> +#include <stdint.h> #include <utils/BitSet.h> #include <utils/KeyedVector.h> #include <utils/RefBase.h> #include <utils/Timers.h> #include <utils/Vector.h> -#include <stdint.h> + +#include <limits> /* * Additional private constants not defined in ndk/ui/input.h. @@ -246,6 +249,13 @@ enum class MotionClassification : uint8_t { */ const char* motionClassificationToString(MotionClassification classification); +/** + * Invalid value for cursor position. Used for non-mouse events, tests and injected events. Don't + * use it for direct comparison with any other value, because NaN isn't equal to itself according to + * IEEE 754. Use isnan() instead to check if a cursor position is valid. + */ +constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits<float>::quiet_NaN(); + /* * Pointer coordinate data. */ @@ -459,6 +469,18 @@ public: inline float getYPrecision() const { return mYPrecision; } + inline float getRawXCursorPosition() const { return mRawXCursorPosition; } + + float getXCursorPosition() const; + + inline float getRawYCursorPosition() const { return mRawYCursorPosition; } + + float getYCursorPosition() const; + + void setCursorPosition(float x, float y); + + static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } + inline nsecs_t getDownTime() const { return mDownTime; } inline void setDownTime(nsecs_t downTime) { mDownTime = downTime; } @@ -600,26 +622,13 @@ public: ssize_t findPointerIndex(int32_t pointerId) const; - void initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - size_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords); + void initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, float xOffset, + float yOffset, float xPrecision, float yPrecision, float rawXCursorPosition, + float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, + size_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords); void copyFrom(const MotionEvent* other, bool keepHistory); @@ -669,6 +678,8 @@ protected: float mYOffset; float mXPrecision; float mYPrecision; + float mRawXCursorPosition; + float mRawYCursorPosition; nsecs_t mDownTime; Vector<PointerProperties> mPointerProperties; Vector<nsecs_t> mSampleEventTimes; diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index 59d16d15af..eaa562bb7b 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -359,6 +359,9 @@ static const InputEventLabel AXES[] = { DEFINE_AXIS(BRAKE), DEFINE_AXIS(DISTANCE), DEFINE_AXIS(TILT), + DEFINE_AXIS(SCROLL), + DEFINE_AXIS(RELATIVE_X), + DEFINE_AXIS(RELATIVE_Y), DEFINE_AXIS(GENERIC_1), DEFINE_AXIS(GENERIC_2), DEFINE_AXIS(GENERIC_3), diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 63606e5911..c056c972d2 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -31,13 +31,17 @@ #include <string> +#include <android-base/chrono_utils.h> + #include <binder/IBinder.h> #include <input/Input.h> +#include <utils/BitSet.h> #include <utils/Errors.h> -#include <utils/Timers.h> #include <utils/RefBase.h> +#include <utils/Timers.h> #include <utils/Vector.h> -#include <utils/BitSet.h> + +#include <android-base/unique_fd.h> namespace android { class Parcel; @@ -113,6 +117,8 @@ struct InputMessage { float yOffset; float xPrecision; float yPrecision; + float xCursorPosition; + float yCursorPosition; uint32_t pointerCount; uint32_t empty3; // Note that PointerCoords requires 8 byte alignment. @@ -161,8 +167,7 @@ protected: virtual ~InputChannel(); public: - InputChannel() = default; - InputChannel(const std::string& name, int fd); + static sp<InputChannel> create(const std::string& name, android::base::unique_fd fd); /* Creates a pair of input channels. * @@ -172,7 +177,7 @@ public: sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel); inline std::string getName() const { return mName; } - inline int getFd() const { return mFd; } + inline int getFd() const { return mFd.get(); } /* Sends a message to the other endpoint. * @@ -203,16 +208,15 @@ public: sp<InputChannel> dup() const; status_t write(Parcel& out) const; - status_t read(const Parcel& from); + static sp<InputChannel> read(const Parcel& from); sp<IBinder> getToken() const; void setToken(const sp<IBinder>& token); private: - void setFd(int fd); - + InputChannel(const std::string& name, android::base::unique_fd fd); std::string mName; - int mFd = -1; + android::base::unique_fd mFd; sp<IBinder> mToken = nullptr; }; @@ -261,27 +265,14 @@ public: * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. * Other errors probably indicate that the channel is broken. */ - status_t publishMotionEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - uint32_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords); + status_t publishMotionEvent(uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, + int32_t action, int32_t actionButton, int32_t flags, + int32_t edgeFlags, int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, nsecs_t eventTime, + uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords); /* Receives the finished signal from the consumer in reply to the original dispatch signal. * If a signal was received, returns the message sequence number, @@ -297,6 +288,7 @@ public: status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); private: + sp<InputChannel> mChannel; }; diff --git a/include/input/LatencyStatistics.h b/include/input/LatencyStatistics.h new file mode 100644 index 0000000000..bd86266901 --- /dev/null +++ b/include/input/LatencyStatistics.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 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 _UI_INPUT_STATISTICS_H +#define _UI_INPUT_STATISTICS_H + +#include <android-base/chrono_utils.h> + +#include <stddef.h> + +namespace android { + +class LatencyStatistics { +private: + /* Minimum sample recorded */ + float mMin; + /* Maximum sample recorded */ + float mMax; + /* Sum of all samples recorded */ + float mSum; + /* Sum of all the squares of samples recorded */ + float mSum2; + /* Count of all samples recorded */ + size_t mCount; + /* The last time statistics were reported */ + std::chrono::steady_clock::time_point mLastReportTime; + /* Statistics Report Frequency */ + const std::chrono::seconds mReportPeriod; + +public: + LatencyStatistics(std::chrono::seconds period); + + void addValue(float); + void reset(); + bool shouldReport(); + + float getMean(); + float getMin(); + float getMax(); + float getStDev(); + size_t getCount(); +}; + +} // namespace android + +#endif // _UI_INPUT_STATISTICS_H diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp index 0a6685e14a..9da9c13e54 100644 --- a/libs/binder/AppOpsManager.cpp +++ b/libs/binder/AppOpsManager.cpp @@ -21,6 +21,13 @@ #include <utils/SystemClock.h> +#include <sys/types.h> + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "AppOpsManager" + namespace android { namespace { @@ -49,6 +56,12 @@ static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) { return gToken; } +thread_local uint64_t notedAppOpsInThisBinderTransaction[2]; +thread_local int32_t uidOfThisBinderTransaction = -1; + +// Whether an appop should be collected: 0 == not initialized, 1 == don't note, 2 == note +uint8_t appOpsToNote[AppOpsManager::_NUM_OP] = {0}; + AppOpsManager::AppOpsManager() { } @@ -102,18 +115,41 @@ int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t ui } int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) { + return noteOp(op, uid, callingPackage, String16("noteOp from native code")); +} + +int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage, + const String16& message) { sp<IAppOpsService> service = getService(); - return service != nullptr + int32_t mode = service != nullptr ? service->noteOperation(op, uid, callingPackage) : APP_OPS_MANAGER_UNAVAILABLE_MODE; + + if (mode == AppOpsManager::MODE_ALLOWED) { + markAppOpNoted(uid, callingPackage, op, message); + } + + return mode; } int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, bool startIfModeDefault) { + return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, + String16("startOpNoThrow from native code")); +} + +int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, + bool startIfModeDefault, const String16& message) { sp<IAppOpsService> service = getService(); - return service != nullptr + int32_t mode = service != nullptr ? service->startOperation(getToken(service), op, uid, callingPackage, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE; + + if (mode == AppOpsManager::MODE_ALLOWED) { + markAppOpNoted(uid, callingPackage, op, message); + } + + return mode; } void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) { @@ -146,5 +182,45 @@ int32_t AppOpsManager::permissionToOpCode(const String16& permission) { return -1; } +void AppOpsManager::setCameraAudioRestriction(int32_t mode) { + sp<IAppOpsService> service = getService(); + if (service != nullptr) { + service->setCameraAudioRestriction(mode); + } +} + +bool AppOpsManager::shouldCollectNotes(int32_t opcode) { + sp<IAppOpsService> service = getService(); + if (service != nullptr) { + return service->shouldCollectNotes(opcode); + } + return false; +} + +void AppOpsManager::markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode, + const String16& message) { + // check it the appops needs to be collected and cache result + if (appOpsToNote[opCode] == 0) { + if (shouldCollectNotes(opCode)) { + appOpsToNote[opCode] = 2; + } else { + appOpsToNote[opCode] = 1; + } + } + + if (appOpsToNote[opCode] != 2) { + return; + } + + noteAsyncOp(String16(), uid, packageName, opCode, message); +} + +void AppOpsManager::noteAsyncOp(const String16& callingPackageName, int32_t uid, + const String16& packageName, int32_t opCode, const String16& message) { + sp<IAppOpsService> service = getService(); + if (service != nullptr) { + return service->noteAsyncOp(callingPackageName, uid, packageName, opCode, message); + } +} } // namespace android diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp index b2bd9e50b0..6c16c2d044 100644 --- a/libs/binder/IAppOpsService.cpp +++ b/libs/binder/IAppOpsService.cpp @@ -111,7 +111,6 @@ public: return reply.readStrongBinder(); } - virtual int32_t permissionToOpCode(const String16& permission) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); @@ -137,6 +136,51 @@ public: } return reply.readInt32(); } + + virtual void setCameraAudioRestriction(int32_t mode) { + Parcel data, reply; + data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); + data.writeInt32(mode); + remote()->transact(SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION, data, &reply); + } + + virtual void noteAsyncOp(const String16& callingPackageName, int32_t uid, + const String16& packageName, int32_t opCode, const String16& message) { + Parcel data, reply; + data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); + + // Convert empty callingPackage into null string + if (callingPackageName.size() != 0) { + data.writeString16(callingPackageName); + } else { + data.writeString16(nullptr, 0); + } + + data.writeInt32(uid); + + // Convert empty packageName into null string + if (packageName.size() != 0) { + data.writeString16(packageName); + } else { + data.writeString16(nullptr, 0); + } + + data.writeInt32(opCode); + data.writeString16(message); + remote()->transact(NOTE_ASYNC_OP_TRANSACTION, data, &reply); + } + + virtual bool shouldCollectNotes(int32_t opCode) { + Parcel data, reply; + data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); + data.writeInt32(opCode); + remote()->transact(SHOULD_COLLECT_NOTES_TRANSACTION, data, &reply); + // fail on exception + if (reply.readExceptionCode() != 0) { + return false; + } + return reply.readBool(); + } }; IMPLEMENT_META_INTERFACE(AppOpsService, "com.android.internal.app.IAppOpsService"); @@ -234,6 +278,32 @@ status_t BnAppOpsService::onTransact( reply->writeInt32(res); return NO_ERROR; } break; + case SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION: { + CHECK_INTERFACE(IAppOpsService, data, reply); + const int32_t mode = data.readInt32(); + setCameraAudioRestriction(mode); + reply->writeNoException(); + return NO_ERROR; + } break; + case NOTE_ASYNC_OP_TRANSACTION: { + CHECK_INTERFACE(IAppOpsService, data, reply); + String16 callingPackageName = data.readString16(); + int32_t uid = data.readInt32(); + String16 packageName = data.readString16(); + int32_t opCode = data.readInt32(); + String16 message = data.readString16(); + noteAsyncOp(callingPackageName, uid, packageName, opCode, message); + reply->writeNoException(); + return NO_ERROR; + } break; + case SHOULD_COLLECT_NOTES_TRANSACTION: { + CHECK_INTERFACE(IAppOpsService, data, reply); + int32_t opCode = data.readInt32(); + bool shouldCollect = shouldCollectNotes(opCode); + reply->writeNoException(); + reply->writeBool(shouldCollect); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index 222b32c921..c2bb811e9f 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -150,10 +150,6 @@ void* IMemory::fastPointer(const sp<IBinder>& binder, ssize_t offset) const } void* IMemory::unsecurePointer() const { - return pointer(); -} - -void* IMemory::pointer() const { ssize_t offset; sp<IMemoryHeap> heap = getMemory(&offset); void* const base = heap!=nullptr ? heap->base() : MAP_FAILED; @@ -162,6 +158,8 @@ void* IMemory::pointer() const { return static_cast<char*>(base) + offset; } +void* IMemory::pointer() const { return unsecurePointer(); } + size_t IMemory::size() const { size_t size; getMemory(nullptr, &size); diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 9b47f80f1d..7d9846a3cc 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -2286,6 +2286,22 @@ void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize, mObjectsSize = 0; break; } + const flat_binder_object* flat + = reinterpret_cast<const flat_binder_object*>(mData + offset); + uint32_t type = flat->hdr.type; + if (!(type == BINDER_TYPE_BINDER || type == BINDER_TYPE_HANDLE || + type == BINDER_TYPE_FD)) { + // We should never receive other types (eg BINDER_TYPE_FDA) as long as we don't support + // them in libbinder. If we do receive them, it probably means a kernel bug; try to + // recover gracefully by clearing out the objects, and releasing the objects we do + // know about. + android_errorWriteLog(0x534e4554, "135930648"); + ALOGE("%s: unsupported type object (%" PRIu32 ") at offset %" PRIu64 "\n", + __func__, type, (uint64_t)offset); + releaseObjects(); + mObjectsSize = 0; + break; + } minOffset = offset + sizeof(flat_binder_object); } scanForFds(); diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 0336d3ebd4..6971c720f3 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -373,7 +373,9 @@ ProcessState::ProcessState(const char *driver) } } +#ifdef __ANDROID__ LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened. Terminating.", driver); +#endif } ProcessState::~ProcessState() diff --git a/libs/binder/fuzzer/Android.bp b/libs/binder/fuzzer/Android.bp new file mode 100644 index 0000000000..a9d2b751a9 --- /dev/null +++ b/libs/binder/fuzzer/Android.bp @@ -0,0 +1,43 @@ +cc_fuzz { + name: "binder_parcel_fuzzer", + defaults: ["libbinder_ndk_host_user"], + host_supported: true, + srcs: [ + "binder.cpp", + "binder_ndk.cpp", + "hwbinder.cpp", + "main.cpp", + "util.cpp", + ], + static_libs: [ + "libbase", + "libbinderthreadstate", + "libcgrouprc", + "libcgrouprc_format", + "libcutils", + "libhidlbase", + "liblog", + "libprocessgroup", + "libjsoncpp", + "libutils", + ], + + target: { + android: { + shared_libs: [ + "libbinder_ndk", + "libbinder", + ], + }, + host: { + static_libs: [ + "libbinder_ndk", + "libbinder", + ], + }, + }, + // This flag enables verbose output in the fuzz target, and is very useful + // for debugging a failure. If you are trying to diagnose how a crash was + // produced, you may find uncommenting the below line very useful. + // cflags: ["-DENABLE_LOG_FUZZ"], +} diff --git a/libs/binder/fuzzer/binder.cpp b/libs/binder/fuzzer/binder.cpp new file mode 100644 index 0000000000..52c730cfb8 --- /dev/null +++ b/libs/binder/fuzzer/binder.cpp @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2019 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 FUZZ_LOG_TAG "binder" + +#include "binder.h" +#include "util.h" + +#include <android/os/IServiceManager.h> + +using ::android::status_t; + +enum ByteEnum : int8_t {}; +enum IntEnum : int32_t {}; +enum LongEnum : int64_t {}; + +class ExampleParcelable : public android::Parcelable { +public: + status_t writeToParcel(android::Parcel* /*parcel*/) const override { + FUZZ_LOG() << "should not reach"; + abort(); + } + status_t readFromParcel(const android::Parcel* parcel) override { + mExampleExtraField++; + return parcel->readInt64(&(this->mExampleUsedData)); + } +private: + int64_t mExampleExtraField = 0; + int64_t mExampleUsedData = 0; +}; + +struct ExampleFlattenable : public android::Flattenable<ExampleFlattenable> { +public: + size_t getFlattenedSize() const { return sizeof(mValue); } + size_t getFdCount() const { return 0; } + status_t flatten(void*& /*buffer*/, size_t& /*size*/, int*& /*fds*/, size_t& /*count*/) const { + FUZZ_LOG() << "should not reach"; + abort(); + } + status_t unflatten(void const*& buffer, size_t& size, int const*& /*fds*/, size_t& /*count*/) { + if (size < sizeof(mValue)) { + return android::NO_MEMORY; + } + android::FlattenableUtils::read(buffer, size, mValue); + return android::OK; + } +private: + int32_t mValue = 0xFEEDBEEF; +}; + +struct ExampleLightFlattenable : public android::LightFlattenablePod<ExampleLightFlattenable> { + int32_t mValue = 0; +}; + +#define PARCEL_READ_WITH_STATUS(T, FUN) \ + [] (const ::android::Parcel& p, uint8_t /*data*/) {\ + FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\ + T t{};\ + status_t status = p.FUN(&t);\ + FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/;\ + } + +#define PARCEL_READ_NO_STATUS(T, FUN) \ + [] (const ::android::Parcel& p, uint8_t /*data*/) {\ + FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\ + T t = p.FUN();\ + (void) t;\ + FUZZ_LOG() << #T " done " /* << " value: " << t*/;\ + } + +#define PARCEL_READ_OPT_STATUS(T, FUN) \ + PARCEL_READ_WITH_STATUS(T, FUN), \ + PARCEL_READ_NO_STATUS(T, FUN) + +// clang-format off +std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS { + PARCEL_READ_NO_STATUS(size_t, dataSize), + PARCEL_READ_NO_STATUS(size_t, dataAvail), + PARCEL_READ_NO_STATUS(size_t, dataPosition), + PARCEL_READ_NO_STATUS(size_t, dataCapacity), + [] (const ::android::Parcel& p, uint8_t pos) { + FUZZ_LOG() << "about to setDataPosition: " << pos; + p.setDataPosition(pos); + FUZZ_LOG() << "setDataPosition done"; + }, + PARCEL_READ_NO_STATUS(size_t, allowFds), + PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors), + [] (const ::android::Parcel& p, uint8_t len) { + std::string interface(len, 'a'); + FUZZ_LOG() << "about to enforceInterface: " << interface; + bool b = p.enforceInterface(::android::String16(interface.c_str())); + FUZZ_LOG() << "enforced interface: " << b; + }, + [] (const ::android::Parcel& p, uint8_t /*len*/) { + FUZZ_LOG() << "about to checkInterface"; + android::sp<android::IBinder> aBinder = new android::BBinder(); + bool b = p.checkInterface(aBinder.get()); + FUZZ_LOG() << "checked interface: " << b; + }, + PARCEL_READ_NO_STATUS(size_t, objectsCount), + PARCEL_READ_NO_STATUS(status_t, errorCheck), + [] (const ::android::Parcel& p, uint8_t len) { + FUZZ_LOG() << "about to read void*"; + std::vector<uint8_t> data(len); + status_t status = p.read(data.data(), len); + FUZZ_LOG() << "read status: " << status; + }, + [] (const ::android::Parcel& p, uint8_t len) { + FUZZ_LOG() << "about to readInplace"; + const void* r = p.readInplace(len); + FUZZ_LOG() << "readInplace done. pointer: " << r << " bytes: " << hexString(r, len); + }, + PARCEL_READ_OPT_STATUS(int32_t, readInt32), + PARCEL_READ_OPT_STATUS(uint32_t, readUint32), + PARCEL_READ_OPT_STATUS(int64_t, readInt64), + PARCEL_READ_OPT_STATUS(uint64_t, readUint64), + PARCEL_READ_OPT_STATUS(float, readFloat), + PARCEL_READ_OPT_STATUS(double, readDouble), + PARCEL_READ_OPT_STATUS(intptr_t, readIntPtr), + PARCEL_READ_OPT_STATUS(bool, readBool), + PARCEL_READ_OPT_STATUS(char16_t, readChar), + PARCEL_READ_OPT_STATUS(int8_t, readByte), + + PARCEL_READ_WITH_STATUS(std::string, readUtf8FromUtf16), + PARCEL_READ_WITH_STATUS(std::unique_ptr<std::string>, readUtf8FromUtf16), + [] (const ::android::Parcel& p, uint8_t /*data*/) { + FUZZ_LOG() << "about to read c-str"; + const char* str = p.readCString(); + FUZZ_LOG() << "read c-str: " << (str ? str : "<empty string>"); + }, + PARCEL_READ_OPT_STATUS(android::String8, readString8), + PARCEL_READ_OPT_STATUS(android::String16, readString16), + PARCEL_READ_WITH_STATUS(std::unique_ptr<android::String16>, readString16), + [] (const ::android::Parcel& p, uint8_t /*data*/) { + FUZZ_LOG() << "about to readString16Inplace"; + size_t outLen = 0; + const char16_t* str = p.readString16Inplace(&outLen); + FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outLen) + << " size: " << outLen; + }, + PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readStrongBinder), + PARCEL_READ_WITH_STATUS(android::sp<android::IBinder>, readNullableStrongBinder), + + // TODO(b/131868573): can force read of arbitrarily sized vector + // PARCEL_READ_WITH_STATUS(std::vector<ByteEnum>, readEnumVector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<ByteEnum>>, readEnumVector), + // PARCEL_READ_WITH_STATUS(std::vector<IntEnum>, readEnumVector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<IntEnum>>, readEnumVector), + // PARCEL_READ_WITH_STATUS(std::vector<LongEnum>, readEnumVector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<LongEnum>>, readEnumVector), + + // only reading one parcelable type for now + // TODO(b/131868573): can force read of arbitrarily sized vector + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<ExampleParcelable>>>, readParcelableVector), + // PARCEL_READ_WITH_STATUS(std::vector<ExampleParcelable>, readParcelableVector), + PARCEL_READ_WITH_STATUS(ExampleParcelable, readParcelable), + PARCEL_READ_WITH_STATUS(std::unique_ptr<ExampleParcelable>, readParcelable), + + // only reading one binder type for now + PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder), + PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder), + + // TODO(b/131868573): can force read of arbitrarily sized vector + // PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector), + // PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::IBinder>>, readStrongBinderVector), + + // TODO(b/131868573): can force read of arbitrarily sized vector + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int8_t>>, readByteVector), + // PARCEL_READ_WITH_STATUS(std::vector<int8_t>, readByteVector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint8_t>>, readByteVector), + // PARCEL_READ_WITH_STATUS(std::vector<uint8_t>, readByteVector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int32_t>>, readInt32Vector), + // PARCEL_READ_WITH_STATUS(std::vector<int32_t>, readInt32Vector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<int64_t>>, readInt64Vector), + // PARCEL_READ_WITH_STATUS(std::vector<int64_t>, readInt64Vector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<uint64_t>>, readUint64Vector), + // PARCEL_READ_WITH_STATUS(std::vector<uint64_t>, readUint64Vector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<float>>, readFloatVector), + // PARCEL_READ_WITH_STATUS(std::vector<float>, readFloatVector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<double>>, readDoubleVector), + // PARCEL_READ_WITH_STATUS(std::vector<double>, readDoubleVector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<bool>>, readBoolVector), + // PARCEL_READ_WITH_STATUS(std::vector<bool>, readBoolVector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<char16_t>>, readCharVector), + // PARCEL_READ_WITH_STATUS(std::vector<char16_t>, readCharVector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<android::String16>>>, readString16Vector), + // PARCEL_READ_WITH_STATUS(std::vector<android::String16>, readString16Vector), + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<std::unique_ptr<std::string>>>, readUtf8VectorFromUtf16Vector), + // PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector), + + [] (const android::Parcel& p, uint8_t /*len*/) { + FUZZ_LOG() << "about to read flattenable"; + ExampleFlattenable f; + status_t status = p.read(f); + FUZZ_LOG() << "read flattenable: " << status; + }, + [] (const android::Parcel& p, uint8_t /*len*/) { + FUZZ_LOG() << "about to read lite flattenable"; + ExampleLightFlattenable f; + status_t status = p.read(f); + FUZZ_LOG() << "read lite flattenable: " << status; + }, + + // TODO(b/131868573): can force read of arbitrarily sized vector + // TODO: resizeOutVector + + PARCEL_READ_NO_STATUS(int32_t, readExceptionCode), + [] (const android::Parcel& p, uint8_t /*len*/) { + FUZZ_LOG() << "about to readNativeHandle"; + native_handle_t* t = p.readNativeHandle(); + FUZZ_LOG() << "readNativeHandle: " << t; + if (t != nullptr) { + FUZZ_LOG() << "about to free readNativeHandle"; + native_handle_close(t); + native_handle_delete(t); + FUZZ_LOG() << "readNativeHandle freed"; + } + }, + PARCEL_READ_NO_STATUS(int, readFileDescriptor), + PARCEL_READ_NO_STATUS(int, readParcelFileDescriptor), + PARCEL_READ_WITH_STATUS(android::base::unique_fd, readUniqueFileDescriptor), + + // TODO(b/131868573): can force read of arbitrarily sized vector + // PARCEL_READ_WITH_STATUS(std::unique_ptr<std::vector<android::base::unique_fd>>, readUniqueFileDescriptorVector), + // PARCEL_READ_WITH_STATUS(std::vector<android::base::unique_fd>, readUniqueFileDescriptorVector), + + [] (const android::Parcel& p, uint8_t len) { + FUZZ_LOG() << "about to readBlob"; + ::android::Parcel::ReadableBlob blob; + status_t status = p.readBlob(len, &blob); + FUZZ_LOG() << "readBlob status: " << status; + }, + [] (const android::Parcel& p, uint8_t options) { + FUZZ_LOG() << "about to readObject"; + bool nullMetaData = options & 0x1; + const void* obj = static_cast<const void*>(p.readObject(nullMetaData)); + FUZZ_LOG() << "readObject: " << obj; + }, + PARCEL_READ_NO_STATUS(uid_t, readCallingWorkSourceUid), + PARCEL_READ_NO_STATUS(size_t, getBlobAshmemSize), + PARCEL_READ_NO_STATUS(size_t, getOpenAshmemSize), +}; +// clang-format on diff --git a/libs/binder/fuzzer/binder.h b/libs/binder/fuzzer/binder.h new file mode 100644 index 0000000000..b224ef49a1 --- /dev/null +++ b/libs/binder/fuzzer/binder.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 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/Parcel.h> +#include <vector> + +#include "parcel_fuzzer.h" + +extern std::vector<ParcelRead<::android::Parcel>> BINDER_PARCEL_READ_FUNCTIONS; diff --git a/libs/binder/fuzzer/binder_ndk.cpp b/libs/binder/fuzzer/binder_ndk.cpp new file mode 100644 index 0000000000..29da8f7537 --- /dev/null +++ b/libs/binder/fuzzer/binder_ndk.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 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 FUZZ_LOG_TAG "binder_ndk" + +#include "binder_ndk.h" + +#include <android/binder_parcel_utils.h> + +#include "util.h" + +// TODO(b/142061461): parent class +class SomeParcelable { +public: + binder_status_t readFromParcel(const AParcel* parcel) { + return AParcel_readInt32(parcel, &mValue); + } + +private: + int32_t mValue = 0; +}; + +#define PARCEL_READ(T, FUN) \ + [](const NdkParcelAdapter& p, uint8_t /*data*/) { \ + FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \ + T t{}; \ + binder_status_t status = FUN(p.aParcel(), &t); \ + FUZZ_LOG() << #T " status: " << status /* << " value: " << t*/; \ + } + +// clang-format off +std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS{ + // methods from binder_parcel.h + [](const NdkParcelAdapter& p, uint8_t pos) { + FUZZ_LOG() << "about to set data position to " << pos; + binder_status_t status = AParcel_setDataPosition(p.aParcel(), pos); + FUZZ_LOG() << "set data position: " << status; + }, + [](const NdkParcelAdapter& p, uint8_t /*data*/) { + FUZZ_LOG() << "about to read status header"; + ndk::ScopedAStatus t; + binder_status_t status = AParcel_readStatusHeader(p.aParcel(), t.getR()); + FUZZ_LOG() << "read status header: " << status; + }, + PARCEL_READ(int32_t, AParcel_readInt32), + PARCEL_READ(uint32_t, AParcel_readUint32), + PARCEL_READ(int64_t, AParcel_readInt64), + PARCEL_READ(uint64_t, AParcel_readUint64), + PARCEL_READ(float, AParcel_readFloat), + PARCEL_READ(double, AParcel_readDouble), + PARCEL_READ(bool, AParcel_readBool), + PARCEL_READ(char16_t, AParcel_readChar), + PARCEL_READ(int8_t, AParcel_readByte), + + // methods from binder_parcel_utils.h + PARCEL_READ(ndk::SpAIBinder, ndk::AParcel_readNullableStrongBinder), + PARCEL_READ(ndk::SpAIBinder, ndk::AParcel_readRequiredStrongBinder), + PARCEL_READ(ndk::ScopedFileDescriptor, ndk::AParcel_readNullableParcelFileDescriptor), + PARCEL_READ(ndk::ScopedFileDescriptor, ndk::AParcel_readRequiredParcelFileDescriptor), + PARCEL_READ(std::string, ndk::AParcel_readString), + PARCEL_READ(std::optional<std::string>, ndk::AParcel_readString), + // TODO(b/131868573): can force process to allocate arbitrary amount of + // memory + // PARCEL_READ(std::vector<std::string>, ndk::AParcel_readVector), + // PARCEL_READ(std::optional<std::vector<std::optional<std::string>>>, + // ndk::AParcel_readVector), PARCEL_READ(std::vector<SomeParcelable>, + // ndk::AParcel_readVector), PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector), + // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector), + // PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector), + // PARCEL_READ(std::optional<std::vector<uint32_t>>, ndk::AParcel_readVector), + // PARCEL_READ(std::vector<int64_t>, ndk::AParcel_readVector), + // PARCEL_READ(std::optional<std::vector<int64_t>>, ndk::AParcel_readVector), + // PARCEL_READ(std::vector<uint64_t>, ndk::AParcel_readVector), + // PARCEL_READ(std::optional<std::vector<uint64_t>>, ndk::AParcel_readVector), + // PARCEL_READ(std::vector<float>, ndk::AParcel_readVector), + // PARCEL_READ(std::optional<std::vector<float>>, ndk::AParcel_readVector), + // PARCEL_READ(std::vector<double>, ndk::AParcel_readVector), + // PARCEL_READ(std::optional<std::vector<double>>, ndk::AParcel_readVector), + // PARCEL_READ(std::vector<bool>, ndk::AParcel_readVector), + // PARCEL_READ(std::optional<std::vector<bool>>, ndk::AParcel_readVector), + // PARCEL_READ(std::vector<char16_t>, ndk::AParcel_readVector), + // PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector), + // PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector), + // PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector), +}; +// clang-format on diff --git a/libs/binder/fuzzer/binder_ndk.h b/libs/binder/fuzzer/binder_ndk.h new file mode 100644 index 0000000000..622cafc7df --- /dev/null +++ b/libs/binder/fuzzer/binder_ndk.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 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 <android/binder_auto_utils.h> +#include <vector> + +#include <android/binder_parcel.h> +#include "parcel_fuzzer.h" + +// libbinder_ndk doesn't export this header which breaks down its API for NDK +// and APEX users, but we need access to it to fuzz. +#include "../ndk/parcel_internal.h" + +class NdkParcelAdapter { +public: + NdkParcelAdapter() : mParcel(new AParcel(nullptr /*binder*/)) {} + + const AParcel* aParcel() const { return mParcel.get(); } + AParcel* aParcel() { return mParcel.get(); } + + size_t dataSize() const { return aParcel()->get()->dataSize(); } + size_t dataAvail() const { return aParcel()->get()->dataAvail(); } + size_t dataPosition() const { return aParcel()->get()->dataPosition(); } + size_t dataCapacity() const { return aParcel()->get()->dataCapacity(); } + android::status_t setData(const uint8_t* buffer, size_t len) { + return aParcel()->get()->setData(buffer, len); + } + +private: + ndk::ScopedAParcel mParcel; +}; + +extern std::vector<ParcelRead<NdkParcelAdapter>> BINDER_NDK_PARCEL_READ_FUNCTIONS; diff --git a/libs/binder/fuzzer/hwbinder.cpp b/libs/binder/fuzzer/hwbinder.cpp new file mode 100644 index 0000000000..0fec393e55 --- /dev/null +++ b/libs/binder/fuzzer/hwbinder.cpp @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2019 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 FUZZ_LOG_TAG "hwbinder" + +#include "hwbinder.h" +#include "util.h" + +#include <android-base/logging.h> +#include <hwbinder/Parcel.h> + +using ::android::status_t; + +// TODO: support scatter-gather types + +std::ostream& operator<<(std::ostream& os, const ::android::sp<::android::hardware::IBinder>& binder) { + os << binder.get(); + return os; +} + +#define PARCEL_READ_OPT_STATUS(T, FUN) \ + PARCEL_READ_NO_STATUS(T, FUN), PARCEL_READ_WITH_STATUS(T, FUN) + +#define PARCEL_READ_NO_STATUS(T, FUN) \ + [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\ + FUZZ_LOG() << "about to read " #T " using " #FUN " with no status";\ + T t = p.FUN();\ + FUZZ_LOG() << #T " value: " << t;\ + } + +#define PARCEL_READ_WITH_STATUS(T, FUN) \ + [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) {\ + FUZZ_LOG() << "about to read " #T " using " #FUN " with status";\ + T t;\ + status_t status = p.FUN(&t);\ + FUZZ_LOG() << #T " status: " << status << " value: " << t;\ + } + +// clang-format off +std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTIONS { + PARCEL_READ_NO_STATUS(size_t, dataSize), + PARCEL_READ_NO_STATUS(size_t, dataAvail), + PARCEL_READ_NO_STATUS(size_t, dataPosition), + PARCEL_READ_NO_STATUS(size_t, dataCapacity), + [] (const ::android::hardware::Parcel& p, uint8_t pos) { + FUZZ_LOG() << "about to setDataPosition: " << pos; + p.setDataPosition(pos); + FUZZ_LOG() << "setDataPosition done"; + }, + [] (const ::android::hardware::Parcel& p, uint8_t length) { + FUZZ_LOG() << "about to enforceInterface"; + std::string interfaceName(length, 'a'); + bool okay = p.enforceInterface(interfaceName.c_str()); + FUZZ_LOG() << "enforceInterface status: " << okay; + }, + PARCEL_READ_NO_STATUS(size_t, objectsCount), + [] (const ::android::hardware::Parcel& p, uint8_t length) { + FUZZ_LOG() << "about to read"; + std::vector<uint8_t> data (length); + status_t status = p.read(data.data(), length); + FUZZ_LOG() << "read status: " << status << " data: " << hexString(data.data(), data.size()); + }, + [] (const ::android::hardware::Parcel& p, uint8_t length) { + FUZZ_LOG() << "about to read"; + std::vector<uint8_t> data (length); + const void* inplace = p.readInplace(length); + FUZZ_LOG() << "read status: " << hexString(inplace, length); + }, + PARCEL_READ_WITH_STATUS(int8_t, readInt8), + PARCEL_READ_WITH_STATUS(uint8_t, readUint8), + PARCEL_READ_WITH_STATUS(int16_t, readInt16), + PARCEL_READ_WITH_STATUS(uint16_t, readUint16), + PARCEL_READ_OPT_STATUS(int32_t, readInt32), + PARCEL_READ_OPT_STATUS(uint32_t, readUint32), + PARCEL_READ_OPT_STATUS(int64_t, readInt64), + PARCEL_READ_OPT_STATUS(uint64_t, readUint64), + PARCEL_READ_OPT_STATUS(float, readFloat), + PARCEL_READ_OPT_STATUS(double, readDouble), + PARCEL_READ_OPT_STATUS(bool, readBool), + [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) { + FUZZ_LOG() << "about to readCString"; + const char* str = p.readCString(); + FUZZ_LOG() << "readCString " << (str ? str : "<null>"); + }, + PARCEL_READ_OPT_STATUS(::android::String16, readString16), + PARCEL_READ_WITH_STATUS(std::unique_ptr<::android::String16>, readString16), + [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) { + FUZZ_LOG() << "about to readString16Inplace"; + size_t outSize = 0; + const char16_t* str = p.readString16Inplace(&outSize); + FUZZ_LOG() << "readString16Inplace: " << hexString(str, sizeof(char16_t) * outSize); + }, + PARCEL_READ_OPT_STATUS(::android::sp<::android::hardware::IBinder>, readStrongBinder), + PARCEL_READ_WITH_STATUS(::android::sp<::android::hardware::IBinder>, readNullableStrongBinder), + [] (const ::android::hardware::Parcel& p, uint8_t size) { + FUZZ_LOG() << "about to readBuffer"; + size_t handle = 0; + const void* data = nullptr; + status_t status = p.readBuffer(size, &handle, &data); + FUZZ_LOG() << "readBuffer status: " << status << " handle: " << handle << " data: " << data; + + // should be null since we don't create any IPC objects + CHECK(data == nullptr) << data; + }, + [] (const ::android::hardware::Parcel& p, uint8_t size) { + FUZZ_LOG() << "about to readNullableBuffer"; + size_t handle = 0; + const void* data = nullptr; + status_t status = p.readNullableBuffer(size, &handle, &data); + FUZZ_LOG() << "readNullableBuffer status: " << status << " handle: " << handle << " data: " << data; + + // should be null since we don't create any IPC objects + CHECK(data == nullptr) << data; + }, + [] (const ::android::hardware::Parcel& p, uint8_t size) { + FUZZ_LOG() << "about to readEmbeddedBuffer"; + size_t handle = 0; + size_t parent_buffer_handle = 0; + size_t parent_offset = 3; + const void* data = nullptr; + status_t status = p.readEmbeddedBuffer(size, &handle, parent_buffer_handle, parent_offset, &data); + FUZZ_LOG() << "readEmbeddedBuffer status: " << status << " handle: " << handle << " data: " << data; + + // should be null since we don't create any IPC objects + CHECK(data == nullptr) << data; + }, + [] (const ::android::hardware::Parcel& p, uint8_t size) { + FUZZ_LOG() << "about to readNullableEmbeddedBuffer"; + size_t handle = 0; + size_t parent_buffer_handle = 0; + size_t parent_offset = 3; + const void* data = nullptr; + status_t status = p.readNullableEmbeddedBuffer(size, &handle, parent_buffer_handle, parent_offset, &data); + FUZZ_LOG() << "readNullableEmbeddedBuffer status: " << status << " handle: " << handle << " data: " << data; + + // should be null since we don't create any IPC objects + CHECK(data == nullptr) << data; + }, + [] (const ::android::hardware::Parcel& p, uint8_t size) { + FUZZ_LOG() << "about to readEmbeddedNativeHandle"; + size_t parent_buffer_handle = size & 0xf; + size_t parent_offset = size >> 4; + const native_handle_t* handle = nullptr; + status_t status = p.readEmbeddedNativeHandle(parent_buffer_handle, parent_offset, &handle); + FUZZ_LOG() << "readEmbeddedNativeHandle status: " << status << " handle: " << handle << " handle: " << handle; + + // should be null since we don't create any IPC objects + CHECK(handle == nullptr) << handle; + }, + [] (const ::android::hardware::Parcel& p, uint8_t size) { + FUZZ_LOG() << "about to readNullableEmbeddedNativeHandle"; + size_t parent_buffer_handle = size & 0xf; + size_t parent_offset = size >> 4; + const native_handle_t* handle = nullptr; + status_t status = p.readNullableEmbeddedNativeHandle(parent_buffer_handle, parent_offset, &handle); + FUZZ_LOG() << "readNullableEmbeddedNativeHandle status: " << status << " handle: " << handle << " handle: " << handle; + + // should be null since we don't create any IPC objects + CHECK(handle == nullptr) << handle; + }, + [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) { + FUZZ_LOG() << "about to readNativeHandleNoDup"; + const native_handle_t* handle = nullptr; + status_t status = p.readNativeHandleNoDup(&handle); + FUZZ_LOG() << "readNativeHandleNoDup status: " << status << " handle: " << handle; + + // should be null since we don't create any IPC objects + CHECK(handle == nullptr) << handle; + CHECK(status != ::android::OK); + }, + [] (const ::android::hardware::Parcel& p, uint8_t /*data*/) { + FUZZ_LOG() << "about to readNullableNativeHandleNoDup"; + const native_handle_t* handle = nullptr; + status_t status = p.readNullableNativeHandleNoDup(&handle); + FUZZ_LOG() << "readNullableNativeHandleNoDup status: " << status << " handle: " << handle; + + // should be null since we don't create any IPC objects + CHECK(handle == nullptr) << handle; + }, +}; +// clang-format on diff --git a/libs/binder/fuzzer/hwbinder.h b/libs/binder/fuzzer/hwbinder.h new file mode 100644 index 0000000000..a6c66beb44 --- /dev/null +++ b/libs/binder/fuzzer/hwbinder.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2019 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 <hwbinder/Parcel.h> +#include <vector> + +#include "parcel_fuzzer.h" + +extern std::vector<ParcelRead<::android::hardware::Parcel>> HWBINDER_PARCEL_READ_FUNCTIONS; diff --git a/libs/binder/fuzzer/main.cpp b/libs/binder/fuzzer/main.cpp new file mode 100644 index 0000000000..6657edb46d --- /dev/null +++ b/libs/binder/fuzzer/main.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2019 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 FUZZ_LOG_TAG "main" + +#include "binder.h" +#include "binder_ndk.h" +#include "hwbinder.h" +#include "util.h" + +#include <android-base/logging.h> + +#include <cstdlib> +#include <ctime> + +template <typename P> +void doFuzz( + const std::vector<ParcelRead<P>>& reads, + const std::vector<uint8_t>& input, + const std::vector<uint8_t>& instructions) { + + P p; + p.setData(input.data(), input.size()); + + // since we are only using a byte to index + CHECK(reads.size() <= 255) << reads.size(); + + for (size_t i = 0; i < instructions.size() - 1; i += 2) { + uint8_t a = instructions[i]; + uint8_t readIdx = a % reads.size(); + + uint8_t b = instructions[i + 1]; + + FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2 + << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(readIdx) + << ") arg: " << static_cast<size_t>(b) << " size: " << p.dataSize() + << " avail: " << p.dataAvail() << " pos: " << p.dataPosition() + << " cap: " << p.dataCapacity(); + + reads[readIdx](p, b); + } +} + +void fuzz(uint8_t options, const std::vector<uint8_t>& input, const std::vector<uint8_t>& instructions) { + uint8_t parcelType = options & 0x3; + + switch (parcelType) { + case 0x0: + doFuzz<::android::hardware::Parcel>(HWBINDER_PARCEL_READ_FUNCTIONS, input, + instructions); + break; + case 0x1: + doFuzz<::android::Parcel>(BINDER_PARCEL_READ_FUNCTIONS, input, instructions); + break; + case 0x2: + doFuzz<NdkParcelAdapter>(BINDER_NDK_PARCEL_READ_FUNCTIONS, input, instructions); + break; + case 0x3: + /*reserved for future use*/ + break; + default: + LOG_ALWAYS_FATAL("unknown parcel type %d", static_cast<int>(parcelType)); + } +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size <= 1) return 0; // no use + + // avoid timeouts, see b/142617274, b/142473153 + if (size > 50000) return 0; + + uint8_t options = *data; + data++; + size--; + + // TODO: generate 'objects' data + + // data to fill out parcel + size_t inputLen = size / 2; + std::vector<uint8_t> input(data, data + inputLen); + data += inputLen; + size -= inputLen; + + // data to use to determine what to do + size_t instructionLen = size; + std::vector<uint8_t> instructions(data, data + instructionLen); + data += instructionLen; + size -= instructionLen; + + CHECK(size == 0) << "size: " << size; + + FUZZ_LOG() << "options: " << (int)options << " inputLen: " << inputLen << " instructionLen: " << instructionLen; + FUZZ_LOG() << "input: " << hexString(input); + FUZZ_LOG() << "instructions: " << hexString(instructions); + + fuzz(options, input, instructions); + return 0; +} diff --git a/libs/binder/fuzzer/parcel_fuzzer.h b/libs/binder/fuzzer/parcel_fuzzer.h new file mode 100644 index 0000000000..10cf17c328 --- /dev/null +++ b/libs/binder/fuzzer/parcel_fuzzer.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2019 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. + */ + +template <typename P> +using ParcelRead = std::function<void(const P& p, uint8_t data)>; diff --git a/libs/binder/fuzzer/util.cpp b/libs/binder/fuzzer/util.cpp new file mode 100644 index 0000000000..479f406d8c --- /dev/null +++ b/libs/binder/fuzzer/util.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 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 FUZZ_LOG_TAG "util" +#include "util.h" + +#include <android-base/logging.h> + +#include <iomanip> +#include <sstream> + +std::string hexString(const void* bytes, size_t len) { + if (bytes == nullptr) return "<null>"; + + const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes); + char chars[] = "0123456789abcdef"; + std::string result; + result.resize(len * 2); + + for (size_t i = 0; i < len; i++) { + result[2 * i] = chars[bytes8[i] >> 4]; + result[2 * i + 1] = chars[bytes8[i] & 0xf]; + } + + return result; +} +std::string hexString(const std::vector<uint8_t>& bytes) { + return hexString(bytes.data(), bytes.size()); +} diff --git a/libs/binder/fuzzer/util.h b/libs/binder/fuzzer/util.h new file mode 100644 index 0000000000..aa504d29f2 --- /dev/null +++ b/libs/binder/fuzzer/util.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 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 <iostream> +#include <sstream> +#include <string> +#include <vector> + +#ifndef FUZZ_LOG_TAG +#error "Must define FUZZ_LOG_TAG" +#endif + +#define FUZZ_LOG() FuzzLog(FUZZ_LOG_TAG).log() + +#ifdef ENABLE_LOG_FUZZ +class FuzzLog { +public: + FuzzLog(const char* tag) : mTag(tag) {} + ~FuzzLog() { std::cout << mTag << ": " << mOs.str() << std::endl; } + + std::stringstream& log() { return mOs; } + +private: + const char* mTag = nullptr; + std::stringstream mOs; +}; +#else +class FuzzLog { +public: + FuzzLog(const char* /*tag*/) {} + template <typename T> + FuzzLog& operator<<(const T& /*t*/) { + return *this; + } + FuzzLog& log() { return *this; } +}; +#endif + +std::string hexString(const void* bytes, size_t len); +std::string hexString(const std::vector<uint8_t>& bytes); diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h index b19cde75b6..0ab40b8627 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h @@ -17,12 +17,14 @@ #ifndef ANDROID_APP_OPS_MANAGER_H #define ANDROID_APP_OPS_MANAGER_H -#ifndef __ANDROID_VNDK__ - #include <binder/IAppOpsService.h> #include <utils/threads.h> +#ifdef __ANDROID_VNDK__ +#error "This header is not visible to vendors" +#endif + // --------------------------------------------------------------------------- namespace android { @@ -109,6 +111,18 @@ public: OP_START_FOREGROUND = 76, OP_BLUETOOTH_SCAN = 77, OP_USE_BIOMETRIC = 78, + OP_ACTIVITY_RECOGNITION = 79, + OP_SMS_FINANCIAL_TRANSACTIONS = 80, + OP_READ_MEDIA_AUDIO = 81, + OP_WRITE_MEDIA_AUDIO = 82, + OP_READ_MEDIA_VIDEO = 83, + OP_WRITE_MEDIA_VIDEO = 84, + OP_READ_MEDIA_IMAGES = 85, + OP_WRITE_MEDIA_IMAGES = 86, + OP_LEGACY_STORAGE = 87, + OP_ACCESS_ACCESSIBILITY = 88, + OP_READ_DEVICE_IDENTIFIERS = 89, + _NUM_OP = 90 }; AppOpsManager(); @@ -116,27 +130,38 @@ public: int32_t checkOp(int32_t op, int32_t uid, const String16& callingPackage); int32_t checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid, const String16& callingPackage); + // @Deprecated, use noteOp(int32_t, int32_t uid, const String16&, const String16&) instead int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage); + int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage, + const String16& message); + // @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&) + // instead int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, bool startIfModeDefault); + int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, + bool startIfModeDefault, const String16& message); void finishOp(int32_t op, int32_t uid, const String16& callingPackage); void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback); void stopWatchingMode(const sp<IAppOpsCallback>& callback); int32_t permissionToOpCode(const String16& permission); + void setCameraAudioRestriction(int32_t mode); + void noteAsyncOp(const String16& callingPackageName, int32_t uid, const String16& packageName, + int32_t opCode, const String16& message); private: Mutex mLock; sp<IAppOpsService> mService; sp<IAppOpsService> getService(); + void markAppOpNoted(int32_t uid, const String16& packageName, int32_t opCode, + const String16& message); + bool shouldCollectNotes(int32_t opCode); }; } // namespace android + // --------------------------------------------------------------------------- -#else // __ANDROID_VNDK__ -#error "This header is not visible to vendors" -#endif // __ANDROID_VNDK__ #endif // ANDROID_APP_OPS_MANAGER_H diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h index b74c623e44..8b8a3c21ce 100644 --- a/libs/binder/include/binder/IAppOpsService.h +++ b/libs/binder/include/binder/IAppOpsService.h @@ -18,11 +18,13 @@ #ifndef ANDROID_IAPP_OPS_SERVICE_H #define ANDROID_IAPP_OPS_SERVICE_H -#ifndef __ANDROID_VNDK__ - #include <binder/IAppOpsCallback.h> #include <binder/IInterface.h> +#ifdef __ANDROID_VNDK__ +#error "This header is not visible to vendors" +#endif + namespace android { // ---------------------------------------------------------------------- @@ -45,6 +47,10 @@ public: virtual int32_t permissionToOpCode(const String16& permission) = 0; virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid, const String16& packageName) = 0; + virtual void setCameraAudioRestriction(int32_t mode) = 0; + virtual void noteAsyncOp(const String16& callingPackageName, int32_t uid, + const String16& packageName, int32_t opCode, const String16& message) = 0; + virtual bool shouldCollectNotes(int32_t opCode) = 0; enum { CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, @@ -56,6 +62,9 @@ public: GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6, PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7, CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8, + NOTE_ASYNC_OP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9, + SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10, + SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+11, }; enum { @@ -81,8 +90,4 @@ public: } // namespace android -#else // __ANDROID_VNDK__ -#error "This header is not visible to vendors" -#endif // __ANDROID_VNDK__ - #endif // ANDROID_IAPP_OPS_SERVICE_H diff --git a/libs/binder/include/binder/IMemory.h b/libs/binder/include/binder/IMemory.h index 98e92c4441..1a36eb0436 100644 --- a/libs/binder/include/binder/IMemory.h +++ b/libs/binder/include/binder/IMemory.h @@ -76,13 +76,34 @@ public: // NOLINTNEXTLINE(google-default-arguments) virtual sp<IMemoryHeap> getMemory(ssize_t* offset=nullptr, size_t* size=nullptr) const = 0; + // helpers + + // Accessing the underlying pointer must be done with caution, as there are + // some inherent security risks associated with it. When receiving an + // IMemory from an untrusted process, there is currently no way to guarantee + // that this process would't change the content after the fact. This may + // lead to TOC/TOU class of security bugs. In most cases, when performance + // is not an issue, the recommended practice is to immediately copy the + // buffer upon reception, then work with the copy, e.g.: + // + // std::string private_copy(mem.size(), '\0'); + // memcpy(private_copy.data(), mem.unsecurePointer(), mem.size()); + // + // In cases where performance is an issue, this matter must be addressed on + // an ad-hoc basis. void* unsecurePointer() const; - // helpers - void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const; - void* pointer() const; size_t size() const; ssize_t offset() const; + +private: + // These are now deprecated and are left here for backward-compatibility + // with prebuilts that may reference these symbol at runtime. + // Instead, new code should use unsecurePointer()/unsecureFastPointer(), + // which do the same thing, but make it more obvious that there are some + // security-related pitfalls associated with them. + void* pointer() const; + void* fastPointer(const sp<IBinder>& heap, ssize_t offset) const; }; class BnMemory : public BnInterface<IMemory> diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index 62a0f9f9b1..22344b6173 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -27,7 +27,7 @@ cc_defaults { }, } -cc_library_shared { +cc_library { name: "libbinder_ndk", defaults: ["libbinder_ndk_host_user"], @@ -69,6 +69,12 @@ cc_library_shared { ], target: { + android: { + // Only one copy of this library on an Android device + static: { + enabled: false, + }, + }, linux: { version_script: "libbinder_ndk.map.txt", }, diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 5e0574ad8a..db4a36b16b 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -73,6 +73,7 @@ enum BinderLibTestTranscationCode { BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, BINDER_LIB_TEST_GET_WORK_SOURCE_TRANSACTION, BINDER_LIB_TEST_ECHO_VECTOR, + BINDER_LIB_TEST_REJECT_BUF, }; pid_t start_server_process(int arg2, bool usePoll = false) @@ -1025,6 +1026,34 @@ TEST_F(BinderLibTest, VectorSent) { EXPECT_EQ(readValue, testValue); } +TEST_F(BinderLibTest, BufRejected) { + Parcel data, reply; + uint32_t buf; + sp<IBinder> server = addServer(); + ASSERT_TRUE(server != nullptr); + + binder_buffer_object obj { + .hdr = { .type = BINDER_TYPE_PTR }, + .buffer = reinterpret_cast<binder_uintptr_t>((void*)&buf), + .length = 4, + .flags = 0, + }; + data.setDataCapacity(1024); + // Write a bogus object at offset 0 to get an entry in the offset table + data.writeFileDescriptor(0); + EXPECT_EQ(data.objectsCount(), 1); + uint8_t *parcelData = const_cast<uint8_t*>(data.data()); + // And now, overwrite it with the buffer object + memcpy(parcelData, &obj, sizeof(obj)); + data.setDataSize(sizeof(obj)); + + status_t ret = server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply); + // Either the kernel should reject this transaction (if it's correct), but + // if it's not, the server implementation should return an error if it + // finds an object in the received Parcel. + EXPECT_NE(NO_ERROR, ret); +} + class BinderLibTestService : public BBinder { public: @@ -1307,6 +1336,9 @@ class BinderLibTestService : public BBinder reply->writeUint64Vector(vector); return NO_ERROR; } + case BINDER_LIB_TEST_REJECT_BUF: { + return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR; + } default: return UNKNOWN_TRANSACTION; }; @@ -1338,6 +1370,9 @@ int run_server(int index, int readypipefd, bool usePoll) */ testService->setExtension(new BBinder()); + // Required for test "BufRejected' + testService->setRequestingSid(true); + /* * We need this below, but can't hold a sp<> because it prevents the * node from being cleaned up automatically. It's safe in this case diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp index 28cb13827d..a8f7d92b41 100644 --- a/libs/cputimeinstate/Android.bp +++ b/libs/cputimeinstate/Android.bp @@ -8,6 +8,7 @@ cc_library { "liblog", "libnetdutils" ], + header_libs: ["bpf_prog_headers"], cflags: [ "-Werror", "-Wall", @@ -19,8 +20,13 @@ cc_test { name: "libtimeinstate_test", srcs: ["testtimeinstate.cpp"], shared_libs: [ + "libbase", + "libbpf", + "libbpf_android", "libtimeinstate", + "libnetdutils", ], + header_libs: ["bpf_prog_headers"], cflags: [ "-Werror", "-Wall", diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 5fd4a95d7b..45fea850aa 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -17,12 +17,16 @@ #define LOG_TAG "libtimeinstate" #include "cputimeinstate.h" +#include <bpf_timeinstate.h> #include <dirent.h> #include <errno.h> #include <inttypes.h> +#include <sys/sysinfo.h> #include <mutex> +#include <numeric> +#include <optional> #include <set> #include <string> #include <unordered_map> @@ -37,44 +41,36 @@ #include <libbpf.h> #include <log/log.h> -#define BPF_FS_PATH "/sys/fs/bpf/" - using android::base::StringPrintf; using android::base::unique_fd; namespace android { namespace bpf { -struct time_key_t { - uint32_t uid; - uint32_t freq; -}; - -struct val_t { - uint64_t ar[100]; -}; - static std::mutex gInitializedMutex; static bool gInitialized = false; static uint32_t gNPolicies = 0; +static uint32_t gNCpus = 0; static std::vector<std::vector<uint32_t>> gPolicyFreqs; static std::vector<std::vector<uint32_t>> gPolicyCpus; static std::set<uint32_t> gAllFreqs; -static unique_fd gMapFd; +static unique_fd gTisMapFd; +static unique_fd gConcurrentMapFd; -static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) { +static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) { std::string data; - if (!android::base::ReadFileToString(path, &data)) return false; + if (!android::base::ReadFileToString(path, &data)) return {}; auto strings = android::base::Split(data, " \n"); + std::vector<uint32_t> ret; for (const auto &s : strings) { if (s.empty()) continue; uint32_t n; - if (!android::base::ParseUint(s, &n)) return false; - out->emplace_back(n); + if (!android::base::ParseUint(s, &n)) return {}; + ret.emplace_back(n); } - return true; + return ret; } static int isPolicyFile(const struct dirent *d) { @@ -93,6 +89,8 @@ static bool initGlobals() { std::lock_guard<std::mutex> guard(gInitializedMutex); if (gInitialized) return true; + gNCpus = get_nprocs_conf(); + struct dirent **dirlist; const char basepath[] = "/sys/devices/system/cpu/cpufreq"; int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles); @@ -111,21 +109,28 @@ static bool initGlobals() { for (const auto &name : {"available", "boost"}) { std::string path = StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name); - if (!readNumbersFromFile(path, &freqs)) return false; + auto nums = readNumbersFromFile(path); + if (!nums) continue; + freqs.insert(freqs.end(), nums->begin(), nums->end()); } + if (freqs.empty()) return false; std::sort(freqs.begin(), freqs.end()); gPolicyFreqs.emplace_back(freqs); for (auto freq : freqs) gAllFreqs.insert(freq); - std::vector<uint32_t> cpus; std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus"); - if (!readNumbersFromFile(path, &cpus)) return false; - gPolicyCpus.emplace_back(cpus); + auto cpus = readNumbersFromFile(path); + if (!cpus) return false; + gPolicyCpus.emplace_back(*cpus); } - gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")}; - if (gMapFd < 0) return false; + gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + if (gTisMapFd < 0) return false; + + gConcurrentMapFd = + unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + if (gConcurrentMapFd < 0) return false; gInitialized = true; return true; @@ -145,97 +150,259 @@ static bool attachTracepointProgram(const std::string &eventType, const std::str // process dies then it must be called again to resume tracking. // This function should *not* be called while tracking is already active; doing so is unnecessary // and can lead to accounting errors. -bool startTrackingUidCpuFreqTimes() { +bool startTrackingUidTimes() { + if (!initGlobals()) return false; + + unique_fd fd(bpf_obj_get(BPF_FS_PATH "map_time_in_state_cpu_policy_map")); + if (fd < 0) return false; + + for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) { + for (auto &cpu : gPolicyCpus[i]) { + if (writeToMapEntry(fd, &cpu, &i, BPF_ANY)) return false; + } + } + + unique_fd fd2(bpf_obj_get(BPF_FS_PATH "map_time_in_state_freq_to_idx_map")); + if (fd2 < 0) return false; + freq_idx_key_t key; + for (uint32_t i = 0; i < gNPolicies; ++i) { + key.policy = i; + for (uint32_t j = 0; j < gPolicyFreqs[i].size(); ++j) { + key.freq = gPolicyFreqs[i][j]; + // Start indexes at 1 so that uninitialized state is distinguishable from lowest freq. + // The uid_times map still uses 0-based indexes, and the sched_switch program handles + // conversion between them, so this does not affect our map reading code. + uint32_t idx = j + 1; + if (writeToMapEntry(fd2, &key, &idx, BPF_ANY)) return false; + } + } + return attachTracepointProgram("sched", "sched_switch") && attachTracepointProgram("power", "cpu_frequency"); } -// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes. -// Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors -// using the format: +// Retrieve the times in ns that uid spent running at each CPU frequency. +// Return contains no value on error, otherwise it contains a vector of vectors using the format: // [[t0_0, t0_1, ...], // [t1_0, t1_1, ...], ...] // where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq. -bool getUidCpuFreqTimes(uint32_t uid, std::vector<std::vector<uint64_t>> *freqTimes) { - if (!gInitialized && !initGlobals()) return false; - time_key_t key = {.uid = uid, .freq = 0}; - - freqTimes->clear(); - freqTimes->resize(gNPolicies); - std::vector<uint32_t> idxs(gNPolicies, 0); - - val_t value; - for (uint32_t freq : gAllFreqs) { - key.freq = freq; - int ret = findMapEntry(gMapFd, &key, &value); - if (ret) { - if (errno == ENOENT) - memset(&value.ar, 0, sizeof(value.ar)); - else - return false; +std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid) { + if (!gInitialized && !initGlobals()) return {}; + + std::vector<std::vector<uint64_t>> out; + uint32_t maxFreqCount = 0; + for (const auto &freqList : gPolicyFreqs) { + if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); + out.emplace_back(freqList.size(), 0); + } + + std::vector<tis_val_t> vals(gNCpus); + time_key_t key = {.uid = uid}; + for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) { + key.bucket = i; + if (findMapEntry(gTisMapFd, &key, vals.data())) { + if (errno != ENOENT) return {}; + continue; } - for (uint32_t i = 0; i < gNPolicies; ++i) { - if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue; - uint64_t time = 0; - for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu]; - idxs[i] += 1; - (*freqTimes)[i].emplace_back(time); + + auto offset = i * FREQS_PER_ENTRY; + auto nextOffset = (i + 1) * FREQS_PER_ENTRY; + for (uint32_t j = 0; j < gNPolicies; ++j) { + if (offset >= gPolicyFreqs[j].size()) continue; + auto begin = out[j].begin() + offset; + auto end = nextOffset < gPolicyFreqs[j].size() ? begin + FREQS_PER_ENTRY : out[j].end(); + + for (const auto &cpu : gPolicyCpus[j]) { + std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>()); + } } } - return true; + return out; } -// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap. -// Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to -// vectors of vectors using the format: +// Retrieve the times in ns that each uid spent running at each CPU freq. +// Return contains no value on error, otherwise it contains a map from uids to vectors of vectors +// using the format: // { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...], // uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... } // where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq. -bool getUidsCpuFreqTimes( - std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *freqTimeMap) { - if (!gInitialized && !initGlobals()) return false; +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> +getUidsCpuFreqTimes() { + if (!gInitialized && !initGlobals()) return {}; + time_key_t key, prevKey; + std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map; + if (getFirstMapKey(gTisMapFd, &key)) { + if (errno == ENOENT) return map; + return std::nullopt; + } - int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times"); - if (fd < 0) return false; - BpfMap<time_key_t, val_t> m(fd); + std::vector<std::vector<uint64_t>> mapFormat; + for (const auto &freqList : gPolicyFreqs) mapFormat.emplace_back(freqList.size(), 0); - std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs; - for (uint32_t i = 0; i < gNPolicies; ++i) { - std::unordered_map<uint32_t, uint32_t> freqIdxs; - for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j; - policyFreqIdxs.emplace_back(freqIdxs); + std::vector<tis_val_t> vals(gNCpus); + do { + if (findMapEntry(gTisMapFd, &key, vals.data())) return {}; + if (map.find(key.uid) == map.end()) map.emplace(key.uid, mapFormat); + + auto offset = key.bucket * FREQS_PER_ENTRY; + auto nextOffset = (key.bucket + 1) * FREQS_PER_ENTRY; + for (uint32_t i = 0; i < gNPolicies; ++i) { + if (offset >= gPolicyFreqs[i].size()) continue; + auto begin = map[key.uid][i].begin() + offset; + auto end = nextOffset < gPolicyFreqs[i].size() ? begin + FREQS_PER_ENTRY : + map[key.uid][i].end(); + for (const auto &cpu : gPolicyCpus[i]) { + std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>()); + } + } + prevKey = key; + } while (!getNextMapKey(gTisMapFd, &prevKey, &key)); + if (errno != ENOENT) return {}; + return map; +} + +static bool verifyConcurrentTimes(const concurrent_time_t &ct) { + uint64_t activeSum = std::accumulate(ct.active.begin(), ct.active.end(), (uint64_t)0); + uint64_t policySum = 0; + for (const auto &vec : ct.policy) { + policySum += std::accumulate(vec.begin(), vec.end(), (uint64_t)0); } + return activeSum == policySum; +} + +// Retrieve the times in ns that uid spent running concurrently with each possible number of other +// tasks on each cluster (policy times) and overall (active times). +// Return contains no value on error, otherwise it contains a concurrent_time_t with the format: +// {.active = [a0, a1, ...], .policy = [[p0_0, p0_1, ...], [p1_0, p1_1, ...], ...]} +// where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent +// running on the ith cluster, concurrently with tasks on j other cpus in the same cluster +std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) { + if (!gInitialized && !initGlobals()) return {}; + concurrent_time_t ret = {.active = std::vector<uint64_t>(gNCpus, 0)}; + for (const auto &cpuList : gPolicyCpus) ret.policy.emplace_back(cpuList.size(), 0); + std::vector<concurrent_val_t> vals(gNCpus); + time_key_t key = {.uid = uid}; + for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) { + if (findMapEntry(gConcurrentMapFd, &key, vals.data())) { + if (errno != ENOENT) return {}; + continue; + } + auto offset = key.bucket * CPUS_PER_ENTRY; + auto nextOffset = (key.bucket + 1) * CPUS_PER_ENTRY; + + auto activeBegin = ret.active.begin() + offset; + auto activeEnd = nextOffset < gNCpus ? activeBegin + CPUS_PER_ENTRY : ret.active.end(); - auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val, - const BpfMap<time_key_t, val_t> &) { - if (freqTimeMap->find(key.uid) == freqTimeMap->end()) { - (*freqTimeMap)[key.uid].resize(gNPolicies); - for (uint32_t i = 0; i < gNPolicies; ++i) { - (*freqTimeMap)[key.uid][i].resize(gPolicyFreqs[i].size(), 0); + for (uint32_t cpu = 0; cpu < gNCpus; ++cpu) { + std::transform(activeBegin, activeEnd, std::begin(vals[cpu].active), activeBegin, + std::plus<uint64_t>()); + } + + for (uint32_t policy = 0; policy < gNPolicies; ++policy) { + if (offset >= gPolicyCpus[policy].size()) continue; + auto policyBegin = ret.policy[policy].begin() + offset; + auto policyEnd = nextOffset < gPolicyCpus[policy].size() ? policyBegin + CPUS_PER_ENTRY + : ret.policy[policy].end(); + + for (const auto &cpu : gPolicyCpus[policy]) { + std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin, + std::plus<uint64_t>()); } } + } + if (!verifyConcurrentTimes(ret) && retry) return getUidConcurrentTimes(uid, false); + return ret; +} + +// Retrieve the times in ns that each uid spent running concurrently with each possible number of +// other tasks on each cluster (policy times) and overall (active times). +// Return contains no value on error, otherwise it contains a map from uids to concurrent_time_t's +// using the format: +// { uid0 -> {.active = [a0, a1, ...], .policy = [[p0_0, p0_1, ...], [p1_0, p1_1, ...], ...] }, ...} +// where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent +// running on the ith cluster, concurrently with tasks on j other cpus in the same cluster. +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes() { + if (!gInitialized && !initGlobals()) return {}; + time_key_t key, prevKey; + std::unordered_map<uint32_t, concurrent_time_t> ret; + if (getFirstMapKey(gConcurrentMapFd, &key)) { + if (errno == ENOENT) return ret; + return {}; + } + + concurrent_time_t retFormat = {.active = std::vector<uint64_t>(gNCpus, 0)}; + for (const auto &cpuList : gPolicyCpus) retFormat.policy.emplace_back(cpuList.size(), 0); + + std::vector<concurrent_val_t> vals(gNCpus); + std::vector<uint64_t>::iterator activeBegin, activeEnd, policyBegin, policyEnd; + + do { + if (findMapEntry(gConcurrentMapFd, &key, vals.data())) return {}; + if (ret.find(key.uid) == ret.end()) ret.emplace(key.uid, retFormat); + + auto offset = key.bucket * CPUS_PER_ENTRY; + auto nextOffset = (key.bucket + 1) * CPUS_PER_ENTRY; + + activeBegin = ret[key.uid].active.begin(); + activeEnd = nextOffset < gNCpus ? activeBegin + CPUS_PER_ENTRY : ret[key.uid].active.end(); + + for (uint32_t cpu = 0; cpu < gNCpus; ++cpu) { + std::transform(activeBegin, activeEnd, std::begin(vals[cpu].active), activeBegin, + std::plus<uint64_t>()); + } + + for (uint32_t policy = 0; policy < gNPolicies; ++policy) { + if (offset >= gPolicyCpus[policy].size()) continue; + policyBegin = ret[key.uid].policy[policy].begin() + offset; + policyEnd = nextOffset < gPolicyCpus[policy].size() ? policyBegin + CPUS_PER_ENTRY + : ret[key.uid].policy[policy].end(); - for (size_t policy = 0; policy < gNPolicies; ++policy) { for (const auto &cpu : gPolicyCpus[policy]) { - auto freqIdx = policyFreqIdxs[policy][key.freq]; - (*freqTimeMap)[key.uid][policy][freqIdx] += val.ar[cpu]; + std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin, + std::plus<uint64_t>()); } } - return android::netdutils::status::ok; - }; - return isOk(m.iterateWithValue(fn)); + prevKey = key; + } while (!getNextMapKey(gConcurrentMapFd, &prevKey, &key)); + if (errno != ENOENT) return {}; + for (const auto &[key, value] : ret) { + if (!verifyConcurrentTimes(value)) { + auto val = getUidConcurrentTimes(key, false); + if (val.has_value()) ret[key] = val.value(); + } + } + return ret; } // Clear all time in state data for a given uid. Returns false on error, true otherwise. -bool clearUidCpuFreqTimes(uint32_t uid) { +// This is only suitable for clearing data when an app is uninstalled; if called on a UID with +// running tasks it will cause time in state vs. concurrent time totals to be inconsistent for that +// UID. +bool clearUidTimes(uint32_t uid) { if (!gInitialized && !initGlobals()) return false; - time_key_t key = {.uid = uid, .freq = 0}; - std::vector<uint32_t> idxs(gNPolicies, 0); - for (auto freq : gAllFreqs) { - key.freq = freq; - if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false; + time_key_t key = {.uid = uid}; + + uint32_t maxFreqCount = 0; + for (const auto &freqList : gPolicyFreqs) { + if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); + } + + tis_val_t zeros = {0}; + std::vector<tis_val_t> vals(gNCpus, zeros); + for (key.bucket = 0; key.bucket <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++key.bucket) { + if (writeToMapEntry(gTisMapFd, &key, vals.data(), BPF_EXIST) && errno != ENOENT) + return false; + if (deleteMapEntry(gTisMapFd, &key) && errno != ENOENT) return false; + } + + concurrent_val_t czeros = {.policy = {0}, .active = {0}}; + std::vector<concurrent_val_t> cvals(gNCpus, czeros); + for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) { + if (writeToMapEntry(gConcurrentMapFd, &key, cvals.data(), BPF_EXIST) && errno != ENOENT) + return false; + if (deleteMapEntry(gConcurrentMapFd, &key) && errno != ENOENT) return false; } return true; } diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h index 9f6103ed9b..f620715dab 100644 --- a/libs/cputimeinstate/cputimeinstate.h +++ b/libs/cputimeinstate/cputimeinstate.h @@ -22,10 +22,19 @@ namespace android { namespace bpf { -bool startTrackingUidCpuFreqTimes(); -bool getUidCpuFreqTimes(unsigned int uid, std::vector<std::vector<uint64_t>> *freqTimes); -bool getUidsCpuFreqTimes(std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *tisMap); -bool clearUidCpuFreqTimes(unsigned int uid); +bool startTrackingUidTimes(); +std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid); +std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> + getUidsCpuFreqTimes(); + +struct concurrent_time_t { + std::vector<uint64_t> active; + std::vector<std::vector<uint64_t>> policy; +}; + +std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry = true); +std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes(); +bool clearUidTimes(unsigned int uid); } // namespace bpf } // namespace android diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 9837865dfb..c0cd3e07ff 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -1,57 +1,354 @@ +#include <bpf_timeinstate.h> + +#include <sys/sysinfo.h> + +#include <numeric> #include <unordered_map> #include <vector> #include <gtest/gtest.h> +#include <android-base/unique_fd.h> +#include <bpf/BpfMap.h> #include <cputimeinstate.h> +#include <libbpf.h> namespace android { namespace bpf { +static constexpr uint64_t NSEC_PER_SEC = 1000000000; +static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365; + using std::vector; -TEST(TimeInStateTest, SingleUid) { - vector<vector<uint64_t>> times; - ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); - EXPECT_FALSE(times.empty()); +TEST(TimeInStateTest, SingleUidTimeInState) { + auto times = getUidCpuFreqTimes(0); + ASSERT_TRUE(times.has_value()); + EXPECT_FALSE(times->empty()); +} + +TEST(TimeInStateTest, SingleUidConcurrentTimes) { + auto concurrentTimes = getUidConcurrentTimes(0); + ASSERT_TRUE(concurrentTimes.has_value()); + ASSERT_FALSE(concurrentTimes->active.empty()); + ASSERT_FALSE(concurrentTimes->policy.empty()); + + uint64_t policyEntries = 0; + for (const auto &policyTimeVec : concurrentTimes->policy) policyEntries += policyTimeVec.size(); + ASSERT_EQ(concurrentTimes->active.size(), policyEntries); +} + +static void TestConcurrentTimesConsistent(const struct concurrent_time_t &concurrentTime) { + size_t maxPolicyCpus = 0; + for (const auto &vec : concurrentTime.policy) { + maxPolicyCpus = std::max(maxPolicyCpus, vec.size()); + } + uint64_t policySum = 0; + for (size_t i = 0; i < maxPolicyCpus; ++i) { + for (const auto &vec : concurrentTime.policy) { + if (i < vec.size()) policySum += vec[i]; + } + ASSERT_LE(concurrentTime.active[i], policySum); + policySum -= concurrentTime.active[i]; + } + policySum = 0; + for (size_t i = 0; i < concurrentTime.active.size(); ++i) { + for (const auto &vec : concurrentTime.policy) { + if (i < vec.size()) policySum += vec[vec.size() - 1 - i]; + } + auto activeSum = concurrentTime.active[concurrentTime.active.size() - 1 - i]; + // This check is slightly flaky because we may read a map entry in the middle of an update + // when active times have been updated but policy times have not. This happens infrequently + // and can be distinguished from more serious bugs by re-running the test: if the underlying + // data itself is inconsistent, the test will fail every time. + ASSERT_LE(activeSum, policySum); + policySum -= activeSum; + } +} + +static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &timeInState, + const struct concurrent_time_t &concurrentTime) { + ASSERT_NO_FATAL_FAILURE(TestConcurrentTimesConsistent(concurrentTime)); + ASSERT_EQ(timeInState.size(), concurrentTime.policy.size()); + uint64_t policySum = 0; + for (uint32_t i = 0; i < timeInState.size(); ++i) { + uint64_t tisSum = + std::accumulate(timeInState[i].begin(), timeInState[i].end(), (uint64_t)0); + uint64_t concurrentSum = std::accumulate(concurrentTime.policy[i].begin(), + concurrentTime.policy[i].end(), (uint64_t)0); + if (tisSum < concurrentSum) + ASSERT_LE(concurrentSum - tisSum, NSEC_PER_SEC); + else + ASSERT_LE(tisSum - concurrentSum, NSEC_PER_SEC); + policySum += concurrentSum; + } + uint64_t activeSum = std::accumulate(concurrentTime.active.begin(), concurrentTime.active.end(), + (uint64_t)0); + EXPECT_EQ(activeSum, policySum); } -TEST(TimeInStateTest, AllUid) { +TEST(TimeInStateTest, SingleUidTimesConsistent) { + auto times = getUidCpuFreqTimes(0); + ASSERT_TRUE(times.has_value()); + + auto concurrentTimes = getUidConcurrentTimes(0); + ASSERT_TRUE(concurrentTimes.has_value()); + + ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes)); +} + +TEST(TimeInStateTest, AllUidTimeInState) { vector<size_t> sizes; - std::unordered_map<uint32_t, vector<vector<uint64_t>>> map; - ASSERT_TRUE(getUidsCpuFreqTimes(&map)); + auto map = getUidsCpuFreqTimes(); + ASSERT_TRUE(map.has_value()); - ASSERT_FALSE(map.empty()); + ASSERT_FALSE(map->empty()); - auto firstEntry = map.begin()->second; + auto firstEntry = map->begin()->second; for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size()); - for (const auto &vec : map) { + for (const auto &vec : *map) { ASSERT_EQ(vec.second.size(), sizes.size()); for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]); } } +TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { + auto map = getUidsCpuFreqTimes(); + ASSERT_TRUE(map.has_value()); + ASSERT_FALSE(map->empty()); + + for (const auto &kv : *map) { + uint32_t uid = kv.first; + auto times1 = kv.second; + auto times2 = getUidCpuFreqTimes(uid); + ASSERT_TRUE(times2.has_value()); + + ASSERT_EQ(times1.size(), times2->size()); + for (uint32_t i = 0; i < times1.size(); ++i) { + ASSERT_EQ(times1[i].size(), (*times2)[i].size()); + for (uint32_t j = 0; j < times1[i].size(); ++j) { + ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC); + } + } + } +} + +TEST(TimeInStateTest, AllUidConcurrentTimes) { + auto map = getUidsConcurrentTimes(); + ASSERT_TRUE(map.has_value()); + ASSERT_FALSE(map->empty()); + + auto firstEntry = map->begin()->second; + for (const auto &kv : *map) { + ASSERT_EQ(kv.second.active.size(), firstEntry.active.size()); + ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size()); + for (size_t i = 0; i < kv.second.policy.size(); ++i) { + ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size()); + } + } +} + +TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) { + auto map = getUidsConcurrentTimes(); + ASSERT_TRUE(map.has_value()); + for (const auto &kv : *map) { + uint32_t uid = kv.first; + auto times1 = kv.second; + auto times2 = getUidConcurrentTimes(uid); + ASSERT_TRUE(times2.has_value()); + for (uint32_t i = 0; i < times1.active.size(); ++i) { + ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC); + } + for (uint32_t i = 0; i < times1.policy.size(); ++i) { + for (uint32_t j = 0; j < times1.policy[i].size(); ++j) { + ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC); + } + } + } +} + +void TestCheckDelta(uint64_t before, uint64_t after) { + // Times should never decrease + ASSERT_LE(before, after); + // UID can't have run for more than ~1s on each CPU + ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf()); +} + +TEST(TimeInStateTest, AllUidTimeInStateMonotonic) { + auto map1 = getUidsCpuFreqTimes(); + ASSERT_TRUE(map1.has_value()); + sleep(1); + auto map2 = getUidsCpuFreqTimes(); + ASSERT_TRUE(map2.has_value()); + + for (const auto &kv : *map1) { + uint32_t uid = kv.first; + auto times = kv.second; + ASSERT_NE(map2->find(uid), map2->end()); + for (uint32_t policy = 0; policy < times.size(); ++policy) { + for (uint32_t freqIdx = 0; freqIdx < times[policy].size(); ++freqIdx) { + auto before = times[policy][freqIdx]; + auto after = (*map2)[uid][policy][freqIdx]; + ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after)); + } + } + } +} + +TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) { + auto map1 = getUidsConcurrentTimes(); + ASSERT_TRUE(map1.has_value()); + ASSERT_FALSE(map1->empty()); + sleep(1); + auto map2 = getUidsConcurrentTimes(); + ASSERT_TRUE(map2.has_value()); + ASSERT_FALSE(map2->empty()); + + for (const auto &kv : *map1) { + uint32_t uid = kv.first; + auto times = kv.second; + ASSERT_NE(map2->find(uid), map2->end()); + for (uint32_t i = 0; i < times.active.size(); ++i) { + auto before = times.active[i]; + auto after = (*map2)[uid].active[i]; + ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after)); + } + for (uint32_t policy = 0; policy < times.policy.size(); ++policy) { + for (uint32_t idx = 0; idx < times.policy[policy].size(); ++idx) { + auto before = times.policy[policy][idx]; + auto after = (*map2)[uid].policy[policy][idx]; + ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after)); + } + } + } +} + +TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) { + auto map = getUidsCpuFreqTimes(); + ASSERT_TRUE(map.has_value()); + + bool foundLargeValue = false; + for (const auto &kv : *map) { + for (const auto &timeVec : kv.second) { + for (const auto &time : timeVec) { + ASSERT_LE(time, NSEC_PER_YEAR); + if (time > UINT32_MAX) foundLargeValue = true; + } + } + } + // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using + // uint64_t as expected, we should have some times higher than that. + ASSERT_TRUE(foundLargeValue); +} + +TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) { + auto concurrentMap = getUidsConcurrentTimes(); + ASSERT_TRUE(concurrentMap); + + bool activeFoundLargeValue = false; + bool policyFoundLargeValue = false; + for (const auto &kv : *concurrentMap) { + for (const auto &time : kv.second.active) { + ASSERT_LE(time, NSEC_PER_YEAR); + if (time > UINT32_MAX) activeFoundLargeValue = true; + } + for (const auto &policyTimeVec : kv.second.policy) { + for (const auto &time : policyTimeVec) { + ASSERT_LE(time, NSEC_PER_YEAR); + if (time > UINT32_MAX) policyFoundLargeValue = true; + } + } + } + // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using + // uint64_t as expected, we should have some times higher than that. + ASSERT_TRUE(activeFoundLargeValue); + ASSERT_TRUE(policyFoundLargeValue); +} + +TEST(TimeInStateTest, AllUidTimesConsistent) { + auto tisMap = getUidsCpuFreqTimes(); + ASSERT_TRUE(tisMap.has_value()); + + auto concurrentMap = getUidsConcurrentTimes(); + ASSERT_TRUE(concurrentMap.has_value()); + + ASSERT_EQ(tisMap->size(), concurrentMap->size()); + for (const auto &kv : *tisMap) { + uint32_t uid = kv.first; + auto times = kv.second; + ASSERT_NE(concurrentMap->find(uid), concurrentMap->end()); + + auto concurrentTimes = (*concurrentMap)[uid]; + ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(times, concurrentTimes)); + } +} + TEST(TimeInStateTest, RemoveUid) { - vector<vector<uint64_t>> times, times2; - ASSERT_TRUE(getUidCpuFreqTimes(0, ×)); - ASSERT_FALSE(times.empty()); + uint32_t uid = 0; + { + // Find an unused UID + auto times = getUidsCpuFreqTimes(); + ASSERT_TRUE(times.has_value()); + ASSERT_FALSE(times->empty()); + for (const auto &kv : *times) uid = std::max(uid, kv.first); + ++uid; + } + { + // Add a map entry for our fake UID by copying a real map entry + android::base::unique_fd fd{ + bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; + ASSERT_GE(fd, 0); + time_key_t k; + ASSERT_FALSE(getFirstMapKey(fd, &k)); + std::vector<tis_val_t> vals(get_nprocs_conf()); + ASSERT_FALSE(findMapEntry(fd, &k, vals.data())); + uint32_t copiedUid = k.uid; + k.uid = uid; + ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST)); + + android::base::unique_fd fd2{ + bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")}; + k.uid = copiedUid; + k.bucket = 0; + std::vector<concurrent_val_t> cvals(get_nprocs_conf()); + ASSERT_FALSE(findMapEntry(fd2, &k, cvals.data())); + k.uid = uid; + ASSERT_FALSE(writeToMapEntry(fd2, &k, cvals.data(), BPF_NOEXIST)); + } + auto times = getUidCpuFreqTimes(uid); + ASSERT_TRUE(times.has_value()); + ASSERT_FALSE(times->empty()); + + auto concurrentTimes = getUidConcurrentTimes(0); + ASSERT_TRUE(concurrentTimes.has_value()); + ASSERT_FALSE(concurrentTimes->active.empty()); + ASSERT_FALSE(concurrentTimes->policy.empty()); uint64_t sum = 0; - for (size_t i = 0; i < times.size(); ++i) { - for (auto x : times[i]) sum += x; + for (size_t i = 0; i < times->size(); ++i) { + for (auto x : (*times)[i]) sum += x; } ASSERT_GT(sum, (uint64_t)0); - ASSERT_TRUE(clearUidCpuFreqTimes(0)); - - ASSERT_TRUE(getUidCpuFreqTimes(0, ×2)); - ASSERT_EQ(times2.size(), times.size()); - for (size_t i = 0; i < times.size(); ++i) { - ASSERT_EQ(times2[i].size(), times[i].size()); - for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]); + uint64_t activeSum = 0; + for (size_t i = 0; i < concurrentTimes->active.size(); ++i) { + activeSum += concurrentTimes->active[i]; } + ASSERT_GT(activeSum, (uint64_t)0); + + ASSERT_TRUE(clearUidTimes(uid)); + + auto allTimes = getUidsCpuFreqTimes(); + ASSERT_TRUE(allTimes.has_value()); + ASSERT_FALSE(allTimes->empty()); + ASSERT_EQ(allTimes->find(uid), allTimes->end()); + + auto allConcurrentTimes = getUidsConcurrentTimes(); + ASSERT_TRUE(allConcurrentTimes.has_value()); + ASSERT_FALSE(allConcurrentTimes->empty()); + ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end()); } } // namespace bpf diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index 56521bf2b4..642c5f2cc0 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -32,5 +32,9 @@ cc_library_shared { "libutils", ], + header_libs: [ + "libnativeloader-headers", + ], + export_include_dirs: ["include"], } diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp index 4a801bec38..85137f5ca9 100644 --- a/libs/graphicsenv/GpuStatsInfo.cpp +++ b/libs/graphicsenv/GpuStatsInfo.cpp @@ -86,6 +86,7 @@ status_t GpuStatsAppInfo::writeToParcel(Parcel* parcel) const { if ((status = parcel->writeInt64Vector(vkDriverLoadingTime)) != OK) return status; if ((status = parcel->writeInt64Vector(angleDriverLoadingTime)) != OK) return status; if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status; + if ((status = parcel->writeBool(falsePrerotation)) != OK) return status; return OK; } @@ -97,6 +98,7 @@ status_t GpuStatsAppInfo::readFromParcel(const Parcel* parcel) { if ((status = parcel->readInt64Vector(&vkDriverLoadingTime)) != OK) return status; if ((status = parcel->readInt64Vector(&angleDriverLoadingTime)) != OK) return status; if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status; + if ((status = parcel->readBool(&falsePrerotation)) != OK) return status; return OK; } @@ -105,6 +107,7 @@ std::string GpuStatsAppInfo::toString() const { StringAppendF(&result, "appPackageName = %s\n", appPackageName.c_str()); StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode); StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse); + StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation); result.append("glDriverLoadingTime:"); for (int32_t loadingTime : glDriverLoadingTime) { StringAppendF(&result, " %d", loadingTime); diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index bb9e263ac4..28591110f0 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -32,6 +32,7 @@ #include <cutils/properties.h> #include <graphicsenv/IGpuService.h> #include <log/log.h> +#include <nativeloader/dlext_namespaces.h> #include <sys/prctl.h> #include <utils/Trace.h> @@ -39,22 +40,6 @@ #include <string> #include <thread> -// TODO(b/37049319) Get this from a header once one exists -extern "C" { -android_namespace_t* android_get_exported_namespace(const char*); -android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, - const char* default_library_path, uint64_t type, - const char* permitted_when_isolated_path, - android_namespace_t* parent); -bool android_link_namespaces(android_namespace_t* from, android_namespace_t* to, - const char* shared_libs_sonames); - -enum { - ANDROID_NAMESPACE_TYPE_ISOLATED = 1, - ANDROID_NAMESPACE_TYPE_SHARED = 2, -}; -} - // TODO(ianelliott@): Get the following from an ANGLE header: #define CURRENT_ANGLE_API_VERSION 2 // Current API verion we are targetting // Version-2 API: @@ -170,11 +155,11 @@ void GraphicsEnv::hintActivityLaunch() { std::lock_guard<std::mutex> lock(mStatsLock); if (mGpuStats.glDriverToSend) { mGpuStats.glDriverToSend = false; - sendGpuStatsLocked(GraphicsEnv::Api::API_GL, true, mGpuStats.glDriverLoadingTime); + sendGpuStatsLocked(GpuStatsInfo::Api::API_GL, true, mGpuStats.glDriverLoadingTime); } if (mGpuStats.vkDriverToSend) { mGpuStats.vkDriverToSend = false; - sendGpuStatsLocked(GraphicsEnv::Api::API_VK, true, mGpuStats.vkDriverLoadingTime); + sendGpuStatsLocked(GpuStatsInfo::Api::API_VK, true, mGpuStats.vkDriverLoadingTime); } }); trySendGpuStatsThread.detach(); @@ -205,34 +190,34 @@ void GraphicsEnv::setGpuStats(const std::string& driverPackageName, mGpuStats.vulkanVersion = vulkanVersion; } -void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) { +void GraphicsEnv::setDriverToLoad(GpuStatsInfo::Driver driver) { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mStatsLock); switch (driver) { - case GraphicsEnv::Driver::GL: - case GraphicsEnv::Driver::GL_UPDATED: - case GraphicsEnv::Driver::ANGLE: { - if (mGpuStats.glDriverToLoad == GraphicsEnv::Driver::NONE || - mGpuStats.glDriverToLoad == GraphicsEnv::Driver::GL) { + case GpuStatsInfo::Driver::GL: + case GpuStatsInfo::Driver::GL_UPDATED: + case GpuStatsInfo::Driver::ANGLE: { + if (mGpuStats.glDriverToLoad == GpuStatsInfo::Driver::NONE || + mGpuStats.glDriverToLoad == GpuStatsInfo::Driver::GL) { mGpuStats.glDriverToLoad = driver; break; } - if (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE) { + if (mGpuStats.glDriverFallback == GpuStatsInfo::Driver::NONE) { mGpuStats.glDriverFallback = driver; } break; } - case Driver::VULKAN: - case Driver::VULKAN_UPDATED: { - if (mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::NONE || - mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::VULKAN) { + case GpuStatsInfo::Driver::VULKAN: + case GpuStatsInfo::Driver::VULKAN_UPDATED: { + if (mGpuStats.vkDriverToLoad == GpuStatsInfo::Driver::NONE || + mGpuStats.vkDriverToLoad == GpuStatsInfo::Driver::VULKAN) { mGpuStats.vkDriverToLoad = driver; break; } - if (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE) { + if (mGpuStats.vkDriverFallback == GpuStatsInfo::Driver::NONE) { mGpuStats.vkDriverFallback = driver; } break; @@ -242,13 +227,13 @@ void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) { } } -void GraphicsEnv::setDriverLoaded(GraphicsEnv::Api api, bool isDriverLoaded, +void GraphicsEnv::setDriverLoaded(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime) { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mStatsLock); const bool doNotSend = mGpuStats.appPackageName.empty(); - if (api == GraphicsEnv::Api::API_GL) { + if (api == GpuStatsInfo::Api::API_GL) { if (doNotSend) mGpuStats.glDriverToSend = true; mGpuStats.glDriverLoadingTime = driverLoadingTime; } else { @@ -260,7 +245,7 @@ void GraphicsEnv::setDriverLoaded(GraphicsEnv::Api api, bool isDriverLoaded, } static sp<IGpuService> getGpuService() { - const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu")); + static const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu")); if (!binder) { ALOGE("Failed to get gpu service"); return nullptr; @@ -269,7 +254,7 @@ static sp<IGpuService> getGpuService() { return interface_cast<IGpuService>(binder); } -void GraphicsEnv::setTargetStats(const Stats stats, const uint64_t value) { +void GraphicsEnv::setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value) { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mStatsLock); @@ -280,7 +265,7 @@ void GraphicsEnv::setTargetStats(const Stats stats, const uint64_t value) { } } -void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, bool isDriverLoaded, +void GraphicsEnv::sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime) { ATRACE_CALL(); @@ -301,16 +286,16 @@ void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, bool isDriverLoaded, mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName.c_str(), mGpuStats.vulkanVersion, static_cast<int32_t>(api), isDriverLoaded, driverLoadingTime); - GraphicsEnv::Driver driver = GraphicsEnv::Driver::NONE; + GpuStatsInfo::Driver driver = GpuStatsInfo::Driver::NONE; bool isIntendedDriverLoaded = false; - if (api == GraphicsEnv::Api::API_GL) { + if (api == GpuStatsInfo::Api::API_GL) { driver = mGpuStats.glDriverToLoad; isIntendedDriverLoaded = - isDriverLoaded && (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE); + isDriverLoaded && (mGpuStats.glDriverFallback == GpuStatsInfo::Driver::NONE); } else { driver = mGpuStats.vkDriverToLoad; isIntendedDriverLoaded = - isDriverLoaded && (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE); + isDriverLoaded && (mGpuStats.vkDriverFallback == GpuStatsInfo::Driver::NONE); } const sp<IGpuService> gpuService = getGpuService(); @@ -322,6 +307,13 @@ void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Api api, bool isDriverLoaded, } } +bool GraphicsEnv::setInjectLayersPrSetDumpable() { + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) { + return false; + } + return true; +} + void* GraphicsEnv::loadLibrary(std::string name) { const android_dlextinfo dlextinfo = { .flags = ANDROID_DLEXT_USE_NAMESPACE, diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp index db16f3cfa6..9f5b0ff46f 100644 --- a/libs/graphicsenv/IGpuService.cpp +++ b/libs/graphicsenv/IGpuService.cpp @@ -30,7 +30,7 @@ public: virtual void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, - const int32_t vulkanVersion, GraphicsEnv::Driver driver, + const int32_t vulkanVersion, GpuStatsInfo::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) { Parcel data, reply; data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); @@ -93,7 +93,7 @@ public: } virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, - const GraphicsEnv::Stats stats, const uint64_t value) { + const GpuStatsInfo::Stats stats, const uint64_t value) { Parcel data, reply; data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); @@ -145,7 +145,7 @@ status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* rep if ((status = data.readInt64(&driverLoadingTime)) != OK) return status; setGpuStats(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime, - appPackageName, vulkanVersion, static_cast<GraphicsEnv::Driver>(driver), + appPackageName, vulkanVersion, static_cast<GpuStatsInfo::Driver>(driver), isDriverLoaded, driverLoadingTime); return OK; @@ -192,7 +192,7 @@ status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* rep if ((status = data.readUint64(&value)) != OK) return status; setTargetStats(appPackageName, driverVersionCode, - static_cast<GraphicsEnv::Stats>(stats), value); + static_cast<GpuStatsInfo::Stats>(stats), value); return OK; } diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h index edcccfea4a..7959652189 100644 --- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h +++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h @@ -70,6 +70,51 @@ public: std::vector<int64_t> vkDriverLoadingTime = {}; std::vector<int64_t> angleDriverLoadingTime = {}; bool cpuVulkanInUse = false; + bool falsePrerotation = false; +}; + +/* + * class for holding the gpu stats in GraphicsEnv before sending to GpuService. + */ +class GpuStatsInfo { +public: + enum Api { + API_GL = 0, + API_VK = 1, + }; + + enum Driver { + NONE = 0, + GL = 1, + GL_UPDATED = 2, + VULKAN = 3, + VULKAN_UPDATED = 4, + ANGLE = 5, + }; + + enum Stats { + CPU_VULKAN_IN_USE = 0, + FALSE_PREROTATION = 1, + }; + + GpuStatsInfo() = default; + GpuStatsInfo(const GpuStatsInfo&) = default; + virtual ~GpuStatsInfo() = default; + + std::string driverPackageName = ""; + std::string driverVersionName = ""; + uint64_t driverVersionCode = 0; + int64_t driverBuildTime = 0; + std::string appPackageName = ""; + int32_t vulkanVersion = 0; + Driver glDriverToLoad = Driver::NONE; + Driver glDriverFallback = Driver::NONE; + Driver vkDriverToLoad = Driver::NONE; + Driver vkDriverFallback = Driver::NONE; + bool glDriverToSend = false; + bool vkDriverToSend = false; + int64_t glDriverLoadingTime = 0; + int64_t vkDriverLoadingTime = 0; }; } // namespace android diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index 937bcd9ac6..83448d4ce7 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -17,6 +17,8 @@ #ifndef ANDROID_UI_GRAPHICS_ENV_H #define ANDROID_UI_GRAPHICS_ENV_H 1 +#include <graphicsenv/GpuStatsInfo.h> + #include <mutex> #include <string> #include <vector> @@ -29,63 +31,14 @@ struct NativeLoaderNamespace; class GraphicsEnv { public: - enum Api { - API_GL = 0, - API_VK = 1, - }; - - enum Driver { - NONE = 0, - GL = 1, - GL_UPDATED = 2, - VULKAN = 3, - VULKAN_UPDATED = 4, - ANGLE = 5, - }; - - enum Stats { - CPU_VULKAN_IN_USE = 0, - }; - -private: - struct GpuStats { - std::string driverPackageName; - std::string driverVersionName; - uint64_t driverVersionCode; - int64_t driverBuildTime; - std::string appPackageName; - int32_t vulkanVersion; - Driver glDriverToLoad; - Driver glDriverFallback; - Driver vkDriverToLoad; - Driver vkDriverFallback; - bool glDriverToSend; - bool vkDriverToSend; - int64_t glDriverLoadingTime; - int64_t vkDriverLoadingTime; - - GpuStats() - : driverPackageName(""), - driverVersionName(""), - driverVersionCode(0), - driverBuildTime(0), - appPackageName(""), - vulkanVersion(0), - glDriverToLoad(Driver::NONE), - glDriverFallback(Driver::NONE), - vkDriverToLoad(Driver::NONE), - vkDriverFallback(Driver::NONE), - glDriverToSend(false), - vkDriverToSend(false), - glDriverLoadingTime(0), - vkDriverLoadingTime(0) {} - }; - -public: static GraphicsEnv& getInstance(); + // Check if device is debuggable. int getCanLoadSystemLibraries(); + /* + * Apis for updatable driver + */ // Set a search path for loading graphics drivers. The path is a list of // directories separated by ':'. A directory can be contained in a zip file // (drivers must be stored uncompressed and page aligned); such elements @@ -95,17 +48,39 @@ public: // graphics drivers. The string is a list of libraries separated by ':', // which is required by android_link_namespaces. void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries); + // Get the updatable driver namespace. android_namespace_t* getDriverNamespace(); + + /* + * Apis for GpuStats + */ + // Hint there's real activity launching on the app process. void hintActivityLaunch(); + // Set the initial GpuStats. void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t versionCode, int64_t driverBuildTime, const std::string& appPackageName, const int32_t vulkanVersion); - void setTargetStats(const Stats stats, const uint64_t value = 0); - void setDriverToLoad(Driver driver); - void setDriverLoaded(Api api, bool isDriverLoaded, int64_t driverLoadingTime); - void sendGpuStatsLocked(Api api, bool isDriverLoaded, int64_t driverLoadingTime); - + // Set stats for target GpuStatsInfo::Stats type. + void setTargetStats(const GpuStatsInfo::Stats stats, const uint64_t value = 0); + // Set which driver is intended to load. + void setDriverToLoad(GpuStatsInfo::Driver driver); + // Set which driver is actually loaded. + void setDriverLoaded(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime); + + /* + * Api for Vk/GL layer injection. Presently, drivers enable certain + * profiling features when prctl(PR_GET_DUMPABLE) returns true. + * Calling this when layer injection metadata is present allows the driver + * to enable profiling even when in a non-debuggable app + */ + bool setInjectLayersPrSetDumpable(); + + /* + * Apis for ANGLE + */ + // Check if the requested app should use ANGLE. bool shouldUseAngle(std::string appName); + // Check if this app process should use ANGLE. bool shouldUseAngle(); // Set a search path for loading ANGLE libraries. The path is a list of // directories separated by ':'. A directory can be contained in a zip file @@ -114,43 +89,75 @@ public: // /system/app/ANGLEPrebuilt/ANGLEPrebuilt.apk!/lib/arm64-v8a void setAngleInfo(const std::string path, const std::string appName, std::string devOptIn, const int rulesFd, const long rulesOffset, const long rulesLength); + // Get the ANGLE driver namespace. android_namespace_t* getAngleNamespace(); + // Get the app name for ANGLE debug message. std::string& getAngleAppName(); + /* + * Apis for debug layer + */ + // Set additional layer search paths. void setLayerPaths(NativeLoaderNamespace* appNamespace, const std::string layerPaths); + // Get the app namespace for loading layers. NativeLoaderNamespace* getAppNamespace(); - + // Get additional layer search paths. const std::string& getLayerPaths(); - + // Set the Vulkan debug layers. void setDebugLayers(const std::string layers); + // Set the GL debug layers. void setDebugLayersGLES(const std::string layers); + // Get the debug layers to load. const std::string& getDebugLayers(); + // Get the debug layers to load. const std::string& getDebugLayersGLES(); private: enum UseAngle { UNKNOWN, YES, NO }; + // Load requested ANGLE library. void* loadLibrary(std::string name); + // Check ANGLE support with the rules. bool checkAngleRules(void* so); + // Update whether ANGLE should be used. void updateUseAngle(); + // Link updatable driver namespace with llndk and vndk-sp libs. bool linkDriverNamespaceLocked(android_namespace_t* vndkNamespace); + // Send the initial complete GpuStats to GpuService. + void sendGpuStatsLocked(GpuStatsInfo::Api api, bool isDriverLoaded, int64_t driverLoadingTime); GraphicsEnv() = default; + // Path to updatable driver libs. std::string mDriverPath; + // Path to additional sphal libs linked to updatable driver namespace. std::string mSphalLibraries; + // This mutex protects mGpuStats and get gpuservice call. std::mutex mStatsLock; - GpuStats mGpuStats; + // Information bookkept for GpuStats. + GpuStatsInfo mGpuStats; + // Path to ANGLE libs. std::string mAnglePath; + // This App's name. std::string mAngleAppName; + // ANGLE developer opt in status. std::string mAngleDeveloperOptIn; + // ANGLE rules. std::vector<char> mRulesBuffer; + // Use ANGLE flag. UseAngle mUseAngle = UNKNOWN; + // Vulkan debug layers libs. std::string mDebugLayers; + // GL debug layers libs. std::string mDebugLayersGLES; + // Additional debug layers search path. std::string mLayerPaths; + // This mutex protects the namespace creation. std::mutex mNamespaceMutex; + // Updatable driver namespace. android_namespace_t* mDriverNamespace = nullptr; + // ANGLE namespace. android_namespace_t* mAngleNamespace = nullptr; + // This App's namespace. NativeLoaderNamespace* mAppNamespace = nullptr; }; diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h index b8d0bd173c..f523d585be 100644 --- a/libs/graphicsenv/include/graphicsenv/IGpuService.h +++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h @@ -37,12 +37,12 @@ public: virtual void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, - const int32_t vulkanVersion, GraphicsEnv::Driver driver, + const int32_t vulkanVersion, GpuStatsInfo::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) = 0; // set target stats. virtual void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, - const GraphicsEnv::Stats stats, const uint64_t value = 0) = 0; + const GpuStatsInfo::Stats stats, const uint64_t value = 0) = 0; // get GPU global stats from GpuStats module. virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const = 0; diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index a615fbf812..e6d442d225 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -15,6 +15,10 @@ cc_library_headers { name: "libgui_headers", vendor_available: true, export_include_dirs: ["include"], + + // we must build this module to get the required header as that is generated + export_shared_lib_headers: [ "android.hidl.token@1.0-utils" ], + shared_libs: [ "android.hidl.token@1.0-utils" ], } cc_library_shared { @@ -28,7 +32,10 @@ cc_library_shared { defaults: ["libgui_bufferqueue-defaults"], srcs: [ + ":libgui_bufferqueue_sources", + "BitTube.cpp", + "BLASTBufferQueue.cpp", "BufferHubConsumer.cpp", "BufferHubProducer.cpp", "BufferItemConsumer.cpp", @@ -100,32 +107,14 @@ cc_library_static { ], defaults: ["libgui_bufferqueue-defaults"], -} - -// Common build config shared by libgui and libgui_bufferqueue_static. -cc_defaults { - name: "libgui_bufferqueue-defaults", - - clang: true, - cflags: [ - "-Wall", - "-Werror", - ], - cppflags: [ - "-Wextra", - "-DDEBUG_ONLY_CODE=0", + srcs: [ + ":libgui_bufferqueue_sources", ], +} - product_variables: { - eng: { - cppflags: [ - "-UDEBUG_ONLY_CODE", - "-DDEBUG_ONLY_CODE=1", - ], - }, - }, - +filegroup { + name: "libgui_bufferqueue_sources", srcs: [ "BufferItem.cpp", "BufferQueue.cpp", @@ -153,6 +142,31 @@ cc_defaults { "bufferqueue/2.0/H2BProducerListener.cpp", "bufferqueue/2.0/types.cpp", ], +} + +// Common build config shared by libgui and libgui_bufferqueue_static. +cc_defaults { + name: "libgui_bufferqueue-defaults", + + clang: true, + cflags: [ + "-Wall", + "-Werror", + ], + + cppflags: [ + "-Wextra", + "-DDEBUG_ONLY_CODE=0", + ], + + product_variables: { + eng: { + cppflags: [ + "-UDEBUG_ONLY_CODE", + "-DDEBUG_ONLY_CODE=1", + ], + }, + }, shared_libs: [ "android.hardware.graphics.bufferqueue@1.0", @@ -200,4 +214,21 @@ cc_defaults { ], } +// GMocks for use by external code +cc_library_static { + name: "libgui_mocks", + vendor_available: false, + + defaults: ["libgui_bufferqueue-defaults"], + static_libs: [ + "libgtest", + "libgmock", + ], + + srcs: [ + "mock/GraphicBufferConsumer.cpp", + "mock/GraphicBufferProducer.cpp", + ], +} + subdirs = ["tests"] diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp new file mode 100644 index 0000000000..9a5017577d --- /dev/null +++ b/libs/gui/BLASTBufferQueue.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2019 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 <gui/BLASTBufferQueue.h> +#include <gui/BufferItemConsumer.h> + +#include <chrono> + +using namespace std::chrono_literals; + +namespace android { + +BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height) + : mSurfaceControl(surface), mWidth(width), mHeight(height) { + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + mBufferItemConsumer = + new BufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true); + mBufferItemConsumer->setName(String8("BLAST Consumer")); + mBufferItemConsumer->setFrameAvailableListener(this); + mBufferItemConsumer->setBufferFreedListener(this); + mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight); + mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888); +} + +void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) { + std::unique_lock _lock{mMutex}; + mSurfaceControl = surface; + mWidth = width; + mHeight = height; + mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight); +} + +static void transactionCallbackThunk(void* context, nsecs_t latchTime, + const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats) { + if (context == nullptr) { + return; + } + BLASTBufferQueue* bq = static_cast<BLASTBufferQueue*>(context); + bq->transactionCallback(latchTime, presentFence, stats); +} + +void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/, + const std::vector<SurfaceControlStats>& stats) { + std::unique_lock _lock{mMutex}; + + if (stats.size() > 0 && mNextCallbackBufferItem.mGraphicBuffer != nullptr) { + mBufferItemConsumer->releaseBuffer(mNextCallbackBufferItem, + stats[0].previousReleaseFence + ? stats[0].previousReleaseFence + : Fence::NO_FENCE); + mNextCallbackBufferItem = BufferItem(); + } + mDequeueWaitCV.notify_all(); + decStrong((void*)transactionCallbackThunk); +} + +void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { + std::unique_lock _lock{mMutex}; + + SurfaceComposerClient::Transaction localTransaction; + bool applyTransaction = true; + SurfaceComposerClient::Transaction* t = &localTransaction; + if (mNextTransaction != nullptr) { + t = mNextTransaction; + mNextTransaction = nullptr; + applyTransaction = false; + } + + int status = OK; + mNextCallbackBufferItem = mLastSubmittedBufferItem; + + mLastSubmittedBufferItem = BufferItem(); + status = mBufferItemConsumer->acquireBuffer(&mLastSubmittedBufferItem, -1, false); + if (status != OK) { + ALOGE("Failed to acquire?"); + } + + auto buffer = mLastSubmittedBufferItem.mGraphicBuffer; + + if (buffer == nullptr) { + ALOGE("Null buffer"); + return; + } + + + // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback. + incStrong((void*)transactionCallbackThunk); + + t->setBuffer(mSurfaceControl, buffer); + t->setAcquireFence(mSurfaceControl, + item.mFence ? new Fence(item.mFence->dup()) : Fence::NO_FENCE); + t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this)); + + t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()}); + t->setCrop(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()}); + + if (applyTransaction) { + ALOGE("Apply transaction"); + t->apply(); + + if (mNextCallbackBufferItem.mGraphicBuffer != nullptr) { + mDequeueWaitCV.wait_for(_lock, 5000ms); + } + } +} + +void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) { + std::unique_lock _lock{mMutex}; + mNextTransaction = t; +} + +} // namespace android diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 528bfb194e..3a7cb44450 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -166,7 +166,9 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, mCore->mFreeBuffers.push_back(front->mSlot); } - listener = mCore->mConnectedProducerListener; + if (mCore->mBufferReleasedCbEnabled) { + listener = mCore->mConnectedProducerListener; + } ++numDroppedBuffers; } @@ -457,7 +459,9 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, mCore->mFreeBuffers.push_back(slot); } - listener = mCore->mConnectedProducerListener; + if (mCore->mBufferReleasedCbEnabled) { + listener = mCore->mConnectedProducerListener; + } BQ_LOGV("releaseBuffer: releasing slot %d", slot); mCore->mDequeueCondition.notify_all(); @@ -668,7 +672,7 @@ status_t BufferQueueConsumer::setMaxAcquiredBufferCount( BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers); mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers; VALIDATE_CONSISTENCY(); - if (delta < 0) { + if (delta < 0 && mCore->mBufferReleasedCbEnabled) { listener = mCore->mConsumerListener; } } diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index e0e3431ca5..d6009d6cd5 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -65,6 +65,7 @@ BufferQueueCore::BufferQueueCore() : mConnectedApi(NO_CONNECTED_API), mLinkedToDeath(), mConnectedProducerListener(), + mBufferReleasedCbEnabled(false), mSlots(), mQueue(), mFreeSlots(), @@ -97,7 +98,9 @@ BufferQueueCore::BufferQueueCore() : mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE, HAL_DATASPACE_UNKNOWN), mLastQueuedSlot(INVALID_BUFFER_SLOT), - mUniqueId(getUniqueId()) + mUniqueId(getUniqueId()), + mAutoPrerotation(false), + mTransformHintInUse(0) { int numStartingBuffers = getMaxBufferCountLocked(); for (int s = 0; s < numStartingBuffers; s++) { @@ -123,10 +126,12 @@ void BufferQueueCore::dumpState(const String8& prefix, String8* outResult) const mQueueBufferCanDrop, mLegacyBufferDrop); outResult->appendFormat("%s default-size=[%dx%d] default-format=%d ", prefix.string(), mDefaultWidth, mDefaultHeight, mDefaultBufferFormat); - outResult->appendFormat("transform-hint=%02x frame-counter=%" PRIu64, mTransformHint, - mFrameCounter); + outResult->appendFormat("%s transform-hint=%02x frame-counter=%" PRIu64 "\n", prefix.string(), + mTransformHint, mFrameCounter); + outResult->appendFormat("%s mTransformHintInUse=%02x mAutoPrerotation=%d\n", prefix.string(), + mTransformHintInUse, mAutoPrerotation); - outResult->appendFormat("\n%sFIFO(%zu):\n", prefix.string(), mQueue.size()); + outResult->appendFormat("%sFIFO(%zu):\n", prefix.string(), mQueue.size()); Fifo::const_iterator current(mQueue.begin()); while (current != mQueue.end()) { double timestamp = current->mTimestamp / 1e9; @@ -260,6 +265,12 @@ void BufferQueueCore::freeAllBuffersLocked() { } void BufferQueueCore::discardFreeBuffersLocked() { + // Notify producer about the discarded buffers. + if (mConnectedProducerListener != nullptr && mFreeBuffers.size() > 0) { + std::vector<int32_t> freeBuffers(mFreeBuffers.begin(), mFreeBuffers.end()); + mConnectedProducerListener->onBuffersDiscarded(freeBuffers); + } + for (int s : mFreeBuffers) { mFreeSlots.insert(s); clearBufferSlotLocked(s); diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 92ab41019e..56746749b9 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -408,6 +408,10 @@ status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* ou if (useDefaultSize) { width = mCore->mDefaultWidth; height = mCore->mDefaultHeight; + if (mCore->mAutoPrerotation && + (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) { + std::swap(width, height); + } } int found = BufferItem::INVALID_BUFFER_SLOT; @@ -960,7 +964,7 @@ status_t BufferQueueProducer::queueBuffer(int slot, output->width = mCore->mDefaultWidth; output->height = mCore->mDefaultHeight; - output->transformHint = mCore->mTransformHint; + output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint; output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size()); output->nextFrameNumber = mCore->mFrameCounter + 1; @@ -1141,9 +1145,6 @@ int BufferQueueProducer::query(int what, int *outValue) { case NATIVE_WINDOW_CONSUMER_IS_PROTECTED: value = static_cast<int32_t>(mCore->mConsumerIsProtected); break; - case NATIVE_WINDOW_MAX_BUFFER_COUNT: - value = static_cast<int32_t>(mCore->mMaxBufferCount); - break; default: return BAD_VALUE; } @@ -1203,11 +1204,12 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, output->width = mCore->mDefaultWidth; output->height = mCore->mDefaultHeight; - output->transformHint = mCore->mTransformHint; + output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint; output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size()); output->nextFrameNumber = mCore->mFrameCounter + 1; output->bufferReplaced = false; + output->maxBufferCount = mCore->mMaxBufferCount; if (listener != nullptr) { // Set up a death notification so that we can disconnect @@ -1221,9 +1223,8 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, } mCore->mLinkedToDeath = listener; } - if (listener->needsReleaseNotify()) { - mCore->mConnectedProducerListener = listener; - } + mCore->mConnectedProducerListener = listener; + mCore->mBufferReleasedCbEnabled = listener->needsReleaseNotify(); } break; default: @@ -1307,6 +1308,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { mCore->mConnectedPid = -1; mCore->mSidebandStream.clear(); mCore->mDequeueCondition.notify_all(); + mCore->mAutoPrerotation = false; listener = mCore->mConsumerListener; } else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { BQ_LOGE("disconnect: not connected (req=%d)", api); @@ -1350,6 +1352,8 @@ status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, PixelFormat format, uint64_t usage) { ATRACE_CALL(); + + const bool useDefaultSize = !width && !height; while (true) { size_t newBufferCount = 0; uint32_t allocWidth = 0; @@ -1376,6 +1380,11 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, allocWidth = width > 0 ? width : mCore->mDefaultWidth; allocHeight = height > 0 ? height : mCore->mDefaultHeight; + if (useDefaultSize && mCore->mAutoPrerotation && + (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) { + std::swap(allocWidth, allocHeight); + } + allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat; allocUsage = usage | mCore->mConsumerUsageBits; allocName.assign(mCore->mConsumerName.string(), mCore->mConsumerName.size()); @@ -1406,6 +1415,11 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, std::unique_lock<std::mutex> lock(mCore->mMutex); uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth; uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight; + if (useDefaultSize && mCore->mAutoPrerotation && + (mCore->mTransformHintInUse & NATIVE_WINDOW_TRANSFORM_ROT_90)) { + std::swap(checkWidth, checkHeight); + } + PixelFormat checkFormat = format != 0 ? format : mCore->mDefaultBufferFormat; uint64_t checkUsage = usage | mCore->mConsumerUsageBits; @@ -1608,4 +1622,14 @@ status_t BufferQueueProducer::getConsumerUsage(uint64_t* outUsage) const { return NO_ERROR; } +status_t BufferQueueProducer::setAutoPrerotation(bool autoPrerotation) { + ATRACE_CALL(); + BQ_LOGV("setAutoPrerotation: %d", autoPrerotation); + + std::lock_guard<std::mutex> lock(mCore->mMutex); + + mCore->mAutoPrerotation = autoPrerotation; + return NO_ERROR; +} + } // namespace android diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 8199c98582..59f1bcd24e 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -46,7 +46,6 @@ #include <utils/String8.h> #include <utils/Trace.h> -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); #define PROT_CONTENT_EXT_STR "EGL_EXT_protected_content" #define EGL_PROTECTED_CONTENT_EXT 0x32C0 diff --git a/libs/gui/HdrMetadata.cpp b/libs/gui/HdrMetadata.cpp index add3ef0458..058cd9aa3b 100644 --- a/libs/gui/HdrMetadata.cpp +++ b/libs/gui/HdrMetadata.cpp @@ -28,8 +28,8 @@ size_t HdrMetadata::getFlattenedSize() const { size += sizeof(cta8613); } if (validTypes & HDR10PLUS) { - size += sizeof(size_t); - size += hdr10plus.size(); + size += sizeof(uint32_t); + size += hdr10plus.size() * sizeof(hdr10plus[0]); } return size; } @@ -47,10 +47,11 @@ status_t HdrMetadata::flatten(void* buffer, size_t size) const { FlattenableUtils::write(buffer, size, cta8613); } if (validTypes & HDR10PLUS) { - size_t metadataSize = hdr10plus.size(); + uint32_t metadataSize = hdr10plus.size(); FlattenableUtils::write(buffer, size, metadataSize); - memcpy(buffer, hdr10plus.data(), metadataSize); - FlattenableUtils::advance(buffer, size, metadataSize); + size_t metadataSizeinByte = metadataSize * sizeof(hdr10plus[0]); + memcpy(buffer, hdr10plus.data(), metadataSizeinByte); + FlattenableUtils::advance(buffer, size, metadataSizeinByte); } return NO_ERROR; @@ -74,20 +75,21 @@ status_t HdrMetadata::unflatten(void const* buffer, size_t size) { FlattenableUtils::read(buffer, size, cta8613); } if (validTypes & HDR10PLUS) { - if (size < sizeof(size_t)) { + if (size < sizeof(uint32_t)) { return NO_MEMORY; } - size_t metadataSize; + uint32_t metadataSize; FlattenableUtils::read(buffer, size, metadataSize); - if (size < metadataSize) { + size_t metadataSizeinByte = metadataSize * sizeof(hdr10plus[0]); + if (size < metadataSizeinByte) { return NO_MEMORY; } hdr10plus.resize(metadataSize); - memcpy(hdr10plus.data(), buffer, metadataSize); - FlattenableUtils::advance(buffer, size, metadataSize); + memcpy(hdr10plus.data(), buffer, metadataSizeinByte); + FlattenableUtils::advance(buffer, size, metadataSizeinByte); } return NO_ERROR; diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 0e03b7d393..0009a57653 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -73,6 +73,7 @@ enum { GET_UNIQUE_ID, GET_CONSUMER_USAGE, SET_LEGACY_BUFFER_DROP, + SET_AUTO_PREROTATION, }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -547,6 +548,17 @@ public: } return actualResult; } + + virtual status_t setAutoPrerotation(bool autoPrerotation) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeBool(autoPrerotation); + status_t result = remote()->transact(SET_AUTO_PREROTATION, data, &reply); + if (result == NO_ERROR) { + result = reply.readInt32(); + } + return result; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -675,6 +687,10 @@ public: status_t getConsumerUsage(uint64_t* outUsage) const override { return mBase->getConsumerUsage(outUsage); } + + status_t setAutoPrerotation(bool autoPrerotation) override { + return mBase->setAutoPrerotation(autoPrerotation); + } }; IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, @@ -688,6 +704,12 @@ status_t IGraphicBufferProducer::setLegacyBufferDrop(bool drop) { return INVALID_OPERATION; } +status_t IGraphicBufferProducer::setAutoPrerotation(bool autoPrerotation) { + // No-op for IGBP other than BufferQueue. + (void)autoPrerotation; + return INVALID_OPERATION; +} + status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) { status_t res = OK; res = parcel->writeUint32(USE_BUFFER_QUEUE); @@ -1050,6 +1072,13 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case SET_AUTO_PREROTATION: { + CHECK_INTERFACE(IGraphicBuffer, data, reply); + bool autoPrerotation = data.readBool(); + status_t result = setAutoPrerotation(autoPrerotation); + reply->writeInt32(result); + return NO_ERROR; + } } return BBinder::onTransact(code, data, reply, flags); } @@ -1141,12 +1170,8 @@ status_t IGraphicBufferProducer::QueueBufferInput::unflatten( // ---------------------------------------------------------------------------- constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() { - return sizeof(width) + - sizeof(height) + - sizeof(transformHint) + - sizeof(numPendingBuffers) + - sizeof(nextFrameNumber) + - sizeof(bufferReplaced); + return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) + + sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount); } size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const { @@ -1170,6 +1195,7 @@ status_t IGraphicBufferProducer::QueueBufferOutput::flatten( FlattenableUtils::write(buffer, size, numPendingBuffers); FlattenableUtils::write(buffer, size, nextFrameNumber); FlattenableUtils::write(buffer, size, bufferReplaced); + FlattenableUtils::write(buffer, size, maxBufferCount); return frameTimestamps.flatten(buffer, size, fds, count); } @@ -1187,6 +1213,7 @@ status_t IGraphicBufferProducer::QueueBufferOutput::unflatten( FlattenableUtils::read(buffer, size, numPendingBuffers); FlattenableUtils::read(buffer, size, nextFrameNumber); FlattenableUtils::read(buffer, size, bufferReplaced); + FlattenableUtils::read(buffer, size, maxBufferCount); return frameTimestamps.unflatten(buffer, size, fds, count); } diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp index 936063a5bd..808e3369f1 100644 --- a/libs/gui/IProducerListener.cpp +++ b/libs/gui/IProducerListener.cpp @@ -24,6 +24,7 @@ namespace android { enum { ON_BUFFER_RELEASED = IBinder::FIRST_CALL_TRANSACTION, NEEDS_RELEASE_NOTIFY, + ON_BUFFERS_DISCARDED, }; class BpProducerListener : public BpInterface<IProducerListener> @@ -56,6 +57,13 @@ public: } return result; } + + virtual void onBuffersDiscarded(const std::vector<int>& discardedSlots) { + Parcel data, reply; + data.writeInterfaceToken(IProducerListener::getInterfaceDescriptor()); + data.writeInt32Vector(discardedSlots); + remote()->transact(ON_BUFFERS_DISCARDED, data, &reply, IBinder::FLAG_ONEWAY); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -76,6 +84,10 @@ public: virtual bool needsReleaseNotify() override { return mBase->needsReleaseNotify(); } + + virtual void onBuffersDiscarded(const std::vector<int32_t>& discardedSlots) override { + return mBase->onBuffersDiscarded(discardedSlots); + } }; IMPLEMENT_HYBRID_META_INTERFACE(ProducerListener, @@ -92,6 +104,17 @@ status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data, CHECK_INTERFACE(IProducerListener, data, reply); reply->writeBool(needsReleaseNotify()); return NO_ERROR; + case ON_BUFFERS_DISCARDED: { + CHECK_INTERFACE(IProducerListener, data, reply); + std::vector<int32_t> discardedSlots; + status_t result = data.readInt32Vector(&discardedSlots); + if (result != NO_ERROR) { + ALOGE("ON_BUFFERS_DISCARDED failed to read discardedSlots: %d", result); + return result; + } + onBuffersDiscarded(discardedSlots); + return NO_ERROR; + } } return BBinder::onTransact(code, data, reply, flags); } @@ -104,4 +127,7 @@ bool BnProducerListener::needsReleaseNotify() { return true; } +void BnProducerListener::onBuffersDiscarded(const std::vector<int32_t>& /*discardedSlots*/) { +} + } // namespace android diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 12deaf0bd6..580579759f 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -69,7 +69,7 @@ public: const sp<IBinder>& applyToken, const InputWindowCommands& commands, int64_t desiredPresentTime, - const client_cache_t& uncacheBuffer, + const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); @@ -90,10 +90,11 @@ public: data.writeInt64(desiredPresentTime); data.writeStrongBinder(uncacheBuffer.token.promote()); data.writeUint64(uncacheBuffer.id); + data.writeBool(hasListenerCallbacks); if (data.writeVectorSize(listenerCallbacks) == NO_ERROR) { for (const auto& [listener, callbackIds] : listenerCallbacks) { - data.writeStrongBinder(IInterface::asBinder(listener)); + data.writeStrongBinder(listener); data.writeInt64Vector(callbackIds); } } @@ -1039,18 +1040,19 @@ status_t BnSurfaceComposer::onTransact( uncachedBuffer.token = data.readStrongBinder(); uncachedBuffer.id = data.readUint64(); + bool hasListenerCallbacks = data.readBool(); + std::vector<ListenerCallbacks> listenerCallbacks; int32_t listenersSize = data.readInt32(); for (int32_t i = 0; i < listenersSize; i++) { - auto listener = - interface_cast<ITransactionCompletedListener>(data.readStrongBinder()); + auto listener = data.readStrongBinder(); std::vector<CallbackId> callbackIds; data.readInt64Vector(&callbackIds); listenerCallbacks.emplace_back(listener, callbackIds); } - setTransactionState(state, displays, stateFlags, applyToken, inputWindowCommands, - desiredPresentTime, uncachedBuffer, listenerCallbacks); + desiredPresentTime, uncachedBuffer, hasListenerCallbacks, + listenerCallbacks); return NO_ERROR; } case BOOT_FINISHED: { diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp index 129558bd15..b98e48b52a 100644 --- a/libs/gui/ISurfaceComposerClient.cpp +++ b/libs/gui/ISurfaceComposerClient.cpp @@ -34,7 +34,8 @@ enum class Tag : uint32_t { CREATE_WITH_SURFACE_PARENT, CLEAR_LAYER_FRAME_STATS, GET_LAYER_FRAME_STATS, - LAST = GET_LAYER_FRAME_STATS, + MIRROR_SURFACE, + LAST = MIRROR_SURFACE, }; } // Anonymous namespace @@ -80,6 +81,12 @@ public: &ISurfaceComposerClient::getLayerFrameStats)>(Tag::GET_LAYER_FRAME_STATS, handle, outStats); } + + status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) override { + return callRemote<decltype(&ISurfaceComposerClient::mirrorSurface)>(Tag::MIRROR_SURFACE, + mirrorFromHandle, + outHandle); + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -105,6 +112,8 @@ status_t BnSurfaceComposerClient::onTransact(uint32_t code, const Parcel& data, return callLocal(data, reply, &ISurfaceComposerClient::clearLayerFrameStats); case Tag::GET_LAYER_FRAME_STATS: return callLocal(data, reply, &ISurfaceComposerClient::getLayerFrameStats); + case Tag::MIRROR_SURFACE: + return callLocal(data, reply, &ISurfaceComposerClient::mirrorSurface); } } diff --git a/libs/gui/ITransactionCompletedListener.cpp b/libs/gui/ITransactionCompletedListener.cpp index 74cd4f1ede..acda6001cc 100644 --- a/libs/gui/ITransactionCompletedListener.cpp +++ b/libs/gui/ITransactionCompletedListener.cpp @@ -151,7 +151,7 @@ status_t ListenerStats::readFromParcel(const Parcel* input) { return NO_ERROR; } -ListenerStats ListenerStats::createEmpty(const sp<ITransactionCompletedListener>& listener, +ListenerStats ListenerStats::createEmpty(const sp<IBinder>& listener, const std::unordered_set<CallbackId>& callbackIds) { ListenerStats listenerStats; listenerStats.listener = listener; diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 42eb9213d6..e004e9584c 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -86,7 +86,6 @@ status_t layer_state_t::write(Parcel& output) const memcpy(output.writeInplace(16 * sizeof(float)), colorTransform.asArray(), 16 * sizeof(float)); output.writeFloat(cornerRadius); - output.writeBool(hasListenerCallbacks); output.writeStrongBinder(cachedBuffer.token.promote()); output.writeUint64(cachedBuffer.id); output.writeParcelable(metadata); @@ -95,6 +94,22 @@ status_t layer_state_t::write(Parcel& output) const output.writeUint32(static_cast<uint32_t>(bgColorDataspace)); output.writeBool(colorSpaceAgnostic); + auto err = output.writeVectorSize(listeners); + if (err) { + return err; + } + + for (auto listener : listeners) { + err = output.writeStrongBinder(listener.transactionCompletedListener); + if (err) { + return err; + } + err = output.writeInt64Vector(listener.callbackIds); + if (err) { + return err; + } + } + return NO_ERROR; } @@ -156,7 +171,6 @@ status_t layer_state_t::read(const Parcel& input) colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float)))); cornerRadius = input.readFloat(); - hasListenerCallbacks = input.readBool(); cachedBuffer.token = input.readStrongBinder(); cachedBuffer.id = input.readUint64(); input.readParcelable(&metadata); @@ -165,16 +179,22 @@ status_t layer_state_t::read(const Parcel& input) bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32()); colorSpaceAgnostic = input.readBool(); + int32_t numListeners = input.readInt32(); + listeners.clear(); + for (int i = 0; i < numListeners; i++) { + auto listener = input.readStrongBinder(); + std::vector<CallbackId> callbackIds; + input.readInt64Vector(&callbackIds); + listeners.emplace_back(listener, callbackIds); + } return NO_ERROR; } status_t ComposerState::write(Parcel& output) const { - output.writeStrongBinder(IInterface::asBinder(client)); return state.write(output); } status_t ComposerState::read(const Parcel& input) { - client = interface_cast<ISurfaceComposerClient>(input.readStrongBinder()); return state.read(input); } @@ -267,8 +287,9 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eFlagsChanged) { what |= eFlagsChanged; - flags = other.flags; - mask = other.mask; + flags &= ~other.mask; + flags |= (other.flags & other.mask); + mask |= other.mask; } if (other.what & eLayerStackChanged) { what |= eLayerStackChanged; @@ -292,9 +313,6 @@ void layer_state_t::merge(const layer_state_t& other) { what |= eOverrideScalingModeChanged; overrideScalingMode = other.overrideScalingMode; } - if (other.what & eGeometryAppliesWithResize) { - what |= eGeometryAppliesWithResize; - } if (other.what & eReparentChildren) { what |= eReparentChildren; reparentHandle = other.reparentHandle; @@ -365,7 +383,6 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eHasListenerCallbacksChanged) { what |= eHasListenerCallbacksChanged; - hasListenerCallbacks = other.hasListenerCallbacks; } #ifndef NO_INPUT diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 9fe5de82d1..e490d6d17d 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -35,6 +35,7 @@ #include <ui/DisplayStatInfo.h> #include <ui/Fence.h> +#include <ui/GraphicBuffer.h> #include <ui/HdrCapabilities.h> #include <ui/Region.h> @@ -96,6 +97,7 @@ Surface::Surface(const sp<IGraphicBufferProducer>& bufferProducer, bool controll mConnectedToCpu = false; mProducerControlledByApp = controlledByApp; mSwapIntervalZero = false; + mMaxBufferCount = 0; } Surface::~Surface() { @@ -961,6 +963,10 @@ int Surface::query(int what, int* value) const { *value = static_cast<int>(mDataSpace); return NO_ERROR; } + case NATIVE_WINDOW_MAX_BUFFER_COUNT: { + *value = mMaxBufferCount; + return NO_ERROR; + } } } return mGraphicBufferProducer->query(what, value); @@ -1072,6 +1078,21 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_GET_CONSUMER_USAGE64: res = dispatchGetConsumerUsage64(args); break; + case NATIVE_WINDOW_SET_AUTO_PREROTATION: + res = dispatchSetAutoPrerotation(args); + break; + case NATIVE_WINDOW_GET_LAST_DEQUEUE_START: + res = dispatchGetLastDequeueStartTime(args); + break; + case NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT: + res = dispatchSetDequeueTimeout(args); + break; + case NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION: + res = dispatchGetLastDequeueDuration(args); + break; + case NATIVE_WINDOW_GET_LAST_QUEUE_DURATION: + res = dispatchGetLastQueueDuration(args); + break; default: res = NAME_NOT_FOUND; break; @@ -1272,6 +1293,34 @@ int Surface::dispatchGetConsumerUsage64(va_list args) { return getConsumerUsage(usage); } +int Surface::dispatchSetAutoPrerotation(va_list args) { + bool autoPrerotation = va_arg(args, int); + return setAutoPrerotation(autoPrerotation); +} + +int Surface::dispatchGetLastDequeueStartTime(va_list args) { + int64_t* lastDequeueStartTime = va_arg(args, int64_t*); + *lastDequeueStartTime = mLastDequeueStartTime; + return NO_ERROR; +} + +int Surface::dispatchSetDequeueTimeout(va_list args) { + nsecs_t timeout = va_arg(args, int64_t); + return setDequeueTimeout(timeout); +} + +int Surface::dispatchGetLastDequeueDuration(va_list args) { + int64_t* lastDequeueDuration = va_arg(args, int64_t*); + *lastDequeueDuration = mLastDequeueDuration; + return NO_ERROR; +} + +int Surface::dispatchGetLastQueueDuration(va_list args) { + int64_t* lastQueueDuration = va_arg(args, int64_t*); + *lastQueueDuration = mLastQueueDuration; + return NO_ERROR; +} + bool Surface::transformToDisplayInverse() { return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) == NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY; @@ -1287,6 +1336,14 @@ int Surface::connect(int api, const sp<IProducerListener>& listener) { } int Surface::connect( + int api, bool reportBufferRemoval, const sp<SurfaceListener>& sListener) { + if (sListener != nullptr) { + mListenerProxy = new ProducerListenerProxy(this, sListener); + } + return connect(api, mListenerProxy, reportBufferRemoval); +} + +int Surface::connect( int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) { ATRACE_CALL(); ALOGV("Surface::connect"); @@ -1298,6 +1355,7 @@ int Surface::connect( mDefaultWidth = output.width; mDefaultHeight = output.height; mNextFrameNumber = output.nextFrameNumber; + mMaxBufferCount = output.maxBufferCount; // Ignore transform hint if sticky transform is set or transform to display inverse flag is // set. Transform hint should be ignored if the client is expected to always submit buffers @@ -1339,6 +1397,8 @@ int Surface::disconnect(int api, IGraphicBufferProducer::DisconnectMode mode) { mScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; mTransform = 0; mStickyTransform = 0; + mAutoPrerotation = false; + mEnableFrameTimestamps = false; if (api == NATIVE_WINDOW_API_CPU) { mConnectedToCpu = false; @@ -1684,6 +1744,28 @@ void Surface::freeAllBuffers() { } } +status_t Surface::getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots, + std::vector<sp<GraphicBuffer>>* outBuffers) { + ALOGV("Surface::getAndFlushBuffersFromSlots"); + for (int32_t i : slots) { + if (i < 0 || i >= NUM_BUFFER_SLOTS) { + ALOGE("%s: Invalid slotIndex: %d", __FUNCTION__, i); + return BAD_VALUE; + } + } + + Mutex::Autolock lock(mMutex); + for (int32_t i : slots) { + if (mSlots[i].buffer == nullptr) { + ALOGW("%s: Discarded slot %d doesn't contain buffer!", __FUNCTION__, i); + continue; + } + outBuffers->push_back(mSlots[i].buffer); + mSlots[i].buffer = nullptr; + } + return OK; +} + void Surface::setSurfaceDamage(android_native_rect_t* rects, size_t numRects) { ATRACE_CALL(); ALOGV("Surface::setSurfaceDamage"); @@ -1903,11 +1985,6 @@ int Surface::getConsumerUsage(uint64_t* outUsage) const { return mGraphicBufferProducer->getConsumerUsage(outUsage); } -nsecs_t Surface::getLastDequeueStartTime() const { - Mutex::Autolock lock(mMutex); - return mLastDequeueStartTime; -} - status_t Surface::getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out) { if (out == nullptr) { ALOGE("%s: out must not be null!", __FUNCTION__); @@ -1951,4 +2028,40 @@ status_t Surface::attachAndQueueBufferWithDataspace(Surface* surface, sp<Graphic return err; } +int Surface::setAutoPrerotation(bool autoPrerotation) { + ATRACE_CALL(); + ALOGV("Surface::setAutoPrerotation (%d)", autoPrerotation); + Mutex::Autolock lock(mMutex); + + if (mAutoPrerotation == autoPrerotation) { + return OK; + } + + status_t err = mGraphicBufferProducer->setAutoPrerotation(autoPrerotation); + if (err == NO_ERROR) { + mAutoPrerotation = autoPrerotation; + } + ALOGE_IF(err, "IGraphicBufferProducer::setAutoPrerotation(%d) returned %s", autoPrerotation, + strerror(-err)); + return err; +} + +void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_t>& slots) { + ATRACE_CALL(); + sp<Surface> parent = mParent.promote(); + if (parent == nullptr) { + return; + } + + std::vector<sp<GraphicBuffer>> discardedBufs; + status_t res = parent->getAndFlushBuffersFromSlots(slots, &discardedBufs); + if (res != OK) { + ALOGE("%s: Failed to get buffers from slots: %s(%d)", __FUNCTION__, + strerror(-res), res); + return; + } + + mSurfaceListener->onBuffersDiscarded(discardedBufs); +} + }; // namespace android diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index def9fe937c..7b256f5c02 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -202,7 +202,8 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener * callbackIds to generate one super map that contains all the sp<IBinder> to sp<SurfaceControl> * that could possibly exist for the callbacks. */ - std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls; + std::unordered_map<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash> + surfaceControls; for (const auto& transactionStats : listenerStats.transactionStats) { for (auto callbackId : transactionStats.callbackIds) { auto& [callbackFunction, callbackSurfaceControls] = mCallbacks[callbackId]; @@ -322,21 +323,148 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) mTransactionNestCount(other.mTransactionNestCount), mAnimation(other.mAnimation), mEarlyWakeup(other.mEarlyWakeup), + mContainsBuffer(other.mContainsBuffer), mDesiredPresentTime(other.mDesiredPresentTime) { mDisplayStates = other.mDisplayStates; mComposerStates = other.mComposerStates; mInputWindowCommands = other.mInputWindowCommands; + mListenerCallbacks = other.mListenerCallbacks; +} + +std::unique_ptr<SurfaceComposerClient::Transaction> +SurfaceComposerClient::Transaction::createFromParcel(const Parcel* parcel) { + auto transaction = std::make_unique<Transaction>(); + if (transaction->readFromParcel(parcel) == NO_ERROR) { + return transaction; + } + return nullptr; +} + +status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) { + const uint32_t forceSynchronous = parcel->readUint32(); + const uint32_t transactionNestCount = parcel->readUint32(); + const bool animation = parcel->readBool(); + const bool earlyWakeup = parcel->readBool(); + const bool containsBuffer = parcel->readBool(); + const int64_t desiredPresentTime = parcel->readInt64(); + + size_t count = static_cast<size_t>(parcel->readUint32()); + if (count > parcel->dataSize()) { + return BAD_VALUE; + } + SortedVector<DisplayState> displayStates; + displayStates.setCapacity(count); + for (size_t i = 0; i < count; i++) { + DisplayState displayState; + if (displayState.read(*parcel) == BAD_VALUE) { + return BAD_VALUE; + } + displayStates.add(displayState); + } + + count = static_cast<size_t>(parcel->readUint32()); + if (count > parcel->dataSize()) { + return BAD_VALUE; + } + std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> listenerCallbacks; + listenerCallbacks.reserve(count); + for (size_t i = 0; i < count; i++) { + sp<ITransactionCompletedListener> listener = + interface_cast<ITransactionCompletedListener>(parcel->readStrongBinder()); + size_t numCallbackIds = parcel->readUint32(); + if (numCallbackIds > parcel->dataSize()) { + return BAD_VALUE; + } + for (size_t j = 0; j < numCallbackIds; j++) { + listenerCallbacks[listener].callbackIds.insert(parcel->readInt64()); + } + size_t numSurfaces = parcel->readUint32(); + if (numSurfaces > parcel->dataSize()) { + return BAD_VALUE; + } + for (size_t j = 0; j < numSurfaces; j++) { + sp<SurfaceControl> surface; + surface = SurfaceControl::readFromParcel(parcel); + listenerCallbacks[listener].surfaceControls.insert(surface); + } + } + + count = static_cast<size_t>(parcel->readUint32()); + if (count > parcel->dataSize()) { + return BAD_VALUE; + } + std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> composerStates; + composerStates.reserve(count); + for (size_t i = 0; i < count; i++) { + sp<IBinder> surfaceControlHandle = parcel->readStrongBinder(); + + ComposerState composerState; + if (composerState.read(*parcel) == BAD_VALUE) { + return BAD_VALUE; + } + composerStates[surfaceControlHandle] = composerState; + } + + InputWindowCommands inputWindowCommands; + inputWindowCommands.read(*parcel); + + // Parsing was successful. Update the object. + mForceSynchronous = forceSynchronous; + mTransactionNestCount = transactionNestCount; + mAnimation = animation; + mEarlyWakeup = earlyWakeup; + mContainsBuffer = containsBuffer; + mDesiredPresentTime = desiredPresentTime; + mDisplayStates = displayStates; + mListenerCallbacks = listenerCallbacks; + mComposerStates = composerStates; + mInputWindowCommands = inputWindowCommands; + return NO_ERROR; +} + +status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const { + parcel->writeUint32(mForceSynchronous); + parcel->writeUint32(mTransactionNestCount); + parcel->writeBool(mAnimation); + parcel->writeBool(mEarlyWakeup); + parcel->writeBool(mContainsBuffer); + parcel->writeInt64(mDesiredPresentTime); + parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size())); + for (auto const& displayState : mDisplayStates) { + displayState.write(*parcel); + } + + parcel->writeUint32(static_cast<uint32_t>(mListenerCallbacks.size())); + for (auto const& [listener, callbackInfo] : mListenerCallbacks) { + parcel->writeStrongBinder(ITransactionCompletedListener::asBinder(listener)); + parcel->writeUint32(static_cast<uint32_t>(callbackInfo.callbackIds.size())); + for (auto callbackId : callbackInfo.callbackIds) { + parcel->writeInt64(callbackId); + } + parcel->writeUint32(static_cast<uint32_t>(callbackInfo.surfaceControls.size())); + for (auto surfaceControl : callbackInfo.surfaceControls) { + surfaceControl->writeToParcel(parcel); + } + } + + parcel->writeUint32(static_cast<uint32_t>(mComposerStates.size())); + for (auto const& [surfaceHandle, composerState] : mComposerStates) { + parcel->writeStrongBinder(surfaceHandle); + composerState.write(*parcel); + } + + mInputWindowCommands.write(*parcel); + return NO_ERROR; } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Transaction&& other) { - for (auto const& kv : other.mComposerStates) { - if (mComposerStates.count(kv.first) == 0) { - mComposerStates[kv.first] = kv.second; + for (auto const& [surfaceHandle, composerState] : other.mComposerStates) { + if (mComposerStates.count(surfaceHandle) == 0) { + mComposerStates[surfaceHandle] = composerState; } else { - mComposerStates[kv.first].state.merge(kv.second.state); + mComposerStates[surfaceHandle].state.merge(composerState.state); } } - other.mComposerStates.clear(); for (auto const& state : other.mDisplayStates) { ssize_t index = mDisplayStates.indexOf(state); @@ -346,46 +474,56 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mDisplayStates.editItemAt(static_cast<size_t>(index)).merge(state); } } - other.mDisplayStates.clear(); for (const auto& [listener, callbackInfo] : other.mListenerCallbacks) { auto& [callbackIds, surfaceControls] = callbackInfo; mListenerCallbacks[listener].callbackIds.insert(std::make_move_iterator( callbackIds.begin()), std::make_move_iterator(callbackIds.end())); + // register surface controls for this listener that is merging + for (const auto& surfaceControl : surfaceControls) { + registerSurfaceControlForCallback(surfaceControl); + } + mListenerCallbacks[listener] .surfaceControls.insert(std::make_move_iterator(surfaceControls.begin()), std::make_move_iterator(surfaceControls.end())); } - other.mListenerCallbacks.clear(); mInputWindowCommands.merge(other.mInputWindowCommands); - other.mInputWindowCommands.clear(); mContainsBuffer = other.mContainsBuffer; - other.mContainsBuffer = false; - mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup; - other.mEarlyWakeup = false; - + other.clear(); return *this; } -void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle, - const sp<ISurfaceComposerClient>& client) { +void SurfaceComposerClient::Transaction::clear() { + mComposerStates.clear(); + mDisplayStates.clear(); + mListenerCallbacks.clear(); + mInputWindowCommands.clear(); + mContainsBuffer = false; + mForceSynchronous = 0; + mTransactionNestCount = 0; + mAnimation = false; + mEarlyWakeup = false; + mDesiredPresentTime = -1; +} + +void SurfaceComposerClient::doDropReferenceTransaction(const sp<IBinder>& handle) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); Vector<ComposerState> composerStates; Vector<DisplayState> displayStates; ComposerState s; - s.client = client; s.state.surface = handle; s.state.what |= layer_state_t::eReparent; s.state.parentHandleForChild = nullptr; composerStates.add(s); sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); - sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, {}); + sf->setTransactionState(composerStates, displayStates, 0, applyToken, {}, -1, {}, false, {}); } void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { @@ -396,7 +534,7 @@ void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { uncacheBuffer.id = cacheId; sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); - sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, {}); + sf->setTransactionState({}, {}, 0, applyToken, {}, -1, uncacheBuffer, false, {}); } void SurfaceComposerClient::Transaction::cacheBuffers() { @@ -405,8 +543,8 @@ void SurfaceComposerClient::Transaction::cacheBuffers() { } size_t count = 0; - for (auto& [sc, cs] : mComposerStates) { - layer_state_t* s = getLayerState(sc); + for (auto& [handle, cs] : mComposerStates) { + layer_state_t* s = getLayerState(handle); if (!(s->what & layer_state_t::eBufferChanged)) { continue; } @@ -445,8 +583,8 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + bool hasListenerCallbacks = !mListenerCallbacks.empty(); std::vector<ListenerCallbacks> listenerCallbacks; - // For every listener with registered callbacks for (const auto& [listener, callbackInfo] : mListenerCallbacks) { auto& [callbackIds, surfaceControls] = callbackInfo; @@ -454,19 +592,24 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { continue; } - listenerCallbacks.emplace_back(listener, std::move(callbackIds)); - - // If the listener has any SurfaceControls set on this Transaction update the surface state - for (const auto& surfaceControl : surfaceControls) { - layer_state_t* s = getLayerState(surfaceControl); - if (!s) { - ALOGE("failed to get layer state"); - continue; + if (surfaceControls.empty()) { + listenerCallbacks.emplace_back(IInterface::asBinder(listener), std::move(callbackIds)); + } else { + // If the listener has any SurfaceControls set on this Transaction update the surface + // state + for (const auto& surfaceControl : surfaceControls) { + layer_state_t* s = getLayerState(surfaceControl); + if (!s) { + ALOGE("failed to get layer state"); + continue; + } + std::vector<CallbackId> callbacks(callbackIds.begin(), callbackIds.end()); + s->what |= layer_state_t::eHasListenerCallbacksChanged; + s->listeners.emplace_back(IInterface::asBinder(listener), callbacks); } - s->what |= layer_state_t::eHasListenerCallbacksChanged; - s->hasListenerCallbacks = true; } } + mListenerCallbacks.clear(); cacheBuffers(); @@ -504,7 +647,7 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { sf->setTransactionState(composerStates, displayStates, flags, applyToken, mInputWindowCommands, mDesiredPresentTime, {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/, - listenerCallbacks); + hasListenerCallbacks, listenerCallbacks); mInputWindowCommands.clear(); mStatus = NO_ERROR; return NO_ERROR; @@ -545,16 +688,15 @@ void SurfaceComposerClient::Transaction::setEarlyWakeup() { mEarlyWakeup = true; } -layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) { - if (mComposerStates.count(sc) == 0) { +layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<IBinder>& handle) { + if (mComposerStates.count(handle) == 0) { // we don't have it, add an initialized layer_state to our list ComposerState s; - s.client = sc->getClient()->mClient; - s.state.surface = sc->getHandle(); - mComposerStates[sc] = s; + s.state.surface = handle; + mComposerStates[handle] = s; } - return &(mComposerStates[sc].state); + return &(mComposerStates[handle].state); } void SurfaceComposerClient::Transaction::registerSurfaceControlForCallback( @@ -626,6 +768,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setRelat layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; + return *this; } s->what |= layer_state_t::eRelativeLayerChanged; s->what &= ~layer_state_t::eLayerChanged; @@ -701,14 +844,15 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setLayer } SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setMetadata( - const sp<SurfaceControl>& sc, uint32_t key, std::vector<uint8_t> data) { + const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } s->what |= layer_state_t::eMetadataChanged; - s->metadata.mMap[key] = std::move(data); + + s->metadata.mMap[key] = {p.data(), p.data() + p.dataSize()}; registerSurfaceControlForCallback(sc); return *this; @@ -1055,6 +1199,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::detachCh layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; + return *this; } s->what |= layer_state_t::eDetachChildren; @@ -1091,19 +1236,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setOverr return *this; } -SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeometryAppliesWithResize( - const sp<SurfaceControl>& sc) { - layer_state_t* s = getLayerState(sc); - if (!s) { - mStatus = BAD_INDEX; - return *this; - } - s->what |= layer_state_t::eGeometryAppliesWithResize; - - registerSurfaceControlForCallback(sc); - return *this; -} - #ifndef NO_INPUT SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo( const sp<SurfaceControl>& sc, @@ -1195,7 +1327,9 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setGeome break; } setMatrix(sc, matrix[0], matrix[1], matrix[2], matrix[3]); - setPosition(sc, x, y); + float offsetX = xScale * source.left; + float offsetY = yScale * source.top; + setPosition(sc, x - offsetX, y - offsetY); return *this; } @@ -1371,6 +1505,20 @@ status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32 return err; } +sp<SurfaceControl> SurfaceComposerClient::mirrorSurface(SurfaceControl* mirrorFromSurface) { + if (mirrorFromSurface == nullptr) { + return nullptr; + } + + sp<IBinder> handle; + sp<IBinder> mirrorFromHandle = mirrorFromSurface->getHandle(); + status_t err = mClient->mirrorSurface(mirrorFromHandle, &handle); + if (err == NO_ERROR) { + return new SurfaceControl(this, handle, nullptr, true /* owned */); + } + return nullptr; +} + status_t SurfaceComposerClient::clearLayerFrameStats(const sp<IBinder>& token) const { if (mStatus != NO_ERROR) { return mStatus; diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 55488dad0b..071314f082 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -65,16 +65,8 @@ SurfaceControl::~SurfaceControl() { // Avoid reparenting the server-side surface to null if we are not the owner of it, // meaning that we retrieved it from another process. - if (mClient != nullptr && mHandle != nullptr && mOwned) { - SurfaceComposerClient::doDropReferenceTransaction(mHandle, mClient->getClient()); - } - release(); -} - -void SurfaceControl::destroy() -{ - if (isValid()) { - SurfaceComposerClient::Transaction().reparent(this, nullptr).apply(); + if (mHandle != nullptr && mOwned) { + SurfaceComposerClient::doDropReferenceTransaction(mHandle); } release(); } @@ -186,8 +178,7 @@ void SurfaceControl::writeToParcel(Parcel* parcel) parcel->writeStrongBinder(IGraphicBufferProducer::asBinder(mGraphicBufferProducer)); } -sp<SurfaceControl> SurfaceControl::readFromParcel(Parcel* parcel) -{ +sp<SurfaceControl> SurfaceControl::readFromParcel(const Parcel* parcel) { sp<IBinder> client = parcel->readStrongBinder(); sp<IBinder> handle = parcel->readStrongBinder(); if (client == nullptr || handle == nullptr) diff --git a/libs/gui/TEST_MAPPING b/libs/gui/TEST_MAPPING new file mode 100644 index 0000000000..1c435304a8 --- /dev/null +++ b/libs/gui/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/native/libs/nativewindow" + } + ] +} diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h new file mode 100644 index 0000000000..6320556289 --- /dev/null +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 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_GUI_BLAST_BUFFER_QUEUE_H +#define ANDROID_GUI_BLAST_BUFFER_QUEUE_H + +#include <gui/IGraphicBufferProducer.h> +#include <gui/BufferItemConsumer.h> +#include <gui/BufferItem.h> +#include <gui/SurfaceComposerClient.h> + +#include <utils/Condition.h> +#include <utils/Mutex.h> +#include <utils/RefBase.h> + +#include <system/window.h> + +namespace android { + +class BufferItemConsumer; + +class BLASTBufferQueue + : public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener +{ +public: + BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height); + sp<IGraphicBufferProducer> getIGraphicBufferProducer() const { + return mProducer; + } + + void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ } + void onFrameReplaced(const BufferItem& item) override {onFrameAvailable(item);} + void onFrameAvailable(const BufferItem& item) override; + + void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats); + void setNextTransaction(SurfaceComposerClient::Transaction *t); + + void update(const sp<SurfaceControl>& surface, int width, int height); + + + virtual ~BLASTBufferQueue() = default; + +private: + friend class BLASTBufferQueueHelper; + + // can't be copied + BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs); + BLASTBufferQueue(const BLASTBufferQueue& rhs); + + sp<SurfaceControl> mSurfaceControl; + + mutable std::mutex mMutex; + + static const int MAX_BUFFERS = 2; + struct BufferInfo { + sp<GraphicBuffer> buffer; + int fence; + }; + + int mDequeuedBuffers = 0; + + int mWidth; + int mHeight; + + BufferItem mLastSubmittedBufferItem; + BufferItem mNextCallbackBufferItem; + sp<Fence> mLastFence; + + std::condition_variable mDequeueWaitCV; + + sp<IGraphicBufferConsumer> mConsumer; + sp<IGraphicBufferProducer> mProducer; + sp<BufferItemConsumer> mBufferItemConsumer; + + SurfaceComposerClient::Transaction* mNextTransaction = nullptr; +}; + +} // namespace android + +#endif // ANDROID_GUI_SURFACE_H diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h index 690a85f395..3c960894da 100644 --- a/libs/gui/include/gui/BufferQueueCore.h +++ b/libs/gui/include/gui/BufferQueueCore.h @@ -189,8 +189,12 @@ private: sp<IProducerListener> mLinkedToDeath; // mConnectedProducerListener is used to handle the onBufferReleased - // notification. + // and onBuffersDiscarded notification. sp<IProducerListener> mConnectedProducerListener; + // mBufferReleasedCbEnabled is used to indicate whether onBufferReleased() + // callback is registered by the listener. When set to false, + // mConnectedProducerListener will not trigger onBufferReleased() callback. + bool mBufferReleasedCbEnabled; // mSlots is an array of buffer slots that must be mirrored on the producer // side. This allows buffer ownership to be transferred between the producer @@ -348,6 +352,14 @@ private: const uint64_t mUniqueId; + // When buffer size is driven by the consumer and mTransformHint specifies + // a 90 or 270 degree rotation, this indicates whether the width and height + // used by dequeueBuffer will be additionally swapped. + bool mAutoPrerotation; + + // mTransformHintInUse is to cache the mTransformHint used by the producer. + uint32_t mTransformHintInUse; + }; // class BufferQueueCore } // namespace android diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index d2a47a6aa8..9ad92a6e78 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h @@ -190,6 +190,9 @@ public: // See IGraphicBufferProducer::getConsumerUsage virtual status_t getConsumerUsage(uint64_t* outUsage) const override; + // See IGraphicBufferProducer::setAutoPrerotation + virtual status_t setAutoPrerotation(bool autoPrerotation); + private: // This is required by the IBinder::DeathRecipient interface virtual void binderDied(const wp<IBinder>& who); diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 3dde8c8c80..abe1e3f6e6 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -412,6 +412,7 @@ public: uint64_t nextFrameNumber{0}; FrameEventHistoryDelta frameTimestamps; bool bufferReplaced{false}; + int maxBufferCount{0}; }; virtual status_t queueBuffer(int slot, const QueueBufferInput& input, @@ -629,6 +630,14 @@ public: // NATIVE_WINDOW_CONSUMER_USAGE_BITS attribute. virtual status_t getConsumerUsage(uint64_t* outUsage) const = 0; + // Enable/disable the auto prerotation at buffer allocation when the buffer + // size is driven by the consumer. + // + // When buffer size is driven by the consumer and the transform hint + // specifies a 90 or 270 degree rotation, if auto prerotation is enabled, + // the width and height used for dequeueBuffer will be additionally swapped. + virtual status_t setAutoPrerotation(bool autoPrerotation); + // Static method exports any IGraphicBufferProducer object to a parcel. It // handles null producer as well. static status_t exportToParcel(const sp<IGraphicBufferProducer>& producer, diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h index a13d8e4945..32a3690ff2 100644 --- a/libs/gui/include/gui/IProducerListener.h +++ b/libs/gui/include/gui/IProducerListener.h @@ -17,6 +17,8 @@ #ifndef ANDROID_GUI_IPRODUCERLISTENER_H #define ANDROID_GUI_IPRODUCERLISTENER_H +#include <vector> + #include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h> #include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h> #include <binder/IInterface.h> @@ -44,6 +46,9 @@ public: // multiple threads. virtual void onBufferReleased() = 0; // Asynchronous virtual bool needsReleaseNotify() = 0; + // onBuffersFreed is called from IGraphicBufferConsumer::discardFreeBuffers + // to notify the producer that certain free buffers are discarded by the consumer. + virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) = 0; // Asynchronous }; class IProducerListener : public ProducerListener, public IInterface @@ -65,6 +70,7 @@ public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0); virtual bool needsReleaseNotify(); + virtual void onBuffersDiscarded(const std::vector<int32_t>& slots); }; class DummyProducerListener : public BnProducerListener diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index c84910b6ec..06be1b36b2 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -140,7 +140,7 @@ public: const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, - const client_cache_t& uncacheBuffer, + const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks) = 0; /* signal that we're done booting. diff --git a/libs/gui/include/gui/ISurfaceComposerClient.h b/libs/gui/include/gui/ISurfaceComposerClient.h index 32ac9e8928..5fe7ca5344 100644 --- a/libs/gui/include/gui/ISurfaceComposerClient.h +++ b/libs/gui/include/gui/ISurfaceComposerClient.h @@ -76,6 +76,8 @@ public: * Requires ACCESS_SURFACE_FLINGER permission */ virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const = 0; + + virtual status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) = 0; }; class BnSurfaceComposerClient : public SafeBnInterface<ISurfaceComposerClient> { diff --git a/libs/gui/include/gui/ITransactionCompletedListener.h b/libs/gui/include/gui/ITransactionCompletedListener.h index 774ad46b15..178ca2d7e2 100644 --- a/libs/gui/include/gui/ITransactionCompletedListener.h +++ b/libs/gui/include/gui/ITransactionCompletedListener.h @@ -31,6 +31,7 @@ namespace android { class ITransactionCompletedListener; +class ListenerCallbacks; using CallbackId = int64_t; @@ -72,10 +73,10 @@ public: status_t writeToParcel(Parcel* output) const override; status_t readFromParcel(const Parcel* input) override; - static ListenerStats createEmpty(const sp<ITransactionCompletedListener>& listener, + static ListenerStats createEmpty(const sp<IBinder>& listener, const std::unordered_set<CallbackId>& callbackIds); - sp<ITransactionCompletedListener> listener; + sp<IBinder> listener; std::vector<TransactionStats> transactionStats; }; @@ -97,17 +98,59 @@ public: class ListenerCallbacks { public: - ListenerCallbacks(const sp<ITransactionCompletedListener>& listener, - const std::unordered_set<CallbackId>& callbacks) + ListenerCallbacks(const sp<IBinder>& listener, const std::unordered_set<CallbackId>& callbacks) : transactionCompletedListener(listener), callbackIds(callbacks.begin(), callbacks.end()) {} - ListenerCallbacks(const sp<ITransactionCompletedListener>& listener, - const std::vector<CallbackId>& ids) + ListenerCallbacks(const sp<IBinder>& listener, const std::vector<CallbackId>& ids) : transactionCompletedListener(listener), callbackIds(ids) {} - sp<ITransactionCompletedListener> transactionCompletedListener; + bool operator==(const ListenerCallbacks& rhs) const { + if (transactionCompletedListener != rhs.transactionCompletedListener) { + return false; + } + if (callbackIds.empty()) { + return rhs.callbackIds.empty(); + } + return callbackIds.front() == rhs.callbackIds.front(); + } + + sp<IBinder> transactionCompletedListener; std::vector<CallbackId> callbackIds; }; +struct IListenerHash { + std::size_t operator()(const sp<IBinder>& strongPointer) const { + return std::hash<IBinder*>{}(strongPointer.get()); + } +}; + +struct CallbackIdsHash { + // CallbackId vectors have several properties that let us get away with this simple hash. + // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is + // empty we can still hash 0. + // 2) CallbackId vectors for the same listener either are identical or contain none of the + // same members. It is sufficient to just check the first CallbackId in the vectors. If + // they match, they are the same. If they do not match, they are not the same. + std::size_t operator()(const std::vector<CallbackId>& callbackIds) const { + return std::hash<CallbackId>{}((callbackIds.empty()) ? 0 : callbackIds.front()); + } +}; + +struct ListenerCallbacksHash { + std::size_t HashCombine(size_t value1, size_t value2) const { + return value1 ^ (value2 + 0x9e3779b9 + (value1 << 6) + (value1 >> 2)); + } + + std::size_t operator()(const ListenerCallbacks& listenerCallbacks) const { + struct IListenerHash listenerHasher; + struct CallbackIdsHash callbackIdsHasher; + + std::size_t listenerHash = listenerHasher(listenerCallbacks.transactionCompletedListener); + std::size_t callbackIdsHash = callbackIdsHasher(listenerCallbacks.callbackIds); + + return HashCombine(listenerHash, callbackIdsHash); + } +}; + } // namespace android diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index 47f0cede3a..d58e019799 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -22,7 +22,12 @@ namespace android { -enum { METADATA_OWNER_UID = 1, METADATA_WINDOW_TYPE = 2, METADATA_TASK_ID = 3 }; +enum { + METADATA_OWNER_UID = 1, + METADATA_WINDOW_TYPE = 2, + METADATA_TASK_ID = 3, + METADATA_MOUSE_CURSOR = 4, +}; struct LayerMetadata : public Parcelable { std::unordered_map<uint32_t, std::vector<uint8_t>> mMap; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index f438eb3d01..a49ed525b6 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -23,6 +23,7 @@ #include <utils/Errors.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/ITransactionCompletedListener.h> #include <math/mat4.h> #ifndef NO_INPUT @@ -71,7 +72,7 @@ struct layer_state_t { eCropChanged_legacy = 0x00000100, eDeferTransaction_legacy = 0x00000200, eOverrideScalingModeChanged = 0x00000400, - eGeometryAppliesWithResize = 0x00000800, + // AVAILABLE 0x00000800, eReparentChildren = 0x00001000, eDetachChildren = 0x00002000, eRelativeLayerChanged = 0x00004000, @@ -123,7 +124,6 @@ struct layer_state_t { surfaceDamageRegion(), api(-1), colorTransform(mat4()), - hasListenerCallbacks(false), bgColorAlpha(0), bgColorDataspace(ui::Dataspace::UNKNOWN), colorSpaceAgnostic(false) { @@ -186,7 +186,6 @@ struct layer_state_t { sp<NativeHandle> sidebandStream; mat4 colorTransform; - bool hasListenerCallbacks; #ifndef NO_INPUT InputWindowInfo inputInfo; #endif @@ -203,10 +202,11 @@ struct layer_state_t { // A color space agnostic layer means the color of this layer can be // interpreted in any color space. bool colorSpaceAgnostic; + + std::vector<ListenerCallbacks> listeners; }; struct ComposerState { - sp<ISurfaceComposerClient> client; layer_state_t state; status_t write(Parcel& output) const; status_t read(const Parcel& input); @@ -274,8 +274,6 @@ struct InputWindowCommands { }; static inline int compare_type(const ComposerState& lhs, const ComposerState& rhs) { - if (lhs.client < rhs.client) return -1; - if (lhs.client > rhs.client) return 1; if (lhs.state.surface < rhs.state.surface) return -1; if (lhs.state.surface > rhs.state.surface) return 1; return 0; diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 5c6a1ee383..e582509b6e 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -20,6 +20,7 @@ #include <gui/BufferQueueDefs.h> #include <gui/HdrMetadata.h> #include <gui/IGraphicBufferProducer.h> +#include <gui/IProducerListener.h> #include <ui/ANativeObjectBase.h> #include <ui/GraphicTypes.h> @@ -35,6 +36,21 @@ namespace android { class ISurfaceComposer; +/* This is the same as ProducerListener except that onBuffersDiscarded is + * called with a vector of graphic buffers instead of buffer slots. + */ +class SurfaceListener : public virtual RefBase +{ +public: + SurfaceListener() = default; + virtual ~SurfaceListener() = default; + + virtual void onBufferReleased() = 0; + virtual bool needsReleaseNotify() = 0; + + virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) = 0; +}; + /* * An implementation of ANativeWindow that feeds graphics buffers into a * BufferQueue. @@ -163,9 +179,6 @@ public: status_t getUniqueId(uint64_t* outId) const; status_t getConsumerUsage(uint64_t* outUsage) const; - // Returns the CLOCK_MONOTONIC start time of the last dequeueBuffer call - nsecs_t getLastDequeueStartTime() const; - protected: virtual ~Surface(); @@ -230,6 +243,11 @@ private: int dispatchGetWideColorSupport(va_list args); int dispatchGetHdrSupport(va_list args); int dispatchGetConsumerUsage64(va_list args); + int dispatchSetAutoPrerotation(va_list args); + int dispatchGetLastDequeueStartTime(va_list args); + int dispatchSetDequeueTimeout(va_list args); + int dispatchGetLastDequeueDuration(va_list args); + int dispatchGetLastQueueDuration(va_list args); bool transformToDisplayInverse(); protected: @@ -265,6 +283,7 @@ public: virtual int setAsyncMode(bool async); virtual int setSharedBufferMode(bool sharedBufferMode); virtual int setAutoRefresh(bool autoRefresh); + virtual int setAutoPrerotation(bool autoPrerotation); virtual int setBuffersDimensions(uint32_t width, uint32_t height); virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); virtual int unlockAndPost(); @@ -283,6 +302,10 @@ public: sp<Fence>* outFence); virtual int attachBuffer(ANativeWindowBuffer*); + virtual int connect( + int api, bool reportBufferRemoval, + const sp<SurfaceListener>& sListener); + // When client connects to Surface with reportBufferRemoval set to true, any buffers removed // from this Surface will be collected and returned here. Once this method returns, these // buffers will no longer be referenced by this Surface unless they are attached to this @@ -299,6 +322,26 @@ protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; + class ProducerListenerProxy : public BnProducerListener { + public: + ProducerListenerProxy(wp<Surface> parent, sp<SurfaceListener> listener) + : mParent(parent), mSurfaceListener(listener) {} + virtual ~ProducerListenerProxy() {} + + virtual void onBufferReleased() { + mSurfaceListener->onBufferReleased(); + } + + virtual bool needsReleaseNotify() { + return mSurfaceListener->needsReleaseNotify(); + } + + virtual void onBuffersDiscarded(const std::vector<int32_t>& slots); + private: + wp<Surface> mParent; + sp<SurfaceListener> mSurfaceListener; + }; + void querySupportedTimestampsLocked() const; void freeAllBuffers(); @@ -434,6 +477,7 @@ protected: // Caches the values that have been passed to the producer. bool mSharedBufferMode; bool mAutoRefresh; + bool mAutoPrerotation; // If in shared buffer mode and auto refresh is enabled, store the shared // buffer slot and return it for all calls to queue/dequeue without going @@ -466,6 +510,11 @@ protected: bool mReportRemovedBuffers = false; std::vector<sp<GraphicBuffer>> mRemovedBuffers; + int mMaxBufferCount; + + sp<IProducerListener> mListenerProxy; + status_t getAndFlushBuffersFromSlots(const std::vector<int32_t>& slots, + std::vector<sp<GraphicBuffer>>* outBuffers); }; } // namespace android diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0e17c7b015..6676be4837 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -163,8 +163,7 @@ public: * Called from SurfaceControl d'tor to 'destroy' the surface (or rather, reparent it * to null), but without needing an sp<SurfaceControl> to avoid infinite ressurection. */ - static void doDropReferenceTransaction(const sp<IBinder>& handle, - const sp<ISurfaceComposerClient>& client); + static void doDropReferenceTransaction(const sp<IBinder>& handle); /** * Uncaches a buffer in ISurfaceComposer. It must be uncached via a transaction so that it is @@ -246,6 +245,17 @@ public: LayerMetadata metadata = LayerMetadata() // metadata ); + // Creates a mirrored hierarchy for the mirrorFromSurface. This returns a SurfaceControl + // which is a parent of the root of the mirrored hierarchy. + // + // Real Hierarchy Mirror + // SC (value that's returned) + // | + // A A' + // | | + // B B' + sp<SurfaceControl> mirrorSurface(SurfaceControl* mirrorFromSurface); + //! Create a virtual display static sp<IBinder> createDisplay(const String8& displayName, bool secure); @@ -270,6 +280,12 @@ public: } }; + struct IBinderHash { + std::size_t operator()(const sp<IBinder>& iBinder) const { + return std::hash<IBinder*>{}(iBinder.get()); + } + }; + struct TCLHash { std::size_t operator()(const sp<ITransactionCompletedListener>& tcl) const { return std::hash<IBinder*>{}((tcl) ? IInterface::asBinder(tcl).get() : nullptr); @@ -285,8 +301,9 @@ public: std::unordered_set<sp<SurfaceControl>, SCHash> surfaceControls; }; - class Transaction { - std::unordered_map<sp<SurfaceControl>, ComposerState, SCHash> mComposerStates; + class Transaction : public Parcelable { + protected: + std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates; SortedVector<DisplayState > mDisplayStates; std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> mListenerCallbacks; @@ -314,7 +331,10 @@ public: InputWindowCommands mInputWindowCommands; int mStatus = NO_ERROR; - layer_state_t* getLayerState(const sp<SurfaceControl>& sc); + layer_state_t* getLayerState(const sp<IBinder>& surfaceHandle); + layer_state_t* getLayerState(const sp<SurfaceControl>& sc) { + return getLayerState(sc->getHandle()); + } DisplayState& getDisplayState(const sp<IBinder>& token); void cacheBuffers(); @@ -325,6 +345,15 @@ public: virtual ~Transaction() = default; Transaction(Transaction const& other); + // Factory method that creates a new Transaction instance from the parcel. + static std::unique_ptr<Transaction> createFromParcel(const Parcel* parcel); + + status_t writeToParcel(Parcel* parcel) const override; + status_t readFromParcel(const Parcel* parcel) override; + + // Clears the contents of the transaction without applying it. + void clear(); + status_t apply(bool synchronous = false); // Merge another transaction in to this one, clearing other // as if it had been applied. @@ -362,8 +391,7 @@ public: Transaction& setCrop_legacy(const sp<SurfaceControl>& sc, const Rect& crop); Transaction& setCornerRadius(const sp<SurfaceControl>& sc, float cornerRadius); Transaction& setLayerStack(const sp<SurfaceControl>& sc, uint32_t layerStack); - Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, - std::vector<uint8_t> data); + Transaction& setMetadata(const sp<SurfaceControl>& sc, uint32_t key, const Parcel& p); // Defers applying any changes made in this transaction until the Layer // identified by handle reaches the given frameNumber. If the Layer identified // by handle is removed, then we will apply this transaction regardless of @@ -430,12 +458,6 @@ public: Transaction& setOverrideScalingMode(const sp<SurfaceControl>& sc, int32_t overrideScalingMode); - // If the size changes in this transaction, all geometry updates specified - // in this transaction will not complete until a buffer of the new size - // arrives. As some elements normally apply immediately, this enables - // freezing the total geometry of a surface until a resize is completed. - Transaction& setGeometryAppliesWithResize(const sp<SurfaceControl>& sc); - #ifndef NO_INPUT Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info); Transaction& transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken); @@ -551,15 +573,10 @@ class TransactionCompletedListener : public BnTransactionCompletedListener { CallbackId mCallbackIdCounter GUARDED_BY(mMutex) = 1; - struct IBinderHash { - std::size_t operator()(const sp<IBinder>& iBinder) const { - return std::hash<IBinder*>{}(iBinder.get()); - } - }; - struct CallbackTranslation { TransactionCompletedCallback callbackFunction; - std::unordered_map<sp<IBinder>, sp<SurfaceControl>, IBinderHash> surfaceControls; + std::unordered_map<sp<IBinder>, sp<SurfaceControl>, SurfaceComposerClient::IBinderHash> + surfaceControls; }; std::unordered_map<CallbackId, CallbackTranslation> mCallbacks GUARDED_BY(mMutex); diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index 23bfc0256b..ae4a14690f 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -44,7 +44,7 @@ class SurfaceComposerClient; class SurfaceControl : public RefBase { public: - static sp<SurfaceControl> readFromParcel(Parcel* parcel); + static sp<SurfaceControl> readFromParcel(const Parcel* parcel); void writeToParcel(Parcel* parcel); static bool isValid(const sp<SurfaceControl>& surface) { @@ -81,7 +81,7 @@ public: status_t getLayerFrameStats(FrameStats* outStats) const; sp<SurfaceComposerClient> getClient() const; - + explicit SurfaceControl(const sp<SurfaceControl>& other); SurfaceControl(const sp<SurfaceComposerClient>& client, const sp<IBinder>& handle, diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h index 98f24c2d44..98f24c2d44 100644 --- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.h +++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.h b/libs/gui/include/gui/mock/GraphicBufferProducer.h index c98f39f43c..c98f39f43c 100644 --- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.h +++ b/libs/gui/include/gui/mock/GraphicBufferProducer.h diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.cpp b/libs/gui/mock/GraphicBufferConsumer.cpp index a17b73f0fc..4a6c0815e1 100644 --- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferConsumer.cpp +++ b/libs/gui/mock/GraphicBufferConsumer.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "mock/gui/MockGraphicBufferConsumer.h" +#include <gui/mock/GraphicBufferConsumer.h> namespace android { namespace mock { diff --git a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp b/libs/gui/mock/GraphicBufferProducer.cpp index a7fd667b54..239a80a2e1 100644 --- a/services/surfaceflinger/tests/unittests/mock/gui/MockGraphicBufferProducer.cpp +++ b/libs/gui/mock/GraphicBufferProducer.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "mock/gui/MockGraphicBufferProducer.h" +#include <gui/mock/GraphicBufferProducer.h> namespace android { namespace mock { diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index cbda6b23ff..7f960abb35 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -13,7 +13,8 @@ cc_test { ], srcs: [ - "BufferItemConsumer_test.cpp", + "BLASTBufferQueue_test.cpp", + "BufferItemConsumer_test.cpp", "BufferQueue_test.cpp", "CpuConsumer_test.cpp", "EndToEndNativeInputTest.cpp", diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp new file mode 100644 index 0000000000..db1ac249b5 --- /dev/null +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2019 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 "BLASTBufferQueue_test" + +#include <gui/BLASTBufferQueue.h> + +#include <android/hardware/graphics/common/1.2/types.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/IProducerListener.h> +#include <gui/SurfaceComposerClient.h> +#include <private/gui/ComposerService.h> +#include <ui/DisplayInfo.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicTypes.h> + +#include <gtest/gtest.h> + +using namespace std::chrono_literals; + +namespace android { + +using Transaction = SurfaceComposerClient::Transaction; +using android::hardware::graphics::common::V1_2::BufferUsage; + +class BLASTBufferQueueHelper { +public: + BLASTBufferQueueHelper(const sp<SurfaceControl>& sc, int width, int height) { + mBlastBufferQueueAdapter = new BLASTBufferQueue(sc, width, height); + } + + void update(const sp<SurfaceControl>& sc, int width, int height) { + mBlastBufferQueueAdapter->update(sc, width, height); + } + + void setNextTransaction(Transaction* next) { + mBlastBufferQueueAdapter->setNextTransaction(next); + } + + int getWidth() { return mBlastBufferQueueAdapter->mWidth; } + + int getHeight() { return mBlastBufferQueueAdapter->mHeight; } + + Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; } + + sp<IGraphicBufferProducer> getIGraphicBufferProducer() { + return mBlastBufferQueueAdapter->getIGraphicBufferProducer(); + } + + const sp<SurfaceControl> getSurfaceControl() { + return mBlastBufferQueueAdapter->mSurfaceControl; + } + + void waitForCallback() { + std::unique_lock lock{mBlastBufferQueueAdapter->mMutex}; + mBlastBufferQueueAdapter->mDequeueWaitCV.wait_for(lock, 1s); + } + +private: + sp<BLASTBufferQueue> mBlastBufferQueueAdapter; +}; + +class BLASTBufferQueueTest : public ::testing::Test { +public: +protected: + BLASTBufferQueueTest() { + const ::testing::TestInfo* const testInfo = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); + } + + ~BLASTBufferQueueTest() { + const ::testing::TestInfo* const testInfo = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGV("End test: %s.%s", testInfo->test_case_name(), testInfo->name()); + } + + void SetUp() { + mComposer = ComposerService::getComposerService(); + mClient = new SurfaceComposerClient(); + mDisplayToken = mClient->getInternalDisplayToken(); + ASSERT_NE(nullptr, mDisplayToken.get()); + Transaction t; + t.setDisplayLayerStack(mDisplayToken, 0); + t.apply(); + t.clear(); + + DisplayInfo info; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplayToken, &info)); + mDisplayWidth = info.w; + mDisplayHeight = info.h; + + mSurfaceControl = mClient->createSurface(String8("TestSurface"), mDisplayWidth, + mDisplayHeight, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState, + /*parent*/ nullptr); + t.setLayerStack(mSurfaceControl, 0) + .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max()) + .setFrame(mSurfaceControl, Rect(0, 0, mDisplayWidth, mDisplayHeight)) + .show(mSurfaceControl) + .setDataspace(mSurfaceControl, ui::Dataspace::V0_SRGB) + .apply(); + } + + void fillBuffer(uint32_t* bufData, uint32_t width, uint32_t height, uint32_t stride, uint8_t r, + uint8_t g, uint8_t b) { + for (uint32_t row = 0; row < height; row++) { + for (uint32_t col = 0; col < width; col++) { + uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col); + *pixel = r; + *(pixel + 1) = g; + *(pixel + 2) = b; + *(pixel + 3) = 255; + } + } + } + + void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b) { + const auto width = mScreenCaptureBuf->getWidth(); + const auto height = mScreenCaptureBuf->getHeight(); + const auto stride = mScreenCaptureBuf->getStride(); + + uint32_t* bufData; + mScreenCaptureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN), + reinterpret_cast<void**>(&bufData)); + + for (uint32_t row = 0; row < height; row++) { + for (uint32_t col = 0; col < width; col++) { + uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col); + EXPECT_EQ(r, *(pixel)); + EXPECT_EQ(g, *(pixel + 1)); + EXPECT_EQ(b, *(pixel + 2)); + } + } + mScreenCaptureBuf->unlock(); + ASSERT_EQ(false, ::testing::Test::HasFailure()); + } + + sp<SurfaceComposerClient> mClient; + sp<ISurfaceComposer> mComposer; + + sp<IBinder> mDisplayToken; + + sp<SurfaceControl> mSurfaceControl; + sp<GraphicBuffer> mScreenCaptureBuf; + + uint32_t mDisplayWidth; + uint32_t mDisplayHeight; +}; + +TEST_F(BLASTBufferQueueTest, CreateBLASTBufferQueue) { + // create BLASTBufferQueue adapter associated with this surface + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl()); + ASSERT_EQ(mDisplayWidth, adapter.getWidth()); + ASSERT_EQ(mDisplayHeight, adapter.getHeight()); + ASSERT_EQ(nullptr, adapter.getNextTransaction()); +} + +TEST_F(BLASTBufferQueueTest, Update) { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + sp<SurfaceControl> updateSurface = + mClient->createSurface(String8("UpdateTest"), mDisplayWidth / 2, mDisplayHeight / 2, + PIXEL_FORMAT_RGBA_8888); + adapter.update(updateSurface, mDisplayWidth / 2, mDisplayHeight / 2); + ASSERT_EQ(updateSurface, adapter.getSurfaceControl()); + ASSERT_EQ(mDisplayWidth / 2, adapter.getWidth()); + ASSERT_EQ(mDisplayHeight / 2, adapter.getHeight()); +} + +TEST_F(BLASTBufferQueueTest, SetNextTransaction) { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + Transaction next; + adapter.setNextTransaction(&next); + ASSERT_EQ(&next, adapter.getNextTransaction()); +} + +TEST_F(BLASTBufferQueueTest, onFrameAvailable_Apply) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + auto igbProducer = adapter.getIGraphicBufferProducer(); + ASSERT_NE(nullptr, igbProducer.get()); + IGraphicBufferProducer::QueueBufferOutput qbOutput; + ASSERT_EQ(NO_ERROR, + igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, + &qbOutput)); + ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(3)); + + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight, + PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); + ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf)); + + uint32_t* bufData; + buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN), + reinterpret_cast<void**>(&bufData)); + fillBuffer(bufData, buf->getWidth(), buf->getHeight(), buf->getStride(), r, g, b); + buf->unlock(); + + IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN, + Rect(mDisplayWidth, mDisplayHeight), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, + Fence::NO_FENCE); + igbProducer->queueBuffer(slot, input, &qbOutput); + + adapter.waitForCallback(); + + // capture screen and verify that it is red + bool capturedSecureLayers; + ASSERT_EQ(NO_ERROR, + mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers, + ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), + mDisplayWidth, mDisplayHeight, + /*useIdentityTransform*/ false)); + ASSERT_NO_FATAL_FAILURE(checkScreenCapture(r, g, b)); +} +} // namespace android diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 119e888edb..6d7b6bb9c6 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -169,6 +169,18 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { ASSERT_EQ(OK, item.mGraphicBuffer->unlock()); } +TEST_F(BufferQueueTest, GetMaxBufferCountInQueueBufferOutput_Succeeds) { + createBufferQueue(); + sp<DummyConsumer> dc(new DummyConsumer); + mConsumer->consumerConnect(dc, false); + int bufferCount = 50; + mConsumer->setMaxBufferCount(bufferCount); + + IGraphicBufferProducer::QueueBufferOutput output; + mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output); + ASSERT_EQ(output.maxBufferCount, bufferCount); +} + TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { createBufferQueue(); sp<DummyConsumer> dc(new DummyConsumer); @@ -998,12 +1010,31 @@ TEST_F(BufferQueueTest, TestOccupancyHistory) { ASSERT_EQ(true, thirdSegment.usedThirdBuffer); } +struct BufferDiscardedListener : public BnProducerListener { +public: + BufferDiscardedListener() = default; + virtual ~BufferDiscardedListener() = default; + + virtual void onBufferReleased() {} + virtual bool needsReleaseNotify() { return false; } + virtual void onBuffersDiscarded(const std::vector<int32_t>& slots) { + mDiscardedSlots.insert(mDiscardedSlots.end(), slots.begin(), slots.end()); + } + + const std::vector<int32_t>& getDiscardedSlots() const { return mDiscardedSlots; } +private: + // No need to use lock given the test triggers the listener in the same + // thread context. + std::vector<int32_t> mDiscardedSlots; +}; + TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { createBufferQueue(); sp<DummyConsumer> dc(new DummyConsumer); ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, + sp<BufferDiscardedListener> pl(new BufferDiscardedListener); + ASSERT_EQ(OK, mProducer->connect(pl, NATIVE_WINDOW_API_CPU, false, &output)); int slot = BufferQueue::INVALID_BUFFER_SLOT; @@ -1044,12 +1075,19 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + int releasedSlot = item.mSlot; + // Acquire 1 buffer, leaving 1 filled buffer in queue ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); // Now discard the free buffers ASSERT_EQ(OK, mConsumer->discardFreeBuffers()); + // Check onBuffersDiscarded is called with correct slots + auto buffersDiscarded = pl->getDiscardedSlots(); + ASSERT_EQ(buffersDiscarded.size(), 1); + ASSERT_EQ(buffersDiscarded[0], releasedSlot); + // Check no free buffers in dump String8 dumpString; mConsumer->dumpState(String8{}, &dumpString); diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index ff1ba0ad17..03b9cd75db 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -133,27 +133,6 @@ public: EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction()); } - void expectMotionEvent(int motionEventType, int x, int y) { - InputEvent *ev = consumeEvent(); - ASSERT_NE(ev, nullptr); - ASSERT_EQ(ev->getType(), AINPUT_EVENT_TYPE_MOTION); - MotionEvent *mev = static_cast<MotionEvent *>(ev); - EXPECT_EQ(motionEventType, mev->getAction()); - EXPECT_EQ(x, mev->getX(0)); - EXPECT_EQ(y, mev->getY(0)); - } - - void expectNoMotionEvent(int motionEventType) { - InputEvent *ev = consumeEvent(); - if (ev == nullptr || ev->getType() != AINPUT_EVENT_TYPE_MOTION) { - // Didn't find an event or a motion event so assume action didn't occur. - return; - } - - MotionEvent *mev = static_cast<MotionEvent *>(ev); - EXPECT_NE(motionEventType, mev->getAction()); - } - ~InputSurface() { mInputFlinger->unregisterInputChannel(mServerChannel); } @@ -278,15 +257,6 @@ void injectTap(int x, int y) { } } -void injectMotionEvent(std::string event, int x, int y) { - char *buf1, *buf2; - asprintf(&buf1, "%d", x); - asprintf(&buf2, "%d", y); - if (fork() == 0) { - execlp("input", "input", "motionevent", event.c_str(), buf1, buf2, NULL); - } -} - TEST_F(InputSurfacesTest, can_receive_input) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(100, 100); @@ -410,6 +380,19 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets) { bgSurface->expectTap(1, 1); } +TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) { + std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); + // In case we pass the very big inset without any checking. + fgSurface->mInputInfo.surfaceInset = INT32_MAX; + fgSurface->showAt(100, 100); + + fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); }); + + // expect no crash for overflow, and inset size to be clamped to surface size + injectTap(202, 202); + fgSurface->expectTap(1, 1); +} + // Ensure we ignore transparent region when getting screen bounds when positioning input frame. TEST_F(InputSurfacesTest, input_ignores_transparent_region) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); @@ -422,9 +405,11 @@ TEST_F(InputSurfacesTest, input_ignores_transparent_region) { surface->expectTap(1, 1); } -// Ensure we send the input to the right surface when the surface visibility changes due to the -// first buffer being submitted. ref: b/120839715 -TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) { +// TODO(b/139494112) update tests once we define expected behavior +// Ensure we still send input to the surface regardless of surface visibility changes due to the +// first buffer being submitted or alpha changes. +// Original bug ref: b/120839715 +TEST_F(InputSurfacesTest, input_ignores_buffer_layer_buffer) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> bufferSurface = InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); @@ -433,14 +418,14 @@ TEST_F(InputSurfacesTest, input_respects_buffer_layer_buffer) { bufferSurface->showAt(10, 10); injectTap(11, 11); - bgSurface->expectTap(1, 1); + bufferSurface->expectTap(1, 1); postBuffer(bufferSurface->mSurfaceControl); injectTap(11, 11); bufferSurface->expectTap(1, 1); } -TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) { +TEST_F(InputSurfacesTest, input_ignores_buffer_layer_alpha) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> bufferSurface = InputSurface::makeBufferInputSurface(mComposerClient, 100, 100); @@ -455,10 +440,10 @@ TEST_F(InputSurfacesTest, input_respects_buffer_layer_alpha) { bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); }); injectTap(11, 11); - bgSurface->expectTap(1, 1); + bufferSurface->expectTap(1, 1); } -TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) { +TEST_F(InputSurfacesTest, input_ignores_color_layer_alpha) { std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100); std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100); @@ -471,7 +456,7 @@ TEST_F(InputSurfacesTest, input_respects_color_layer_alpha) { fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); }); injectTap(11, 11); - bgSurface->expectTap(1, 1); + fgSurface->expectTap(1, 1); } TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { @@ -491,26 +476,6 @@ TEST_F(InputSurfacesTest, input_respects_container_layer_visiblity) { bgSurface->expectTap(1, 1); } -TEST_F(InputSurfacesTest, transfer_touch_focus) { - std::unique_ptr<InputSurface> fromSurface = makeSurface(100, 100); - - fromSurface->showAt(10, 10); - injectMotionEvent("DOWN", 11, 11); - fromSurface->expectMotionEvent(AMOTION_EVENT_ACTION_DOWN, 1, 1); - - std::unique_ptr<InputSurface> toSurface = makeSurface(100, 100); - toSurface->showAt(10, 10); - - sp<IBinder> fromToken = fromSurface->mServerChannel->getToken(); - sp<IBinder> toToken = toSurface->mServerChannel->getToken(); - SurfaceComposerClient::Transaction t; - t.transferTouchFocus(fromToken, toToken).apply(true); - - injectMotionEvent("UP", 11, 11); - toSurface->expectMotionEvent(AMOTION_EVENT_ACTION_UP, 1, 1); - fromSurface->expectNoMotionEvent(AMOTION_EVENT_ACTION_UP); -} - TEST_F(InputSurfacesTest, input_respects_outscreen) { std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->showAt(-1, -1); diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp index d33ecfbdb9..c9de37d957 100644 --- a/libs/gui/tests/RegionSampling_test.cpp +++ b/libs/gui/tests/RegionSampling_test.cpp @@ -297,4 +297,70 @@ TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) { composer->removeRegionSamplingListener(grayListener); } +TEST_F(RegionSamplingTest, DISABLED_TestIfInvalidInputParameters) { + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<Listener> listener = new Listener(); + const Rect sampleArea{100, 100, 200, 200}; + // Invalid input sampleArea + EXPECT_EQ(BAD_VALUE, + composer->addRegionSamplingListener(Rect::INVALID_RECT, mTopLayer->getHandle(), + listener)); + listener->reset(); + // Invalid input binder + EXPECT_EQ(NO_ERROR, composer->addRegionSamplingListener(sampleArea, NULL, listener)); + // Invalid input listener + EXPECT_EQ(BAD_VALUE, + composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), NULL)); + EXPECT_EQ(BAD_VALUE, composer->removeRegionSamplingListener(NULL)); + // remove the listener + composer->removeRegionSamplingListener(listener); +} + +TEST_F(RegionSamplingTest, DISABLED_TestCallbackAfterRemoveListener) { + fill_render(rgba_green); + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<Listener> listener = new Listener(); + const Rect sampleArea{100, 100, 200, 200}; + composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); + fill_render(rgba_green); + + EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_green, error_margin); + + listener->reset(); + composer->removeRegionSamplingListener(listener); + fill_render(rgba_green); + EXPECT_FALSE(listener->wait_event(100ms)) + << "callback should stop after remove the region sampling listener"; +} + +TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromMovingLayer) { + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<Listener> listener = new Listener(); + Rect sampleArea{100, 100, 200, 200}; + + // Test: listener in (100, 100). See layer before move, no layer after move. + fill_render(rgba_blue); + composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); + EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_blue, error_margin); + listener->reset(); + SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply(); + EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_gray, error_margin); + composer->removeRegionSamplingListener(listener); + + // Test: listener offset to (600, 600). No layer before move, see layer after move. + fill_render(rgba_green); + sampleArea.offsetTo(600, 600); + composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); + EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_gray, error_margin); + listener->reset(); + SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply(); + EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_green, error_margin); + composer->removeRegionSamplingListener(listener); +} + } // namespace android::test diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index 65e09f2540..c85e84489d 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -28,8 +28,6 @@ #include <utils/Log.h> #include <utils/Thread.h> -extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name); - namespace android { class SurfaceTextureClientTest : public ::testing::Test { diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index d3708586f5..a4fdb35595 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -57,6 +57,37 @@ class FakeProducerFrameEventHistory; static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max(); +class DummySurfaceListener : public SurfaceListener { +public: + DummySurfaceListener(bool enableReleasedCb = false) : + mEnableReleaseCb(enableReleasedCb), + mBuffersReleased(0) {} + virtual ~DummySurfaceListener() = default; + + virtual void onBufferReleased() { + mBuffersReleased++; + } + virtual bool needsReleaseNotify() { + return mEnableReleaseCb; + } + virtual void onBuffersDiscarded(const std::vector<sp<GraphicBuffer>>& buffers) { + mDiscardedBuffers.insert(mDiscardedBuffers.end(), buffers.begin(), buffers.end()); + } + + int getReleaseNotifyCount() const { + return mBuffersReleased; + } + const std::vector<sp<GraphicBuffer>>& getDiscardedBuffers() const { + return mDiscardedBuffers; + } +private: + // No need to use lock given the test triggers the listener in the same + // thread context. + bool mEnableReleaseCb; + int32_t mBuffersReleased; + std::vector<sp<GraphicBuffer>> mDiscardedBuffers; +}; + class SurfaceTest : public ::testing::Test { protected: SurfaceTest() { @@ -88,6 +119,86 @@ protected: mComposerClient->dispose(); } + void testSurfaceListener(bool hasSurfaceListener, bool enableReleasedCb, + int32_t extraDiscardedBuffers) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<DummyConsumer> dummyConsumer(new DummyConsumer); + consumer->consumerConnect(dummyConsumer, false); + consumer->setConsumerName(String8("TestConsumer")); + + sp<Surface> surface = new Surface(producer); + sp<ANativeWindow> window(surface); + sp<DummySurfaceListener> listener; + if (hasSurfaceListener) { + listener = new DummySurfaceListener(enableReleasedCb); + } + ASSERT_EQ(OK, surface->connect( + NATIVE_WINDOW_API_CPU, + /*reportBufferRemoval*/true, + /*listener*/listener)); + const int BUFFER_COUNT = 4 + extraDiscardedBuffers; + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); + + ANativeWindowBuffer* buffers[BUFFER_COUNT]; + // Dequeue first to allocate a number of buffers + for (int i = 0; i < BUFFER_COUNT; i++) { + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffers[i])); + } + for (int i = 0; i < BUFFER_COUNT; i++) { + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffers[i], -1)); + } + + ANativeWindowBuffer* buffer; + // Fill BUFFER_COUNT-1 buffers + for (int i = 0; i < BUFFER_COUNT-1; i++) { + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffer)); + ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, -1)); + } + + // Dequeue 1 buffer + ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(window.get(), &buffer)); + + // Acquire and free 1+extraDiscardedBuffers buffer, check onBufferReleased is called. + std::vector<BufferItem> releasedItems; + releasedItems.resize(1+extraDiscardedBuffers); + for (int i = 0; i < releasedItems.size(); i++) { + ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&releasedItems[i], 0)); + ASSERT_EQ(NO_ERROR, consumer->releaseBuffer(releasedItems[i].mSlot, + releasedItems[i].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, + Fence::NO_FENCE)); + } + int32_t expectedReleaseCb = (enableReleasedCb ? releasedItems.size() : 0); + if (hasSurfaceListener) { + ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount()); + } + + // Acquire 1 buffer, leaving 1+extraDiscardedBuffers filled buffer in queue + BufferItem item; + ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&item, 0)); + + // Discard free buffers + ASSERT_EQ(NO_ERROR, consumer->discardFreeBuffers()); + + if (hasSurfaceListener) { + ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount()); + + // Check onBufferDiscarded is called with correct buffer + auto discardedBuffers = listener->getDiscardedBuffers(); + ASSERT_EQ(discardedBuffers.size(), releasedItems.size()); + for (int i = 0; i < releasedItems.size(); i++) { + ASSERT_EQ(discardedBuffers[i], releasedItems[i].mGraphicBuffer); + } + + ASSERT_EQ(expectedReleaseCb, listener->getReleaseNotifyCount()); + } + + // Disconnect the surface + ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); + } + sp<Surface> mSurface; sp<SurfaceComposerClient> mComposerClient; sp<SurfaceControl> mSurfaceControl; @@ -480,6 +591,21 @@ TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) { ASSERT_LE(removedBuffers.size(), 1u); } +TEST_F(SurfaceTest, SurfaceListenerTest) { + // Test discarding 1 free buffers with no listener + testSurfaceListener(/*hasListener*/false, /*enableReleaseCb*/false, /*extraDiscardedBuffers*/0); + // Test discarding 2 free buffers with no listener + testSurfaceListener(/*hasListener*/false, /*enableReleaseCb*/false, /*extraDiscardedBuffers*/1); + // Test discarding 1 free buffers with a listener, disabling onBufferReleased + testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/false, /*extraDiscardedBuffers*/0); + // Test discarding 2 free buffers with a listener, disabling onBufferReleased + testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/false, /*extraDiscardedBuffers*/1); + // Test discarding 1 free buffers with a listener, enabling onBufferReleased + testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/true, /*extraDiscardedBuffers*/0); + // Test discarding 3 free buffers with a listener, enabling onBufferReleased + testSurfaceListener(/*hasListener*/true, /*enableReleasedCb*/true, /*extraDiscardedBuffers*/2); +} + TEST_F(SurfaceTest, TestGetLastDequeueStartTime) { sp<ANativeWindow> anw(mSurface); ASSERT_EQ(NO_ERROR, native_window_api_connect(anw.get(), NATIVE_WINDOW_API_CPU)); @@ -491,7 +617,7 @@ TEST_F(SurfaceTest, TestGetLastDequeueStartTime) { anw->dequeueBuffer(anw.get(), &buffer, &fenceFd); nsecs_t after = systemTime(CLOCK_MONOTONIC); - nsecs_t lastDequeueTime = mSurface->getLastDequeueStartTime(); + nsecs_t lastDequeueTime = ANativeWindow_getLastDequeueStartTime(anw.get()); ASSERT_LE(before, lastDequeueTime); ASSERT_GE(after, lastDequeueTime); } @@ -562,6 +688,7 @@ public: const sp<IBinder>& /*applyToken*/, const InputWindowCommands& /*inputWindowCommands*/, int64_t /*desiredPresentTime*/, const client_cache_t& /*cachedBuffer*/, + bool /*hasListenerCallbacks*/, const std::vector<ListenerCallbacks>& /*listenerCallbacks*/) override { } @@ -1746,4 +1873,74 @@ TEST_F(GetFrameTimestampsTest, PresentUnsupportedNoSync) { EXPECT_EQ(-1, outDisplayPresentTime); } +TEST_F(SurfaceTest, DequeueWithConsumerDrivenSize) { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<DummyConsumer> dummyConsumer(new DummyConsumer); + consumer->consumerConnect(dummyConsumer, false); + consumer->setDefaultBufferSize(10, 10); + + sp<Surface> surface = new Surface(producer); + sp<ANativeWindow> window(surface); + native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); + native_window_set_buffers_dimensions(window.get(), 0, 0); + + int fence; + ANativeWindowBuffer* buffer; + + // Buffer size is driven by the consumer + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); + EXPECT_EQ(10, buffer->width); + EXPECT_EQ(10, buffer->height); + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); + + // Buffer size is driven by the consumer + consumer->setDefaultBufferSize(10, 20); + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); + EXPECT_EQ(10, buffer->width); + EXPECT_EQ(20, buffer->height); + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); + + // Transform hint isn't synced to producer before queueBuffer or connect + consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270); + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); + EXPECT_EQ(10, buffer->width); + EXPECT_EQ(20, buffer->height); + ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); + + // Transform hint is synced to producer but no auto prerotation + consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270); + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); + EXPECT_EQ(10, buffer->width); + EXPECT_EQ(20, buffer->height); + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); + + // Prerotation is driven by the consumer with the transform hint used by producer + native_window_set_auto_prerotation(window.get(), true); + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); + EXPECT_EQ(20, buffer->width); + EXPECT_EQ(10, buffer->height); + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); + + // Turn off auto prerotaton + native_window_set_auto_prerotation(window.get(), false); + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); + EXPECT_EQ(10, buffer->width); + EXPECT_EQ(20, buffer->height); + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); + + // Test auto prerotation bit is disabled after disconnect + native_window_set_auto_prerotation(window.get(), true); + native_window_api_disconnect(window.get(), NATIVE_WINDOW_API_CPU); + native_window_api_connect(window.get(), NATIVE_WINDOW_API_CPU); + consumer->setTransformHint(NATIVE_WINDOW_TRANSFORM_ROT_270); + native_window_set_buffers_dimensions(window.get(), 0, 0); + ASSERT_EQ(NO_ERROR, window->dequeueBuffer(window.get(), &buffer, &fence)); + EXPECT_EQ(10, buffer->width); + EXPECT_EQ(20, buffer->height); + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); +} + } // namespace android diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 2d788119cd..8efaf3d90b 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -48,6 +48,7 @@ cc_library { "InputTransport.cpp", "InputWindow.cpp", "ISetInputWindowsListener.cpp", + "LatencyStatistics.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", ], @@ -55,7 +56,7 @@ cc_library { shared_libs: [ "libutils", "libbinder", - "libui" + "libui", ], sanitize: { diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index d6a73bfd27..8ec51653a8 100644 --- a/libs/input/IInputFlinger.cpp +++ b/libs/input/IInputFlinger.cpp @@ -45,16 +45,6 @@ public: IBinder::FLAG_ONEWAY); } - virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) { - Parcel data, reply; - data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); - - data.writeStrongBinder(fromToken); - data.writeStrongBinder(toToken); - remote()->transact(BnInputFlinger::TRANSFER_TOUCH_FOCUS, data, &reply, - IBinder::FLAG_ONEWAY); - } - virtual void registerInputChannel(const sp<InputChannel>& channel) { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); @@ -92,25 +82,16 @@ status_t BnInputFlinger::onTransact( } case REGISTER_INPUT_CHANNEL_TRANSACTION: { CHECK_INTERFACE(IInputFlinger, data, reply); - sp<InputChannel> channel = new InputChannel(); - channel->read(data); + sp<InputChannel> channel = InputChannel::read(data); registerInputChannel(channel); break; } case UNREGISTER_INPUT_CHANNEL_TRANSACTION: { CHECK_INTERFACE(IInputFlinger, data, reply); - sp<InputChannel> channel = new InputChannel(); - channel->read(data); + sp<InputChannel> channel = InputChannel::read(data); unregisterInputChannel(channel); break; } - case TRANSFER_TOUCH_FOCUS: { - CHECK_INTERFACE(IInputFlinger, data, reply); - sp<IBinder> fromToken = data.readStrongBinder(); - sp<IBinder> toToken = data.readStrongBinder(); - transferTouchFocus(fromToken, toToken); - break; - } default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 9fd25f9cb7..34b305e548 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "Input" //#define LOG_NDEBUG 0 -#include <math.h> #include <limits.h> #include <input/Input.h> @@ -235,26 +234,14 @@ void PointerProperties::copyFrom(const PointerProperties& other) { // --- MotionEvent --- -void MotionEvent::initialize( - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - size_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) { +void MotionEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, + int32_t metaState, int32_t buttonState, + MotionClassification classification, float xOffset, float yOffset, + float xPrecision, float yPrecision, float rawXCursorPosition, + float rawYCursorPosition, nsecs_t downTime, nsecs_t eventTime, + size_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords) { InputEvent::initialize(deviceId, source, displayId); mAction = action; mActionButton = actionButton; @@ -267,6 +254,8 @@ void MotionEvent::initialize( mYOffset = yOffset; mXPrecision = xPrecision; mYPrecision = yPrecision; + mRawXCursorPosition = rawXCursorPosition; + mRawYCursorPosition = rawYCursorPosition; mDownTime = downTime; mPointerProperties.clear(); mPointerProperties.appendArray(pointerProperties, pointerCount); @@ -288,6 +277,8 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mYOffset = other->mYOffset; mXPrecision = other->mXPrecision; mYPrecision = other->mYPrecision; + mRawXCursorPosition = other->mRawXCursorPosition; + mRawYCursorPosition = other->mRawYCursorPosition; mDownTime = other->mDownTime; mPointerProperties = other->mPointerProperties; @@ -312,6 +303,21 @@ void MotionEvent::addSample( mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } +float MotionEvent::getXCursorPosition() const { + const float rawX = getRawXCursorPosition(); + return rawX + mXOffset; +} + +float MotionEvent::getYCursorPosition() const { + const float rawY = getRawYCursorPosition(); + return rawY + mYOffset; +} + +void MotionEvent::setCursorPosition(float x, float y) { + mRawXCursorPosition = x - mXOffset; + mRawYCursorPosition = y - mYOffset; +} + const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; } @@ -431,6 +437,15 @@ void MotionEvent::transform(const float matrix[9]) { float originX, originY; transformPoint(matrix, 0, 0, &originX, &originY); + // Apply the transformation to cursor position. + if (isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition)) { + float x = mRawXCursorPosition + oldXOffset; + float y = mRawYCursorPosition + oldYOffset; + transformPoint(matrix, x, y, &x, &y); + mRawXCursorPosition = x - mXOffset; + mRawYCursorPosition = y - mYOffset; + } + // Apply the transformation to all samples. size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { @@ -470,6 +485,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mYOffset = parcel->readFloat(); mXPrecision = parcel->readFloat(); mYPrecision = parcel->readFloat(); + mRawXCursorPosition = parcel->readFloat(); + mRawYCursorPosition = parcel->readFloat(); mDownTime = parcel->readInt64(); mPointerProperties.clear(); @@ -521,6 +538,8 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeFloat(mYOffset); parcel->writeFloat(mXPrecision); parcel->writeFloat(mYPrecision); + parcel->writeFloat(mRawXCursorPosition); + parcel->writeFloat(mRawYCursorPosition); parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index d02cb8ea46..366c93cf1f 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -191,6 +191,10 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.xPrecision = body.motion.xPrecision; // float yPrecision msg->body.motion.yPrecision = body.motion.yPrecision; + // float xCursorPosition + msg->body.motion.xCursorPosition = body.motion.xCursorPosition; + // float yCursorPosition + msg->body.motion.yCursorPosition = body.motion.yCursorPosition; // uint32_t pointerCount msg->body.motion.pointerCount = body.motion.pointerCount; //struct Pointer pointers[MAX_POINTERS] @@ -222,35 +226,28 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { // --- InputChannel --- -InputChannel::InputChannel(const std::string& name, int fd) : - mName(name) { +sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd) { + const int result = fcntl(fd, F_SETFL, O_NONBLOCK); + if (result != 0) { + LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(), + strerror(errno)); + return nullptr; + } + return new InputChannel(name, std::move(fd)); +} + +InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd) + : mName(name), mFd(std::move(fd)) { #if DEBUG_CHANNEL_LIFECYCLE ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), fd); #endif - - setFd(fd); } InputChannel::~InputChannel() { #if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel destroyed: name='%s', fd=%d", - mName.c_str(), mFd); + ALOGD("Input channel destroyed: name='%s', fd=%d", mName.c_str(), mFd.get()); #endif - - ::close(mFd); -} - -void InputChannel::setFd(int fd) { - if (mFd > 0) { - ::close(mFd); - } - mFd = fd; - if (mFd > 0) { - int result = fcntl(mFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " - "non-blocking. errno=%d", mName.c_str(), errno); - } } status_t InputChannel::openInputChannelPair(const std::string& name, @@ -271,13 +268,13 @@ status_t InputChannel::openInputChannelPair(const std::string& name, setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); - std::string serverChannelName = name; - serverChannelName += " (server)"; - outServerChannel = new InputChannel(serverChannelName, sockets[0]); + std::string serverChannelName = name + " (server)"; + android::base::unique_fd serverFd(sockets[0]); + outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd)); - std::string clientChannelName = name; - clientChannelName += " (client)"; - outClientChannel = new InputChannel(clientChannelName, sockets[1]); + std::string clientChannelName = name + " (client)"; + android::base::unique_fd clientFd(sockets[1]); + outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd)); return OK; } @@ -287,7 +284,7 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { - nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { @@ -322,7 +319,7 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { - nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT); + nRead = ::recv(mFd.get(), msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { @@ -360,39 +357,51 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { } sp<InputChannel> InputChannel::dup() const { - int fd = ::dup(getFd()); - return fd >= 0 ? new InputChannel(getName(), fd) : nullptr; + android::base::unique_fd newFd(::dup(getFd())); + if (!newFd.ok()) { + ALOGE("Could not duplicate fd %i for channel %s: %s", getFd(), mName.c_str(), + strerror(errno)); + const bool hitFdLimit = errno == EMFILE || errno == ENFILE; + // If this process is out of file descriptors, then throwing that might end up exploding + // on the other side of a binder call, which isn't really helpful. + // Better to just crash here and hope that the FD leak is slow. + // Other failures could be client errors, so we still propagate those back to the caller. + LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s", + getName().c_str()); + return nullptr; + } + return InputChannel::create(mName, std::move(newFd)); } - status_t InputChannel::write(Parcel& out) const { - status_t s = out.writeString8(String8(getName().c_str())); - + status_t s = out.writeCString(getName().c_str()); if (s != OK) { return s; } + s = out.writeStrongBinder(mToken); if (s != OK) { return s; } - s = out.writeDupFileDescriptor(getFd()); - + s = out.writeUniqueFileDescriptor(mFd); return s; } -status_t InputChannel::read(const Parcel& from) { - mName = from.readString8(); - mToken = from.readStrongBinder(); - - int rawFd = from.readFileDescriptor(); - setFd(::dup(rawFd)); - - if (mFd < 0) { - return BAD_VALUE; +sp<InputChannel> InputChannel::read(const Parcel& from) { + std::string name = from.readCString(); + sp<IBinder> token = from.readStrongBinder(); + android::base::unique_fd rawFd; + status_t fdResult = from.readUniqueFileDescriptor(&rawFd); + if (fdResult != OK) { + return nullptr; } - return OK; + sp<InputChannel> channel = InputChannel::create(name, std::move(rawFd)); + if (channel != nullptr) { + channel->setToken(token); + } + return channel; } sp<IBinder> InputChannel::getToken() const { @@ -465,26 +474,12 @@ status_t InputPublisher::publishKeyEvent( } status_t InputPublisher::publishMotionEvent( - uint32_t seq, - int32_t deviceId, - int32_t source, - int32_t displayId, - int32_t action, - int32_t actionButton, - int32_t flags, - int32_t edgeFlags, - int32_t metaState, - int32_t buttonState, - MotionClassification classification, - float xOffset, - float yOffset, - float xPrecision, - float yPrecision, - nsecs_t downTime, - nsecs_t eventTime, - uint32_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) { + uint32_t seq, int32_t deviceId, int32_t source, int32_t displayId, int32_t action, + int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, + int32_t buttonState, MotionClassification classification, float xOffset, float yOffset, + float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { std::string message = StringPrintf( "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")", @@ -532,6 +527,8 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.yOffset = yOffset; msg.body.motion.xPrecision = xPrecision; msg.body.motion.yPrecision = yPrecision; + msg.body.motion.xCursorPosition = xCursorPosition; + msg.body.motion.yCursorPosition = yCursorPosition; msg.body.motion.downTime = downTime; msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; @@ -539,6 +536,7 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } + return mChannel->sendMessage(&msg); } @@ -1135,26 +1133,16 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); } - event->initialize( - msg->body.motion.deviceId, - msg->body.motion.source, - msg->body.motion.displayId, - msg->body.motion.action, - msg->body.motion.actionButton, - msg->body.motion.flags, - msg->body.motion.edgeFlags, - msg->body.motion.metaState, - msg->body.motion.buttonState, - msg->body.motion.classification, - msg->body.motion.xOffset, - msg->body.motion.yOffset, - msg->body.motion.xPrecision, - msg->body.motion.yPrecision, - msg->body.motion.downTime, - msg->body.motion.eventTime, - pointerCount, - pointerProperties, - pointerCoords); + event->initialize(msg->body.motion.deviceId, msg->body.motion.source, + msg->body.motion.displayId, msg->body.motion.action, + msg->body.motion.actionButton, msg->body.motion.flags, + msg->body.motion.edgeFlags, msg->body.motion.metaState, + msg->body.motion.buttonState, msg->body.motion.classification, + msg->body.motion.xOffset, msg->body.motion.yOffset, + msg->body.motion.xPrecision, msg->body.motion.yPrecision, + msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, + msg->body.motion.downTime, msg->body.motion.eventTime, pointerCount, + pointerProperties, pointerCoords); } void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index 0c22bfefed..56900c129e 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -38,29 +38,29 @@ KeyMap::KeyMap() { KeyMap::~KeyMap() { } -status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, +status_t KeyMap::load(const InputDeviceIdentifier& deviceIdentifier, const PropertyMap* deviceConfiguration) { // Use the configured key layout if available. if (deviceConfiguration) { String8 keyLayoutName; if (deviceConfiguration->tryGetProperty(String8("keyboard.layout"), keyLayoutName)) { - status_t status = loadKeyLayout(deviceIdenfifier, keyLayoutName.c_str()); + status_t status = loadKeyLayout(deviceIdentifier, keyLayoutName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard layout '%s' but " "it was not found.", - deviceIdenfifier.name.c_str(), keyLayoutName.string()); + deviceIdentifier.name.c_str(), keyLayoutName.string()); } } String8 keyCharacterMapName; if (deviceConfiguration->tryGetProperty(String8("keyboard.characterMap"), keyCharacterMapName)) { - status_t status = loadKeyCharacterMap(deviceIdenfifier, keyCharacterMapName.c_str()); + status_t status = loadKeyCharacterMap(deviceIdentifier, keyCharacterMapName.c_str()); if (status == NAME_NOT_FOUND) { ALOGE("Configuration for keyboard device '%s' requested keyboard character " "map '%s' but it was not found.", - deviceIdenfifier.name.c_str(), keyLayoutName.string()); + deviceIdentifier.name.c_str(), keyCharacterMapName.string()); } } @@ -70,25 +70,25 @@ status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, } // Try searching by device identifier. - if (probeKeyMap(deviceIdenfifier, "")) { + if (probeKeyMap(deviceIdentifier, "")) { return OK; } // Fall back on the Generic key map. // TODO Apply some additional heuristics here to figure out what kind of // generic key map to use (US English, etc.) for typical external keyboards. - if (probeKeyMap(deviceIdenfifier, "Generic")) { + if (probeKeyMap(deviceIdentifier, "Generic")) { return OK; } // Try the Virtual key map as a last resort. - if (probeKeyMap(deviceIdenfifier, "Virtual")) { + if (probeKeyMap(deviceIdentifier, "Virtual")) { return OK; } // Give up! ALOGE("Could not determine key map for device '%s' and no default key maps were found!", - deviceIdenfifier.name.c_str()); + deviceIdentifier.name.c_str()); return NAME_NOT_FOUND; } diff --git a/libs/input/LatencyStatistics.cpp b/libs/input/LatencyStatistics.cpp new file mode 100644 index 0000000000..394da22a62 --- /dev/null +++ b/libs/input/LatencyStatistics.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2019 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 <input/LatencyStatistics.h> + +#include <android-base/chrono_utils.h> + +#include <cmath> +#include <limits> + +namespace android { + +LatencyStatistics::LatencyStatistics(std::chrono::seconds period) : mReportPeriod(period) { + reset(); +} + +/** + * Add a raw value to the statistics + */ +void LatencyStatistics::addValue(float value) { + if (value < mMin) { + mMin = value; + } + if (value > mMax) { + mMax = value; + } + mSum += value; + mSum2 += value * value; + mCount++; +} + +/** + * Get the mean. Should not be called if no samples have been added. + */ +float LatencyStatistics::getMean() { + return mSum / mCount; +} + +/** + * Get the standard deviation. Should not be called if no samples have been added. + */ +float LatencyStatistics::getStDev() { + float mean = getMean(); + return sqrt(mSum2 / mCount - mean * mean); +} + +float LatencyStatistics::getMin() { + return mMin; +} + +float LatencyStatistics::getMax() { + return mMax; +} + +size_t LatencyStatistics::getCount() { + return mCount; +} + +/** + * Reset internal state. The variable 'when' is the time when the data collection started. + * Call this to start a new data collection window. + */ +void LatencyStatistics::reset() { + mMax = std::numeric_limits<float>::lowest(); + mMin = std::numeric_limits<float>::max(); + mSum = 0; + mSum2 = 0; + mCount = 0; + mLastReportTime = std::chrono::steady_clock::now(); +} + +bool LatencyStatistics::shouldReport() { + std::chrono::duration timeSinceReport = std::chrono::steady_clock::now() - mLastReportTime; + return mCount != 0 && timeSinceReport >= mReportPeriod; +} + +} // namespace android diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index ade931e01a..c1c35e1b89 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -7,6 +7,7 @@ cc_test { "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", "InputWindow_test.cpp", + "LatencyStatistics_test.cpp", "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", ], diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index f1675c0d36..af74edd65d 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -22,11 +22,12 @@ #include <time.h> #include <errno.h> +#include <binder/Binder.h> #include <gtest/gtest.h> #include <input/InputTransport.h> -#include <utils/Timers.h> #include <utils/StopWatch.h> #include <utils/StrongPointer.h> +#include <utils/Timers.h> namespace android { @@ -43,20 +44,28 @@ TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptor // of a pipe and to check for EPIPE on the other end after the channel is destroyed. Pipe pipe; - sp<InputChannel> inputChannel = new InputChannel("channel name", pipe.sendFd); + android::base::unique_fd sendFd(pipe.sendFd); + + sp<InputChannel> inputChannel = InputChannel::create("channel name", std::move(sendFd)); + EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created"; EXPECT_STREQ("channel name", inputChannel->getName().c_str()) << "channel should have provided name"; - EXPECT_EQ(pipe.sendFd, inputChannel->getFd()) - << "channel should have provided fd"; + EXPECT_NE(-1, inputChannel->getFd()) << "channel should have valid fd"; - inputChannel.clear(); // destroys input channel + // InputChannel should be the owner of the file descriptor now + ASSERT_FALSE(sendFd.ok()); +} - EXPECT_EQ(-EPIPE, pipe.readSignal()) - << "channel should have closed fd when destroyed"; +TEST_F(InputChannelTest, SetAndGetToken) { + Pipe pipe; + sp<InputChannel> channel = + InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd)); + EXPECT_EQ(channel->getToken(), nullptr); - // clean up fds of Pipe endpoints that were closed so we don't try to close them again - pipe.sendFd = -1; + sp<IBinder> token = new BBinder(); + channel->setToken(token); + EXPECT_EQ(token, channel->getToken()); } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 2b75c82bb1..b90857c99c 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -255,11 +255,11 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); event->initialize(2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, 0, - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, - AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, - MotionClassification::NONE, X_OFFSET, Y_OFFSET, 2.0f, 2.1f, - ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, - 2, pointerProperties, pointerCoords); + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, + AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, + X_OFFSET, Y_OFFSET, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, ARBITRARY_DOWN_TIME, + ARBITRARY_EVENT_TIME, 2, pointerProperties, pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); @@ -571,10 +571,11 @@ TEST_F(MotionEventTest, Transform) { } MotionEvent event; event.initialize(0 /*deviceId*/, AINPUT_SOURCE_UNKNOWN, DISPLAY_ID, AMOTION_EVENT_ACTION_MOVE, - 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, - AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, - 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); + 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, 0 /*yOffset*/, + 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, + 2 /*yCursorPosition*/, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, + pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -602,6 +603,14 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(tanf(angle), tanf(event.getOrientation(i)), 0.1); } + // Check cursor positions. The original cursor position is at (3 + RADIUS, 2), where the center + // of the circle is (3, 2), so the cursor position is to the right of the center of the circle. + // The choice of triangular functions in this test defines the angle of rotation clockwise + // relative to the y-axis. Therefore the cursor position's angle is 90 degrees. Here we swap the + // triangular function so that we don't have to add the 90 degrees. + ASSERT_NEAR(cosf(PI_180 * ROTATION) * RADIUS, event.getXCursorPosition(), 0.001); + ASSERT_NEAR(sinf(PI_180 * ROTATION) * RADIUS, event.getYCursorPosition(), 0.001); + // Applying the transformation should preserve the raw X and Y of the first point. ASSERT_NEAR(originalRawX, event.getRawX(0), 0.001); ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); @@ -626,11 +635,44 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { for (MotionClassification classification : classifications) { event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, - classification, 0, 0, 0, 0, 0 /*downTime*/, 0 /*eventTime*/, - pointerCount, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + 0, classification, 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, 0 /*eventTime*/, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); } } +TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { + MotionEvent event; + constexpr size_t pointerCount = 1; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerCoords[i].clear(); + } + + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, + 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, 0, + 0, 0, 0, 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, 0 /*downTime*/, + 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); + event.offsetLocation(20, 60); + ASSERT_EQ(280, event.getRawXCursorPosition()); + ASSERT_EQ(540, event.getRawYCursorPosition()); + ASSERT_EQ(300, event.getXCursorPosition()); + ASSERT_EQ(600, event.getYCursorPosition()); +} + +TEST_F(MotionEventTest, SetCursorPosition) { + MotionEvent event; + initializeEventWithHistory(&event); + event.setSource(AINPUT_SOURCE_MOUSE); + + event.setCursorPosition(3, 4); + ASSERT_EQ(3, event.getXCursorPosition()); + ASSERT_EQ(4, event.getYCursorPosition()); +} + } // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index f2cd1be33f..a362f3281d 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -146,6 +146,8 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { constexpr float yOffset = -20; constexpr float xPrecision = 0.25; constexpr float yPrecision = 0.5; + constexpr float xCursorPosition = 1.3; + constexpr float yCursorPosition = 50.6; constexpr nsecs_t downTime = 3; constexpr size_t pointerCount = 3; constexpr nsecs_t eventTime = 4; @@ -168,10 +170,12 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); } - status = mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, - flags, edgeFlags, metaState, buttonState, classification, - xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount, - pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(seq, deviceId, source, displayId, action, actionButton, + flags, edgeFlags, metaState, buttonState, classification, + xOffset, yOffset, xPrecision, yPrecision, + xCursorPosition, yCursorPosition, downTime, eventTime, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -199,6 +203,10 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(classification, motionEvent->getClassification()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); + EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition()); + EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); + EXPECT_EQ(xCursorPosition + xOffset, motionEvent->getXCursorPosition()); + EXPECT_EQ(yCursorPosition + yOffset, motionEvent->getYCursorPosition()); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); @@ -266,9 +274,11 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -279,9 +289,11 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerProperties pointerProperties[pointerCount]; PointerCoords pointerCoords[pointerCount]; - status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -297,9 +309,11 @@ TEST_F(InputPublisherAndConsumerTest, pointerCoords[i].clear(); } - status = mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - MotionClassification::NONE, 0, 0, 0, 0, 0, 0, - pointerCount, pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, MotionClassification::NONE, + 0, 0, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, 0, + pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } diff --git a/libs/input/tests/LatencyStatistics_test.cpp b/libs/input/tests/LatencyStatistics_test.cpp new file mode 100644 index 0000000000..eb12d4ef6f --- /dev/null +++ b/libs/input/tests/LatencyStatistics_test.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <input/LatencyStatistics.h> +#include <cmath> +#include <limits> +#include <thread> + +namespace android { +namespace test { + +TEST(LatencyStatisticsTest, ResetStats) { + LatencyStatistics stats{5min}; + stats.addValue(5.0); + stats.addValue(19.3); + stats.addValue(20); + stats.reset(); + + ASSERT_EQ(stats.getCount(), 0u); + ASSERT_EQ(std::isnan(stats.getStDev()), true); + ASSERT_EQ(std::isnan(stats.getMean()), true); +} + +TEST(LatencyStatisticsTest, AddStatsValue) { + LatencyStatistics stats{5min}; + stats.addValue(5.0); + + ASSERT_EQ(stats.getMin(), 5.0); + ASSERT_EQ(stats.getMax(), 5.0); + ASSERT_EQ(stats.getCount(), 1u); + ASSERT_EQ(stats.getMean(), 5.0); + ASSERT_EQ(stats.getStDev(), 0.0); +} + +TEST(LatencyStatisticsTest, AddMultipleStatsValue) { + LatencyStatistics stats{5min}; + stats.addValue(4.0); + stats.addValue(6.0); + stats.addValue(8.0); + stats.addValue(10.0); + + float stdev = stats.getStDev(); + + ASSERT_EQ(stats.getMin(), 4.0); + ASSERT_EQ(stats.getMax(), 10.0); + ASSERT_EQ(stats.getCount(), 4u); + ASSERT_EQ(stats.getMean(), 7.0); + ASSERT_EQ(stdev * stdev, 5.0); +} + +TEST(LatencyStatisticsTest, ShouldReportStats) { + LatencyStatistics stats{0min}; + stats.addValue(5.0); + + std::this_thread::sleep_for(1us); + + ASSERT_EQ(stats.shouldReport(), true); +} + +} // namespace test +} // namespace android
\ No newline at end of file diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 62023fb328..8d8cf06c91 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -64,8 +64,10 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, yOffset, 68); CHECK_OFFSET(InputMessage::Body::Motion, xPrecision, 72); CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 80); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 88); + CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 80); + CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 84); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96); CHECK_OFFSET(InputMessage::Body::Finished, seq, 0); CHECK_OFFSET(InputMessage::Body::Finished, handled, 4); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 368446ff4d..968e2fa6bc 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -176,12 +176,13 @@ static std::vector<MotionEvent> createMotionEventStream( EXPECT_EQ(pointerIndex, pointerCount); MotionEvent event; - event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, - action, 0 /*actionButton*/, 0 /*flags*/, - AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, - MotionClassification::NONE, - 0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, - 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); + event.initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, action, + 0 /*actionButton*/, 0 /*flags*/, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, + 0 /*buttonState*/, MotionClassification::NONE, 0 /*xOffset*/, + 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0 /*downTime*/, + entry.eventTime.count(), pointerCount, properties, coords); events.emplace_back(event); } diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index 8435dac636..0ba01f4da4 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -33,6 +33,12 @@ static int32_t query(ANativeWindow* window, int what) { return res < 0 ? res : value; } +static int64_t query64(ANativeWindow* window, int what) { + int64_t value; + int res = window->perform(window, what, &value); + return res < 0 ? res : value; +} + static bool isDataSpaceValid(ANativeWindow* window, int32_t dataSpace) { bool supported = false; switch (dataSpace) { @@ -262,3 +268,27 @@ int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMo int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh) { return native_window_set_auto_refresh(window, autoRefresh); } + +int ANativeWindow_setAutoPrerotation(ANativeWindow* window, bool autoPrerotation) { + return native_window_set_auto_prerotation(window, autoPrerotation); +} + +/************************************************************************************************** + * apex-stable + **************************************************************************************************/ + +int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window) { + return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION); +} + +int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window) { + return query64(window, NATIVE_WINDOW_GET_LAST_QUEUE_DURATION); +} + +int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window) { + return query64(window, NATIVE_WINDOW_GET_LAST_DEQUEUE_START); +} + +int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout) { + return window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, timeout); +} diff --git a/libs/nativewindow/TEST_MAPPING b/libs/nativewindow/TEST_MAPPING new file mode 100644 index 0000000000..3d7f3c28f4 --- /dev/null +++ b/libs/nativewindow/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libnativewindow_test" + } + ] +} diff --git a/libs/nativewindow/include/apex/window.h b/libs/nativewindow/include/apex/window.h new file mode 100644 index 0000000000..869b22ec19 --- /dev/null +++ b/libs/nativewindow/include/apex/window.h @@ -0,0 +1,60 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include <nativebase/nativebase.h> + +// apex is a superset of the NDK +#include <android/native_window.h> + +__BEGIN_DECLS + +/** + * Retrieves how long it took for the last time a buffer was dequeued. + * + * \return a negative value on error, otherwise returns the duration in + * nanoseconds + */ +int64_t ANativeWindow_getLastDequeueDuration(ANativeWindow* window); + +/** + * Retrieves how long it took for the last time a buffer was queued. + * + * \return a negative value on error, otherwise returns the duration in + * nanoseconds. + */ +int64_t ANativeWindow_getLastQueueDuration(ANativeWindow* window); + +/** + * Retrieves the system time in nanoseconds when the last time a buffer + * was dequeued. + * + * \return a negative value on error, otherwise returns the duration in + * nanoseconds. + */ +int64_t ANativeWindow_getLastDequeueStartTime(ANativeWindow* window); + +/** + * Sets a timeout in nanoseconds for dequeue calls. All subsequent dequeue calls + * made by the window will return -ETIMEDOUT after the timeout if the dequeue + * takes too long. + * + * \return NO_ERROR on succes, -errno on error. + */ +int ANativeWindow_setDequeueTimeout(ANativeWindow* window, int64_t timeout); + +__END_DECLS diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index 61590e0196..1814ab5568 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -41,7 +41,8 @@ #include <unistd.h> #include <stdbool.h> -// system/window.h is a superset of the vndk +// system/window.h is a superset of the vndk and apex apis +#include <apex/window.h> #include <vndk/window.h> @@ -62,9 +63,9 @@ __BEGIN_DECLS /* attributes queriable with query() */ enum { - NATIVE_WINDOW_WIDTH = 0, - NATIVE_WINDOW_HEIGHT = 1, - NATIVE_WINDOW_FORMAT = 2, + NATIVE_WINDOW_WIDTH = 0, + NATIVE_WINDOW_HEIGHT = 1, + NATIVE_WINDOW_FORMAT = 2, /* see ANativeWindowQuery in vndk/window.h */ NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS = ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS, @@ -92,7 +93,6 @@ enum { */ NATIVE_WINDOW_CONCRETE_TYPE = 5, - /* * Default width and height of ANativeWindow buffers, these are the * dimensions of the window buffers irrespective of the @@ -147,11 +147,15 @@ enum { /* * Returns the duration of the last dequeueBuffer call in microseconds + * Deprecated: please use NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION in + * perform() instead, which supports nanosecond precision. */ NATIVE_WINDOW_LAST_DEQUEUE_DURATION = 14, /* * Returns the duration of the last queueBuffer call in microseconds + * Deprecated: please use NATIVE_WINDOW_GET_LAST_QUEUE_DURATION in + * perform() instead, which supports nanosecond precision. */ NATIVE_WINDOW_LAST_QUEUE_DURATION = 15, @@ -203,41 +207,46 @@ enum { */ enum { // clang-format off - NATIVE_WINDOW_SET_USAGE = 0, /* deprecated */ - NATIVE_WINDOW_CONNECT = 1, /* deprecated */ - NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */ - NATIVE_WINDOW_SET_CROP = 3, /* private */ - NATIVE_WINDOW_SET_BUFFER_COUNT = 4, - NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */ - NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6, - NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7, - NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8, - NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9, - NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */ - NATIVE_WINDOW_LOCK = 11, /* private */ - NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */ - NATIVE_WINDOW_API_CONNECT = 13, /* private */ - NATIVE_WINDOW_API_DISCONNECT = 14, /* private */ - NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */ - NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* deprecated, unimplemented */ - NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17, /* private */ - NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18, - NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19, - NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */ - NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21, - NATIVE_WINDOW_SET_AUTO_REFRESH = 22, - NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION = 23, - NATIVE_WINDOW_GET_NEXT_FRAME_ID = 24, - NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25, - NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26, - NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27, - NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28, - NATIVE_WINDOW_GET_HDR_SUPPORT = 29, - NATIVE_WINDOW_SET_USAGE64 = 30, - NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31, - NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32, - NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33, + NATIVE_WINDOW_SET_USAGE = 0, /* deprecated */ + NATIVE_WINDOW_CONNECT = 1, /* deprecated */ + NATIVE_WINDOW_DISCONNECT = 2, /* deprecated */ + NATIVE_WINDOW_SET_CROP = 3, /* private */ + NATIVE_WINDOW_SET_BUFFER_COUNT = 4, + NATIVE_WINDOW_SET_BUFFERS_GEOMETRY = 5, /* deprecated */ + NATIVE_WINDOW_SET_BUFFERS_TRANSFORM = 6, + NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP = 7, + NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS = 8, + NATIVE_WINDOW_SET_BUFFERS_FORMAT = 9, + NATIVE_WINDOW_SET_SCALING_MODE = 10, /* private */ + NATIVE_WINDOW_LOCK = 11, /* private */ + NATIVE_WINDOW_UNLOCK_AND_POST = 12, /* private */ + NATIVE_WINDOW_API_CONNECT = 13, /* private */ + NATIVE_WINDOW_API_DISCONNECT = 14, /* private */ + NATIVE_WINDOW_SET_BUFFERS_USER_DIMENSIONS = 15, /* private */ + NATIVE_WINDOW_SET_POST_TRANSFORM_CROP = 16, /* deprecated, unimplemented */ + NATIVE_WINDOW_SET_BUFFERS_STICKY_TRANSFORM = 17, /* private */ + NATIVE_WINDOW_SET_SIDEBAND_STREAM = 18, + NATIVE_WINDOW_SET_BUFFERS_DATASPACE = 19, + NATIVE_WINDOW_SET_SURFACE_DAMAGE = 20, /* private */ + NATIVE_WINDOW_SET_SHARED_BUFFER_MODE = 21, + NATIVE_WINDOW_SET_AUTO_REFRESH = 22, + NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION = 23, + NATIVE_WINDOW_GET_NEXT_FRAME_ID = 24, + NATIVE_WINDOW_ENABLE_FRAME_TIMESTAMPS = 25, + NATIVE_WINDOW_GET_COMPOSITOR_TIMING = 26, + NATIVE_WINDOW_GET_FRAME_TIMESTAMPS = 27, + NATIVE_WINDOW_GET_WIDE_COLOR_SUPPORT = 28, + NATIVE_WINDOW_GET_HDR_SUPPORT = 29, + NATIVE_WINDOW_SET_USAGE64 = 30, + NATIVE_WINDOW_GET_CONSUMER_USAGE64 = 31, + NATIVE_WINDOW_SET_BUFFERS_SMPTE2086_METADATA = 32, + NATIVE_WINDOW_SET_BUFFERS_CTA861_3_METADATA = 33, NATIVE_WINDOW_SET_BUFFERS_HDR10_PLUS_METADATA = 34, + NATIVE_WINDOW_SET_AUTO_PREROTATION = 35, + NATIVE_WINDOW_GET_LAST_DEQUEUE_START = 36, /* private */ + NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT = 37, /* private */ + NATIVE_WINDOW_GET_LAST_DEQUEUE_DURATION = 38, /* private */ + NATIVE_WINDOW_GET_LAST_QUEUE_DURATION = 39, /* private */ // clang-format on }; @@ -985,4 +994,18 @@ static inline int native_window_get_consumer_usage(struct ANativeWindow* window, return window->perform(window, NATIVE_WINDOW_GET_CONSUMER_USAGE64, outUsage); } +/* + * native_window_set_auto_prerotation(..., autoPrerotation) + * Enable/disable the auto prerotation at buffer allocation when the buffer size + * is driven by the consumer. + * + * When buffer size is driven by the consumer and the transform hint specifies + * a 90 or 270 degree rotation, if auto prerotation is enabled, the width and + * height used for dequeueBuffer will be additionally swapped. + */ +static inline int native_window_set_auto_prerotation(struct ANativeWindow* window, + bool autoPrerotation) { + return window->perform(window, NATIVE_WINDOW_SET_AUTO_PREROTATION, autoPrerotation); +} + __END_DECLS diff --git a/libs/nativewindow/include/vndk/window.h b/libs/nativewindow/include/vndk/window.h index 995ba44d20..500052c936 100644 --- a/libs/nativewindow/include/vndk/window.h +++ b/libs/nativewindow/include/vndk/window.h @@ -316,6 +316,15 @@ int ANativeWindow_setSharedBufferMode(ANativeWindow* window, bool sharedBufferMo */ int ANativeWindow_setAutoRefresh(ANativeWindow* window, bool autoRefresh); +/* + * Enable/disable the auto prerotation at buffer allocation when the buffer size + * is driven by the consumer. + * + * When buffer size is driven by the consumer and the transform hint specifies + * a 90 or 270 degree rotation, if auto prerotation is enabled, the width and + * height used for dequeueBuffer will be additionally swapped. + */ +int ANativeWindow_setAutoPrerotation(ANativeWindow* window, bool autoPrerotation); /*****************************************************************************/ diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index bad8b11540..b741faa11f 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -22,12 +22,16 @@ LIBNATIVEWINDOW { ANativeWindow_getBuffersDataSpace; # introduced=28 ANativeWindow_getFormat; ANativeWindow_getHeight; + ANativeWindow_getLastDequeueDuration; # apex # introduced=30 + ANativeWindow_getLastDequeueStartTime; # apex # introduced=30 + ANativeWindow_getLastQueueDuration; # apex # introduced=30 ANativeWindow_getWidth; ANativeWindow_lock; ANativeWindow_query; # vndk ANativeWindow_queryf; # vndk ANativeWindow_queueBuffer; # vndk ANativeWindow_release; + ANativeWindow_setAutoPrerotation; # vndk ANativeWindow_setAutoRefresh; # vndk ANativeWindow_setBufferCount; # vndk ANativeWindow_setBuffersDataSpace; # introduced=28 @@ -36,6 +40,7 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersGeometry; ANativeWindow_setBuffersTimestamp; # vndk ANativeWindow_setBuffersTransform; + ANativeWindow_setDequeueTimeout; # apex # introduced=30 ANativeWindow_setSharedBufferMode; # vndk ANativeWindow_setSwapInterval; # vndk ANativeWindow_setUsage; # vndk diff --git a/libs/nativewindow/tests/ANativeWindowTest.cpp b/libs/nativewindow/tests/ANativeWindowTest.cpp new file mode 100644 index 0000000000..6cf8291da2 --- /dev/null +++ b/libs/nativewindow/tests/ANativeWindowTest.cpp @@ -0,0 +1,168 @@ +/* + * Copyright 2019 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 "ANativeWindow_test" +//#define LOG_NDEBUG 0 + +#include <gtest/gtest.h> +#include <gui/BufferItemConsumer.h> +#include <gui/BufferQueue.h> +#include <gui/Surface.h> +#include <log/log.h> +#include <sync/sync.h> +// We need to use the private system apis since not everything is visible to +// apexes yet. +#include <system/window.h> + +using namespace android; + +class TestableSurface final : public Surface { +public: + explicit TestableSurface(const sp<IGraphicBufferProducer>& bufferProducer) + : Surface(bufferProducer) {} + + // Exposes the internal last dequeue duration that's stored on the Surface. + nsecs_t getLastDequeueDuration() const { return mLastDequeueDuration; } + + // Exposes the internal last queue duration that's stored on the Surface. + nsecs_t getLastQueueDuration() const { return mLastQueueDuration; } + + // Exposes the internal last dequeue start time that's stored on the Surface. + nsecs_t getLastDequeueStartTime() const { return mLastDequeueStartTime; } +}; + +class ANativeWindowTest : public ::testing::Test { +protected: + void SetUp() override { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGV("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + BufferQueue::createBufferQueue(&mProducer, &mConsumer); + mItemConsumer = new BufferItemConsumer(mConsumer, GRALLOC_USAGE_SW_READ_OFTEN); + mWindow = new TestableSurface(mProducer); + const int success = native_window_api_connect(mWindow.get(), NATIVE_WINDOW_API_CPU); + EXPECT_EQ(0, success); + } + + void TearDown() override { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGV("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + const int success = native_window_api_disconnect(mWindow.get(), NATIVE_WINDOW_API_CPU); + EXPECT_EQ(0, success); + } + sp<IGraphicBufferProducer> mProducer; + sp<IGraphicBufferConsumer> mConsumer; + sp<BufferItemConsumer> mItemConsumer; + + sp<TestableSurface> mWindow; +}; + +TEST_F(ANativeWindowTest, getLastDequeueDuration_noDequeue_returnsZero) { + int result = ANativeWindow_getLastDequeueDuration(mWindow.get()); + EXPECT_EQ(0, result); + EXPECT_EQ(0, mWindow->getLastDequeueDuration()); +} + +TEST_F(ANativeWindowTest, getLastDequeueDuration_withDequeue_returnsTime) { + ANativeWindowBuffer* buffer; + int fd; + int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd); + close(fd); + EXPECT_EQ(0, result); + + result = ANativeWindow_getLastDequeueDuration(mWindow.get()); + EXPECT_GT(result, 0); + EXPECT_EQ(result, mWindow->getLastDequeueDuration()); +} + +TEST_F(ANativeWindowTest, getLastQueueDuration_noDequeue_returnsZero) { + int result = ANativeWindow_getLastQueueDuration(mWindow.get()); + EXPECT_EQ(0, result); + EXPECT_EQ(0, mWindow->getLastQueueDuration()); +} + +TEST_F(ANativeWindowTest, getLastQueueDuration_noQueue_returnsZero) { + ANativeWindowBuffer* buffer; + int fd; + int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd); + close(fd); + EXPECT_EQ(0, result); + + result = ANativeWindow_getLastQueueDuration(mWindow.get()); + EXPECT_EQ(result, 0); + EXPECT_EQ(result, mWindow->getLastQueueDuration()); +} + +TEST_F(ANativeWindowTest, getLastQueueDuration_withQueue_returnsTime) { + ANativeWindowBuffer* buffer; + int fd; + int result = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd); + close(fd); + EXPECT_EQ(0, result); + + result = ANativeWindow_queueBuffer(mWindow.get(), buffer, 0); + + result = ANativeWindow_getLastQueueDuration(mWindow.get()); + EXPECT_GT(result, 0); + EXPECT_EQ(result, mWindow->getLastQueueDuration()); +} + +TEST_F(ANativeWindowTest, getLastDequeueStartTime_noDequeue_returnsZero) { + int64_t result = ANativeWindow_getLastDequeueStartTime(mWindow.get()); + EXPECT_EQ(0, result); + EXPECT_EQ(0, mWindow->getLastQueueDuration()); +} + +TEST_F(ANativeWindowTest, getLastDequeueStartTime_withDequeue_returnsTime) { + ANativeWindowBuffer* buffer; + int fd; + int dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd); + close(fd); + EXPECT_EQ(0, dequeueResult); + + int64_t result = ANativeWindow_getLastDequeueStartTime(mWindow.get()); + EXPECT_GT(result, 0); + EXPECT_EQ(result, mWindow->getLastDequeueStartTime()); +} + +TEST_F(ANativeWindowTest, setDequeueTimeout_causesDequeueTimeout) { + nsecs_t timeout = milliseconds_to_nanoseconds(100); + int result = ANativeWindow_setDequeueTimeout(mWindow.get(), timeout); + EXPECT_EQ(0, result); + + // The two dequeues should not timeout... + ANativeWindowBuffer* buffer; + int fd; + int dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd); + close(fd); + EXPECT_EQ(0, dequeueResult); + int queueResult = ANativeWindow_queueBuffer(mWindow.get(), buffer, -1); + EXPECT_EQ(0, queueResult); + dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd); + close(fd); + EXPECT_EQ(0, dequeueResult); + queueResult = ANativeWindow_queueBuffer(mWindow.get(), buffer, -1); + EXPECT_EQ(0, queueResult); + + // ...but the third one should since the queue depth is too deep. + nsecs_t start = systemTime(SYSTEM_TIME_MONOTONIC); + dequeueResult = ANativeWindow_dequeueBuffer(mWindow.get(), &buffer, &fd); + nsecs_t end = systemTime(SYSTEM_TIME_MONOTONIC); + close(fd); + EXPECT_EQ(TIMED_OUT, dequeueResult); + EXPECT_GE(end - start, timeout); +} diff --git a/libs/nativewindow/tests/Android.bp b/libs/nativewindow/tests/Android.bp index 20071be668..cdb3d2054f 100644 --- a/libs/nativewindow/tests/Android.bp +++ b/libs/nativewindow/tests/Android.bp @@ -15,13 +15,22 @@ // cc_test { - name: "AHardwareBufferTest", + name: "libnativewindow_test", + test_suites: [ + "device-tests", + ], shared_libs: [ + "libgui", + "liblog", "libnativewindow", + "libsync", + "libutils", "android.hardware.graphics.common@1.0", ], srcs: [ "AHardwareBufferTest.cpp", - "c_compatibility.c"], + "ANativeWindowTest.cpp", + "c_compatibility.c", + ], cflags: ["-Wall", "-Werror"], } diff --git a/libs/nativewindow/tests/c_compatibility.c b/libs/nativewindow/tests/c_compatibility.c index befd88fd07..aa9b4f7e78 100644 --- a/libs/nativewindow/tests/c_compatibility.c +++ b/libs/nativewindow/tests/c_compatibility.c @@ -16,6 +16,7 @@ #include <android/hardware_buffer.h> #include <android/native_window.h> +#include <apex/window.h> #include <vndk/hardware_buffer.h> #include <vndk/window.h> diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index cc252d67ae..136ad0da62 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -71,9 +71,6 @@ cc_library_static { "-fvisibility=hidden", "-Werror=format", ], - cppflags: [ - "-fwhole-program-vtables", // requires ThinLTO - ], srcs: [ ":librenderengine_sources", ":librenderengine_gl_sources", diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index d2a7525113..b8cf0ea083 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -457,12 +457,10 @@ Framebuffer* GLESRenderEngine::getFramebufferForDrawing() { } void GLESRenderEngine::primeCache() const { - ProgramCache::getInstance().primeCache(mInProtectedContext ? mProtectedEGLContext : mEGLContext, - mFeatureFlags & USE_COLOR_MANAGEMENT); -} - -bool GLESRenderEngine::isCurrent() const { - return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext(); + ProgramCache::getInstance().primeCache( + mInProtectedContext ? mProtectedEGLContext : mEGLContext, + mFeatureFlags & USE_COLOR_MANAGEMENT, + mFeatureFlags & PRECACHE_TONE_MAPPER_SHADER_ONLY); } base::unique_fd GLESRenderEngine::flush() { @@ -831,11 +829,14 @@ void GLESRenderEngine::handleRoundedCorners(const DisplaySettings& display, drawMesh(mesh); // The middle part of the layer can turn off blending. - const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, bounds.bottom - radius); - setScissor(middleRect); - mState.cornerRadius = 0.0; - disableBlending(); - drawMesh(mesh); + if (topRect.bottom < bottomRect.top) { + const Rect middleRect(bounds.left, bounds.top + radius, bounds.right, + bounds.bottom - radius); + setScissor(middleRect); + mState.cornerRadius = 0.0; + disableBlending(); + drawMesh(mesh); + } disableScissor(); } @@ -855,7 +856,6 @@ status_t GLESRenderEngine::bindFrameBuffer(Framebuffer* framebuffer) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0); uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); - ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d", glStatus); @@ -1076,33 +1076,6 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, return NO_ERROR; } -void GLESRenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, - ui::Transform::orientation_flags rotation) { - setViewportAndProjection(Rect(vpw, vph), sourceCrop); - - if (rotation == ui::Transform::ROT_0) { - return; - } - - // Apply custom rotation to the projection. - float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f; - mat4 m = mState.projectionMatrix; - switch (rotation) { - case ui::Transform::ROT_90: - m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m; - break; - case ui::Transform::ROT_180: - m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m; - break; - case ui::Transform::ROT_270: - m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m; - break; - default: - break; - } - mState.projectionMatrix = m; -} - void GLESRenderEngine::setViewportAndProjection(Rect viewport, Rect clip) { ATRACE_CALL(); mVpWidth = viewport.getWidth(); @@ -1166,14 +1139,6 @@ void GLESRenderEngine::setupLayerTexturing(const Texture& texture) { mState.textureEnabled = true; } -void GLESRenderEngine::setupLayerBlackedOut() { - glBindTexture(GL_TEXTURE_2D, mProtectedTexName); - Texture texture(Texture::TEXTURE_2D, mProtectedTexName); - texture.setDimensions(1, 1); // FIXME: we should get that from somewhere - mState.texture = texture; - mState.textureEnabled = true; -} - void GLESRenderEngine::setColorTransform(const mat4& colorTransform) { mState.colorMatrix = colorTransform; } diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index dd60e50c67..501b0441e3 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -51,7 +51,6 @@ class GLESRenderEngine : public impl::RenderEngine { public: static std::unique_ptr<GLESRenderEngine> create(int hwcFormat, uint32_t featureFlags, uint32_t imageCacheSize); - static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); GLESRenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy, @@ -59,17 +58,7 @@ public: uint32_t imageCacheSize); ~GLESRenderEngine() override EXCLUDES(mRenderingMutex); - std::unique_ptr<Framebuffer> createFramebuffer() override; - std::unique_ptr<Image> createImage() override; - void primeCache() const override; - bool isCurrent() const override; - base::unique_fd flush() override; - bool finish() override; - bool waitFence(base::unique_fd fenceFd) override; - void clearWithColor(float red, float green, float blue, float alpha) override; - void fillRegionWithColor(const Region& region, float red, float green, float blue, - float alpha) override; void genTextures(size_t count, uint32_t* names) override; void deleteTextures(size_t count, uint32_t const* names) override; void bindExternalTextureImage(uint32_t texName, const Image& image) override; @@ -79,7 +68,6 @@ public: void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex); status_t bindFrameBuffer(Framebuffer* framebuffer) override; void unbindFrameBuffer(Framebuffer* framebuffer) override; - void checkErrors() const override; bool isProtected() const override { return mInProtectedContext; } bool supportsProtectedContent() const override; @@ -88,9 +76,7 @@ public: ANativeWindowBuffer* buffer, const bool useFramebufferCache, base::unique_fd&& bufferFence, base::unique_fd* drawFence) override; - // internal to RenderEngine EGLDisplay getEGLDisplay() const { return mEGLDisplay; } - EGLConfig getEGLConfig() const { return mEGLConfig; } // Creates an output image for rendering to EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected, bool useFramebufferCache) @@ -112,27 +98,6 @@ protected: Framebuffer* getFramebufferForDrawing() override; void dump(std::string& result) override EXCLUDES(mRenderingMutex) EXCLUDES(mFramebufferImageCacheMutex); - void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, - ui::Transform::orientation_flags rotation) override; - void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, - const half4& color, float cornerRadius) override; - void setupLayerTexturing(const Texture& texture) override; - void setupLayerBlackedOut() override; - void setupFillWithColor(float r, float g, float b, float a) override; - void setColorTransform(const mat4& colorTransform) override; - void disableTexturing() override; - void disableBlending() override; - void setupCornerRadiusCropSize(float width, float height) override; - - // HDR and color management related functions and state - void setSourceY410BT2020(bool enable) override; - void setSourceDataSpace(ui::Dataspace source) override; - void setOutputDataSpace(ui::Dataspace dataspace) override; - void setDisplayMaxLuminance(const float maxLuminance) override; - - // drawing - void drawMesh(const Mesh& mesh) override; - size_t getMaxTextureSize() const override; size_t getMaxViewportDims() const override; @@ -144,12 +109,16 @@ private: GLES_VERSION_3_0 = 0x30000, }; + static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); static GlesVersion parseGlesVersion(const char* str); static EGLContext createEglContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, bool useContextPriority, Protection protection); static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config, int hwcFormat, Protection protection); + std::unique_ptr<Framebuffer> createFramebuffer(); + std::unique_ptr<Image> createImage(); + void checkErrors() const; void setScissor(const Rect& region); void disableScissor(); bool waitSync(EGLSyncKHR sync, EGLint flags); @@ -176,6 +145,28 @@ private: // blending is an expensive operation, we want to turn off blending when it's not necessary. void handleRoundedCorners(const DisplaySettings& display, const LayerSettings& layer, const Mesh& mesh); + base::unique_fd flush(); + bool finish(); + bool waitFence(base::unique_fd fenceFd); + void clearWithColor(float red, float green, float blue, float alpha); + void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha); + void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, + const half4& color, float cornerRadius); + void setupLayerTexturing(const Texture& texture); + void setupFillWithColor(float r, float g, float b, float a); + void setColorTransform(const mat4& colorTransform); + void disableTexturing(); + void disableBlending(); + void setupCornerRadiusCropSize(float width, float height); + + // HDR and color management related functions and state + void setSourceY410BT2020(bool enable); + void setSourceDataSpace(ui::Dataspace source); + void setOutputDataSpace(ui::Dataspace dataspace); + void setDisplayMaxLuminance(const float maxLuminance); + + // drawing + void drawMesh(const Mesh& mesh); EGLDisplay mEGLDisplay; EGLConfig mEGLConfig; diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp index d242677f0c..494623e67e 100644 --- a/libs/renderengine/gl/ProgramCache.cpp +++ b/libs/renderengine/gl/ProgramCache.cpp @@ -77,9 +77,38 @@ Formatter& dedent(Formatter& f) { return f; } -void ProgramCache::primeCache(EGLContext context, bool useColorManagement) { +void ProgramCache::primeCache( + EGLContext context, bool useColorManagement, bool toneMapperShaderOnly) { auto& cache = mCaches[context]; uint32_t shaderCount = 0; + + if (toneMapperShaderOnly) { + Key shaderKey; + // base settings used by HDR->SDR tonemap only + shaderKey.set(Key::BLEND_MASK | Key::INPUT_TRANSFORM_MATRIX_MASK | + Key::OUTPUT_TRANSFORM_MATRIX_MASK | Key::OUTPUT_TF_MASK | + Key::OPACITY_MASK | Key::ALPHA_MASK | + Key::ROUNDED_CORNERS_MASK | Key::TEXTURE_MASK, + Key::BLEND_NORMAL | Key::INPUT_TRANSFORM_MATRIX_ON | + Key::OUTPUT_TRANSFORM_MATRIX_ON | Key::OUTPUT_TF_SRGB | + Key::OPACITY_OPAQUE | Key::ALPHA_EQ_ONE | + Key::ROUNDED_CORNERS_OFF | Key::TEXTURE_EXT); + for (int i = 0; i < 4; i++) { + // Cache input transfer for HLG & ST2084 + shaderKey.set(Key::INPUT_TF_MASK, (i & 1) ? + Key::INPUT_TF_HLG : Key::INPUT_TF_ST2084); + + // Cache Y410 input on or off + shaderKey.set(Key::Y410_BT2020_MASK, (i & 2) ? + Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF); + if (cache.count(shaderKey) == 0) { + cache.emplace(shaderKey, generateProgram(shaderKey)); + shaderCount++; + } + } + return; + } + uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK | Key::ROUNDED_CORNERS_MASK; // Prime the cache for all combinations of the above masks, diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h index 400ad74fca..175c6e83cc 100644 --- a/libs/renderengine/gl/ProgramCache.h +++ b/libs/renderengine/gl/ProgramCache.h @@ -179,7 +179,7 @@ public: ~ProgramCache() = default; // Generate shaders to populate the cache - void primeCache(const EGLContext context, bool useColorManagement); + void primeCache(const EGLContext context, bool useColorManagement, bool toneMapperShaderOnly); size_t getSize(const EGLContext context) { return mCaches[context].size(); } diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index c6a7bd843a..9dcd5103fa 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -66,6 +66,9 @@ public: // Create a protected context when if possible ENABLE_PROTECTED_CONTEXT = 1 << 2, + + // Only precache HDR to SDR tone-mapping shaders + PRECACHE_TONE_MAPPER_SHADER_ONLY = 1 << 3, }; static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags, @@ -77,10 +80,6 @@ public: // This interface, while still in use until a suitable replacement is built, // should be considered deprecated, minus some methods which still may be // used to support legacy behavior. - - virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0; - virtual std::unique_ptr<Image> createImage() = 0; - virtual void primeCache() const = 0; // dump the extension strings. always call the base class. @@ -88,24 +87,6 @@ public: virtual bool useNativeFenceSync() const = 0; virtual bool useWaitSync() const = 0; - - virtual bool isCurrent() const = 0; - - // helpers - // flush submits RenderEngine command stream for execution and returns a - // native fence fd that is signaled when the execution has completed. It - // returns -1 on errors. - virtual base::unique_fd flush() = 0; - // finish waits until RenderEngine command stream has been executed. It - // returns false on errors. - virtual bool finish() = 0; - // waitFence inserts a wait on an external fence fd to RenderEngine - // command stream. It returns false on errors. - virtual bool waitFence(base::unique_fd fenceFd) = 0; - - virtual void clearWithColor(float red, float green, float blue, float alpha) = 0; - virtual void fillRegionWithColor(const Region& region, float red, float green, float blue, - float alpha) = 0; virtual void genTextures(size_t count, uint32_t* names) = 0; virtual void deleteTextures(size_t count, uint32_t const* names) = 0; virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0; @@ -136,40 +117,6 @@ public: virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0; virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0; - // set-up - virtual void checkErrors() const = 0; - virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, - ui::Transform::orientation_flags rotation) = 0; - virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture, - const half4& color, float cornerRadius) = 0; - virtual void setupLayerTexturing(const Texture& texture) = 0; - virtual void setupLayerBlackedOut() = 0; - virtual void setupFillWithColor(float r, float g, float b, float a) = 0; - // Sets up the crop size for corner radius clipping. - // - // Having corner radius will force GPU composition on the layer and its children, drawing it - // with a special shader. The shader will receive the radius and the crop rectangle as input, - // modifying the opacity of the destination texture, multiplying it by a number between 0 and 1. - // We query Layer#getRoundedCornerState() to retrieve the radius as well as the rounded crop - // rectangle to figure out how to apply the radius for this layer. The crop rectangle will be - // in local layer coordinate space, so we have to take the layer transform into account when - // walking up the tree. - virtual void setupCornerRadiusCropSize(float width, float height) = 0; - - // Set a color transform matrix that is applied in linear space right before OETF. - virtual void setColorTransform(const mat4& /* colorTransform */) = 0; - virtual void disableTexturing() = 0; - virtual void disableBlending() = 0; - - // HDR and color management support - virtual void setSourceY410BT2020(bool enable) = 0; - virtual void setSourceDataSpace(ui::Dataspace source) = 0; - virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0; - virtual void setDisplayMaxLuminance(const float maxLuminance) = 0; - - // drawing - virtual void drawMesh(const Mesh& mesh) = 0; - // queries virtual size_t getMaxTextureSize() const = 0; virtual size_t getMaxViewportDims() const = 0; diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index b4d3ef24ba..0750e86988 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -34,20 +34,12 @@ public: RenderEngine(); ~RenderEngine() override; - MOCK_METHOD0(createFramebuffer, std::unique_ptr<renderengine::Framebuffer>()); - MOCK_METHOD0(createImage, std::unique_ptr<renderengine::Image>()); MOCK_METHOD0(getFramebufferForDrawing, Framebuffer*()); MOCK_CONST_METHOD0(primeCache, void()); MOCK_METHOD1(dump, void(std::string&)); MOCK_CONST_METHOD0(useNativeFenceSync, bool()); MOCK_CONST_METHOD0(useWaitSync, bool()); MOCK_CONST_METHOD0(isCurrent, bool()); - MOCK_METHOD0(flush, base::unique_fd()); - MOCK_METHOD0(finish, bool()); - MOCK_METHOD1(waitFence, bool(base::unique_fd*)); - bool waitFence(base::unique_fd fd) override { return waitFence(&fd); }; - MOCK_METHOD4(clearWithColor, void(float, float, float, float)); - MOCK_METHOD5(fillRegionWithColor, void(const Region&, float, float, float, float)); MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&)); @@ -55,22 +47,6 @@ public: MOCK_METHOD3(bindExternalTextureBuffer, status_t(uint32_t, const sp<GraphicBuffer>&, const sp<Fence>&)); MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t)); - MOCK_CONST_METHOD0(checkErrors, void()); - MOCK_METHOD4(setViewportAndProjection, - void(size_t, size_t, Rect, ui::Transform::orientation_flags)); - MOCK_METHOD5(setupLayerBlending, void(bool, bool, bool, const half4&, float)); - MOCK_METHOD1(setupLayerTexturing, void(const Texture&)); - MOCK_METHOD0(setupLayerBlackedOut, void()); - MOCK_METHOD4(setupFillWithColor, void(float, float, float, float)); - MOCK_METHOD2(setupCornerRadiusCropSize, void(float, float)); - MOCK_METHOD1(setColorTransform, void(const mat4&)); - MOCK_METHOD1(setSaturationMatrix, void(const mat4&)); - MOCK_METHOD0(disableTexturing, void()); - MOCK_METHOD0(disableBlending, void()); - MOCK_METHOD1(setSourceY410BT2020, void(bool)); - MOCK_METHOD1(setSourceDataSpace, void(ui::Dataspace)); - MOCK_METHOD1(setOutputDataSpace, void(ui::Dataspace)); - MOCK_METHOD1(setDisplayMaxLuminance, void(const float)); MOCK_METHOD1(bindFrameBuffer, status_t(renderengine::Framebuffer*)); MOCK_METHOD1(unbindFrameBuffer, void(renderengine::Framebuffer*)); MOCK_METHOD1(drawMesh, void(const renderengine::Mesh&)); diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp index 5200545a53..8ed09f8ff0 100644 --- a/libs/sensor/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -199,6 +199,10 @@ status_t BnSensorServer::onTransact( int32_t type = data.readInt32(); int32_t format = data.readInt32(); native_handle_t *resource = data.readNativeHandle(); + // Avoid a crash in native_handle_close if resource is nullptr + if (resource == nullptr) { + return BAD_VALUE; + } sp<ISensorEventConnection> ch = createSensorDirectConnection(opPackageName, size, type, format, resource); native_handle_close(resource); diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index 139987e6a9..abc910302c 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -577,7 +577,8 @@ void Sensor::flattenString8(void*& buffer, size_t& size, uint32_t len = static_cast<uint32_t>(string8.length()); FlattenableUtils::write(buffer, size, len); memcpy(static_cast<char*>(buffer), string8.string(), len); - FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len)); + FlattenableUtils::advance(buffer, size, len); + size -= FlattenableUtils::align<4>(buffer); } bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& outputString8) { diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 2518e9374d..47137f1771 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -66,6 +66,7 @@ cc_library_shared { "Gralloc.cpp", "Gralloc2.cpp", "Gralloc3.cpp", + "Gralloc4.cpp", "GraphicBuffer.cpp", "GraphicBufferAllocator.cpp", "GraphicBufferMapper.cpp", @@ -90,10 +91,12 @@ cc_library_shared { "android.frameworks.bufferhub@1.0", "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.allocator@3.0", + "android.hardware.graphics.allocator@4.0", "android.hardware.graphics.common@1.2", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@2.1", "android.hardware.graphics.mapper@3.0", + "android.hardware.graphics.mapper@4.0", "libbase", "libcutils", "libhidlbase", @@ -186,3 +189,11 @@ subdirs = [ "tests", "tools", ] + +filegroup { + name: "libui_host_common", + srcs: [ + "Rect.cpp", + "PixelFormat.cpp" + ], +} diff --git a/libs/ui/BufferHubBuffer.cpp b/libs/ui/BufferHubBuffer.cpp index da91a979fe..1dfc1e9e88 100644 --- a/libs/ui/BufferHubBuffer.cpp +++ b/libs/ui/BufferHubBuffer.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define LOG_TAG "BufferHubBuffer" #include <poll.h> #include <android-base/unique_fd.h> diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp new file mode 100644 index 0000000000..dc105c095c --- /dev/null +++ b/libs/ui/Gralloc4.cpp @@ -0,0 +1,405 @@ +/* + * Copyright 2019 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 "Gralloc4" + +#include <hidl/ServiceManagement.h> +#include <hwbinder/IPCThreadState.h> +#include <ui/Gralloc4.h> + +#include <inttypes.h> +#include <log/log.h> +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wzero-length-array" +#include <sync/sync.h> +#pragma clang diagnostic pop + +using android::hardware::graphics::allocator::V4_0::IAllocator; +using android::hardware::graphics::common::V1_2::BufferUsage; +using android::hardware::graphics::mapper::V4_0::BufferDescriptor; +using android::hardware::graphics::mapper::V4_0::Error; +using android::hardware::graphics::mapper::V4_0::IMapper; +using android::hardware::graphics::mapper::V4_0::YCbCrLayout; + +namespace android { + +namespace { + +static constexpr Error kTransactionError = Error::NO_RESOURCES; + +uint64_t getValidUsageBits() { + static const uint64_t validUsageBits = []() -> uint64_t { + uint64_t bits = 0; + for (const auto bit : + hardware::hidl_enum_range<hardware::graphics::common::V1_2::BufferUsage>()) { + bits = bits | bit; + } + return bits; + }(); + return validUsageBits; +} + +static inline IMapper::Rect sGralloc4Rect(const Rect& rect) { + IMapper::Rect outRect{}; + outRect.left = rect.left; + outRect.top = rect.top; + outRect.width = rect.width(); + outRect.height = rect.height(); + return outRect; +} +static inline void sBufferDescriptorInfo(uint32_t width, uint32_t height, + android::PixelFormat format, uint32_t layerCount, + uint64_t usage, + IMapper::BufferDescriptorInfo* outDescriptorInfo) { + outDescriptorInfo->width = width; + outDescriptorInfo->height = height; + outDescriptorInfo->layerCount = layerCount; + outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format); + outDescriptorInfo->usage = usage; +} + +} // anonymous namespace + +void Gralloc4Mapper::preload() { + android::hardware::preloadPassthroughService<IMapper>(); +} + +Gralloc4Mapper::Gralloc4Mapper() { + mMapper = IMapper::getService(); + if (mMapper == nullptr) { + ALOGI("mapper 4.x is not supported"); + return; + } + if (mMapper->isRemote()) { + LOG_ALWAYS_FATAL("gralloc-mapper must be in passthrough mode"); + } +} + +bool Gralloc4Mapper::isLoaded() const { + return mMapper != nullptr; +} + +status_t Gralloc4Mapper::validateBufferDescriptorInfo( + IMapper::BufferDescriptorInfo* descriptorInfo) const { + uint64_t validUsageBits = getValidUsageBits(); + + if (descriptorInfo->usage & ~validUsageBits) { + ALOGE("buffer descriptor contains invalid usage bits 0x%" PRIx64, + descriptorInfo->usage & ~validUsageBits); + return BAD_VALUE; + } + return NO_ERROR; +} + +status_t Gralloc4Mapper::createDescriptor(void* bufferDescriptorInfo, + void* outBufferDescriptor) const { + IMapper::BufferDescriptorInfo* descriptorInfo = + static_cast<IMapper::BufferDescriptorInfo*>(bufferDescriptorInfo); + BufferDescriptor* outDescriptor = static_cast<BufferDescriptor*>(outBufferDescriptor); + + status_t status = validateBufferDescriptorInfo(descriptorInfo); + if (status != NO_ERROR) { + return status; + } + + Error error; + auto hidl_cb = [&](const auto& tmpError, const auto& tmpDescriptor) { + error = tmpError; + if (error != Error::NONE) { + return; + } + *outDescriptor = tmpDescriptor; + }; + + hardware::Return<void> ret = mMapper->createDescriptor(*descriptorInfo, hidl_cb); + + return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); +} + +status_t Gralloc4Mapper::importBuffer(const hardware::hidl_handle& rawHandle, + buffer_handle_t* outBufferHandle) const { + Error error; + auto ret = mMapper->importBuffer(rawHandle, [&](const auto& tmpError, const auto& tmpBuffer) { + error = tmpError; + if (error != Error::NONE) { + return; + } + *outBufferHandle = static_cast<buffer_handle_t>(tmpBuffer); + }); + + return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); +} + +void Gralloc4Mapper::freeBuffer(buffer_handle_t bufferHandle) const { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + auto ret = mMapper->freeBuffer(buffer); + + auto error = (ret.isOk()) ? static_cast<Error>(ret) : kTransactionError; + ALOGE_IF(error != Error::NONE, "freeBuffer(%p) failed with %d", buffer, error); +} + +status_t Gralloc4Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, + uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, + uint32_t stride) const { + IMapper::BufferDescriptorInfo descriptorInfo; + sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); + + auto buffer = const_cast<native_handle_t*>(bufferHandle); + auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride); + + return static_cast<status_t>((ret.isOk()) ? static_cast<Error>(ret) : kTransactionError); +} + +void Gralloc4Mapper::getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, + uint32_t* outNumInts) const { + *outNumFds = uint32_t(bufferHandle->numFds); + *outNumInts = uint32_t(bufferHandle->numInts); + + Error error; + auto buffer = const_cast<native_handle_t*>(bufferHandle); + auto ret = mMapper->getTransportSize(buffer, + [&](const auto& tmpError, const auto& tmpNumFds, + const auto& tmpNumInts) { + error = tmpError; + if (error != Error::NONE) { + return; + } + *outNumFds = tmpNumFds; + *outNumInts = tmpNumInts; + }); + + error = (ret.isOk()) ? error : kTransactionError; + + ALOGE_IF(error != Error::NONE, "getTransportSize(%p) failed with %d", buffer, error); +} + +status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + IMapper::Rect accessRegion = sGralloc4Rect(bounds); + + // put acquireFence in a hidl_handle + hardware::hidl_handle acquireFenceHandle; + NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); + if (acquireFence >= 0) { + auto h = native_handle_init(acquireFenceStorage, 1, 0); + h->data[0] = acquireFence; + acquireFenceHandle = h; + } + + Error error; + auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle, + [&](const auto& tmpError, const auto& tmpData, + const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) { + error = tmpError; + if (error != Error::NONE) { + return; + } + *outData = tmpData; + if (outBytesPerPixel) { + *outBytesPerPixel = tmpBytesPerPixel; + } + if (outBytesPerStride) { + *outBytesPerStride = tmpBytesPerStride; + } + }); + + // we own acquireFence even on errors + if (acquireFence >= 0) { + close(acquireFence); + } + + error = (ret.isOk()) ? error : kTransactionError; + + ALOGW_IF(error != Error::NONE, "lock(%p, ...) failed: %d", bufferHandle, error); + + return static_cast<status_t>(error); +} + +status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, android_ycbcr* ycbcr) const { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + IMapper::Rect accessRegion = sGralloc4Rect(bounds); + + // put acquireFence in a hidl_handle + hardware::hidl_handle acquireFenceHandle; + NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0); + if (acquireFence >= 0) { + auto h = native_handle_init(acquireFenceStorage, 1, 0); + h->data[0] = acquireFence; + acquireFenceHandle = h; + } + + YCbCrLayout layout; + Error error; + auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle, + [&](const auto& tmpError, const auto& tmpLayout) { + error = tmpError; + if (error != Error::NONE) { + return; + } + + layout = tmpLayout; + }); + + if (error == Error::NONE) { + ycbcr->y = layout.y; + ycbcr->cb = layout.cb; + ycbcr->cr = layout.cr; + ycbcr->ystride = static_cast<size_t>(layout.yStride); + ycbcr->cstride = static_cast<size_t>(layout.cStride); + ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep); + } + + // we own acquireFence even on errors + if (acquireFence >= 0) { + close(acquireFence); + } + + return static_cast<status_t>((ret.isOk()) ? error : kTransactionError); +} + +int Gralloc4Mapper::unlock(buffer_handle_t bufferHandle) const { + auto buffer = const_cast<native_handle_t*>(bufferHandle); + + int releaseFence = -1; + Error error; + auto ret = mMapper->unlock(buffer, [&](const auto& tmpError, const auto& tmpReleaseFence) { + error = tmpError; + if (error != Error::NONE) { + return; + } + + auto fenceHandle = tmpReleaseFence.getNativeHandle(); + if (fenceHandle && fenceHandle->numFds == 1) { + int fd = dup(fenceHandle->data[0]); + if (fd >= 0) { + releaseFence = fd; + } else { + ALOGD("failed to dup unlock release fence"); + sync_wait(fenceHandle->data[0], -1); + } + } + }); + + if (!ret.isOk()) { + error = kTransactionError; + } + + if (error != Error::NONE) { + ALOGE("unlock(%p) failed with %d", buffer, error); + } + + return releaseFence; +} + +status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, + bool* outSupported) const { + IMapper::BufferDescriptorInfo descriptorInfo; + sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); + + Error error; + auto ret = mMapper->isSupported(descriptorInfo, + [&](const auto& tmpError, const auto& tmpSupported) { + error = tmpError; + if (error != Error::NONE) { + return; + } + if (outSupported) { + *outSupported = tmpSupported; + } + }); + + if (!ret.isOk()) { + error = kTransactionError; + } + + if (error != Error::NONE) { + ALOGE("isSupported(%u, %u, %d, %u, ...) failed with %d", width, height, format, layerCount, + error); + } + + return static_cast<status_t>(error); +} + +Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) { + mAllocator = IAllocator::getService(); + if (mAllocator == nullptr) { + ALOGW("allocator 3.x is not supported"); + return; + } +} + +bool Gralloc4Allocator::isLoaded() const { + return mAllocator != nullptr; +} + +std::string Gralloc4Allocator::dumpDebugInfo() const { + std::string debugInfo; + + mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); }); + + return debugInfo; +} + +status_t Gralloc4Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, uint32_t bufferCount, + uint32_t* outStride, buffer_handle_t* outBufferHandles) const { + IMapper::BufferDescriptorInfo descriptorInfo; + sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo); + + BufferDescriptor descriptor; + status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo), + static_cast<void*>(&descriptor)); + if (error != NO_ERROR) { + return error; + } + + auto ret = mAllocator->allocate(descriptor, bufferCount, + [&](const auto& tmpError, const auto& tmpStride, + const auto& tmpBuffers) { + error = static_cast<status_t>(tmpError); + if (tmpError != Error::NONE) { + return; + } + + // import buffers + for (uint32_t i = 0; i < bufferCount; i++) { + error = mMapper.importBuffer(tmpBuffers[i], + &outBufferHandles[i]); + if (error != NO_ERROR) { + for (uint32_t j = 0; j < i; j++) { + mMapper.freeBuffer(outBufferHandles[j]); + outBufferHandles[j] = nullptr; + } + return; + } + } + *outStride = tmpStride; + }); + + // make sure the kernel driver sees BC_FREE_BUFFER and closes the fds now + hardware::IPCThreadState::self()->flushCommands(); + + return (ret.isOk()) ? error : static_cast<status_t>(kTransactionError); +} + +} // namespace android diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index 3fc6a2d34a..579e68e917 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -626,7 +626,7 @@ status_t GraphicBuffer::unflattenBufferHubBuffer(void const*& buffer, size_t& si bufferHubBuffer->desc().layers, bufferHubBuffer->desc().usage, bufferHubBuffer->desc().stride); mBufferId = bufferHubBuffer->id(); - mBufferHubBuffer.reset(std::move(bufferHubBuffer.get())); + mBufferHubBuffer = std::move(bufferHubBuffer); return NO_ERROR; } diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp index 9c7d1fda90..eb787a2e40 100644 --- a/libs/ui/GraphicBufferAllocator.cpp +++ b/libs/ui/GraphicBufferAllocator.cpp @@ -33,6 +33,7 @@ #include <ui/Gralloc.h> #include <ui/Gralloc2.h> #include <ui/Gralloc3.h> +#include <ui/Gralloc4.h> #include <ui/GraphicBufferMapper.h> namespace android { @@ -47,16 +48,23 @@ KeyedVector<buffer_handle_t, GraphicBufferAllocator::alloc_rec_t> GraphicBufferAllocator::sAllocList; GraphicBufferAllocator::GraphicBufferAllocator() : mMapper(GraphicBufferMapper::getInstance()) { + mAllocator = std::make_unique<const Gralloc4Allocator>( + reinterpret_cast<const Gralloc4Mapper&>(mMapper.getGrallocMapper())); + if (mAllocator->isLoaded()) { + return; + } mAllocator = std::make_unique<const Gralloc3Allocator>( reinterpret_cast<const Gralloc3Mapper&>(mMapper.getGrallocMapper())); - if (!mAllocator->isLoaded()) { - mAllocator = std::make_unique<const Gralloc2Allocator>( - reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper())); + if (mAllocator->isLoaded()) { + return; } - - if (!mAllocator->isLoaded()) { - LOG_ALWAYS_FATAL("gralloc-allocator is missing"); + mAllocator = std::make_unique<const Gralloc2Allocator>( + reinterpret_cast<const Gralloc2Mapper&>(mMapper.getGrallocMapper())); + if (mAllocator->isLoaded()) { + return; } + + LOG_ALWAYS_FATAL("gralloc-allocator is missing"); } GraphicBufferAllocator::~GraphicBufferAllocator() {} diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp index 25b7247b1b..4d087d151c 100644 --- a/libs/ui/GraphicBufferMapper.cpp +++ b/libs/ui/GraphicBufferMapper.cpp @@ -35,6 +35,7 @@ #include <ui/Gralloc.h> #include <ui/Gralloc2.h> #include <ui/Gralloc3.h> +#include <ui/Gralloc4.h> #include <ui/GraphicBuffer.h> #include <system/graphics.h> @@ -47,20 +48,27 @@ ANDROID_SINGLETON_STATIC_INSTANCE( GraphicBufferMapper ) void GraphicBufferMapper::preloadHal() { Gralloc2Mapper::preload(); Gralloc3Mapper::preload(); + Gralloc4Mapper::preload(); } GraphicBufferMapper::GraphicBufferMapper() { + mMapper = std::make_unique<const Gralloc4Mapper>(); + if (mMapper->isLoaded()) { + mMapperVersion = Version::GRALLOC_4; + return; + } mMapper = std::make_unique<const Gralloc3Mapper>(); - if (!mMapper->isLoaded()) { - mMapper = std::make_unique<const Gralloc2Mapper>(); - mMapperVersion = Version::GRALLOC_2; - } else { + if (mMapper->isLoaded()) { mMapperVersion = Version::GRALLOC_3; + return; } - - if (!mMapper->isLoaded()) { - LOG_ALWAYS_FATAL("gralloc-mapper is missing"); + mMapper = std::make_unique<const Gralloc2Mapper>(); + if (mMapper->isLoaded()) { + mMapperVersion = Version::GRALLOC_2; + return; } + + LOG_ALWAYS_FATAL("gralloc-mapper is missing"); } status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle, diff --git a/libs/ui/include/ui/DisplayInfo.h b/libs/ui/include/ui/DisplayInfo.h index 8976d2d584..07722104de 100644 --- a/libs/ui/include/ui/DisplayInfo.h +++ b/libs/ui/include/ui/DisplayInfo.h @@ -24,6 +24,8 @@ namespace android { +constexpr uint32_t NO_LAYER_STACK = static_cast<uint32_t>(-1); + struct DisplayInfo { uint32_t w{0}; uint32_t h{0}; @@ -37,6 +39,7 @@ struct DisplayInfo { nsecs_t presentationDeadline{0}; uint32_t viewportW{0}; uint32_t viewportH{0}; + uint32_t layerStack{NO_LAYER_STACK}; }; /* Display orientations as defined in Surface.java and ISurfaceComposer.h. */ @@ -47,6 +50,6 @@ enum { DISPLAY_ORIENTATION_270 = 3 }; -}; // namespace android +} // namespace android #endif // ANDROID_COMPOSER_DISPLAY_INFO_H diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h new file mode 100644 index 0000000000..14b65bc220 --- /dev/null +++ b/libs/ui/include/ui/Gralloc4.h @@ -0,0 +1,95 @@ +/* + * Copyright 2019 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_UI_GRALLOC4_H +#define ANDROID_UI_GRALLOC4_H + +#include <string> + +#include <android/hardware/graphics/allocator/4.0/IAllocator.h> +#include <android/hardware/graphics/common/1.1/types.h> +#include <android/hardware/graphics/mapper/4.0/IMapper.h> +#include <ui/Gralloc.h> +#include <ui/PixelFormat.h> +#include <ui/Rect.h> +#include <utils/StrongPointer.h> + +namespace android { + +class Gralloc4Mapper : public GrallocMapper { +public: + static void preload(); + + Gralloc4Mapper(); + + bool isLoaded() const override; + + status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override; + + status_t importBuffer(const hardware::hidl_handle& rawHandle, + buffer_handle_t* outBufferHandle) const override; + + void freeBuffer(buffer_handle_t bufferHandle) const override; + + status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height, + android::PixelFormat format, uint32_t layerCount, uint64_t usage, + uint32_t stride) const override; + + void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds, + uint32_t* outNumInts) const override; + + status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, void** outData, int32_t* outBytesPerPixel, + int32_t* outBytesPerStride) const override; + + status_t lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds, + int acquireFence, android_ycbcr* ycbcr) const override; + + int unlock(buffer_handle_t bufferHandle) const override; + + status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format, + uint32_t layerCount, uint64_t usage, bool* outSupported) const override; + +private: + // Determines whether the passed info is compatible with the mapper. + status_t validateBufferDescriptorInfo( + hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* descriptorInfo) const; + + sp<hardware::graphics::mapper::V4_0::IMapper> mMapper; +}; + +class Gralloc4Allocator : public GrallocAllocator { +public: + // An allocator relies on a mapper, and that mapper must be alive at all + // time. + Gralloc4Allocator(const Gralloc4Mapper& mapper); + + bool isLoaded() const override; + + std::string dumpDebugInfo() const override; + + status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, + uint64_t usage, uint32_t bufferCount, uint32_t* outStride, + buffer_handle_t* outBufferHandles) const override; + +private: + const Gralloc4Mapper& mMapper; + sp<hardware::graphics::allocator::V4_0::IAllocator> mAllocator; +}; + +} // namespace android + +#endif // ANDROID_UI_GRALLOC4_H diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h index 24614549be..c401a4863c 100644 --- a/libs/ui/include/ui/GraphicBufferMapper.h +++ b/libs/ui/include/ui/GraphicBufferMapper.h @@ -44,6 +44,7 @@ public: enum Version { GRALLOC_2, GRALLOC_3, + GRALLOC_4, }; static void preloadHal(); static inline GraphicBufferMapper& get() { return getInstance(); } diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 99434b7273..0452f8404d 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -45,6 +45,24 @@ cc_test { cflags: ["-Wall", "-Werror"], } +// This test has a main method, and requires a separate binary to be built. +cc_test { + name: "GraphicBufferOverBinder_test", + srcs: ["GraphicBufferOverBinder_test.cpp"], + cflags: ["-Wall", "-Werror"], + header_libs: [ + "libdvr_headers", + ], + shared_libs: [ + "android.frameworks.bufferhub@1.0", + "libbinder", + "libgui", + "liblog", + "libui", + "libutils", + ], +} + cc_test { name: "BufferHub_test", header_libs: [ diff --git a/libs/ui/tests/GraphicBufferOverBinder_test.cpp b/libs/ui/tests/GraphicBufferOverBinder_test.cpp new file mode 100644 index 0000000000..7c0a44a64f --- /dev/null +++ b/libs/ui/tests/GraphicBufferOverBinder_test.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2019 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 "GraphicBufferOverBinder_test" + +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/ProcessState.h> +#include <gtest/gtest.h> +#include <gui/BufferQueue.h> +#include <gui/IGraphicBufferConsumer.h> +#include <gui/IGraphicBufferProducer.h> +#include <ui/BufferHubBuffer.h> +#include <ui/GraphicBuffer.h> +#include <utils/Log.h> + +namespace android { + +constexpr uint32_t kTestWidth = 1024; +constexpr uint32_t kTestHeight = 1; +constexpr uint32_t kTestFormat = HAL_PIXEL_FORMAT_BLOB; +constexpr uint32_t kTestLayerCount = 1; +constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN; +static const String16 kTestServiceName = String16("GraphicBufferOverBinderTestService"); +enum GraphicBufferOverBinderTestServiceCode { + GRAPHIC_BUFFER = IBinder::FIRST_CALL_TRANSACTION, + GRAPHIC_BUFFER_FROM_BUFFER_HUB_BUFFER, +}; + +class GraphicBufferOverBinderTestService : public BBinder { +public: + GraphicBufferOverBinderTestService() { + // GraphicBuffer + mGraphicBuffer = new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, + kTestUsage); + ALOGI("mGraphicBuffer id %" PRIi32, mGraphicBuffer->getBufferId()); + + // BufferHub-backed GraphicBuffer + std::unique_ptr<BufferHubBuffer> bufferHubBuffer = + BufferHubBuffer::create(kTestWidth, kTestHeight, kTestLayerCount, kTestFormat, + kTestUsage, /*userMetadataSize=*/0); + mBufferhubBackedGraphicBuffer = new GraphicBuffer(std::move(bufferHubBuffer)); + if (!mBufferhubBackedGraphicBuffer->isBufferHubBuffer()) { + ALOGE("Failed to back GraphicBuffer with BufferHub."); + } + if (bufferHubBuffer != nullptr) { + ALOGE("Failed to move BufferHubBuffer to GraphicBuffer"); + } + ALOGI("mBufferhubBackedGraphicBuffer id %" PRIi32, + mBufferhubBackedGraphicBuffer->getBufferId()); + } + + ~GraphicBufferOverBinderTestService() = default; + + virtual status_t onTransact(uint32_t code, const Parcel& /*data*/, Parcel* reply, + uint32_t /*flags*/ = 0) { + switch (code) { + case GRAPHIC_BUFFER: { + return reply->write(*mGraphicBuffer); + } + case GRAPHIC_BUFFER_FROM_BUFFER_HUB_BUFFER: { + return reply->write(*mBufferhubBackedGraphicBuffer); + } + default: + return UNKNOWN_TRANSACTION; + }; + } + +protected: + sp<GraphicBuffer> mGraphicBuffer; + sp<GraphicBuffer> mBufferhubBackedGraphicBuffer; +}; + +static int runBinderServer() { + ProcessState::self()->startThreadPool(); + + sp<IServiceManager> sm = defaultServiceManager(); + sp<GraphicBufferOverBinderTestService> service = new GraphicBufferOverBinderTestService; + sm->addService(kTestServiceName, service, false); + + ALOGI("Binder server running..."); + + while (true) { + int stat, retval; + retval = wait(&stat); + if (retval == -1 && errno == ECHILD) { + break; + } + } + + ALOGI("Binder server exiting..."); + return 0; +} + +class GraphicBufferOverBinderTest : public ::testing::TestWithParam<uint32_t> { +protected: + virtual void SetUp() { + mService = defaultServiceManager()->getService(kTestServiceName); + if (mService == nullptr) { + ALOGE("Failed to connect to the test service."); + return; + } + + ALOGI("Binder service is ready for client."); + } + + status_t GetGraphicBuffer(sp<GraphicBuffer>* outBuf, uint32_t opCode) { + Parcel data; + Parcel reply; + status_t error = mService->transact(opCode, data, &reply); + if (error != NO_ERROR) { + ALOGE("Failed to get graphic buffer over binder, error=%d.", error); + return error; + } + + *outBuf = new GraphicBuffer(); + return reply.read(**outBuf); + } + +private: + sp<IBinder> mService; +}; + +TEST_F(GraphicBufferOverBinderTest, SendGraphicBufferOverBinder) { + sp<GraphicBuffer> gb; + EXPECT_EQ(GetGraphicBuffer(&gb, GRAPHIC_BUFFER), OK); + EXPECT_NE(gb, nullptr); + EXPECT_FALSE(gb->isBufferHubBuffer()); + void* vaddr; + EXPECT_EQ(gb->lock(kTestUsage, &vaddr), OK); + EXPECT_EQ(gb->unlock(), OK); +} + +TEST_F(GraphicBufferOverBinderTest, SendGraphicBufferFromBufferHubBufferOverBinder) { + sp<GraphicBuffer> gb; + EXPECT_EQ(GetGraphicBuffer(&gb, GRAPHIC_BUFFER_FROM_BUFFER_HUB_BUFFER), NO_ERROR); + EXPECT_NE(gb, nullptr); + EXPECT_TRUE(gb->isBufferHubBuffer()); + void* vaddr; + EXPECT_EQ(gb->lock(kTestUsage, &vaddr), OK); + EXPECT_EQ(gb->unlock(), OK); +} + +} // namespace android + +int main(int argc, char** argv) { + pid_t pid = fork(); + if (pid == 0) { + android::ProcessState::self()->startThreadPool(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); + + } else { + ALOGI("Test process pid: %d.", pid); + return android::runBinderServer(); + } +} diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp index 127f7eedd6..5e0b094b7b 100644 --- a/libs/ui/tests/GraphicBuffer_test.cpp +++ b/libs/ui/tests/GraphicBuffer_test.cpp @@ -44,11 +44,28 @@ TEST_F(GraphicBufferTest, AllocateNoError) { TEST_F(GraphicBufferTest, AllocateBadDimensions) { PixelFormat format = PIXEL_FORMAT_RGBA_8888; + if (std::numeric_limits<size_t>::max() / std::numeric_limits<uint32_t>::max() / + bytesPerPixel(format) >= + std::numeric_limits<uint32_t>::max()) { + GTEST_SUCCEED() << "Cannot overflow with this format"; + } uint32_t width, height; width = height = std::numeric_limits<uint32_t>::max(); sp<GraphicBuffer> gb(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage, std::string("test"))); ASSERT_EQ(BAD_VALUE, gb->initCheck()); + + const size_t targetArea = std::numeric_limits<size_t>::max() / bytesPerPixel(format); + const size_t widthCandidate = targetArea / std::numeric_limits<uint32_t>::max(); + if (widthCandidate == 0) { + width = 1; + } else { + width = std::numeric_limits<uint32_t>::max(); + } + height = (targetArea / width) + 1; + sp<GraphicBuffer> gb2(new GraphicBuffer(width, height, format, kTestLayerCount, kTestUsage, + std::string("test"))); + ASSERT_EQ(BAD_VALUE, gb2->initCheck()); } TEST_F(GraphicBufferTest, CreateFromBufferHubBuffer) { diff --git a/libs/vr/libbufferhub/consumer_buffer.cpp b/libs/vr/libbufferhub/consumer_buffer.cpp index 115e8666e5..7823e36d3d 100644 --- a/libs/vr/libbufferhub/consumer_buffer.cpp +++ b/libs/vr/libbufferhub/consumer_buffer.cpp @@ -52,12 +52,6 @@ int ConsumerBuffer::LocalAcquire(DvrNativeBufferMetadata* out_meta, while (!buffer_state_->compare_exchange_weak( current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, std::memory_order_acquire)) { - ALOGD( - "%s Failed to acquire the buffer. Current buffer state was changed to " - "%" PRIx32 - " when trying to acquire the buffer and modify the buffer state to " - "%" PRIx32 ". About to try again if the buffer is still posted.", - __FUNCTION__, current_buffer_state, updated_buffer_state); if (!BufferHubDefs::isClientPosted(current_buffer_state, client_state_mask())) { ALOGE( @@ -152,12 +146,6 @@ int ConsumerBuffer::LocalRelease(const DvrNativeBufferMetadata* meta, while (!buffer_state_->compare_exchange_weak( current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, std::memory_order_acquire)) { - ALOGD( - "%s: Failed to release the buffer. Current buffer state was changed to " - "%" PRIx32 - " when trying to release the buffer and modify the buffer state to " - "%" PRIx32 ". About to try again.", - __FUNCTION__, current_buffer_state, updated_buffer_state); // The failure of compare_exchange_weak updates current_buffer_state. updated_buffer_state = current_buffer_state & (~client_state_mask()); } diff --git a/libs/vr/libbufferhub/producer_buffer.cpp b/libs/vr/libbufferhub/producer_buffer.cpp index 3d88ba5dbe..aa9d07282b 100644 --- a/libs/vr/libbufferhub/producer_buffer.cpp +++ b/libs/vr/libbufferhub/producer_buffer.cpp @@ -96,13 +96,6 @@ int ProducerBuffer::LocalPost(const DvrNativeBufferMetadata* meta, while (!buffer_state_->compare_exchange_weak( current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, std::memory_order_acquire)) { - ALOGD( - "%s: Failed to post the buffer. Current buffer state was changed to " - "%" PRIx32 - " when trying to post the buffer and modify the buffer state to " - "%" PRIx32 - ". About to try again if the buffer is still gained by this client.", - __FUNCTION__, current_buffer_state, updated_buffer_state); if (!BufferHubDefs::isClientGained(current_buffer_state, client_state_mask())) { ALOGE( @@ -186,15 +179,6 @@ int ProducerBuffer::LocalGain(DvrNativeBufferMetadata* out_meta, while (!buffer_state_->compare_exchange_weak( current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, std::memory_order_acquire)) { - ALOGD( - "%s: Failed to gain the buffer. Current buffer state was changed to " - "%" PRIx32 - " when trying to gain the buffer and modify the buffer state to " - "%" PRIx32 - ". About to try again if the buffer is still not read by other " - "clients.", - __FUNCTION__, current_buffer_state, updated_buffer_state); - if (BufferHubDefs::isAnyClientAcquired(current_buffer_state) || BufferHubDefs::isAnyClientGained(current_buffer_state) || (BufferHubDefs::isAnyClientPosted( diff --git a/libs/vr/libdisplay/display_client.cpp b/libs/vr/libdisplay/display_client.cpp index f67e258cee..62856dfbf8 100644 --- a/libs/vr/libdisplay/display_client.cpp +++ b/libs/vr/libdisplay/display_client.cpp @@ -178,6 +178,10 @@ Status<std::string> DisplayClient::GetConfigurationData( return status; } +Status<uint8_t> DisplayClient::GetDisplayIdentificationPort() { + return InvokeRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>(); +} + Status<std::unique_ptr<Surface>> DisplayClient::CreateSurface( const SurfaceAttributes& attributes) { int error; diff --git a/libs/vr/libdisplay/include/private/dvr/display_client.h b/libs/vr/libdisplay/include/private/dvr/display_client.h index f8f5b3ddb3..81546ac5c2 100644 --- a/libs/vr/libdisplay/include/private/dvr/display_client.h +++ b/libs/vr/libdisplay/include/private/dvr/display_client.h @@ -72,6 +72,7 @@ class DisplayClient : public pdx::ClientBase<DisplayClient> { public: pdx::Status<Metrics> GetDisplayMetrics(); pdx::Status<std::string> GetConfigurationData(ConfigFileType config_type); + pdx::Status<uint8_t> GetDisplayIdentificationPort(); pdx::Status<std::unique_ptr<IonBuffer>> SetupGlobalBuffer( DvrGlobalBufferKey key, size_t size, uint64_t usage); pdx::Status<void> DeleteGlobalBuffer(DvrGlobalBufferKey key); diff --git a/libs/vr/libdisplay/include/private/dvr/display_protocol.h b/libs/vr/libdisplay/include/private/dvr/display_protocol.h index 3786d1d5e9..9f4cc4afcc 100644 --- a/libs/vr/libdisplay/include/private/dvr/display_protocol.h +++ b/libs/vr/libdisplay/include/private/dvr/display_protocol.h @@ -191,7 +191,8 @@ struct SurfaceInfo { enum class ConfigFileType : uint32_t { kLensMetrics, kDeviceMetrics, - kDeviceConfiguration + kDeviceConfiguration, + kDeviceEdid }; struct DisplayProtocol { @@ -210,6 +211,7 @@ struct DisplayProtocol { kOpGetSurfaceInfo, kOpCreateQueue, kOpSetAttributes, + kOpGetDisplayIdentificationPort, }; // Aliases. @@ -220,6 +222,8 @@ struct DisplayProtocol { PDX_REMOTE_METHOD(GetMetrics, kOpGetMetrics, Metrics(Void)); PDX_REMOTE_METHOD(GetConfigurationData, kOpGetConfigurationData, std::string(ConfigFileType config_type)); + PDX_REMOTE_METHOD(GetDisplayIdentificationPort, + kOpGetDisplayIdentificationPort, uint8_t(Void)); PDX_REMOTE_METHOD(SetupGlobalBuffer, kOpSetupGlobalBuffer, LocalNativeBufferHandle(DvrGlobalBufferKey key, size_t size, uint64_t usage)); diff --git a/libs/vr/libdvr/include/dvr/dvr_api.h b/libs/vr/libdvr/include/dvr/dvr_api.h index e383bb2cb3..b7abb99559 100644 --- a/libs/vr/libdvr/include/dvr/dvr_api.h +++ b/libs/vr/libdvr/include/dvr/dvr_api.h @@ -85,6 +85,8 @@ enum { DVR_CONFIGURATION_DATA_DEVICE_METRICS = 1, // Request the per device configuration data file. DVR_CONFIGURATION_DATA_DEVICE_CONFIG = 2, + // Request the edid data for the display. + DVR_CONFIGURATION_DATA_DEVICE_EDID = 3, }; // dvr_display_manager.h diff --git a/libs/vr/libvrflinger/Android.bp b/libs/vr/libvrflinger/Android.bp index 12ce74b1ea..04493f0cd3 100644 --- a/libs/vr/libvrflinger/Android.bp +++ b/libs/vr/libvrflinger/Android.bp @@ -35,11 +35,12 @@ staticLibraries = [ ] sharedLibraries = [ - "android.frameworks.vr.composer@1.0", + "android.frameworks.vr.composer@2.0", "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", + "android.hardware.graphics.composer@2.4", "libbinder", "libbase", "libbufferhubqueue", diff --git a/libs/vr/libvrflinger/display_service.cpp b/libs/vr/libvrflinger/display_service.cpp index 87162c0b5a..582fed3a4a 100644 --- a/libs/vr/libvrflinger/display_service.cpp +++ b/libs/vr/libvrflinger/display_service.cpp @@ -18,6 +18,8 @@ #include <private/dvr/trusted_uids.h> #include <private/dvr/types.h> +#include "DisplayHardware/DisplayIdentification.h" + using android::dvr::display::DisplayProtocol; using android::pdx::Channel; using android::pdx::ErrorStatus; @@ -139,6 +141,11 @@ Status<void> DisplayService::HandleMessage(pdx::Message& message) { *this, &DisplayService::OnGetConfigurationData, message); return {}; + case DisplayProtocol::GetDisplayIdentificationPort::Opcode: + DispatchRemoteMethod<DisplayProtocol::GetDisplayIdentificationPort>( + *this, &DisplayService::OnGetDisplayIdentificationPort, message); + return {}; + case DisplayProtocol::CreateSurface::Opcode: DispatchRemoteMethod<DisplayProtocol::CreateSurface>( *this, &DisplayService::OnCreateSurface, message); @@ -194,6 +201,7 @@ Status<display::Metrics> DisplayService::OnGetMetrics( pdx::Status<std::string> DisplayService::OnGetConfigurationData( pdx::Message& /*message*/, display::ConfigFileType config_type) { std::string property_name; + DisplayIdentificationData display_identification_data; switch (config_type) { case display::ConfigFileType::kLensMetrics: property_name = kDvrLensMetricsProperty; @@ -204,6 +212,14 @@ pdx::Status<std::string> DisplayService::OnGetConfigurationData( case display::ConfigFileType::kDeviceConfiguration: property_name = kDvrDeviceConfigProperty; break; + case display::ConfigFileType::kDeviceEdid: + display_identification_data = + hardware_composer_.GetCurrentDisplayIdentificationData(); + if (display_identification_data.size() == 0) { + return ErrorStatus(ENOENT); + } + return std::string(display_identification_data.begin(), + display_identification_data.end()); default: return ErrorStatus(EINVAL); } @@ -220,6 +236,11 @@ pdx::Status<std::string> DisplayService::OnGetConfigurationData( return std::move(data); } +pdx::Status<uint8_t> DisplayService::OnGetDisplayIdentificationPort( + pdx::Message& /*message*/) { + return hardware_composer_.GetCurrentDisplayPort(); +} + // Creates a new DisplaySurface and associates it with this channel. This may // only be done once per channel. Status<display::SurfaceInfo> DisplayService::OnCreateSurface( diff --git a/libs/vr/libvrflinger/display_service.h b/libs/vr/libvrflinger/display_service.h index e0f2eddfea..89f1eaee33 100644 --- a/libs/vr/libvrflinger/display_service.h +++ b/libs/vr/libvrflinger/display_service.h @@ -80,6 +80,7 @@ class DisplayService : public pdx::ServiceBase<DisplayService> { pdx::Status<display::Metrics> OnGetMetrics(pdx::Message& message); pdx::Status<std::string> OnGetConfigurationData( pdx::Message& message, display::ConfigFileType config_type); + pdx::Status<uint8_t> OnGetDisplayIdentificationPort(pdx::Message& message); pdx::Status<display::SurfaceInfo> OnCreateSurface( pdx::Message& message, const display::SurfaceAttributes& attributes); pdx::Status<BorrowedNativeBufferHandle> OnSetupGlobalBuffer( diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp index e1240d6311..67607af6c4 100644 --- a/libs/vr/libvrflinger/hardware_composer.cpp +++ b/libs/vr/libvrflinger/hardware_composer.cpp @@ -51,8 +51,6 @@ const char kDvrStandaloneProperty[] = "ro.boot.vr"; const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns"; -const char kUseExternalDisplayProperty[] = "persist.vr.use_external_display"; - // Surface flinger uses "VSYNC-sf" and "VSYNC-app" for its version of these // events. Name ours similarly. const char kVsyncTraceEventName[] = "VSYNC-vrflinger"; @@ -139,6 +137,20 @@ HardwareComposer::~HardwareComposer(void) { composer_callback_->SetVsyncService(nullptr); } +void HardwareComposer::UpdateEdidData(Hwc2::Composer* composer, + hwc2_display_t hw_id) { + const auto error = composer->getDisplayIdentificationData( + hw_id, &display_port_, &display_identification_data_); + if (error != android::hardware::graphics::composer::V2_1::Error::NONE) { + if (error != + android::hardware::graphics::composer::V2_1::Error::UNSUPPORTED) { + ALOGI("hardware_composer: identification data error\n"); + } else { + ALOGI("hardware_composer: identification data unsupported\n"); + } + } +} + bool HardwareComposer::Initialize( Hwc2::Composer* composer, hwc2_display_t primary_display_id, RequestDisplayCallback request_display_callback) { @@ -166,6 +178,8 @@ bool HardwareComposer::Initialize( "HardwareComposer: Failed to create interrupt event fd : %s", strerror(errno)); + UpdateEdidData(composer, primary_display_id); + post_thread_ = std::thread(&HardwareComposer::PostThread, this); initialized_ = true; @@ -965,18 +979,9 @@ bool HardwareComposer::UpdateTargetDisplay() { external_display_ = GetDisplayParams(composer_.get(), *displays.external_display, /*is_primary*/ false); - if (property_get_bool(kUseExternalDisplayProperty, false)) { - ALOGI("External display connected. Switching to external display."); - target_display_ = &(*external_display_); - target_display_changed = true; - } else { - ALOGI("External display connected, but sysprop %s is unset, so" - " using primary display.", kUseExternalDisplayProperty); - if (was_using_external_display) { - target_display_ = &primary_display_; - target_display_changed = true; - } - } + ALOGI("External display connected. Switching to external display."); + target_display_ = &(*external_display_); + target_display_changed = true; } else { // External display was disconnected external_display_ = std::nullopt; @@ -999,6 +1004,9 @@ bool HardwareComposer::UpdateTargetDisplay() { EnableDisplay(*external_display_, false); } + // Update the cached edid data for the current display. + UpdateEdidData(composer_.get(), target_display_->id); + // Turn the new target display on. EnableDisplay(*target_display_, true); diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h index db0d6a7670..989ce3588d 100644 --- a/libs/vr/libvrflinger/hardware_composer.h +++ b/libs/vr/libvrflinger/hardware_composer.h @@ -25,6 +25,7 @@ #include <private/dvr/shared_buffer_helpers.h> #include <private/dvr/vsync_service.h> +#include "DisplayHardware/DisplayIdentification.h" #include "acquired_buffer.h" #include "display_surface.h" @@ -334,6 +335,14 @@ class HardwareComposer { int OnNewGlobalBuffer(DvrGlobalBufferKey key, IonBuffer& ion_buffer); void OnDeletedGlobalBuffer(DvrGlobalBufferKey key); + // Gets the edid data for the current active display (internal or external) + DisplayIdentificationData GetCurrentDisplayIdentificationData() { + return display_identification_data_; + } + + // Gets the edid port for the current active display (internal or external) + uint8_t GetCurrentDisplayPort() { return display_port_; } + private: DisplayParams GetDisplayParams(Hwc2::Composer* composer, hwc2_display_t display, bool is_primary); @@ -544,6 +553,11 @@ class HardwareComposer { bool vsync_trace_parity_ = false; sp<VsyncService> vsync_service_; + // Edid section. + void UpdateEdidData(Hwc2::Composer* composer, hwc2_display_t hw_id); + DisplayIdentificationData display_identification_data_; + uint8_t display_port_; + static constexpr int kPostThreadInterrupted = 1; HardwareComposer(const HardwareComposer&) = delete; diff --git a/opengl/libagl/Android.bp b/opengl/libagl/Android.bp deleted file mode 100644 index f5bf015bad..0000000000 --- a/opengl/libagl/Android.bp +++ /dev/null @@ -1,100 +0,0 @@ -// -// Build the software OpenGL ES library -// - -cc_defaults { - name: "libGLES_android_defaults", - - cflags: [ - "-DLOG_TAG=\"libagl\"", - "-DGL_GLEXT_PROTOTYPES", - "-DEGL_EGLEXT_PROTOTYPES", - "-fvisibility=hidden", - "-Wall", - "-Werror", - ], - - shared_libs: [ - "libcutils", - "libhardware", - "libutils", - "liblog", - "libpixelflinger", - "libETC1", - "libui", - "libnativewindow", - ], - - header_libs: [ - "bionic_libc_platform_headers", - ], - - arch: { - arm: { - cflags: ["-fstrict-aliasing"], - }, - - mips: { - cflags: [ - "-fstrict-aliasing", - // The graphics code can generate division by zero - "-mno-check-zero-division", - ], - }, - }, -} - -cc_library_shared { - name: "libGLES_android", - defaults: ["libGLES_android_defaults"], - - whole_static_libs: ["libGLES_android_arm"], - - srcs: [ - "egl.cpp", - "state.cpp", - "texture.cpp", - "Tokenizer.cpp", - "TokenManager.cpp", - "TextureObjectManager.cpp", - "BufferObjectManager.cpp", - ], - - arch: { - arm: { - srcs: [ - "fixed_asm.S", - "iterators.S", - ], - }, - - mips: { - rev6: { - srcs: ["arch-mips/fixed_asm.S"], - }, - }, - }, - - relative_install_path: "egl", -} - -cc_library_static { - name: "libGLES_android_arm", - defaults: ["libGLES_android_defaults"], - - srcs: [ - "array.cpp", - "fp.cpp", - "light.cpp", - "matrix.cpp", - "mipmap.cpp", - "primitives.cpp", - "vertex.cpp", - ], - - arch: { - arm: { - instruction_set: "arm", - }, - }, -} diff --git a/opengl/libagl/BufferObjectManager.cpp b/opengl/libagl/BufferObjectManager.cpp deleted file mode 100644 index 3d93c190ba..0000000000 --- a/opengl/libagl/BufferObjectManager.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/* - ** Copyright 2008, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> - -#include <cutils/atomic.h> -#include <utils/RefBase.h> -#include <utils/KeyedVector.h> -#include <utils/Errors.h> - -#include <GLES/gl.h> - -#include "BufferObjectManager.h" - - -namespace android { - -using namespace gl; - -// ---------------------------------------------------------------------------- - -EGLBufferObjectManager::EGLBufferObjectManager() -: TokenManager(), mCount(0) -{ -} - -EGLBufferObjectManager::~EGLBufferObjectManager() -{ - // destroy all the buffer objects and their storage - GLsizei n = mBuffers.size(); - for (GLsizei i=0 ; i<n ; i++) { - buffer_t* bo = mBuffers.valueAt(i); - free(bo->data); - delete bo; - } -} - -buffer_t const* EGLBufferObjectManager::bind(GLuint buffer) -{ - Mutex::Autolock _l(mLock); - int32_t i = mBuffers.indexOfKey(buffer); - if (i >= 0) { - return mBuffers.valueAt(i); - } - buffer_t* bo = new buffer_t; - bo->data = 0; - bo->usage = GL_STATIC_DRAW; - bo->size = 0; - bo->name = buffer; - mBuffers.add(buffer, bo); - return bo; -} - -int EGLBufferObjectManager::allocateStore(buffer_t* bo, - GLsizeiptr size, GLenum usage) -{ - Mutex::Autolock _l(mLock); - if (size != bo->size) { - uint8_t* data = (uint8_t*)malloc(size); - if (data == 0) - return -1; - free(bo->data); - bo->data = data; - bo->size = size; - } - bo->usage = usage; - return 0; -} - -void EGLBufferObjectManager::deleteBuffers(GLsizei n, const GLuint* buffers) -{ - Mutex::Autolock _l(mLock); - while (n--) { - const GLuint t = *buffers++; - if (t) { - int32_t index = mBuffers.indexOfKey(t); - if (index >= 0) { - buffer_t* bo = mBuffers.valueAt(index); - free(bo->data); - mBuffers.removeItemsAt(index); - delete bo; - } - } - } -} - -// ---------------------------------------------------------------------------- -}; // namespace android diff --git a/opengl/libagl/BufferObjectManager.h b/opengl/libagl/BufferObjectManager.h deleted file mode 100644 index fcdae5b635..0000000000 --- a/opengl/libagl/BufferObjectManager.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - ** - ** Copyright 2006, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#ifndef ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H -#define ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H - -#include <atomic> -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> - -#include <utils/RefBase.h> -#include <utils/KeyedVector.h> -#include <utils/Errors.h> - -#include <GLES/gl.h> - -#include "Tokenizer.h" -#include "TokenManager.h" - - -namespace android { - -// ---------------------------------------------------------------------------- - -namespace gl { - -struct buffer_t { - GLsizeiptr size; - GLenum usage; - uint8_t* data; - uint32_t name; -}; - -}; - -class EGLBufferObjectManager : public TokenManager -{ -public: - EGLBufferObjectManager(); - ~EGLBufferObjectManager(); - - // protocol for sp<> - inline void incStrong(const void* id) const; - inline void decStrong(const void* id) const; - typedef void weakref_type; - - gl::buffer_t const* bind(GLuint buffer); - int allocateStore(gl::buffer_t* bo, GLsizeiptr size, GLenum usage); - void deleteBuffers(GLsizei n, const GLuint* buffers); - -private: - mutable std::atomic_size_t mCount; - mutable Mutex mLock; - KeyedVector<GLuint, gl::buffer_t*> mBuffers; -}; - -void EGLBufferObjectManager::incStrong(const void* /*id*/) const { - mCount.fetch_add(1, std::memory_order_relaxed); -} -void EGLBufferObjectManager::decStrong(const void* /*id*/) const { - if (mCount.fetch_sub(1, std::memory_order_release) == 0) { - std::atomic_thread_fence(std::memory_order_acquire); - delete this; - } -} - -// ---------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_OPENGLES_BUFFER_OBJECT_MANAGER_H - diff --git a/opengl/libagl/TextureObjectManager.cpp b/opengl/libagl/TextureObjectManager.cpp deleted file mode 100644 index 06d45cc22e..0000000000 --- a/opengl/libagl/TextureObjectManager.cpp +++ /dev/null @@ -1,325 +0,0 @@ -/* - ** Copyright 2006, The Android Open Source Project - ** - ** Licensed under the Apache License, Version 2.0 (the "License"); - ** you may not use this file except in compliance with the License. - ** You may obtain a copy of the License at - ** - ** http://www.apache.org/licenses/LICENSE-2.0 - ** - ** Unless required by applicable law or agreed to in writing, software - ** distributed under the License is distributed on an "AS IS" BASIS, - ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ** See the License for the specific language governing permissions and - ** limitations under the License. - */ - -#include <stdio.h> -#include <stdlib.h> -#include "context.h" -#include "TextureObjectManager.h" - -namespace android { -// ---------------------------------------------------------------------------- - -EGLTextureObject::EGLTextureObject() - : mSize(0) -{ - init(); -} - -EGLTextureObject::~EGLTextureObject() -{ - if (!direct) { - if (mSize && surface.data) - free(surface.data); - if (mMipmaps) - freeMipmaps(); - } -} - -void EGLTextureObject::init() -{ - memset(&surface, 0, sizeof(surface)); - surface.version = sizeof(surface); - mMipmaps = 0; - mNumExtraLod = 0; - mIsComplete = false; - wraps = GL_REPEAT; - wrapt = GL_REPEAT; - min_filter = GL_LINEAR; - mag_filter = GL_LINEAR; - internalformat = 0; - memset(crop_rect, 0, sizeof(crop_rect)); - generate_mipmap = GL_FALSE; - direct = GL_FALSE; - buffer = 0; -} - -void EGLTextureObject::copyParameters(const sp<EGLTextureObject>& old) -{ - wraps = old->wraps; - wrapt = old->wrapt; - min_filter = old->min_filter; - mag_filter = old->mag_filter; - memcpy(crop_rect, old->crop_rect, sizeof(crop_rect)); - generate_mipmap = old->generate_mipmap; - direct = old->direct; -} - -status_t EGLTextureObject::allocateMipmaps() -{ - // here, by construction, mMipmaps=0 && mNumExtraLod=0 - - if (!surface.data) - return NO_INIT; - - int w = surface.width; - int h = surface.height; - const int numLods = 31 - gglClz(max(w,h)); - if (numLods <= 0) - return NO_ERROR; - - mMipmaps = (GGLSurface*)malloc(numLods * sizeof(GGLSurface)); - if (!mMipmaps) - return NO_MEMORY; - - memset(mMipmaps, 0, numLods * sizeof(GGLSurface)); - mNumExtraLod = numLods; - return NO_ERROR; -} - -void EGLTextureObject::freeMipmaps() -{ - if (mMipmaps) { - for (int i=0 ; i<mNumExtraLod ; i++) { - if (mMipmaps[i].data) { - free(mMipmaps[i].data); - } - } - free(mMipmaps); - mMipmaps = 0; - mNumExtraLod = 0; - } -} - -const GGLSurface& EGLTextureObject::mip(int lod) const -{ - if (lod<=0 || !mMipmaps) - return surface; - lod = min(lod-1, mNumExtraLod-1); - return mMipmaps[lod]; -} - -GGLSurface& EGLTextureObject::editMip(int lod) -{ - return const_cast<GGLSurface&>(mip(lod)); -} - -status_t EGLTextureObject::setSurface(GGLSurface const* s) -{ - // XXX: glFlush() on 's' - if (mSize && surface.data) { - free(surface.data); - } - surface = *s; - internalformat = 0; - buffer = 0; - - // we should keep the crop_rect, but it's delicate because - // the new size of the surface could make it invalid. - // so for now, we just loose it. - memset(crop_rect, 0, sizeof(crop_rect)); - - // it would be nice if we could keep the generate_mipmap flag, - // we would have to generate them right now though. - generate_mipmap = GL_FALSE; - - direct = GL_TRUE; - mSize = 0; // we don't own this surface - if (mMipmaps) - freeMipmaps(); - mIsComplete = true; - return NO_ERROR; -} - -status_t EGLTextureObject::setImage(ANativeWindowBuffer* native_buffer) -{ - GGLSurface sur; - sur.version = sizeof(GGLSurface); - sur.width = native_buffer->width; - sur.height= native_buffer->height; - sur.stride= native_buffer->stride; - sur.format= native_buffer->format; - sur.data = 0; - setSurface(&sur); - buffer = native_buffer; - return NO_ERROR; -} - -status_t EGLTextureObject::reallocate( - GLint level, int w, int h, int s, - int format, int compressedFormat, int bpr) -{ - const size_t size = h * bpr; - if (level == 0) - { - if (size!=mSize || !surface.data) { - if (mSize && surface.data) { - free(surface.data); - } - surface.data = (GGLubyte*)malloc(size); - if (!surface.data) { - mSize = 0; - mIsComplete = false; - return NO_MEMORY; - } - mSize = size; - } - surface.version = sizeof(GGLSurface); - surface.width = w; - surface.height = h; - surface.stride = s; - surface.format = format; - surface.compressedFormat = compressedFormat; - if (mMipmaps) - freeMipmaps(); - mIsComplete = true; - } - else - { - if (!mMipmaps) { - if (allocateMipmaps() != NO_ERROR) - return NO_MEMORY; - } - - ALOGW_IF(level-1 >= mNumExtraLod, - "specifying mipmap level %d, but # of level is %d", - level, mNumExtraLod+1); - - GGLSurface& mipmap = editMip(level); - if (mipmap.data) - free(mipmap.data); - - mipmap.data = (GGLubyte*)malloc(size); - if (!mipmap.data) { - memset(&mipmap, 0, sizeof(GGLSurface)); - mIsComplete = false; - return NO_MEMORY; - } - - mipmap.version = sizeof(GGLSurface); - mipmap.width = w; - mipmap.height = h; - mipmap.stride = s; - mipmap.format = format; - mipmap.compressedFormat = compressedFormat; - - // check if the texture is complete - mIsComplete = true; - const GGLSurface* prev = &surface; - for (int i=0 ; i<mNumExtraLod ; i++) { - const GGLSurface* curr = mMipmaps + i; - if (curr->format != surface.format) { - mIsComplete = false; - break; - } - - uint32_t w = (prev->width >> 1) ? : 1; - uint32_t h = (prev->height >> 1) ? : 1; - if (w != curr->width || h != curr->height) { - mIsComplete = false; - break; - } - prev = curr; - } - } - return NO_ERROR; -} - -// ---------------------------------------------------------------------------- - -EGLSurfaceManager::EGLSurfaceManager() - : TokenManager() -{ -} - -EGLSurfaceManager::~EGLSurfaceManager() -{ - // everything gets freed automatically here... -} - -sp<EGLTextureObject> EGLSurfaceManager::createTexture(GLuint name) -{ - sp<EGLTextureObject> result; - - Mutex::Autolock _l(mLock); - if (mTextures.indexOfKey(name) >= 0) - return result; // already exists! - - result = new EGLTextureObject(); - - status_t err = mTextures.add(name, result); - if (err < 0) - result.clear(); - - return result; -} - -sp<EGLTextureObject> EGLSurfaceManager::removeTexture(GLuint name) -{ - Mutex::Autolock _l(mLock); - const ssize_t index = mTextures.indexOfKey(name); - if (index >= 0) { - sp<EGLTextureObject> result(mTextures.valueAt(index)); - mTextures.removeItemsAt(index); - return result; - } - return 0; -} - -sp<EGLTextureObject> EGLSurfaceManager::replaceTexture(GLuint name) -{ - sp<EGLTextureObject> tex; - Mutex::Autolock _l(mLock); - const ssize_t index = mTextures.indexOfKey(name); - if (index >= 0) { - const sp<EGLTextureObject>& old = mTextures.valueAt(index); - const uint32_t refs = old->getStrongCount(); - if (ggl_likely(refs == 1)) { - // we're the only owner - tex = old; - } else { - // keep the texture's parameters - tex = new EGLTextureObject(); - tex->copyParameters(old); - mTextures.removeItemsAt(index); - mTextures.add(name, tex); - } - } - return tex; -} - -void EGLSurfaceManager::deleteTextures(GLsizei n, const GLuint *tokens) -{ - // free all textures - Mutex::Autolock _l(mLock); - for (GLsizei i=0 ; i<n ; i++) { - const GLuint t(*tokens++); - if (t) { - mTextures.removeItem(t); - } - } -} - -sp<EGLTextureObject> EGLSurfaceManager::texture(GLuint name) -{ - Mutex::Autolock _l(mLock); - const ssize_t index = mTextures.indexOfKey(name); - if (index >= 0) - return mTextures.valueAt(index); - return 0; -} - -// ---------------------------------------------------------------------------- -}; // namespace android diff --git a/opengl/libagl/TextureObjectManager.h b/opengl/libagl/TextureObjectManager.h deleted file mode 100644 index 9cf8771329..0000000000 --- a/opengl/libagl/TextureObjectManager.h +++ /dev/null @@ -1,111 +0,0 @@ -/* -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_SURFACE_H -#define ANDROID_OPENGLES_SURFACE_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> - -#include <cutils/atomic.h> -#include <utils/threads.h> -#include <utils/RefBase.h> -#include <utils/KeyedVector.h> -#include <utils/Errors.h> - -#include <private/pixelflinger/ggl_context.h> - -#include <GLES/gl.h> -#include <EGL/egl.h> -#include <EGL/eglext.h> - -#include "Tokenizer.h" -#include "TokenManager.h" - - -namespace android { - -// ---------------------------------------------------------------------------- - -class EGLTextureObject : public LightRefBase<EGLTextureObject> -{ -public: - EGLTextureObject(); - ~EGLTextureObject(); - - status_t setSurface(GGLSurface const* s); - status_t setImage(ANativeWindowBuffer* buffer); - void setImageBits(void* vaddr) { surface.data = (GGLubyte*)vaddr; } - - status_t reallocate(GLint level, - int w, int h, int s, - int format, int compressedFormat, int bpr); - inline size_t size() const { return mSize; } - const GGLSurface& mip(int lod) const; - GGLSurface& editMip(int lod); - bool hasMipmaps() const { return mMipmaps!=0; } - bool isComplete() const { return mIsComplete; } - void copyParameters(const sp<EGLTextureObject>& old); - -private: - status_t allocateMipmaps(); - void freeMipmaps(); - void init(); - size_t mSize; - GGLSurface *mMipmaps; - int mNumExtraLod; - bool mIsComplete; - -public: - GGLSurface surface; - GLenum wraps; - GLenum wrapt; - GLenum min_filter; - GLenum mag_filter; - GLenum internalformat; - GLint crop_rect[4]; - GLint generate_mipmap; - GLint direct; - ANativeWindowBuffer* buffer; -}; - -// ---------------------------------------------------------------------------- - -class EGLSurfaceManager : - public LightRefBase<EGLSurfaceManager>, - public TokenManager -{ -public: - EGLSurfaceManager(); - ~EGLSurfaceManager(); - - sp<EGLTextureObject> createTexture(GLuint name); - sp<EGLTextureObject> removeTexture(GLuint name); - sp<EGLTextureObject> replaceTexture(GLuint name); - void deleteTextures(GLsizei n, const GLuint *tokens); - sp<EGLTextureObject> texture(GLuint name); - -private: - mutable Mutex mLock; - KeyedVector< GLuint, sp<EGLTextureObject> > mTextures; -}; - -// ---------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_OPENGLES_SURFACE_H - diff --git a/opengl/libagl/TokenManager.cpp b/opengl/libagl/TokenManager.cpp deleted file mode 100644 index eea6025d53..0000000000 --- a/opengl/libagl/TokenManager.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* libs/opengles/surface.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdio.h> -#include <stdlib.h> -#include "TokenManager.h" - -namespace android { -// ---------------------------------------------------------------------------- - -TokenManager::TokenManager() -{ - // token 0 is always reserved - mTokenizer.reserve(0); -} - -TokenManager::~TokenManager() -{ -} - -status_t TokenManager::getToken(GLsizei n, GLuint *tokens) -{ - Mutex::Autolock _l(mLock); - for (GLsizei i=0 ; i<n ; i++) - *tokens++ = mTokenizer.acquire(); - return NO_ERROR; -} - -void TokenManager::recycleTokens(GLsizei n, const GLuint *tokens) -{ - Mutex::Autolock _l(mLock); - for (int i=0 ; i<n ; i++) { - const GLuint token = *tokens++; - if (token) { - mTokenizer.release(token); - } - } -} - -bool TokenManager::isTokenValid(GLuint token) const -{ - Mutex::Autolock _l(mLock); - return mTokenizer.isAcquired(token); -} - -// ---------------------------------------------------------------------------- -}; // namespace android - diff --git a/opengl/libagl/TokenManager.h b/opengl/libagl/TokenManager.h deleted file mode 100644 index 49c1469e7f..0000000000 --- a/opengl/libagl/TokenManager.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_TOKEN_MANAGER_H -#define ANDROID_OPENGLES_TOKEN_MANAGER_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> - -#include <utils/threads.h> - -#include <GLES/gl.h> - -#include "Tokenizer.h" - -namespace android { - -// ---------------------------------------------------------------------------- - -class TokenManager -{ -public: - TokenManager(); - ~TokenManager(); - - status_t getToken(GLsizei n, GLuint *tokens); - void recycleTokens(GLsizei n, const GLuint *tokens); - bool isTokenValid(GLuint token) const; - -private: - mutable Mutex mLock; - Tokenizer mTokenizer; -}; - -// ---------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_OPENGLES_TOKEN_MANAGER_H - diff --git a/opengl/libagl/Tokenizer.cpp b/opengl/libagl/Tokenizer.cpp deleted file mode 100644 index ac0a48cc02..0000000000 --- a/opengl/libagl/Tokenizer.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* libs/opengles/Tokenizer.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdio.h> - -#include "Tokenizer.h" - -// ---------------------------------------------------------------------------- - -namespace android { - -ANDROID_BASIC_TYPES_TRAITS(Tokenizer::run_t) - -Tokenizer::Tokenizer() -{ -} - -Tokenizer::Tokenizer(const Tokenizer& other) - : mRanges(other.mRanges) -{ -} - -Tokenizer::~Tokenizer() -{ -} - -uint32_t Tokenizer::acquire() -{ - if (!mRanges.size() || mRanges[0].first) { - _insertTokenAt(0,0); - return 0; - } - - // just extend the first run - const run_t& run = mRanges[0]; - uint32_t token = run.first + run.length; - _insertTokenAt(token, 1); - return token; -} - -bool Tokenizer::isAcquired(uint32_t token) const -{ - return (_indexOrderOf(token) >= 0); -} - -status_t Tokenizer::reserve(uint32_t token) -{ - size_t o; - const ssize_t i = _indexOrderOf(token, &o); - if (i >= 0) { - return BAD_VALUE; // this token is already taken - } - ssize_t err = _insertTokenAt(token, o); - return (err<0) ? err : status_t(NO_ERROR); -} - -status_t Tokenizer::release(uint32_t token) -{ - const ssize_t i = _indexOrderOf(token); - if (i >= 0) { - const run_t& run = mRanges[i]; - if ((token >= run.first) && (token < run.first+run.length)) { - // token in this range, we need to split - run_t& run = mRanges.editItemAt(i); - if ((token == run.first) || (token == run.first+run.length-1)) { - if (token == run.first) { - run.first += 1; - } - run.length -= 1; - if (run.length == 0) { - // XXX: should we systematically remove a run that's empty? - mRanges.removeItemsAt(i); - } - } else { - // split the run - run_t new_run; - new_run.first = token+1; - new_run.length = run.first+run.length - new_run.first; - run.length = token - run.first; - mRanges.insertAt(new_run, i+1); - } - return NO_ERROR; - } - } - return NAME_NOT_FOUND; -} - -ssize_t Tokenizer::_indexOrderOf(uint32_t token, size_t* order) const -{ - // binary search - ssize_t err = NAME_NOT_FOUND; - ssize_t l = 0; - ssize_t h = mRanges.size()-1; - ssize_t mid; - const run_t* a = mRanges.array(); - while (l <= h) { - mid = l + (h - l)/2; - const run_t* const curr = a + mid; - int c = 0; - if (token < curr->first) c = 1; - else if (token >= curr->first+curr->length) c = -1; - if (c == 0) { - err = l = mid; - break; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; - } - } - if (order) *order = l; - return err; -} - -ssize_t Tokenizer::_insertTokenAt(uint32_t token, size_t index) -{ - const size_t c = mRanges.size(); - - if (index >= 1) { - // do we need to merge with the previous run? - run_t& p = mRanges.editItemAt(index-1); - if (p.first+p.length == token) { - p.length += 1; - if (index < c) { - const run_t& n = mRanges[index]; - if (token+1 == n.first) { - p.length += n.length; - mRanges.removeItemsAt(index); - } - } - return index; - } - } - - if (index < c) { - // do we need to merge with the next run? - run_t& n = mRanges.editItemAt(index); - if (token+1 == n.first) { - n.first -= 1; - n.length += 1; - return index; - } - } - - return mRanges.insertAt(run_t(token,1), index); -} - -void Tokenizer::dump() const -{ - const run_t* ranges = mRanges.array(); - const size_t c = mRanges.size(); - ALOGD("Tokenizer (%p, size = %zu)\n", this, c); - for (size_t i=0 ; i<c ; i++) { - ALOGD("%zu: (%u, %u)\n", i, ranges[i].first, ranges[i].length); - } -} - -}; // namespace android - diff --git a/opengl/libagl/Tokenizer.h b/opengl/libagl/Tokenizer.h deleted file mode 100644 index ac555cb7d6..0000000000 --- a/opengl/libagl/Tokenizer.h +++ /dev/null @@ -1,59 +0,0 @@ -/* libs/opengles/Tokenizer.h -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - - -#ifndef ANDROID_OPENGLES_TOKENIZER_H -#define ANDROID_OPENGLES_TOKENIZER_H - -#include <utils/Vector.h> -#include <utils/Errors.h> - -// ---------------------------------------------------------------------------- - -namespace android { - -class Tokenizer -{ -public: - Tokenizer(); - Tokenizer(const Tokenizer& other); - ~Tokenizer(); - - uint32_t acquire(); - status_t reserve(uint32_t token); - status_t release(uint32_t token); - bool isAcquired(uint32_t token) const; - - void dump() const; - - struct run_t { - run_t() {}; - run_t(uint32_t f, uint32_t l) : first(f), length(l) {} - uint32_t first; - uint32_t length; - }; -private: - ssize_t _indexOrderOf(uint32_t token, size_t* order=0) const; - ssize_t _insertTokenAt(uint32_t token, size_t index); - Vector<run_t> mRanges; -}; - -}; // namespace android - -// ---------------------------------------------------------------------------- - -#endif // ANDROID_OPENGLES_TOKENIZER_H diff --git a/opengl/libagl/arch-mips/fixed_asm.S b/opengl/libagl/arch-mips/fixed_asm.S deleted file mode 100644 index a30ffc5473..0000000000 --- a/opengl/libagl/arch-mips/fixed_asm.S +++ /dev/null @@ -1,61 +0,0 @@ -/* libs/opengles/arch-mips/fixed_asm.S -** -** Copyright 2012, 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. -*/ - - - .text - .align 4 - -/* - * this version rounds-to-nearest and saturates numbers - * outside the range (but not NaNs). - */ - - .global gglFloatToFixed - .ent gglFloatToFixed - .type gglFloatToFixed, @function -gglFloatToFixed: -#if !defined(__mips_soft_float) - mfc1 $a0,$f12 -#endif - srl $t0,$a0,31 /* t0 <- sign bit */ - srl $t1,$a0,23 - andi $t1,$t1,0xff /* get the e */ - li $t2,0x8e - subu $t1,$t2,$t1 /* t1=127+15-e */ - blez $t1,0f /* t1<=0? */ - sll $t2,$a0,8 /* mantissa<<8 */ - lui $t3,0x8000 - or $t2,$t2,$t3 /* add the missing 1 */ - subu $t1,$t1,1 - srl $v0,$t2,$t1 - sltiu $t3,$t1,32 /* t3=1 if t1<32, else t3=0. t1>=32 means the float value is too small. */ - andi $t4,$v0,0x1 - srl $v0,$v0,1 /* scale to 16.16 */ - addu $v0,$v0,$t4 /* round-to-nearest */ - subu $t2,$zero,$v0 - movn $v0,$t2,$t0 /* if negative? */ - or $t1,$a0,$zero /* a0=0? */ - movz $v0,$zero,$t1 - movz $v0,$zero,$t3 /* t3=0 then res=0 */ - jr $ra -0: - lui $t1,0x8000 - and $v0,$a0,$t1 /* keep only the sign bit */ - li $t1,0x7fffffff - movz $v0,$t1,$t0 /* positive, maximum value */ - jr $ra - .end gglFloatToFixed diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp deleted file mode 100644 index 2d36c6194e..0000000000 --- a/opengl/libagl/array.cpp +++ /dev/null @@ -1,1590 +0,0 @@ -/* -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdlib.h> -#include <stdio.h> - -#include "context.h" -#include "fp.h" -#include "state.h" -#include "matrix.h" -#include "vertex.h" -#include "light.h" -#include "primitives.h" -#include "texture.h" -#include "BufferObjectManager.h" - -// ---------------------------------------------------------------------------- - -#define VC_CACHE_STATISTICS 0 -#define VC_CACHE_TYPE_NONE 0 -#define VC_CACHE_TYPE_INDEXED 1 -#define VC_CACHE_TYPE_LRU 2 -#define VC_CACHE_TYPE VC_CACHE_TYPE_INDEXED - -#if VC_CACHE_STATISTICS -#include <utils/Timers.h> -#endif - -// ---------------------------------------------------------------------------- - -namespace android { - -static void validate_arrays(ogles_context_t* c, GLenum mode); - -static void compileElements__generic(ogles_context_t*, - vertex_t*, GLint, GLsizei); -static void compileElement__generic(ogles_context_t*, - vertex_t*, GLint); - -static void drawPrimitivesPoints(ogles_context_t*, GLint, GLsizei); -static void drawPrimitivesLineStrip(ogles_context_t*, GLint, GLsizei); -static void drawPrimitivesLineLoop(ogles_context_t*, GLint, GLsizei); -static void drawPrimitivesLines(ogles_context_t*, GLint, GLsizei); -static void drawPrimitivesTriangleStrip(ogles_context_t*, GLint, GLsizei); -static void drawPrimitivesTriangleFan(ogles_context_t*, GLint, GLsizei); -static void drawPrimitivesTriangles(ogles_context_t*, GLint, GLsizei); - -static void drawIndexedPrimitivesPoints(ogles_context_t*, - GLsizei, const GLvoid*); -static void drawIndexedPrimitivesLineStrip(ogles_context_t*, - GLsizei, const GLvoid*); -static void drawIndexedPrimitivesLineLoop(ogles_context_t*, - GLsizei, const GLvoid*); -static void drawIndexedPrimitivesLines(ogles_context_t*, - GLsizei, const GLvoid*); -static void drawIndexedPrimitivesTriangleStrip(ogles_context_t*, - GLsizei, const GLvoid*); -static void drawIndexedPrimitivesTriangleFan(ogles_context_t*, - GLsizei, const GLvoid*); -static void drawIndexedPrimitivesTriangles(ogles_context_t*, - GLsizei, const GLvoid*); - -// ---------------------------------------------------------------------------- - -typedef void (*arrays_prims_fct_t)(ogles_context_t*, GLint, GLsizei); -static const arrays_prims_fct_t drawArraysPrims[] = { - drawPrimitivesPoints, - drawPrimitivesLines, - drawPrimitivesLineLoop, - drawPrimitivesLineStrip, - drawPrimitivesTriangles, - drawPrimitivesTriangleStrip, - drawPrimitivesTriangleFan -}; - -typedef void (*elements_prims_fct_t)(ogles_context_t*, GLsizei, const GLvoid*); -static const elements_prims_fct_t drawElementsPrims[] = { - drawIndexedPrimitivesPoints, - drawIndexedPrimitivesLines, - drawIndexedPrimitivesLineLoop, - drawIndexedPrimitivesLineStrip, - drawIndexedPrimitivesTriangles, - drawIndexedPrimitivesTriangleStrip, - drawIndexedPrimitivesTriangleFan -}; - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -void ogles_init_array(ogles_context_t* c) -{ - c->arrays.vertex.size = 4; - c->arrays.vertex.type = GL_FLOAT; - c->arrays.color.size = 4; - c->arrays.color.type = GL_FLOAT; - c->arrays.normal.size = 4; - c->arrays.normal.type = GL_FLOAT; - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - c->arrays.texture[i].size = 4; - c->arrays.texture[i].type = GL_FLOAT; - } - c->vc.init(); - - if (!c->vc.vBuffer) { - // this could have failed - ogles_error(c, GL_OUT_OF_MEMORY); - } -} - -void ogles_uninit_array(ogles_context_t* c) -{ - c->vc.uninit(); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Array fetchers -#endif - -static void currentColor(ogles_context_t* c, GLfixed* v, const GLvoid*) { - memcpy(v, c->current.color.v, sizeof(vec4_t)); -} -static void currentNormal(ogles_context_t* c, GLfixed* v, const GLvoid*) { - memcpy(v, c->currentNormal.v, sizeof(vec3_t)); -} -static void currentTexCoord(ogles_context_t* c, GLfixed* v, const GLvoid*) { - memcpy(v, c->current.texture[c->arrays.tmu].v, sizeof(vec4_t)); -} - - -static void fetchNop(ogles_context_t*, GLfixed*, const GLvoid*) { -} -static void fetch2b(ogles_context_t*, GLfixed* v, const GLbyte* p) { - v[0] = gglIntToFixed(p[0]); - v[1] = gglIntToFixed(p[1]); -} -static void fetch2s(ogles_context_t*, GLfixed* v, const GLshort* p) { - v[0] = gglIntToFixed(p[0]); - v[1] = gglIntToFixed(p[1]); -} -static void fetch2x(ogles_context_t*, GLfixed* v, const GLfixed* p) { - memcpy(v, p, 2*sizeof(GLfixed)); -} -static void fetch2f(ogles_context_t*, GLfixed* v, const GLfloat* p) { - v[0] = gglFloatToFixed(p[0]); - v[1] = gglFloatToFixed(p[1]); -} -static void fetch3b(ogles_context_t*, GLfixed* v, const GLbyte* p) { - v[0] = gglIntToFixed(p[0]); - v[1] = gglIntToFixed(p[1]); - v[2] = gglIntToFixed(p[2]); -} -static void fetch3s(ogles_context_t*, GLfixed* v, const GLshort* p) { - v[0] = gglIntToFixed(p[0]); - v[1] = gglIntToFixed(p[1]); - v[2] = gglIntToFixed(p[2]); -} -static void fetch3x(ogles_context_t*, GLfixed* v, const GLfixed* p) { - memcpy(v, p, 3*sizeof(GLfixed)); -} -static void fetch3f(ogles_context_t*, GLfixed* v, const GLfloat* p) { - v[0] = gglFloatToFixed(p[0]); - v[1] = gglFloatToFixed(p[1]); - v[2] = gglFloatToFixed(p[2]); -} -static void fetch4b(ogles_context_t*, GLfixed* v, const GLbyte* p) { - v[0] = gglIntToFixed(p[0]); - v[1] = gglIntToFixed(p[1]); - v[2] = gglIntToFixed(p[2]); - v[3] = gglIntToFixed(p[3]); -} -static void fetch4s(ogles_context_t*, GLfixed* v, const GLshort* p) { - v[0] = gglIntToFixed(p[0]); - v[1] = gglIntToFixed(p[1]); - v[2] = gglIntToFixed(p[2]); - v[3] = gglIntToFixed(p[3]); -} -static void fetch4x(ogles_context_t*, GLfixed* v, const GLfixed* p) { - memcpy(v, p, 4*sizeof(GLfixed)); -} -static void fetch4f(ogles_context_t*, GLfixed* v, const GLfloat* p) { - v[0] = gglFloatToFixed(p[0]); - v[1] = gglFloatToFixed(p[1]); - v[2] = gglFloatToFixed(p[2]); - v[3] = gglFloatToFixed(p[3]); -} -static void fetchExpand4ub(ogles_context_t*, GLfixed* v, const GLubyte* p) { - v[0] = GGL_UB_TO_X(p[0]); - v[1] = GGL_UB_TO_X(p[1]); - v[2] = GGL_UB_TO_X(p[2]); - v[3] = GGL_UB_TO_X(p[3]); -} -static void fetchClamp4x(ogles_context_t*, GLfixed* v, const GLfixed* p) { - v[0] = gglClampx(p[0]); - v[1] = gglClampx(p[1]); - v[2] = gglClampx(p[2]); - v[3] = gglClampx(p[3]); -} -static void fetchClamp4f(ogles_context_t*, GLfixed* v, const GLfloat* p) { - v[0] = gglClampx(gglFloatToFixed(p[0])); - v[1] = gglClampx(gglFloatToFixed(p[1])); - v[2] = gglClampx(gglFloatToFixed(p[2])); - v[3] = gglClampx(gglFloatToFixed(p[3])); -} -static void fetchExpand3ub(ogles_context_t*, GLfixed* v, const GLubyte* p) { - v[0] = GGL_UB_TO_X(p[0]); - v[1] = GGL_UB_TO_X(p[1]); - v[2] = GGL_UB_TO_X(p[2]); - v[3] = 0x10000; -} -static void fetchClamp3x(ogles_context_t*, GLfixed* v, const GLfixed* p) { - v[0] = gglClampx(p[0]); - v[1] = gglClampx(p[1]); - v[2] = gglClampx(p[2]); - v[3] = 0x10000; -} -static void fetchClamp3f(ogles_context_t*, GLfixed* v, const GLfloat* p) { - v[0] = gglClampx(gglFloatToFixed(p[0])); - v[1] = gglClampx(gglFloatToFixed(p[1])); - v[2] = gglClampx(gglFloatToFixed(p[2])); - v[3] = 0x10000; -} -static void fetchExpand3b(ogles_context_t*, GLfixed* v, const GLbyte* p) { - v[0] = GGL_B_TO_X(p[0]); - v[1] = GGL_B_TO_X(p[1]); - v[2] = GGL_B_TO_X(p[2]); -} -static void fetchExpand3s(ogles_context_t*, GLfixed* v, const GLshort* p) { - v[0] = GGL_S_TO_X(p[0]); - v[1] = GGL_S_TO_X(p[1]); - v[2] = GGL_S_TO_X(p[2]); -} - -typedef array_t::fetcher_t fn_t; - -static const fn_t color_fct[2][16] = { // size={3,4}, type={ub,f,x} - { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0, - (fn_t)fetch3f, 0, 0, 0, 0, 0, - (fn_t)fetch3x }, - { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0, - (fn_t)fetch4f, 0, 0, 0, 0, 0, - (fn_t)fetch4x }, -}; -static const fn_t color_clamp_fct[2][16] = { // size={3,4}, type={ub,f,x} - { 0, (fn_t)fetchExpand3ub, 0, 0, 0, 0, - (fn_t)fetchClamp3f, 0, 0, 0, 0, 0, - (fn_t)fetchClamp3x }, - { 0, (fn_t)fetchExpand4ub, 0, 0, 0, 0, - (fn_t)fetchClamp4f, 0, 0, 0, 0, 0, - (fn_t)fetchClamp4x }, -}; -static const fn_t normal_fct[1][16] = { // size={3}, type={b,s,f,x} - { (fn_t)fetchExpand3b, 0, - (fn_t)fetchExpand3s, 0, 0, 0, - (fn_t)fetch3f, 0, 0, 0, 0, 0, - (fn_t)fetch3x }, -}; -static const fn_t vertex_fct[3][16] = { // size={2,3,4}, type={b,s,f,x} - { (fn_t)fetch2b, 0, - (fn_t)fetch2s, 0, 0, 0, - (fn_t)fetch2f, 0, 0, 0, 0, 0, - (fn_t)fetch3x }, - { (fn_t)fetch3b, 0, - (fn_t)fetch3s, 0, 0, 0, - (fn_t)fetch3f, 0, 0, 0, 0, 0, - (fn_t)fetch3x }, - { (fn_t)fetch4b, 0, - (fn_t)fetch4s, 0, 0, 0, - (fn_t)fetch4f, 0, 0, 0, 0, 0, - (fn_t)fetch4x } -}; -static const fn_t texture_fct[3][16] = { // size={2,3,4}, type={b,s,f,x} - { (fn_t)fetch2b, 0, - (fn_t)fetch2s, 0, 0, 0, - (fn_t)fetch2f, 0, 0, 0, 0, 0, - (fn_t)fetch2x }, - { (fn_t)fetch3b, 0, - (fn_t)fetch3s, 0, 0, 0, - (fn_t)fetch3f, 0, 0, 0, 0, 0, - (fn_t)fetch3x }, - { (fn_t)fetch4b, 0, - (fn_t)fetch4s, 0, 0, 0, - (fn_t)fetch4f, 0, 0, 0, 0, 0, - (fn_t)fetch4x } -}; - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark array_t -#endif - -void array_t::init( - GLint size, GLenum type, GLsizei stride, - const GLvoid *pointer, const buffer_t* bo, GLsizei count) -{ - if (!stride) { - stride = size; - switch (type) { - case GL_SHORT: - case GL_UNSIGNED_SHORT: - stride *= 2; - break; - case GL_FLOAT: - case GL_FIXED: - stride *= 4; - break; - } - } - this->size = size; - this->type = type; - this->stride = stride; - this->pointer = pointer; - this->bo = bo; - this->bounds = count; -} - -inline void array_t::resolve() -{ - physical_pointer = (bo) ? (bo->data + uintptr_t(pointer)) : pointer; -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark vertex_cache_t -#endif - -void vertex_cache_t::init() -{ - // make sure the size of vertex_t allows cache-line alignment - CTA<(sizeof(vertex_t) & 0x1F) == 0> assertAlignedSize; - (void)assertAlignedSize; // suppress unused warning. - - const int align = 32; - const size_t s = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; - const size_t size = s*sizeof(vertex_t) + align; - base = malloc(size); - if (base) { - memset(base, 0, size); - vBuffer = (vertex_t*)((size_t(base) + align - 1) & ~(align-1)); - vCache = vBuffer + VERTEX_BUFFER_SIZE; - sequence = 0; - } -} - -void vertex_cache_t::uninit() -{ - free(base); - base = vBuffer = vCache = 0; -} - -void vertex_cache_t::clear() -{ -#if VC_CACHE_STATISTICS - startTime = systemTime(SYSTEM_TIME_THREAD); - total = 0; - misses = 0; -#endif - -#if VC_CACHE_TYPE == VC_CACHE_TYPE_LRU - vertex_t* v = vBuffer; - size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; - do { - v->mru = 0; - v++; - } while (--count); -#endif - - sequence += INDEX_SEQ; - if (sequence >= 0x80000000LU) { - sequence = INDEX_SEQ; - vertex_t* v = vBuffer; - size_t count = VERTEX_BUFFER_SIZE + VERTEX_CACHE_SIZE; - do { - v->index = 0; - v++; - } while (--count); - } -} - -#if VC_CACHE_STATISTICS -void vertex_cache_t::dump_stats(GLenum mode) -{ - nsecs_t time = systemTime(SYSTEM_TIME_THREAD) - startTime; - uint32_t hits = total - misses; - uint32_t prim_count; - switch (mode) { - case GL_POINTS: prim_count = total; break; - case GL_LINE_STRIP: prim_count = total - 1; break; - case GL_LINE_LOOP: prim_count = total - 1; break; - case GL_LINES: prim_count = total / 2; break; - case GL_TRIANGLE_STRIP: prim_count = total - 2; break; - case GL_TRIANGLE_FAN: prim_count = total - 2; break; - case GL_TRIANGLES: prim_count = total / 3; break; - default: return; - } - printf( "total=%5u, hits=%5u, miss=%5u, hitrate=%3u%%," - " prims=%5u, time=%6u us, prims/s=%d, v/t=%f\n", - total, hits, misses, (hits*100)/total, - prim_count, int(ns2us(time)), int(prim_count*float(seconds(1))/time), - float(misses) / prim_count); -} -#else -void vertex_cache_t::dump_stats(GLenum /*mode*/) -{ -} -#endif - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -static __attribute__((noinline)) -void enableDisableClientState(ogles_context_t* c, GLenum array, bool enable) -{ - const int tmu = c->arrays.activeTexture; - array_t* a; - switch (array) { - case GL_COLOR_ARRAY: a = &c->arrays.color; break; - case GL_NORMAL_ARRAY: a = &c->arrays.normal; break; - case GL_TEXTURE_COORD_ARRAY: a = &c->arrays.texture[tmu]; break; - case GL_VERTEX_ARRAY: a = &c->arrays.vertex; break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - a->enable = enable ? GL_TRUE : GL_FALSE; -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Vertex Cache -#endif - -static __attribute__((noinline)) -vertex_t* cache_vertex(ogles_context_t* c, vertex_t* v, uint32_t index) -{ - #if VC_CACHE_STATISTICS - c->vc.misses++; - #endif - if (ggl_unlikely(v->locked)) { - // we're just looking for an entry in the cache that is not locked. - // and we know that there cannot be more than 2 locked entries - // because a triangle needs at most 3 vertices. - // We never use the first and second entries because they might be in - // use by the striper or faner. Any other entry will do as long as - // it's not locked. - // We compute directly the index of a "free" entry from the locked - // state of v[2] and v[3]. - v = c->vc.vBuffer + 2; - v += v[0].locked | (v[1].locked<<1); - } - // note: compileElement clears v->flags - c->arrays.compileElement(c, v, index); - v->locked = 1; - return v; -} - -static __attribute__((noinline)) -vertex_t* fetch_vertex(ogles_context_t* c, size_t index) -{ - index |= c->vc.sequence; - -#if VC_CACHE_TYPE == VC_CACHE_TYPE_INDEXED - - vertex_t* const v = c->vc.vCache + - (index & (vertex_cache_t::VERTEX_CACHE_SIZE-1)); - - if (ggl_likely(v->index == index)) { - v->locked = 1; - return v; - } - return cache_vertex(c, v, index); - -#elif VC_CACHE_TYPE == VC_CACHE_TYPE_LRU - - vertex_t* v = c->vc.vCache + - (index & ((vertex_cache_t::VERTEX_CACHE_SIZE-1)>>1))*2; - - // always record LRU in v[0] - if (ggl_likely(v[0].index == index)) { - v[0].locked = 1; - v[0].mru = 0; - return &v[0]; - } - - if (ggl_likely(v[1].index == index)) { - v[1].locked = 1; - v[0].mru = 1; - return &v[1]; - } - - const int lru = 1 - v[0].mru; - v[0].mru = lru; - return cache_vertex(c, &v[lru], index); - -#elif VC_CACHE_TYPE == VC_CACHE_TYPE_NONE - - // just for debugging... - vertex_t* v = c->vc.vBuffer + 2; - return cache_vertex(c, v, index); - -#endif -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Primitive Assembly -#endif - -void drawPrimitivesPoints(ogles_context_t* c, GLint first, GLsizei count) -{ - if (ggl_unlikely(count < 1)) - return; - - // vertex cache size must be multiple of 1 - const GLsizei vcs = - (vertex_cache_t::VERTEX_BUFFER_SIZE + - vertex_cache_t::VERTEX_CACHE_SIZE); - do { - vertex_t* v = c->vc.vBuffer; - GLsizei num = count > vcs ? vcs : count; - c->arrays.cull = vertex_t::CLIP_ALL; - c->arrays.compileElements(c, v, first, num); - first += num; - count -= num; - if (!c->arrays.cull) { - // quick/trivial reject of the whole batch - do { - const uint32_t cc = v[0].flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderPoint(c, v); - v++; - num--; - } while (num); - } - } while (count); -} - -// ---------------------------------------------------------------------------- - -void drawPrimitivesLineStrip(ogles_context_t* c, GLint first, GLsizei count) -{ - if (ggl_unlikely(count < 2)) - return; - - vertex_t *v, *v0, *v1; - c->arrays.cull = vertex_t::CLIP_ALL; - c->arrays.compileElement(c, c->vc.vBuffer, first); - first += 1; - count -= 1; - - // vertex cache size must be multiple of 1 - const GLsizei vcs = - (vertex_cache_t::VERTEX_BUFFER_SIZE + - vertex_cache_t::VERTEX_CACHE_SIZE - 1); - do { - v0 = c->vc.vBuffer + 0; - v = c->vc.vBuffer + 1; - GLsizei num = count > vcs ? vcs : count; - c->arrays.compileElements(c, v, first, num); - first += num; - count -= num; - if (!c->arrays.cull) { - // quick/trivial reject of the whole batch - do { - v1 = v++; - const uint32_t cc = v0->flags & v1->flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderLine(c, v0, v1); - v0 = v1; - num--; - } while (num); - } - // copy back the last processed vertex - c->vc.vBuffer[0] = *v0; - c->arrays.cull = v0->flags & vertex_t::CLIP_ALL; - } while (count); -} - -void drawPrimitivesLineLoop(ogles_context_t* c, GLint first, GLsizei count) -{ - if (ggl_unlikely(count < 2)) - return; - drawPrimitivesLineStrip(c, first, count); - if (ggl_likely(count >= 3)) { - vertex_t* v0 = c->vc.vBuffer; - vertex_t* v1 = c->vc.vBuffer + 1; - c->arrays.compileElement(c, v1, first); - const uint32_t cc = v0->flags & v1->flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderLine(c, v0, v1); - } -} - -void drawPrimitivesLines(ogles_context_t* c, GLint first, GLsizei count) -{ - if (ggl_unlikely(count < 2)) - return; - - // vertex cache size must be multiple of 2 - const GLsizei vcs = - ((vertex_cache_t::VERTEX_BUFFER_SIZE + - vertex_cache_t::VERTEX_CACHE_SIZE) / 2) * 2; - do { - vertex_t* v = c->vc.vBuffer; - GLsizei num = count > vcs ? vcs : count; - c->arrays.cull = vertex_t::CLIP_ALL; - c->arrays.compileElements(c, v, first, num); - first += num; - count -= num; - if (!c->arrays.cull) { - // quick/trivial reject of the whole batch - num -= 2; - do { - const uint32_t cc = v[0].flags & v[1].flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderLine(c, v, v+1); - v += 2; - num -= 2; - } while (num >= 0); - } - } while (count >= 2); -} - -// ---------------------------------------------------------------------------- - -static void drawPrimitivesTriangleFanOrStrip(ogles_context_t* c, - GLint first, GLsizei count, int winding) -{ - // winding == 2 : fan - // winding == 1 : strip - - if (ggl_unlikely(count < 3)) - return; - - vertex_t *v, *v0, *v1, *v2; - c->arrays.cull = vertex_t::CLIP_ALL; - c->arrays.compileElements(c, c->vc.vBuffer, first, 2); - first += 2; - count -= 2; - - // vertex cache size must be multiple of 2. This is extremely important - // because it allows us to preserve the same winding when the whole - // batch is culled. We also need 2 extra vertices in the array, because - // we always keep the two first ones. - const GLsizei vcs = - ((vertex_cache_t::VERTEX_BUFFER_SIZE + - vertex_cache_t::VERTEX_CACHE_SIZE - 2) / 2) * 2; - do { - v0 = c->vc.vBuffer + 0; - v1 = c->vc.vBuffer + 1; - v = c->vc.vBuffer + 2; - GLsizei num = count > vcs ? vcs : count; - c->arrays.compileElements(c, v, first, num); - first += num; - count -= num; - if (!c->arrays.cull) { - // quick/trivial reject of the whole batch - do { - v2 = v++; - const uint32_t cc = v0->flags & v1->flags & v2->flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderTriangle(c, v0, v1, v2); - swap(((winding^=1) ? v1 : v0), v2); - num--; - } while (num); - } - if (count) { - v0 = c->vc.vBuffer + 2 + vcs - 2; - v1 = c->vc.vBuffer + 2 + vcs - 1; - if ((winding&2) == 0) { - // for strips copy back the two last compiled vertices - c->vc.vBuffer[0] = *v0; - } - c->vc.vBuffer[1] = *v1; - c->arrays.cull = v0->flags & v1->flags & vertex_t::CLIP_ALL; - } - } while (count > 0); -} - -void drawPrimitivesTriangleStrip(ogles_context_t* c, - GLint first, GLsizei count) { - drawPrimitivesTriangleFanOrStrip(c, first, count, 1); -} - -void drawPrimitivesTriangleFan(ogles_context_t* c, - GLint first, GLsizei count) { - drawPrimitivesTriangleFanOrStrip(c, first, count, 2); -} - -void drawPrimitivesTriangles(ogles_context_t* c, GLint first, GLsizei count) -{ - if (ggl_unlikely(count < 3)) - return; - - // vertex cache size must be multiple of 3 - const GLsizei vcs = - ((vertex_cache_t::VERTEX_BUFFER_SIZE + - vertex_cache_t::VERTEX_CACHE_SIZE) / 3) * 3; - do { - vertex_t* v = c->vc.vBuffer; - GLsizei num = count > vcs ? vcs : count; - c->arrays.cull = vertex_t::CLIP_ALL; - c->arrays.compileElements(c, v, first, num); - first += num; - count -= num; - if (!c->arrays.cull) { - // quick/trivial reject of the whole batch - num -= 3; - do { - const uint32_t cc = v[0].flags & v[1].flags & v[2].flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderTriangle(c, v, v+1, v+2); - v += 3; - num -= 3; - } while (num >= 0); - } - } while (count >= 3); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -// this looks goofy, but gcc does a great job with this... -static inline unsigned int read_index(int type, const GLvoid*& p) { - unsigned int r; - if (type) { - r = *(const GLubyte*)p; - p = (const GLubyte*)p + 1; - } else { - r = *(const GLushort*)p; - p = (const GLushort*)p + 1; - } - return r; -} - -// ---------------------------------------------------------------------------- - -void drawIndexedPrimitivesPoints(ogles_context_t* c, - GLsizei count, const GLvoid *indices) -{ - if (ggl_unlikely(count < 1)) - return; - const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); - do { - vertex_t * v = fetch_vertex(c, read_index(type, indices)); - if (ggl_likely(!(v->flags & vertex_t::CLIP_ALL))) - c->prims.renderPoint(c, v); - v->locked = 0; - count--; - } while(count); -} - -// ---------------------------------------------------------------------------- - -void drawIndexedPrimitivesLineStrip(ogles_context_t* c, - GLsizei count, const GLvoid *indices) -{ - if (ggl_unlikely(count < 2)) - return; - - vertex_t * const v = c->vc.vBuffer; - vertex_t* v0 = v; - vertex_t* v1; - - const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); - c->arrays.compileElement(c, v0, read_index(type, indices)); - count -= 1; - do { - v1 = fetch_vertex(c, read_index(type, indices)); - const uint32_t cc = v0->flags & v1->flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderLine(c, v0, v1); - v0->locked = 0; - v0 = v1; - count--; - } while (count); - v1->locked = 0; -} - -void drawIndexedPrimitivesLineLoop(ogles_context_t* c, - GLsizei count, const GLvoid *indices) -{ - if (ggl_unlikely(count <= 2)) { - drawIndexedPrimitivesLines(c, count, indices); - return; - } - - vertex_t * const v = c->vc.vBuffer; - vertex_t* v0 = v; - vertex_t* v1; - - const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); - c->arrays.compileElement(c, v0, read_index(type, indices)); - count -= 1; - do { - v1 = fetch_vertex(c, read_index(type, indices)); - const uint32_t cc = v0->flags & v1->flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderLine(c, v0, v1); - v0->locked = 0; - v0 = v1; - count--; - } while (count); - v1->locked = 0; - - v1 = c->vc.vBuffer; - const uint32_t cc = v0->flags & v1->flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderLine(c, v0, v1); -} - -void drawIndexedPrimitivesLines(ogles_context_t* c, - GLsizei count, const GLvoid *indices) -{ - if (ggl_unlikely(count < 2)) - return; - - count -= 2; - const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); - do { - vertex_t* const v0 = fetch_vertex(c, read_index(type, indices)); - vertex_t* const v1 = fetch_vertex(c, read_index(type, indices)); - const uint32_t cc = v0->flags & v1->flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderLine(c, v0, v1); - v0->locked = 0; - v1->locked = 0; - count -= 2; - } while (count >= 0); -} - -// ---------------------------------------------------------------------------- - -static void drawIndexedPrimitivesTriangleFanOrStrip(ogles_context_t* c, - GLsizei count, const GLvoid *indices, int winding) -{ - // winding == 2 : fan - // winding == 1 : strip - - if (ggl_unlikely(count < 3)) - return; - - vertex_t * const v = c->vc.vBuffer; - vertex_t* v0 = v; - vertex_t* v1 = v+1; - vertex_t* v2; - - const int type = (c->arrays.indicesType == GL_UNSIGNED_BYTE); - c->arrays.compileElement(c, v0, read_index(type, indices)); - c->arrays.compileElement(c, v1, read_index(type, indices)); - count -= 2; - - // note: GCC 4.1.1 here makes a prety interesting optimization - // where it duplicates the loop below based on c->arrays.indicesType - - do { - v2 = fetch_vertex(c, read_index(type, indices)); - const uint32_t cc = v0->flags & v1->flags & v2->flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderTriangle(c, v0, v1, v2); - vertex_t* & consumed = ((winding^=1) ? v1 : v0); - consumed->locked = 0; - consumed = v2; - count--; - } while (count); - v0->locked = v1->locked = 0; - v2->locked = 0; -} - -void drawIndexedPrimitivesTriangleStrip(ogles_context_t* c, - GLsizei count, const GLvoid *indices) { - drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 1); -} - -void drawIndexedPrimitivesTriangleFan(ogles_context_t* c, - GLsizei count, const GLvoid *indices) { - drawIndexedPrimitivesTriangleFanOrStrip(c, count, indices, 2); -} - -void drawIndexedPrimitivesTriangles(ogles_context_t* c, - GLsizei count, const GLvoid *indices) -{ - if (ggl_unlikely(count < 3)) - return; - - count -= 3; - if (ggl_likely(c->arrays.indicesType == GL_UNSIGNED_SHORT)) { - // This case is probably our most common case... - uint16_t const * p = (uint16_t const *)indices; - do { - vertex_t* const v0 = fetch_vertex(c, *p++); - vertex_t* const v1 = fetch_vertex(c, *p++); - vertex_t* const v2 = fetch_vertex(c, *p++); - const uint32_t cc = v0->flags & v1->flags & v2->flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderTriangle(c, v0, v1, v2); - v0->locked = 0; - v1->locked = 0; - v2->locked = 0; - count -= 3; - } while (count >= 0); - } else { - uint8_t const * p = (uint8_t const *)indices; - do { - vertex_t* const v0 = fetch_vertex(c, *p++); - vertex_t* const v1 = fetch_vertex(c, *p++); - vertex_t* const v2 = fetch_vertex(c, *p++); - const uint32_t cc = v0->flags & v1->flags & v2->flags; - if (ggl_likely(!(cc & vertex_t::CLIP_ALL))) - c->prims.renderTriangle(c, v0, v1, v2); - v0->locked = 0; - v1->locked = 0; - v2->locked = 0; - count -= 3; - } while (count >= 0); - } -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Array compilers -#endif - -void compileElement__generic(ogles_context_t* c, - vertex_t* v, GLint first) -{ - v->flags = 0; - v->index = first; - first &= vertex_cache_t::INDEX_MASK; - const GLubyte* vp = c->arrays.vertex.element(first); - v->obj.z = 0; - v->obj.w = 0x10000; - c->arrays.vertex.fetch(c, v->obj.v, vp); - c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj); - c->arrays.perspective(c, v); -} - -void compileElements__generic(ogles_context_t* c, - vertex_t* v, GLint first, GLsizei count) -{ - const GLubyte* vp = c->arrays.vertex.element( - first & vertex_cache_t::INDEX_MASK); - const size_t stride = c->arrays.vertex.stride; - transform_t const* const mvp = &c->transforms.mvp; - do { - v->flags = 0; - v->index = first++; - v->obj.z = 0; - v->obj.w = 0x10000; - c->arrays.vertex.fetch(c, v->obj.v, vp); - c->arrays.mvp_transform(mvp, &v->clip, &v->obj); - c->arrays.perspective(c, v); - vp += stride; - v++; - } while (--count); -} - -/* -void compileElements__3x_full(ogles_context_t* c, - vertex_t* v, GLint first, GLsizei count) -{ - const GLfixed* vp = (const GLfixed*)c->arrays.vertex.element(first); - const size_t stride = c->arrays.vertex.stride / 4; -// const GLfixed* const& m = c->transforms.mvp.matrix.m; - - GLfixed m[16]; - memcpy(&m, c->transforms.mvp.matrix.m, sizeof(m)); - - do { - const GLfixed rx = vp[0]; - const GLfixed ry = vp[1]; - const GLfixed rz = vp[2]; - vp += stride; - v->index = first++; - v->clip.x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); - v->clip.y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]); - v->clip.z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]); - v->clip.w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]); - - const GLfixed w = v->clip.w; - uint32_t clip = 0; - if (v->clip.x < -w) clip |= vertex_t::CLIP_L; - if (v->clip.x > w) clip |= vertex_t::CLIP_R; - if (v->clip.y < -w) clip |= vertex_t::CLIP_B; - if (v->clip.y > w) clip |= vertex_t::CLIP_T; - if (v->clip.z < -w) clip |= vertex_t::CLIP_N; - if (v->clip.z > w) clip |= vertex_t::CLIP_F; - v->flags = clip; - c->arrays.cull &= clip; - - //c->arrays.perspective(c, v); - v++; - } while (--count); -} -*/ - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark clippers -#endif - -static void clipVec4(vec4_t& nv, - GLfixed t, const vec4_t& s, const vec4_t& p) -{ - for (int i=0; i<4 ; i++) - nv.v[i] = gglMulAddx(t, s.v[i] - p.v[i], p.v[i], 28); -} - -static void clipVertex(ogles_context_t* c, vertex_t* nv, - GLfixed t, const vertex_t* s, const vertex_t* p) -{ - clipVec4(nv->clip, t, s->clip, p->clip); - nv->fog = gglMulAddx(t, s->fog - p->fog, p->fog, 28); - ogles_vertex_project(c, nv); - nv->flags |= vertex_t::LIT | vertex_t::EYE | vertex_t::TT; - nv->flags &= ~vertex_t::CLIP_ALL; -} - -static void clipVertexC(ogles_context_t* c, vertex_t* nv, - GLfixed t, const vertex_t* s, const vertex_t* p) -{ - clipVec4(nv->color, t, s->color, p->color); - clipVertex(c, nv, t, s, p); -} - -static void clipVertexT(ogles_context_t* c, vertex_t* nv, - GLfixed t, const vertex_t* s, const vertex_t* p) -{ - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - if (c->rasterizer.state.texture[i].enable) - clipVec4(nv->texture[i], t, s->texture[i], p->texture[i]); - } - clipVertex(c, nv, t, s, p); -} - -static void clipVertexAll(ogles_context_t* c, vertex_t* nv, - GLfixed t, const vertex_t* s, const vertex_t* p) -{ - clipVec4(nv->color, t, s->color, p->color); - clipVertexT(c, nv, t, s, p); -} - -static void clipEye(ogles_context_t* c, vertex_t* nv, - GLfixed t, const vertex_t* s, const vertex_t* p) -{ - nv->clear(); - c->arrays.clipVertex(c, nv, t, p, s); - clipVec4(nv->eye, t, s->eye, p->eye); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -void validate_arrays(ogles_context_t* c, GLenum mode) -{ - uint32_t enables = c->rasterizer.state.enables; - - // Perspective correction is not need if Ortho transform, but - // the user can still provide the w coordinate manually, so we can't - // automatically turn it off (in fact we could when the 4th coordinate - // is not spcified in the vertex array). - // W interpolation is never needed for points. - GLboolean perspective = - c->perspective && mode!=GL_POINTS && (enables & GGL_ENABLE_TMUS); - c->rasterizer.procs.enableDisable(c, GGL_W_LERP, perspective); - - // set anti-aliasing - GLboolean smooth = GL_FALSE; - switch (mode) { - case GL_POINTS: - smooth = c->point.smooth; - break; - case GL_LINES: - case GL_LINE_LOOP: - case GL_LINE_STRIP: - smooth = c->line.smooth; - break; - } - if (((enables & GGL_ENABLE_AA)?1:0) != smooth) - c->rasterizer.procs.enableDisable(c, GGL_AA, smooth); - - // set the shade model for this primitive - c->rasterizer.procs.shadeModel(c, - (mode == GL_POINTS) ? GL_FLAT : c->lighting.shadeModel); - - // compute all the matrices we'll need... - uint32_t want = - transform_state_t::MVP | - transform_state_t::VIEWPORT; - if (c->lighting.enable) { // needs normal transforms and eye coords - want |= transform_state_t::MVUI; - want |= transform_state_t::MODELVIEW; - } - if (enables & GGL_ENABLE_TMUS) { // needs texture transforms - want |= transform_state_t::TEXTURE; - } - if (c->clipPlanes.enable || (enables & GGL_ENABLE_FOG)) { - want |= transform_state_t::MODELVIEW; // needs eye coords - } - ogles_validate_transform(c, want); - - // textures... - if (enables & GGL_ENABLE_TMUS) - ogles_validate_texture(c); - - // vertex compilers - c->arrays.compileElement = compileElement__generic; - c->arrays.compileElements = compileElements__generic; - - // vertex transform - c->arrays.mvp_transform = - c->transforms.mvp.pointv[c->arrays.vertex.size - 2]; - - c->arrays.mv_transform = - c->transforms.modelview.transform.pointv[c->arrays.vertex.size - 2]; - - /* - * *********************************************************************** - * pick fetchers - * *********************************************************************** - */ - - array_machine_t& am = c->arrays; - am.vertex.fetch = fetchNop; - am.normal.fetch = currentNormal; - am.color.fetch = currentColor; - - if (am.vertex.enable) { - am.vertex.resolve(); - if (am.vertex.bo || am.vertex.pointer) { - am.vertex.fetch = vertex_fct[am.vertex.size-2][am.vertex.type & 0xF]; - } - } - - if (am.normal.enable) { - am.normal.resolve(); - if (am.normal.bo || am.normal.pointer) { - am.normal.fetch = normal_fct[am.normal.size-3][am.normal.type & 0xF]; - } - } - - if (am.color.enable) { - am.color.resolve(); - if (c->lighting.enable) { - if (am.color.bo || am.color.pointer) { - am.color.fetch = color_fct[am.color.size-3][am.color.type & 0xF]; - } - } else { - if (am.color.bo || am.color.pointer) { - am.color.fetch = color_clamp_fct[am.color.size-3][am.color.type & 0xF]; - } - } - } - - int activeTmuCount = 0; - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - am.texture[i].fetch = currentTexCoord; - if (c->rasterizer.state.texture[i].enable) { - - // texture fetchers... - if (am.texture[i].enable) { - am.texture[i].resolve(); - if (am.texture[i].bo || am.texture[i].pointer) { - am.texture[i].fetch = texture_fct[am.texture[i].size-2][am.texture[i].type & 0xF]; - } - } - - // texture transform... - const int index = c->arrays.texture[i].size - 2; - c->arrays.tex_transform[i] = - c->transforms.texture[i].transform.pointv[index]; - - am.tmu = i; - activeTmuCount++; - } - } - - // pick the vertex-clipper - uint32_t clipper = 0; - // we must reload 'enables' here - enables = c->rasterizer.state.enables; - if (enables & GGL_ENABLE_SMOOTH) - clipper |= 1; // we need to interpolate colors - if (enables & GGL_ENABLE_TMUS) - clipper |= 2; // we need to interpolate textures - switch (clipper) { - case 0: c->arrays.clipVertex = clipVertex; break; - case 1: c->arrays.clipVertex = clipVertexC; break; - case 2: c->arrays.clipVertex = clipVertexT; break; - case 3: c->arrays.clipVertex = clipVertexAll; break; - } - c->arrays.clipEye = clipEye; - - // pick the primitive rasterizer - ogles_validate_primitives(c); -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- - -using namespace android; - -#if 0 -#pragma mark - -#pragma mark array API -#endif - -void glVertexPointer( - GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) -{ - ogles_context_t* c = ogles_context_t::get(); - if (size<2 || size>4 || stride<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - switch (type) { - case GL_BYTE: - case GL_SHORT: - case GL_FIXED: - case GL_FLOAT: - break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->arrays.vertex.init(size, type, stride, pointer, c->arrays.array_buffer, 0); -} - -void glColorPointer( - GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) -{ - ogles_context_t* c = ogles_context_t::get(); - if (size!=4 || stride<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - switch (type) { - case GL_UNSIGNED_BYTE: - case GL_FIXED: - case GL_FLOAT: - break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->arrays.color.init(size, type, stride, pointer, c->arrays.array_buffer, 0); -} - -void glNormalPointer( - GLenum type, GLsizei stride, const GLvoid *pointer) -{ - ogles_context_t* c = ogles_context_t::get(); - if (stride<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - switch (type) { - case GL_BYTE: - case GL_SHORT: - case GL_FIXED: - case GL_FLOAT: - break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->arrays.normal.init(3, type, stride, pointer, c->arrays.array_buffer, 0); -} - -void glTexCoordPointer( - GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) -{ - ogles_context_t* c = ogles_context_t::get(); - if (size<2 || size>4 || stride<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - switch (type) { - case GL_BYTE: - case GL_SHORT: - case GL_FIXED: - case GL_FLOAT: - break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - const int tmu = c->arrays.activeTexture; - c->arrays.texture[tmu].init(size, type, stride, pointer, - c->arrays.array_buffer, 0); -} - - -void glEnableClientState(GLenum array) { - ogles_context_t* c = ogles_context_t::get(); - enableDisableClientState(c, array, true); -} - -void glDisableClientState(GLenum array) { - ogles_context_t* c = ogles_context_t::get(); - enableDisableClientState(c, array, false); -} - -void glClientActiveTexture(GLenum texture) -{ - ogles_context_t* c = ogles_context_t::get(); - if (texture<GL_TEXTURE0 || texture>=GL_TEXTURE0+GGL_TEXTURE_UNIT_COUNT) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->arrays.activeTexture = texture - GL_TEXTURE0; -} - -void glDrawArrays(GLenum mode, GLint first, GLsizei count) -{ - ogles_context_t* c = ogles_context_t::get(); - if (count<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - switch (mode) { - case GL_POINTS: - case GL_LINE_STRIP: - case GL_LINE_LOOP: - case GL_LINES: - case GL_TRIANGLE_STRIP: - case GL_TRIANGLE_FAN: - case GL_TRIANGLES: - break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - - if (count == 0 || !c->arrays.vertex.enable) - return; - if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK)) - return; // all triangles are culled - - - validate_arrays(c, mode); - - const uint32_t enables = c->rasterizer.state.enables; - if (enables & GGL_ENABLE_TMUS) - ogles_lock_textures(c); - - drawArraysPrims[mode](c, first, count); - - if (enables & GGL_ENABLE_TMUS) - ogles_unlock_textures(c); - -#if VC_CACHE_STATISTICS - c->vc.total = count; - c->vc.dump_stats(mode); -#endif -} - -void glDrawElements( - GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) -{ - ogles_context_t* c = ogles_context_t::get(); - if (count<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - switch (mode) { - case GL_POINTS: - case GL_LINE_STRIP: - case GL_LINE_LOOP: - case GL_LINES: - case GL_TRIANGLE_STRIP: - case GL_TRIANGLE_FAN: - case GL_TRIANGLES: - break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - switch (type) { - case GL_UNSIGNED_BYTE: - case GL_UNSIGNED_SHORT: - c->arrays.indicesType = type; - break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (count == 0 || !c->arrays.vertex.enable) - return; - if ((c->cull.enable) && (c->cull.cullFace == GL_FRONT_AND_BACK)) - return; // all triangles are culled - - // clear the vertex-cache - c->vc.clear(); - validate_arrays(c, mode); - - // if indices are in a buffer object, the pointer is treated as an - // offset in that buffer. - if (c->arrays.element_array_buffer) { - indices = c->arrays.element_array_buffer->data + uintptr_t(indices); - } - - const uint32_t enables = c->rasterizer.state.enables; - if (enables & GGL_ENABLE_TMUS) - ogles_lock_textures(c); - - drawElementsPrims[mode](c, count, indices); - - if (enables & GGL_ENABLE_TMUS) - ogles_unlock_textures(c); - - -#if VC_CACHE_STATISTICS - c->vc.total = count; - c->vc.dump_stats(mode); -#endif -} - -// ---------------------------------------------------------------------------- -// buffers -// ---------------------------------------------------------------------------- - -void glBindBuffer(GLenum target, GLuint buffer) -{ - ogles_context_t* c = ogles_context_t::get(); - if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - // create a buffer object, or bind an existing one - buffer_t const* bo = 0; - if (buffer) { - bo = c->bufferObjectManager->bind(buffer); - if (!bo) { - ogles_error(c, GL_OUT_OF_MEMORY); - return; - } - } - ((target == GL_ARRAY_BUFFER) ? - c->arrays.array_buffer : c->arrays.element_array_buffer) = bo; -} - -void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) -{ - ogles_context_t* c = ogles_context_t::get(); - if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (size<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - if ((usage!=GL_STATIC_DRAW) && (usage!=GL_DYNAMIC_DRAW)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ? - c->arrays.array_buffer : c->arrays.element_array_buffer); - - if (bo == 0) { - // can't modify buffer 0 - ogles_error(c, GL_INVALID_OPERATION); - return; - } - - buffer_t* edit_bo = const_cast<buffer_t*>(bo); - if (c->bufferObjectManager->allocateStore(edit_bo, size, usage) != 0) { - ogles_error(c, GL_OUT_OF_MEMORY); - return; - } - if (data) { - memcpy(bo->data, data, size); - } -} - -void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) -{ - ogles_context_t* c = ogles_context_t::get(); - if ((target!=GL_ARRAY_BUFFER) && (target!=GL_ELEMENT_ARRAY_BUFFER)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (offset<0 || size<0 || data==0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - buffer_t const* bo = ((target == GL_ARRAY_BUFFER) ? - c->arrays.array_buffer : c->arrays.element_array_buffer); - - if (bo == 0) { - // can't modify buffer 0 - ogles_error(c, GL_INVALID_OPERATION); - return; - } - if (offset+size > bo->size) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - memcpy(bo->data + offset, data, size); -} - -void glDeleteBuffers(GLsizei n, const GLuint* buffers) -{ - ogles_context_t* c = ogles_context_t::get(); - if (n<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - for (int i=0 ; i<n ; i++) { - GLuint name = buffers[i]; - if (name) { - // unbind bound deleted buffers... - if (c->arrays.element_array_buffer) { - if (c->arrays.element_array_buffer->name == name) { - c->arrays.element_array_buffer = 0; - } - } - if (c->arrays.array_buffer) { - if (c->arrays.array_buffer->name == name) { - c->arrays.array_buffer = 0; - } - } - if (c->arrays.vertex.bo) { - if (c->arrays.vertex.bo->name == name) { - c->arrays.vertex.bo = 0; - } - } - if (c->arrays.normal.bo) { - if (c->arrays.normal.bo->name == name) { - c->arrays.normal.bo = 0; - } - } - if (c->arrays.color.bo) { - if (c->arrays.color.bo->name == name) { - c->arrays.color.bo = 0; - } - } - for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) { - if (c->arrays.texture[t].bo) { - if (c->arrays.texture[t].bo->name == name) { - c->arrays.texture[t].bo = 0; - } - } - } - } - } - c->bufferObjectManager->deleteBuffers(n, buffers); - c->bufferObjectManager->recycleTokens(n, buffers); -} - -void glGenBuffers(GLsizei n, GLuint* buffers) -{ - ogles_context_t* c = ogles_context_t::get(); - if (n<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - c->bufferObjectManager->getToken(n, buffers); -} diff --git a/opengl/libagl/array.h b/opengl/libagl/array.h deleted file mode 100644 index e15697860a..0000000000 --- a/opengl/libagl/array.h +++ /dev/null @@ -1,37 +0,0 @@ -/* libs/opengles/array.h -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_ARRAY_H -#define ANDROID_OPENGLES_ARRAY_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> - -namespace android { - -namespace gl { -struct ogles_context_t; -}; - -void ogles_init_array(ogles_context_t* c); -void ogles_uninit_array(ogles_context_t* c); - -}; // namespace android - -#endif // ANDROID_OPENGLES_ARRAY_H - diff --git a/opengl/libagl/context.h b/opengl/libagl/context.h deleted file mode 100644 index 6e77a2366f..0000000000 --- a/opengl/libagl/context.h +++ /dev/null @@ -1,647 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_OPENGLES_CONTEXT_H -#define ANDROID_OPENGLES_CONTEXT_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> -#include <pthread.h> -#ifdef __ANDROID__ -#include <bionic/tls.h> -#endif - -#include <private/pixelflinger/ggl_context.h> - -#include <system/window.h> - -#include <GLES/gl.h> -#include <GLES/glext.h> - -namespace android { - - -const unsigned int OGLES_NUM_COMPRESSED_TEXTURE_FORMATS = 10 -#ifdef GL_OES_compressed_ETC1_RGB8_texture - + 1 -#endif - ; - -class EGLTextureObject; -class EGLSurfaceManager; -class EGLBufferObjectManager; - -namespace gl { - -struct ogles_context_t; -struct matrixx_t; -struct transform_t; -struct buffer_t; - -ogles_context_t* getGlContext(); - -template<typename T> -static inline void swap(T& a, T& b) { - T t(a); a = b; b = t; -} -template<typename T> -inline T max(T a, T b) { - return a<b ? b : a; -} -template<typename T> -inline T max(T a, T b, T c) { - return max(a, max(b, c)); -} -template<typename T> -inline T min(T a, T b) { - return a<b ? a : b; -} -template<typename T> -inline T min(T a, T b, T c) { - return min(a, min(b, c)); -} -template<typename T> -inline T min(T a, T b, T c, T d) { - return min(min(a,b), min(c,d)); -} - -// ---------------------------------------------------------------------------- -// vertices -// ---------------------------------------------------------------------------- - -struct vec3_t { - union { - struct { GLfixed x, y, z; }; - struct { GLfixed r, g, b; }; - struct { GLfixed S, T, R; }; - GLfixed v[3]; - }; -}; - -struct vec4_t { - union { - struct { GLfixed x, y, z, w; }; - struct { GLfixed r, g, b, a; }; - struct { GLfixed S, T, R, Q; }; - GLfixed v[4]; - }; -}; - -struct vertex_t { - enum { - // these constant matter for our clipping - CLIP_L = 0x0001, // clipping flags - CLIP_R = 0x0002, - CLIP_B = 0x0004, - CLIP_T = 0x0008, - CLIP_N = 0x0010, - CLIP_F = 0x0020, - - EYE = 0x0040, - RESERVED = 0x0080, - - USER_CLIP_0 = 0x0100, // user clipping flags - USER_CLIP_1 = 0x0200, - USER_CLIP_2 = 0x0400, - USER_CLIP_3 = 0x0800, - USER_CLIP_4 = 0x1000, - USER_CLIP_5 = 0x2000, - - LIT = 0x4000, // lighting has been applied - TT = 0x8000, // texture coords transformed - - FRUSTUM_CLIP_ALL= 0x003F, - USER_CLIP_ALL = 0x3F00, - CLIP_ALL = 0x3F3F, - }; - - // the fields below are arranged to minimize d-cache usage - // we group together, by cache-line, the fields most likely to be used - - union { - vec4_t obj; - vec4_t eye; - }; - vec4_t clip; - - uint32_t flags; - size_t index; // cache tag, and vertex index - GLfixed fog; - uint8_t locked; - uint8_t mru; - uint8_t reserved[2]; - vec4_t window; - - vec4_t color; - vec4_t texture[GGL_TEXTURE_UNIT_COUNT]; -#ifdef __LP64__ - uint32_t reserved1[2]; -#else - uint32_t reserved1[4]; -#endif - - inline void clear() { - flags = index = locked = mru = 0; - } -}; - -struct point_size_t { - GGLcoord size; - GLboolean smooth; -}; - -struct line_width_t { - GGLcoord width; - GLboolean smooth; -}; - -struct polygon_offset_t { - GLfixed factor; - GLfixed units; - GLboolean enable; -}; - -// ---------------------------------------------------------------------------- -// arrays -// ---------------------------------------------------------------------------- - -struct array_t { - typedef void (*fetcher_t)(ogles_context_t*, GLfixed*, const GLvoid*); - fetcher_t fetch; - GLvoid const* physical_pointer; - GLint size; - GLsizei stride; - GLvoid const* pointer; - buffer_t const* bo; - uint16_t type; - GLboolean enable; - GLboolean pad; - GLsizei bounds; - void init(GLint, GLenum, GLsizei, const GLvoid *, const buffer_t*, GLsizei); - inline void resolve(); - inline const GLubyte* element(GLint i) const { - return (const GLubyte*)physical_pointer + i * stride; - } -}; - -struct array_machine_t { - array_t vertex; - array_t normal; - array_t color; - array_t texture[GGL_TEXTURE_UNIT_COUNT]; - uint8_t activeTexture; - uint8_t tmu; - uint16_t cull; - uint32_t flags; - GLenum indicesType; - buffer_t const* array_buffer; - buffer_t const* element_array_buffer; - - void (*compileElements)(ogles_context_t*, vertex_t*, GLint, GLsizei); - void (*compileElement)(ogles_context_t*, vertex_t*, GLint); - - void (*mvp_transform)(transform_t const*, vec4_t*, vec4_t const*); - void (*mv_transform)(transform_t const*, vec4_t*, vec4_t const*); - void (*tex_transform[2])(transform_t const*, vec4_t*, vec4_t const*); - void (*perspective)(ogles_context_t*c, vertex_t* v); - void (*clipVertex)(ogles_context_t* c, vertex_t* nv, - GGLfixed t, const vertex_t* s, const vertex_t* p); - void (*clipEye)(ogles_context_t* c, vertex_t* nv, - GGLfixed t, const vertex_t* s, const vertex_t* p); -}; - -struct vertex_cache_t { - enum { - // must be at least 4 - // 3 vertice for triangles - // or 2 + 2 for indexed triangles w/ cache contention - VERTEX_BUFFER_SIZE = 8, - // must be a power of two and at least 3 - VERTEX_CACHE_SIZE = 64, // 8 KB - - INDEX_BITS = 16, - INDEX_MASK = ((1LU<<INDEX_BITS)-1), - INDEX_SEQ = 1LU<<INDEX_BITS, - }; - vertex_t* vBuffer; - vertex_t* vCache; - uint32_t sequence; - void* base; - uint32_t total; - uint32_t misses; - int64_t startTime; - void init(); - void uninit(); - void clear(); - void dump_stats(GLenum mode); -}; - -// ---------------------------------------------------------------------------- -// fog -// ---------------------------------------------------------------------------- - -struct fog_t { - GLfixed density; - GLfixed start; - GLfixed end; - GLfixed invEndMinusStart; - GLenum mode; - GLfixed (*fog)(ogles_context_t* c, GLfixed z); -}; - -// ---------------------------------------------------------------------------- -// user clip planes -// ---------------------------------------------------------------------------- - -const unsigned int OGLES_MAX_CLIP_PLANES = 6; - -struct clip_plane_t { - vec4_t equation; -}; - -struct user_clip_planes_t { - clip_plane_t plane[OGLES_MAX_CLIP_PLANES]; - uint32_t enable; -}; - -// ---------------------------------------------------------------------------- -// lighting -// ---------------------------------------------------------------------------- - -const unsigned int OGLES_MAX_LIGHTS = 8; - -struct light_t { - vec4_t ambient; - vec4_t diffuse; - vec4_t specular; - vec4_t implicitAmbient; - vec4_t implicitDiffuse; - vec4_t implicitSpecular; - vec4_t position; // position in eye space - vec4_t objPosition; - vec4_t normalizedObjPosition; - vec4_t spotDir; - vec4_t normalizedSpotDir; - GLfixed spotExp; - GLfixed spotCutoff; - GLfixed spotCutoffCosine; - GLfixed attenuation[3]; - GLfixed rConstAttenuation; - GLboolean enable; -}; - -struct material_t { - vec4_t ambient; - vec4_t diffuse; - vec4_t specular; - vec4_t emission; - GLfixed shininess; -}; - -struct light_model_t { - vec4_t ambient; - GLboolean twoSide; -}; - -struct color_material_t { - GLenum face; - GLenum mode; - GLboolean enable; -}; - -struct lighting_t { - light_t lights[OGLES_MAX_LIGHTS]; - material_t front; - light_model_t lightModel; - color_material_t colorMaterial; - vec4_t implicitSceneEmissionAndAmbient; - vec4_t objViewer; - uint32_t enabledLights; - GLboolean enable; - GLenum shadeModel; - typedef void (*light_fct_t)(ogles_context_t*, vertex_t*); - void (*lightVertex)(ogles_context_t* c, vertex_t* v); - void (*lightTriangle)(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2); -}; - -struct culling_t { - GLenum cullFace; - GLenum frontFace; - GLboolean enable; -}; - -// ---------------------------------------------------------------------------- -// textures -// ---------------------------------------------------------------------------- - -struct texture_unit_t { - GLuint name; - EGLTextureObject* texture; - uint8_t dirty; -}; - -struct texture_state_t -{ - texture_unit_t tmu[GGL_TEXTURE_UNIT_COUNT]; - int active; // active tmu - EGLTextureObject* defaultTexture; - GGLContext* ggl; - uint8_t packAlignment; - uint8_t unpackAlignment; -}; - -// ---------------------------------------------------------------------------- -// transformation and matrices -// ---------------------------------------------------------------------------- - -struct matrixf_t; - -struct matrixx_t { - GLfixed m[16]; - void load(const matrixf_t& rhs); -}; - -struct matrix_stack_t; - - -struct matrixf_t { - void loadIdentity(); - void load(const matrixf_t& rhs); - - inline GLfloat* editElements() { return m; } - inline GLfloat const* elements() const { return m; } - - void set(const GLfixed* rhs); - void set(const GLfloat* rhs); - - static void multiply(matrixf_t& r, - const matrixf_t& lhs, const matrixf_t& rhs); - - void dump(const char* what); - -private: - friend struct matrix_stack_t; - GLfloat m[16]; - void load(const GLfixed* rhs); - void load(const GLfloat* rhs); - void multiply(const matrixf_t& rhs); - void translate(GLfloat x, GLfloat y, GLfloat z); - void scale(GLfloat x, GLfloat y, GLfloat z); - void rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z); -}; - -enum { - OP_IDENTITY = 0x00, - OP_TRANSLATE = 0x01, - OP_UNIFORM_SCALE = 0x02, - OP_SCALE = 0x05, - OP_ROTATE = 0x08, - OP_SKEW = 0x10, - OP_ALL = 0x1F -}; - -struct transform_t { - enum { - FLAGS_2D_PROJECTION = 0x1 - }; - matrixx_t matrix; - uint32_t flags; - uint32_t ops; - - union { - struct { - void (*point2)(transform_t const* t, vec4_t*, vec4_t const*); - void (*point3)(transform_t const* t, vec4_t*, vec4_t const*); - void (*point4)(transform_t const* t, vec4_t*, vec4_t const*); - }; - void (*pointv[3])(transform_t const* t, vec4_t*, vec4_t const*); - }; - - void loadIdentity(); - void picker(); - void dump(const char* what); -}; - -struct mvui_transform_t : public transform_t -{ - void picker(); -}; - -struct matrix_stack_t { - enum { - DO_PICKER = 0x1, - DO_FLOAT_TO_FIXED = 0x2 - }; - transform_t transform; - uint8_t maxDepth; - uint8_t depth; - uint8_t dirty; - uint8_t reserved; - matrixf_t *stack; - uint8_t *ops; - void init(int depth); - void uninit(); - void loadIdentity(); - void load(const GLfixed* rhs); - void load(const GLfloat* rhs); - void multiply(const matrixf_t& rhs); - void translate(GLfloat x, GLfloat y, GLfloat z); - void scale(GLfloat x, GLfloat y, GLfloat z); - void rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z); - GLint push(); - GLint pop(); - void validate(); - matrixf_t& top() { return stack[depth]; } - const matrixf_t& top() const { return stack[depth]; } - uint32_t top_ops() const { return ops[depth]; } - inline bool isRigidBody() const { - return !(ops[depth] & ~(OP_TRANSLATE|OP_UNIFORM_SCALE|OP_ROTATE)); - } -}; - -struct vp_transform_t { - transform_t transform; - matrixf_t matrix; - GLfloat zNear; - GLfloat zFar; - void loadIdentity(); -}; - -struct transform_state_t { - enum { - MODELVIEW = 0x01, - PROJECTION = 0x02, - VIEWPORT = 0x04, - TEXTURE = 0x08, - MVUI = 0x10, - MVIT = 0x20, - MVP = 0x40, - }; - matrix_stack_t *current; - matrix_stack_t modelview; - matrix_stack_t projection; - matrix_stack_t texture[GGL_TEXTURE_UNIT_COUNT]; - - // modelview * projection - transform_t mvp __attribute__((aligned(32))); - // viewport transformation - vp_transform_t vpt __attribute__((aligned(32))); - // same for 4-D vertices - transform_t mvp4; - // full modelview inverse transpose - transform_t mvit4; - // upper 3x3 of mv-inverse-transpose (for normals) - mvui_transform_t mvui; - - GLenum matrixMode; - GLenum rescaleNormals; - uint32_t dirty; - void invalidate(); - void update_mvp(); - void update_mvit(); - void update_mvui(); -}; - -struct viewport_t { - GLint x; - GLint y; - GLsizei w; - GLsizei h; - struct { - GLint x; - GLint y; - } surfaceport; - struct { - GLint x; - GLint y; - GLsizei w; - GLsizei h; - } scissor; -}; - -// ---------------------------------------------------------------------------- -// Lerping -// ---------------------------------------------------------------------------- - -struct compute_iterators_t -{ - void initTriangle( - vertex_t const* v0, - vertex_t const* v1, - vertex_t const* v2); - - void initLine( - vertex_t const* v0, - vertex_t const* v1); - - inline void initLerp(vertex_t const* v0, uint32_t enables); - - int iteratorsScale(int32_t it[3], - int32_t c0, int32_t c1, int32_t c2) const; - - void iterators1616(GGLfixed it[3], - GGLfixed c0, GGLfixed c1, GGLfixed c2) const; - - void iterators0032(int32_t it[3], - int32_t c0, int32_t c1, int32_t c2) const; - - void iterators0032(int64_t it[3], - int32_t c0, int32_t c1, int32_t c2) const; - - GGLcoord area() const { return m_area; } - -private: - // don't change order of members here -- used by iterators.S - GGLcoord m_dx01, m_dy10, m_dx20, m_dy02; - GGLcoord m_x0, m_y0; - GGLcoord m_area; - uint8_t m_scale; - uint8_t m_area_scale; - uint8_t m_reserved[2]; - -}; - -// ---------------------------------------------------------------------------- -// state -// ---------------------------------------------------------------------------- - -#ifdef __ANDROID__ - // We have a dedicated TLS slot in bionic - inline void setGlThreadSpecific(ogles_context_t *value) { - __get_tls()[TLS_SLOT_OPENGL] = value; - } - inline ogles_context_t* getGlThreadSpecific() { - return static_cast<ogles_context_t*>(__get_tls()[TLS_SLOT_OPENGL]); - } -#else - extern pthread_key_t gGLKey; - inline void setGlThreadSpecific(ogles_context_t *value) { - pthread_setspecific(gGLKey, value); - } - inline ogles_context_t* getGlThreadSpecific() { - return static_cast<ogles_context_t*>(pthread_getspecific(gGLKey)); - } -#endif - - -struct prims_t { - typedef ogles_context_t* GL; - void (*renderPoint)(GL, vertex_t*); - void (*renderLine)(GL, vertex_t*, vertex_t*); - void (*renderTriangle)(GL, vertex_t*, vertex_t*, vertex_t*); -}; - -struct ogles_context_t { - context_t rasterizer; - array_machine_t arrays __attribute__((aligned(32))); - texture_state_t textures; - transform_state_t transforms; - vertex_cache_t vc; - prims_t prims; - culling_t cull; - lighting_t lighting; - user_clip_planes_t clipPlanes; - compute_iterators_t lerp __attribute__((aligned(32))); - vertex_t current; - vec4_t currentColorClamped; - vec3_t currentNormal; - viewport_t viewport; - point_size_t point; - line_width_t line; - polygon_offset_t polygonOffset; - fog_t fog; - uint32_t perspective : 1; - uint32_t transformTextures : 1; - EGLSurfaceManager* surfaceManager; - EGLBufferObjectManager* bufferObjectManager; - - GLenum error; - - static inline ogles_context_t* get() { - return getGlThreadSpecific(); - } - -}; - -}; // namespace gl -}; // namespace android - -using namespace android::gl; - -#endif // ANDROID_OPENGLES_CONTEXT_H - diff --git a/opengl/libagl/dxt.cpp b/opengl/libagl/dxt.cpp deleted file mode 100644 index 238c81fae9..0000000000 --- a/opengl/libagl/dxt.cpp +++ /dev/null @@ -1,636 +0,0 @@ -/* libs/opengles/dxt.cpp -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define TIMING 0 - -#if TIMING -#include <sys/time.h> // for optimization timing -#include <stdio.h> -#include <stdlib.h> -#endif - -#include <GLES/gl.h> -#include <utils/Endian.h> - -#include "context.h" - -#define TIMING 0 - -namespace android { - -static uint8_t avg23tab[64*64]; -static volatile int tables_initialized = 0; - -// Definitions below are equivalent to these over the valid range of arguments -// #define div5(x) ((x)/5) -// #define div7(x) ((x)/7) - -// Use fixed-point to divide by 5 and 7 -// 3277 = 2^14/5 + 1 -// 2341 = 2^14/7 + 1 -#define div5(x) (((x)*3277) >> 14) -#define div7(x) (((x)*2341) >> 14) - -// Table with entry [a << 6 | b] = (2*a + b)/3 for 0 <= a,b < 64 -#define avg23(x0,x1) avg23tab[((x0) << 6) | (x1)] - -// Extract 5/6/5 RGB -#define red(x) (((x) >> 11) & 0x1f) -#define green(x) (((x) >> 5) & 0x3f) -#define blue(x) ( (x) & 0x1f) - -/* - * Convert 5/6/5 RGB (as 3 ints) to 8/8/8 - * - * Operation count: 8 <<, 0 &, 5 | - */ -inline static int rgb565SepTo888(int r, int g, int b) - -{ - return ((((r << 3) | (r >> 2)) << 16) | - (((g << 2) | (g >> 4)) << 8) | - ((b << 3) | (b >> 2))); -} - -/* - * Convert 5/6/5 RGB (as a single 16-bit word) to 8/8/8 - * - * r4r3r2r1 r0g5g4g3 g2g1g0b4 b3b2b1b0 rgb - * r4r3r2 r1r0g5g4 g3g2g1g0 b4b3b2b1 b0 0 0 0 rgb << 3 - * r4r3r2r1 r0r4r3r2 g5g4g3g2 g1g0g5g4 b4b3b2b1 b0b4b3b2 desired result - * - * Construct the 24-bit RGB word as: - * - * r4r3r2r1 r0------ -------- -------- -------- -------- (rgb << 8) & 0xf80000 - * r4r3r2 -------- -------- -------- -------- (rgb << 3) & 0x070000 - * g5g4g3g2 g1g0---- -------- -------- (rgb << 5) & 0x00fc00 - * g5g4 -------- -------- (rgb >> 1) & 0x000300 - * b4b3b2b1 b0------ (rgb << 3) & 0x0000f8 - * b4b3b2 (rgb >> 2) & 0x000007 - * - * Operation count: 5 <<, 6 &, 5 | (n.b. rgb >> 3 is used twice) - */ -inline static int rgb565To888(int rgb) - -{ - int rgb3 = rgb >> 3; - return (((rgb << 8) & 0xf80000) | - ( rgb3 & 0x070000) | - ((rgb << 5) & 0x00fc00) | - ((rgb >> 1) & 0x000300) | - ( rgb3 & 0x0000f8) | - ((rgb >> 2) & 0x000007)); -} - -#if __BYTE_ORDER == __BIG_ENDIAN -static uint32_t swap(uint32_t x) { - int b0 = (x >> 24) & 0xff; - int b1 = (x >> 16) & 0xff; - int b2 = (x >> 8) & 0xff; - int b3 = (x ) & 0xff; - - return (uint32_t)((b3 << 24) | (b2 << 16) | (b1 << 8) | b0); -} -#endif - -static void -init_tables() -{ - if (tables_initialized) { - return; - } - - for (int i = 0; i < 64; i++) { - for (int j = 0; j < 64; j++) { - int avg = (2*i + j)/3; - avg23tab[(i << 6) | j] = avg; - } - } - - asm volatile ("" : : : "memory"); - tables_initialized = 1; -} - -/* - * Utility to scan a DXT1 compressed texture to determine whether it - * contains a transparent pixel (color0 < color1, code == 3). This - * may be useful if the application lacks information as to whether - * the true format is GL_COMPRESSED_RGB_S3TC_DXT1_EXT or - * GL_COMPRESSED_RGBA_S3TC_DXT1_EXT. - */ -bool -DXT1HasAlpha(const GLvoid *data, int width, int height) { -#if TIMING - struct timeval start_t, end_t; - struct timezone tz; - - gettimeofday(&start_t, &tz); -#endif - - bool hasAlpha = false; - - int xblocks = (width + 3)/4; - int yblocks = (height + 3)/4; - int numblocks = xblocks*yblocks; - - uint32_t const *d32 = (uint32_t *)data; - for (int b = 0; b < numblocks; b++) { - uint32_t colors = *d32++; - -#if __BYTE_ORDER == __BIG_ENDIAN - colors = swap(colors); -#endif - - uint16_t color0 = colors & 0xffff; - uint16_t color1 = colors >> 16; - - if (color0 < color1) { - // There's no need to endian-swap within 'bits' - // since we don't care which pixel is the transparent one - uint32_t bits = *d32++; - - // Detect if any (odd, even) pair of bits are '11' - // bits: b31 b30 b29 ... b3 b2 b1 b0 - // bits >> 1: b31 b31 b30 ... b4 b3 b2 b1 - // &: b31 (b31 & b30) (b29 & b28) ... (b2 & b1) (b1 & b0) - // & 0x55..: 0 (b31 & b30) 0 ... 0 (b1 & b0) - if (((bits & (bits >> 1)) & 0x55555555) != 0) { - hasAlpha = true; - goto done; - } - } else { - // Skip 4 bytes - ++d32; - } - } - - done: -#if TIMING - gettimeofday(&end_t, &tz); - long usec = (end_t.tv_sec - start_t.tv_sec)*1000000 + - (end_t.tv_usec - start_t.tv_usec); - - printf("Scanned w=%d h=%d in %ld usec\n", width, height, usec); -#endif - - return hasAlpha; -} - -static void -decodeDXT1(const GLvoid *data, int width, int height, - void *surface, int stride, - bool hasAlpha) - -{ - init_tables(); - - uint32_t const *d32 = (uint32_t *)data; - - // Color table for the current block - uint16_t c[4]; - c[0] = c[1] = c[2] = c[3] = 0; - - // Specified colors from the previous block - uint16_t prev_color0 = 0x0000; - uint16_t prev_color1 = 0x0000; - - uint16_t* rowPtr = (uint16_t*)surface; - for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) { - uint16_t *blockPtr = rowPtr; - for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) { - uint32_t colors = *d32++; - uint32_t bits = *d32++; - -#if __BYTE_ORDER == __BIG_ENDIAN - colors = swap(colors); - bits = swap(bits); -#endif - - // Raw colors - uint16_t color0 = colors & 0xffff; - uint16_t color1 = colors >> 16; - - // If the new block has the same base colors as the - // previous one, we don't need to recompute the color - // table c[] - if (color0 != prev_color0 || color1 != prev_color1) { - // Store raw colors for comparison with next block - prev_color0 = color0; - prev_color1 = color1; - - int r0 = red(color0); - int g0 = green(color0); - int b0 = blue(color0); - - int r1 = red(color1); - int g1 = green(color1); - int b1 = blue(color1); - - if (hasAlpha) { - c[0] = (r0 << 11) | ((g0 >> 1) << 6) | (b0 << 1) | 0x1; - c[1] = (r1 << 11) | ((g1 >> 1) << 6) | (b1 << 1) | 0x1; - } else { - c[0] = color0; - c[1] = color1; - } - - int r2, g2, b2, r3, g3, b3, a3; - - int bbits = bits >> 1; - bool has2 = ((bbits & ~bits) & 0x55555555) != 0; - bool has3 = ((bbits & bits) & 0x55555555) != 0; - - if (has2 || has3) { - if (color0 > color1) { - r2 = avg23(r0, r1); - g2 = avg23(g0, g1); - b2 = avg23(b0, b1); - - r3 = avg23(r1, r0); - g3 = avg23(g1, g0); - b3 = avg23(b1, b0); - a3 = 1; - } else { - r2 = (r0 + r1) >> 1; - g2 = (g0 + g1) >> 1; - b2 = (b0 + b1) >> 1; - - r3 = g3 = b3 = a3 = 0; - } - if (hasAlpha) { - c[2] = (r2 << 11) | ((g2 >> 1) << 6) | - (b2 << 1) | 0x1; - c[3] = (r3 << 11) | ((g3 >> 1) << 6) | - (b3 << 1) | a3; - } else { - c[2] = (r2 << 11) | (g2 << 5) | b2; - c[3] = (r3 << 11) | (g3 << 5) | b3; - } - } - } - - uint16_t* blockRowPtr = blockPtr; - for (int y = 0; y < 4; y++, blockRowPtr += stride) { - // Don't process rows past the botom - if (base_y + y >= height) { - break; - } - - int w = min(width - base_x, 4); - for (int x = 0; x < w; x++) { - int code = bits & 0x3; - bits >>= 2; - - blockRowPtr[x] = c[code]; - } - } - } - } -} - -// Output data as internalformat=GL_RGBA, type=GL_UNSIGNED_BYTE -static void -decodeDXT3(const GLvoid *data, int width, int height, - void *surface, int stride) - -{ - init_tables(); - - uint32_t const *d32 = (uint32_t *)data; - - // Specified colors from the previous block - uint16_t prev_color0 = 0x0000; - uint16_t prev_color1 = 0x0000; - - // Color table for the current block - uint32_t c[4]; - c[0] = c[1] = c[2] = c[3] = 0; - - uint32_t* rowPtr = (uint32_t*)surface; - for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) { - uint32_t *blockPtr = rowPtr; - for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) { - -#if __BYTE_ORDER == __BIG_ENDIAN - uint32_t alphahi = *d32++; - uint32_t alphalo = *d32++; - alphahi = swap(alphahi); - alphalo = swap(alphalo); -#else - uint32_t alphalo = *d32++; - uint32_t alphahi = *d32++; -#endif - - uint32_t colors = *d32++; - uint32_t bits = *d32++; - -#if __BYTE_ORDER == __BIG_ENDIAN - colors = swap(colors); - bits = swap(bits); -#endif - - uint64_t alpha = ((uint64_t)alphahi << 32) | alphalo; - - // Raw colors - uint16_t color0 = colors & 0xffff; - uint16_t color1 = colors >> 16; - - // If the new block has the same base colors as the - // previous one, we don't need to recompute the color - // table c[] - if (color0 != prev_color0 || color1 != prev_color1) { - // Store raw colors for comparison with next block - prev_color0 = color0; - prev_color1 = color1; - - int bbits = bits >> 1; - bool has2 = ((bbits & ~bits) & 0x55555555) != 0; - bool has3 = ((bbits & bits) & 0x55555555) != 0; - - if (has2 || has3) { - int r0 = red(color0); - int g0 = green(color0); - int b0 = blue(color0); - - int r1 = red(color1); - int g1 = green(color1); - int b1 = blue(color1); - - int r2 = avg23(r0, r1); - int g2 = avg23(g0, g1); - int b2 = avg23(b0, b1); - - int r3 = avg23(r1, r0); - int g3 = avg23(g1, g0); - int b3 = avg23(b1, b0); - - c[0] = rgb565SepTo888(r0, g0, b0); - c[1] = rgb565SepTo888(r1, g1, b1); - c[2] = rgb565SepTo888(r2, g2, b2); - c[3] = rgb565SepTo888(r3, g3, b3); - } else { - // Convert to 8 bits - c[0] = rgb565To888(color0); - c[1] = rgb565To888(color1); - } - } - - uint32_t* blockRowPtr = blockPtr; - for (int y = 0; y < 4; y++, blockRowPtr += stride) { - // Don't process rows past the botom - if (base_y + y >= height) { - break; - } - - int w = min(width - base_x, 4); - for (int x = 0; x < w; x++) { - int a = alpha & 0xf; - alpha >>= 4; - - int code = bits & 0x3; - bits >>= 2; - - blockRowPtr[x] = c[code] | (a << 28) | (a << 24); - } - } - } - } -} - -// Output data as internalformat=GL_RGBA, type=GL_UNSIGNED_BYTE -static void -decodeDXT5(const GLvoid *data, int width, int height, - void *surface, int stride) - -{ - init_tables(); - - uint32_t const *d32 = (uint32_t *)data; - - // Specified alphas from the previous block - uint8_t prev_alpha0 = 0x00; - uint8_t prev_alpha1 = 0x00; - - // Specified colors from the previous block - uint16_t prev_color0 = 0x0000; - uint16_t prev_color1 = 0x0000; - - // Alpha table for the current block - uint8_t a[8]; - a[0] = a[1] = a[2] = a[3] = a[4] = a[5] = a[6] = a[7] = 0; - - // Color table for the current block - uint32_t c[4]; - c[0] = c[1] = c[2] = c[3] = 0; - - int good_a5 = 0; - int bad_a5 = 0; - int good_a6 = 0; - int bad_a6 = 0; - int good_a7 = 0; - int bad_a7 = 0; - - uint32_t* rowPtr = (uint32_t*)surface; - for (int base_y = 0; base_y < height; base_y += 4, rowPtr += 4*stride) { - uint32_t *blockPtr = rowPtr; - for (int base_x = 0; base_x < width; base_x += 4, blockPtr += 4) { - -#if __BYTE_ORDER == __BIG_ENDIAN - uint32_t alphahi = *d32++; - uint32_t alphalo = *d32++; - alphahi = swap(alphahi); - alphalo = swap(alphalo); -#else - uint32_t alphalo = *d32++; - uint32_t alphahi = *d32++; -#endif - - uint32_t colors = *d32++; - uint32_t bits = *d32++; - -#if __BYTE_ORDER == __BIG_ENDIANx - colors = swap(colors); - bits = swap(bits); -#endif - - uint64_t alpha = ((uint64_t)alphahi << 32) | alphalo; - uint64_t alpha0 = alpha & 0xff; - alpha >>= 8; - uint64_t alpha1 = alpha & 0xff; - alpha >>= 8; - - if (alpha0 != prev_alpha0 || alpha1 != prev_alpha1) { - prev_alpha0 = alpha0; - prev_alpha1 = alpha1; - - a[0] = alpha0; - a[1] = alpha1; - int a01 = alpha0 + alpha1 - 1; - if (alpha0 > alpha1) { - a[2] = div7(6*alpha0 + alpha1); - a[4] = div7(4*alpha0 + 3*alpha1); - a[6] = div7(2*alpha0 + 5*alpha1); - - // Use symmetry to derive half of the values - // A few values will be off by 1 (~.5%) - // Alternate which values are computed directly - // and which are derived to try to reduce bias - a[3] = a01 - a[6]; - a[5] = a01 - a[4]; - a[7] = a01 - a[2]; - } else { - a[2] = div5(4*alpha0 + alpha1); - a[4] = div5(2*alpha0 + 3*alpha1); - a[3] = a01 - a[4]; - a[5] = a01 - a[2]; - a[6] = 0x00; - a[7] = 0xff; - } - } - - // Raw colors - uint16_t color0 = colors & 0xffff; - uint16_t color1 = colors >> 16; - - // If the new block has the same base colors as the - // previous one, we don't need to recompute the color - // table c[] - if (color0 != prev_color0 || color1 != prev_color1) { - // Store raw colors for comparison with next block - prev_color0 = color0; - prev_color1 = color1; - - int bbits = bits >> 1; - bool has2 = ((bbits & ~bits) & 0x55555555) != 0; - bool has3 = ((bbits & bits) & 0x55555555) != 0; - - if (has2 || has3) { - int r0 = red(color0); - int g0 = green(color0); - int b0 = blue(color0); - - int r1 = red(color1); - int g1 = green(color1); - int b1 = blue(color1); - - int r2 = avg23(r0, r1); - int g2 = avg23(g0, g1); - int b2 = avg23(b0, b1); - - int r3 = avg23(r1, r0); - int g3 = avg23(g1, g0); - int b3 = avg23(b1, b0); - - c[0] = rgb565SepTo888(r0, g0, b0); - c[1] = rgb565SepTo888(r1, g1, b1); - c[2] = rgb565SepTo888(r2, g2, b2); - c[3] = rgb565SepTo888(r3, g3, b3); - } else { - // Convert to 8 bits - c[0] = rgb565To888(color0); - c[1] = rgb565To888(color1); - } - } - - uint32_t* blockRowPtr = blockPtr; - for (int y = 0; y < 4; y++, blockRowPtr += stride) { - // Don't process rows past the botom - if (base_y + y >= height) { - break; - } - - int w = min(width - base_x, 4); - for (int x = 0; x < w; x++) { - int acode = alpha & 0x7; - alpha >>= 3; - - int code = bits & 0x3; - bits >>= 2; - - blockRowPtr[x] = c[code] | (a[acode] << 24); - } - } - } - } -} - -/* - * Decode a DXT-compressed texture into memory. DXT textures consist of - * a series of 4x4 pixel blocks in left-to-right, top-down order. - * The number of blocks is given by ceil(width/4)*ceil(height/4). - * - * 'data' points to the texture data. 'width' and 'height' indicate the - * dimensions of the texture. We assume width and height are >= 0 but - * do not require them to be powers of 2 or divisible by any factor. - * - * The output is written to 'surface' with each scanline separated by - * 'stride' 2- or 4-byte words. - * - * 'format' indicates the type of compression and must be one of the following: - * - * GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - * The output is written as 5/6/5 opaque RGB (16 bit words). - * 8 bytes are read from 'data' for each block. - * - * GL_COMPRESSED_RGBA_S3TC_DXT1_EXT - * The output is written as 5/5/5/1 RGBA (16 bit words) - * 8 bytes are read from 'data' for each block. - * - * GL_COMPRESSED_RGBA_S3TC_DXT3_EXT - * GL_COMPRESSED_RGBA_S3TC_DXT5_EXT - * The output is written as 8/8/8/8 ARGB (32 bit words) - * 16 bytes are read from 'data' for each block. - */ -void -decodeDXT(const GLvoid *data, int width, int height, - void *surface, int stride, int format) -{ -#if TIMING - struct timeval start_t, end_t; - struct timezone tz; - - gettimeofday(&start_t, &tz); -#endif - - switch (format) { - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - decodeDXT1(data, width, height, surface, stride, false); - break; - - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - decodeDXT1(data, width, height, surface, stride, true); - break; - - case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: - decodeDXT3(data, width, height, surface, stride); - break; - - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - decodeDXT5(data, width, height, surface, stride); - break; - } - -#if TIMING - gettimeofday(&end_t, &tz); - long usec = (end_t.tv_sec - start_t.tv_sec)*1000000 + - (end_t.tv_usec - start_t.tv_usec); - - printf("Loaded w=%d h=%d in %ld usec\n", width, height, usec); -#endif -} - -} // namespace android diff --git a/opengl/libagl/dxt.h b/opengl/libagl/dxt.h deleted file mode 100644 index d95a36cd1a..0000000000 --- a/opengl/libagl/dxt.h +++ /dev/null @@ -1,33 +0,0 @@ -/* libs/opengles/dxt.h -** -** Copyright 2007, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_TEXTURE_H -#define ANDROID_OPENGLES_TEXTURE_H - -#include <stdlib.h> - -#include <GLES/gl.h> - -namespace android { - - bool DXT1HasAlpha(const GLvoid *data, int width, int height); - void decodeDXT(const GLvoid *data, int width, int height, - void *surface, int stride, int format); - -} // namespace android - -#endif // ANDROID_OPENGLES_TEXTURE_H diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp deleted file mode 100644 index be437054ce..0000000000 --- a/opengl/libagl/egl.cpp +++ /dev/null @@ -1,2230 +0,0 @@ -/* -** -** Copyright 2007 The Android Open Source Project -** -** Licensed under the Apache License Version 2.0(the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing software -** distributed under the License is distributed on an "AS IS" BASIS -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <assert.h> -#include <atomic> -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/mman.h> -#include <unistd.h> - -#include <log/log.h> - -#include <utils/threads.h> -#include <ui/ANativeObjectBase.h> -#include <ui/Fence.h> -#include <ui/GraphicBufferMapper.h> -#include <ui/Rect.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES/gl.h> -#include <GLES/glext.h> - -#include <pixelflinger/format.h> -#include <pixelflinger/pixelflinger.h> - -#include "context.h" -#include "state.h" -#include "texture.h" -#include "matrix.h" - -#undef NELEM -#define NELEM(x) (sizeof(x)/sizeof(*(x))) - -// ---------------------------------------------------------------------------- - -EGLBoolean EGLAPI eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, - EGLint left, EGLint top, EGLint width, EGLint height); - - -typedef struct egl_native_pixmap_t -{ - int32_t version; /* must be 32 */ - int32_t width; - int32_t height; - int32_t stride; - uint8_t* data; - uint8_t format; - uint8_t rfu[3]; - union { - uint32_t compressedFormat; - int32_t vstride; - }; - int32_t reserved; -} egl_native_pixmap_t; - - -// ---------------------------------------------------------------------------- -namespace android { - -// ---------------------------------------------------------------------------- - -const unsigned int NUM_DISPLAYS = 1; - -#ifndef __ANDROID__ -static pthread_mutex_t gInitMutex = PTHREAD_MUTEX_INITIALIZER; -#endif -static pthread_mutex_t gErrorKeyMutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_key_t gEGLErrorKey = -1; -#ifndef __ANDROID__ -namespace gl { -pthread_key_t gGLKey = -1; -}; // namespace gl -#endif - -template<typename T> -static T setError(GLint error, T returnValue) { - if (ggl_unlikely(gEGLErrorKey == -1)) { - pthread_mutex_lock(&gErrorKeyMutex); - if (gEGLErrorKey == -1) - pthread_key_create(&gEGLErrorKey, NULL); - pthread_mutex_unlock(&gErrorKeyMutex); - } - pthread_setspecific(gEGLErrorKey, (void*)(uintptr_t)error); - return returnValue; -} - -static GLint getError() { - if (ggl_unlikely(gEGLErrorKey == -1)) - return EGL_SUCCESS; - GLint error = (GLint)(uintptr_t)pthread_getspecific(gEGLErrorKey); - if (error == 0) { - // The TLS key has been created by another thread, but the value for - // this thread has not been initialized. - return EGL_SUCCESS; - } - pthread_setspecific(gEGLErrorKey, (void*)(uintptr_t)EGL_SUCCESS); - return error; -} - -// ---------------------------------------------------------------------------- - -struct egl_display_t -{ - egl_display_t() : type(0), initialized(0) { } - - static egl_display_t& get_display(EGLDisplay dpy); - - static EGLBoolean is_valid(EGLDisplay dpy) { - return ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS) ? EGL_FALSE : EGL_TRUE; - } - - NativeDisplayType type; - std::atomic_size_t initialized; -}; - -static egl_display_t gDisplays[NUM_DISPLAYS]; - -egl_display_t& egl_display_t::get_display(EGLDisplay dpy) { - return gDisplays[uintptr_t(dpy)-1U]; -} - -struct egl_context_t { - enum { - IS_CURRENT = 0x00010000, - NEVER_CURRENT = 0x00020000 - }; - uint32_t flags; - EGLDisplay dpy; - EGLConfig config; - EGLSurface read; - EGLSurface draw; - - static inline egl_context_t* context(EGLContext ctx) { - ogles_context_t* const gl = static_cast<ogles_context_t*>(ctx); - return static_cast<egl_context_t*>(gl->rasterizer.base); - } -}; - -// ---------------------------------------------------------------------------- - -struct egl_surface_t -{ - enum { - PAGE_FLIP = 0x00000001, - MAGIC = 0x31415265 - }; - - uint32_t magic; - EGLDisplay dpy; - EGLConfig config; - EGLContext ctx; - bool zombie; - - egl_surface_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat); - virtual ~egl_surface_t(); - bool isValid() const; - virtual bool initCheck() const = 0; - - virtual EGLBoolean bindDrawSurface(ogles_context_t* gl) = 0; - virtual EGLBoolean bindReadSurface(ogles_context_t* gl) = 0; - virtual EGLBoolean connect() { return EGL_TRUE; } - virtual void disconnect() {} - virtual EGLint getWidth() const = 0; - virtual EGLint getHeight() const = 0; - - virtual EGLint getHorizontalResolution() const; - virtual EGLint getVerticalResolution() const; - virtual EGLint getRefreshRate() const; - virtual EGLint getSwapBehavior() const; - virtual EGLBoolean swapBuffers(); - virtual EGLBoolean setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h); -protected: - GGLSurface depth; -}; - -egl_surface_t::egl_surface_t(EGLDisplay dpy, - EGLConfig config, - int32_t depthFormat) - : magic(MAGIC), dpy(dpy), config(config), ctx(0), zombie(false) -{ - depth.version = sizeof(GGLSurface); - depth.data = 0; - depth.format = depthFormat; -} -egl_surface_t::~egl_surface_t() -{ - magic = 0; - free(depth.data); -} -bool egl_surface_t::isValid() const { - ALOGE_IF(magic != MAGIC, "invalid EGLSurface (%p)", this); - return magic == MAGIC; -} - -EGLBoolean egl_surface_t::swapBuffers() { - return EGL_FALSE; -} -EGLint egl_surface_t::getHorizontalResolution() const { - return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); -} -EGLint egl_surface_t::getVerticalResolution() const { - return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); -} -EGLint egl_surface_t::getRefreshRate() const { - return (60 * EGL_DISPLAY_SCALING); -} -EGLint egl_surface_t::getSwapBehavior() const { - return EGL_BUFFER_PRESERVED; -} -EGLBoolean egl_surface_t::setSwapRectangle( - EGLint /*l*/, EGLint /*t*/, EGLint /*w*/, EGLint /*h*/) -{ - return EGL_FALSE; -} - -// ---------------------------------------------------------------------------- - -struct egl_window_surface_v2_t : public egl_surface_t -{ - egl_window_surface_v2_t( - EGLDisplay dpy, EGLConfig config, - int32_t depthFormat, - ANativeWindow* window); - - ~egl_window_surface_v2_t(); - - virtual bool initCheck() const { return true; } // TODO: report failure if ctor fails - virtual EGLBoolean swapBuffers(); - virtual EGLBoolean bindDrawSurface(ogles_context_t* gl); - virtual EGLBoolean bindReadSurface(ogles_context_t* gl); - virtual EGLBoolean connect(); - virtual void disconnect(); - virtual EGLint getWidth() const { return width; } - virtual EGLint getHeight() const { return height; } - virtual EGLint getHorizontalResolution() const; - virtual EGLint getVerticalResolution() const; - virtual EGLint getRefreshRate() const; - virtual EGLint getSwapBehavior() const; - virtual EGLBoolean setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h); - -private: - status_t lock(ANativeWindowBuffer* buf, int usage, void** vaddr); - status_t unlock(ANativeWindowBuffer* buf); - ANativeWindow* nativeWindow; - ANativeWindowBuffer* buffer; - ANativeWindowBuffer* previousBuffer; - int width; - int height; - void* bits; - GGLFormat const* pixelFormatTable; - - struct Rect { - inline Rect() { }; - inline Rect(int32_t w, int32_t h) - : left(0), top(0), right(w), bottom(h) { } - inline Rect(int32_t l, int32_t t, int32_t r, int32_t b) - : left(l), top(t), right(r), bottom(b) { } - Rect& andSelf(const Rect& r) { - left = max(left, r.left); - top = max(top, r.top); - right = min(right, r.right); - bottom = min(bottom, r.bottom); - return *this; - } - bool isEmpty() const { - return (left>=right || top>=bottom); - } - void dump(char const* what) { - ALOGD("%s { %5d, %5d, w=%5d, h=%5d }", - what, left, top, right-left, bottom-top); - } - - int32_t left; - int32_t top; - int32_t right; - int32_t bottom; - }; - - struct Region { - inline Region() : count(0) { } - typedef Rect const* const_iterator; - const_iterator begin() const { return storage; } - const_iterator end() const { return storage+count; } - static Region subtract(const Rect& lhs, const Rect& rhs) { - Region reg; - Rect* storage = reg.storage; - if (!lhs.isEmpty()) { - if (lhs.top < rhs.top) { // top rect - storage->left = lhs.left; - storage->top = lhs.top; - storage->right = lhs.right; - storage->bottom = rhs.top; - storage++; - } - const int32_t top = max(lhs.top, rhs.top); - const int32_t bot = min(lhs.bottom, rhs.bottom); - if (top < bot) { - if (lhs.left < rhs.left) { // left-side rect - storage->left = lhs.left; - storage->top = top; - storage->right = rhs.left; - storage->bottom = bot; - storage++; - } - if (lhs.right > rhs.right) { // right-side rect - storage->left = rhs.right; - storage->top = top; - storage->right = lhs.right; - storage->bottom = bot; - storage++; - } - } - if (lhs.bottom > rhs.bottom) { // bottom rect - storage->left = lhs.left; - storage->top = rhs.bottom; - storage->right = lhs.right; - storage->bottom = lhs.bottom; - storage++; - } - reg.count = storage - reg.storage; - } - return reg; - } - bool isEmpty() const { - return count<=0; - } - private: - Rect storage[4]; - ssize_t count; - }; - - void copyBlt( - ANativeWindowBuffer* dst, void* dst_vaddr, - ANativeWindowBuffer* src, void const* src_vaddr, - const Region& clip); - - Rect dirtyRegion; - Rect oldDirtyRegion; -}; - -egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy, - EGLConfig config, - int32_t depthFormat, - ANativeWindow* window) - : egl_surface_t(dpy, config, depthFormat), - nativeWindow(window), buffer(0), previousBuffer(0), bits(NULL) -{ - - pixelFormatTable = gglGetPixelFormatTable(); - - // keep a reference on the window - nativeWindow->common.incRef(&nativeWindow->common); - nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &width); - nativeWindow->query(nativeWindow, NATIVE_WINDOW_HEIGHT, &height); -} - -egl_window_surface_v2_t::~egl_window_surface_v2_t() { - if (buffer) { - buffer->common.decRef(&buffer->common); - } - if (previousBuffer) { - previousBuffer->common.decRef(&previousBuffer->common); - } - nativeWindow->common.decRef(&nativeWindow->common); -} - -EGLBoolean egl_window_surface_v2_t::connect() -{ - // we're intending to do software rendering - native_window_set_usage(nativeWindow, - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); - - // dequeue a buffer - int fenceFd = -1; - if (nativeWindow->dequeueBuffer(nativeWindow, &buffer, - &fenceFd) != NO_ERROR) { - return setError(EGL_BAD_ALLOC, EGL_FALSE); - } - - // wait for the buffer - sp<Fence> fence(new Fence(fenceFd)); - if (fence->wait(Fence::TIMEOUT_NEVER) != NO_ERROR) { - nativeWindow->cancelBuffer(nativeWindow, buffer, fenceFd); - return setError(EGL_BAD_ALLOC, EGL_FALSE); - } - - // allocate a corresponding depth-buffer - width = buffer->width; - height = buffer->height; - if (depth.format) { - depth.width = width; - depth.height = height; - depth.stride = depth.width; // use the width here - uint64_t allocSize = static_cast<uint64_t>(depth.stride) * - static_cast<uint64_t>(depth.height) * 2; - if (depth.stride < 0 || depth.height > INT_MAX || - allocSize > UINT32_MAX) { - return setError(EGL_BAD_ALLOC, EGL_FALSE); - } - depth.data = (GGLubyte*)malloc(allocSize); - if (depth.data == 0) { - return setError(EGL_BAD_ALLOC, EGL_FALSE); - } - } - - // keep a reference on the buffer - buffer->common.incRef(&buffer->common); - - // pin the buffer down - if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) { - ALOGE("connect() failed to lock buffer %p (%ux%u)", - buffer, buffer->width, buffer->height); - return setError(EGL_BAD_ACCESS, EGL_FALSE); - // FIXME: we should make sure we're not accessing the buffer anymore - } - return EGL_TRUE; -} - -void egl_window_surface_v2_t::disconnect() -{ - if (buffer && bits) { - bits = NULL; - unlock(buffer); - } - if (buffer) { - nativeWindow->cancelBuffer(nativeWindow, buffer, -1); - buffer->common.decRef(&buffer->common); - buffer = 0; - } - if (previousBuffer) { - previousBuffer->common.decRef(&previousBuffer->common); - previousBuffer = 0; - } -} - -status_t egl_window_surface_v2_t::lock( - ANativeWindowBuffer* buf, int usage, void** vaddr) -{ - auto& mapper = GraphicBufferMapper::get(); - return mapper.lock(buf->handle, usage, - android::Rect(buf->width, buf->height), vaddr); -} - -status_t egl_window_surface_v2_t::unlock(ANativeWindowBuffer* buf) -{ - if (!buf) return BAD_VALUE; - auto& mapper = GraphicBufferMapper::get(); - return mapper.unlock(buf->handle); -} - -void egl_window_surface_v2_t::copyBlt( - ANativeWindowBuffer* dst, void* dst_vaddr, - ANativeWindowBuffer* src, void const* src_vaddr, - const Region& clip) -{ - // NOTE: dst and src must be the same format - - Region::const_iterator cur = clip.begin(); - Region::const_iterator end = clip.end(); - - const size_t bpp = pixelFormatTable[src->format].size; - const size_t dbpr = dst->stride * bpp; - const size_t sbpr = src->stride * bpp; - - uint8_t const * const src_bits = (uint8_t const *)src_vaddr; - uint8_t * const dst_bits = (uint8_t *)dst_vaddr; - - while (cur != end) { - const Rect& r(*cur++); - ssize_t w = r.right - r.left; - ssize_t h = r.bottom - r.top; - if (w <= 0 || h<=0) continue; - size_t size = w * bpp; - uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp; - uint8_t * d = dst_bits + (r.left + dst->stride * r.top) * bpp; - if (dbpr==sbpr && size==sbpr) { - size *= h; - h = 1; - } - do { - memcpy(d, s, size); - d += dbpr; - s += sbpr; - } while (--h > 0); - } -} - -EGLBoolean egl_window_surface_v2_t::swapBuffers() -{ - if (!buffer) { - return setError(EGL_BAD_ACCESS, EGL_FALSE); - } - - /* - * Handle eglSetSwapRectangleANDROID() - * We copyback from the front buffer - */ - if (!dirtyRegion.isEmpty()) { - dirtyRegion.andSelf(Rect(buffer->width, buffer->height)); - if (previousBuffer) { - // This was const Region copyBack, but that causes an - // internal compile error on simulator builds - /*const*/ Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion)); - if (!copyBack.isEmpty()) { - void* prevBits; - if (lock(previousBuffer, - GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) { - // copy from previousBuffer to buffer - copyBlt(buffer, bits, previousBuffer, prevBits, copyBack); - unlock(previousBuffer); - } - } - } - oldDirtyRegion = dirtyRegion; - } - - if (previousBuffer) { - previousBuffer->common.decRef(&previousBuffer->common); - previousBuffer = 0; - } - - unlock(buffer); - previousBuffer = buffer; - nativeWindow->queueBuffer(nativeWindow, buffer, -1); - buffer = 0; - - // dequeue a new buffer - int fenceFd = -1; - if (nativeWindow->dequeueBuffer(nativeWindow, &buffer, &fenceFd) == NO_ERROR) { - sp<Fence> fence(new Fence(fenceFd)); - if (fence->wait(Fence::TIMEOUT_NEVER)) { - nativeWindow->cancelBuffer(nativeWindow, buffer, fenceFd); - return setError(EGL_BAD_ALLOC, EGL_FALSE); - } - - // reallocate the depth-buffer if needed - if ((width != buffer->width) || (height != buffer->height)) { - // TODO: we probably should reset the swap rect here - // if the window size has changed - width = buffer->width; - height = buffer->height; - if (depth.data) { - free(depth.data); - depth.width = width; - depth.height = height; - depth.stride = buffer->stride; - uint64_t allocSize = static_cast<uint64_t>(depth.stride) * - static_cast<uint64_t>(depth.height) * 2; - if (depth.stride < 0 || depth.height > INT_MAX || - allocSize > UINT32_MAX) { - setError(EGL_BAD_ALLOC, EGL_FALSE); - return EGL_FALSE; - } - depth.data = (GGLubyte*)malloc(allocSize); - if (depth.data == 0) { - setError(EGL_BAD_ALLOC, EGL_FALSE); - return EGL_FALSE; - } - } - } - - // keep a reference on the buffer - buffer->common.incRef(&buffer->common); - - // finally pin the buffer down - if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) { - ALOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)", - buffer, buffer->width, buffer->height); - return setError(EGL_BAD_ACCESS, EGL_FALSE); - // FIXME: we should make sure we're not accessing the buffer anymore - } - } else { - return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE); - } - - return EGL_TRUE; -} - -EGLBoolean egl_window_surface_v2_t::setSwapRectangle( - EGLint l, EGLint t, EGLint w, EGLint h) -{ - dirtyRegion = Rect(l, t, l+w, t+h); - return EGL_TRUE; -} - -EGLBoolean egl_window_surface_v2_t::bindDrawSurface(ogles_context_t* gl) -{ - GGLSurface buffer; - buffer.version = sizeof(GGLSurface); - buffer.width = this->buffer->width; - buffer.height = this->buffer->height; - buffer.stride = this->buffer->stride; - buffer.data = (GGLubyte*)bits; - buffer.format = this->buffer->format; - gl->rasterizer.procs.colorBuffer(gl, &buffer); - if (depth.data != gl->rasterizer.state.buffers.depth.data) - gl->rasterizer.procs.depthBuffer(gl, &depth); - - return EGL_TRUE; -} -EGLBoolean egl_window_surface_v2_t::bindReadSurface(ogles_context_t* gl) -{ - GGLSurface buffer; - buffer.version = sizeof(GGLSurface); - buffer.width = this->buffer->width; - buffer.height = this->buffer->height; - buffer.stride = this->buffer->stride; - buffer.data = (GGLubyte*)bits; // FIXME: hopefully is is LOCKED!!! - buffer.format = this->buffer->format; - gl->rasterizer.procs.readBuffer(gl, &buffer); - return EGL_TRUE; -} -EGLint egl_window_surface_v2_t::getHorizontalResolution() const { - return (nativeWindow->xdpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); -} -EGLint egl_window_surface_v2_t::getVerticalResolution() const { - return (nativeWindow->ydpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); -} -EGLint egl_window_surface_v2_t::getRefreshRate() const { - return (60 * EGL_DISPLAY_SCALING); // FIXME -} -EGLint egl_window_surface_v2_t::getSwapBehavior() const -{ - /* - * EGL_BUFFER_PRESERVED means that eglSwapBuffers() completely preserves - * the content of the swapped buffer. - * - * EGL_BUFFER_DESTROYED means that the content of the buffer is lost. - * - * However when ANDROID_swap_retcangle is supported, EGL_BUFFER_DESTROYED - * only applies to the area specified by eglSetSwapRectangleANDROID(), that - * is, everything outside of this area is preserved. - * - * This implementation of EGL assumes the later case. - * - */ - - return EGL_BUFFER_DESTROYED; -} - -// ---------------------------------------------------------------------------- - -struct egl_pixmap_surface_t : public egl_surface_t -{ - egl_pixmap_surface_t( - EGLDisplay dpy, EGLConfig config, - int32_t depthFormat, - egl_native_pixmap_t const * pixmap); - - virtual ~egl_pixmap_surface_t() { } - - virtual bool initCheck() const { return !depth.format || depth.data!=0; } - virtual EGLBoolean bindDrawSurface(ogles_context_t* gl); - virtual EGLBoolean bindReadSurface(ogles_context_t* gl); - virtual EGLint getWidth() const { return nativePixmap.width; } - virtual EGLint getHeight() const { return nativePixmap.height; } -private: - egl_native_pixmap_t nativePixmap; -}; - -egl_pixmap_surface_t::egl_pixmap_surface_t(EGLDisplay dpy, - EGLConfig config, - int32_t depthFormat, - egl_native_pixmap_t const * pixmap) - : egl_surface_t(dpy, config, depthFormat), nativePixmap(*pixmap) -{ - if (depthFormat) { - depth.width = pixmap->width; - depth.height = pixmap->height; - depth.stride = depth.width; // use the width here - uint64_t allocSize = static_cast<uint64_t>(depth.stride) * - static_cast<uint64_t>(depth.height) * 2; - if (depth.stride < 0 || depth.height > INT_MAX || - allocSize > UINT32_MAX) { - setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); - return; - } - depth.data = (GGLubyte*)malloc(allocSize); - if (depth.data == 0) { - setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); - } - } -} -EGLBoolean egl_pixmap_surface_t::bindDrawSurface(ogles_context_t* gl) -{ - GGLSurface buffer; - buffer.version = sizeof(GGLSurface); - buffer.width = nativePixmap.width; - buffer.height = nativePixmap.height; - buffer.stride = nativePixmap.stride; - buffer.data = nativePixmap.data; - buffer.format = nativePixmap.format; - - gl->rasterizer.procs.colorBuffer(gl, &buffer); - if (depth.data != gl->rasterizer.state.buffers.depth.data) - gl->rasterizer.procs.depthBuffer(gl, &depth); - return EGL_TRUE; -} -EGLBoolean egl_pixmap_surface_t::bindReadSurface(ogles_context_t* gl) -{ - GGLSurface buffer; - buffer.version = sizeof(GGLSurface); - buffer.width = nativePixmap.width; - buffer.height = nativePixmap.height; - buffer.stride = nativePixmap.stride; - buffer.data = nativePixmap.data; - buffer.format = nativePixmap.format; - gl->rasterizer.procs.readBuffer(gl, &buffer); - return EGL_TRUE; -} - -// ---------------------------------------------------------------------------- - -struct egl_pbuffer_surface_t : public egl_surface_t -{ - egl_pbuffer_surface_t( - EGLDisplay dpy, EGLConfig config, int32_t depthFormat, - int32_t w, int32_t h, int32_t f); - - virtual ~egl_pbuffer_surface_t(); - - virtual bool initCheck() const { return pbuffer.data != 0; } - virtual EGLBoolean bindDrawSurface(ogles_context_t* gl); - virtual EGLBoolean bindReadSurface(ogles_context_t* gl); - virtual EGLint getWidth() const { return pbuffer.width; } - virtual EGLint getHeight() const { return pbuffer.height; } -private: - GGLSurface pbuffer; -}; - -egl_pbuffer_surface_t::egl_pbuffer_surface_t(EGLDisplay dpy, - EGLConfig config, int32_t depthFormat, - int32_t w, int32_t h, int32_t f) - : egl_surface_t(dpy, config, depthFormat) -{ - size_t size = w*h; - switch (f) { - case GGL_PIXEL_FORMAT_A_8: size *= 1; break; - case GGL_PIXEL_FORMAT_RGB_565: size *= 2; break; - case GGL_PIXEL_FORMAT_RGBA_8888: size *= 4; break; - case GGL_PIXEL_FORMAT_RGBX_8888: size *= 4; break; - case GGL_PIXEL_FORMAT_BGRA_8888: size *= 4; break; - default: - ALOGE("incompatible pixel format for pbuffer (format=%d)", f); - pbuffer.data = 0; - break; - } - pbuffer.version = sizeof(GGLSurface); - pbuffer.width = w; - pbuffer.height = h; - pbuffer.stride = w; - pbuffer.data = (GGLubyte*)malloc(size); - pbuffer.format = f; - - if (depthFormat) { - depth.width = pbuffer.width; - depth.height = pbuffer.height; - depth.stride = depth.width; // use the width here - uint64_t allocSize = static_cast<uint64_t>(depth.stride) * - static_cast<uint64_t>(depth.height) * 2; - if (depth.stride < 0 || depth.height > INT_MAX || - allocSize > UINT32_MAX) { - setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); - return; - } - depth.data = (GGLubyte*)malloc(allocSize); - if (depth.data == 0) { - setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); - return; - } - } -} -egl_pbuffer_surface_t::~egl_pbuffer_surface_t() { - free(pbuffer.data); -} -EGLBoolean egl_pbuffer_surface_t::bindDrawSurface(ogles_context_t* gl) -{ - gl->rasterizer.procs.colorBuffer(gl, &pbuffer); - if (depth.data != gl->rasterizer.state.buffers.depth.data) - gl->rasterizer.procs.depthBuffer(gl, &depth); - return EGL_TRUE; -} -EGLBoolean egl_pbuffer_surface_t::bindReadSurface(ogles_context_t* gl) -{ - gl->rasterizer.procs.readBuffer(gl, &pbuffer); - return EGL_TRUE; -} - -// ---------------------------------------------------------------------------- - -struct config_pair_t { - GLint key; - GLint value; -}; - -struct configs_t { - const config_pair_t* array; - int size; -}; - -struct config_management_t { - GLint key; - bool (*match)(GLint reqValue, GLint confValue); - static bool atLeast(GLint reqValue, GLint confValue) { - return (reqValue == EGL_DONT_CARE) || (confValue >= reqValue); - } - static bool exact(GLint reqValue, GLint confValue) { - return (reqValue == EGL_DONT_CARE) || (confValue == reqValue); - } - static bool mask(GLint reqValue, GLint confValue) { - return (confValue & reqValue) == reqValue; - } - static bool ignore(GLint /*reqValue*/, GLint /*confValue*/) { - return true; - } -}; - -// ---------------------------------------------------------------------------- - -#define VERSION_MAJOR 1 -#define VERSION_MINOR 2 -static char const * const gVendorString = "Google Inc."; -static char const * const gVersionString = "1.2 Android Driver 1.2.0"; -static char const * const gClientApiString = "OpenGL_ES"; -static char const * const gExtensionsString = - "EGL_KHR_fence_sync " - "EGL_KHR_image_base " - // "KHR_image_pixmap " - "EGL_ANDROID_image_native_buffer " - "EGL_ANDROID_swap_rectangle " - ; - -// ---------------------------------------------------------------------------- - -struct extention_map_t { - const char * const name; - __eglMustCastToProperFunctionPointerType address; -}; - -static const extention_map_t gExtentionMap[] = { - { "glDrawTexsOES", - (__eglMustCastToProperFunctionPointerType)&glDrawTexsOES }, - { "glDrawTexiOES", - (__eglMustCastToProperFunctionPointerType)&glDrawTexiOES }, - { "glDrawTexfOES", - (__eglMustCastToProperFunctionPointerType)&glDrawTexfOES }, - { "glDrawTexxOES", - (__eglMustCastToProperFunctionPointerType)&glDrawTexxOES }, - { "glDrawTexsvOES", - (__eglMustCastToProperFunctionPointerType)&glDrawTexsvOES }, - { "glDrawTexivOES", - (__eglMustCastToProperFunctionPointerType)&glDrawTexivOES }, - { "glDrawTexfvOES", - (__eglMustCastToProperFunctionPointerType)&glDrawTexfvOES }, - { "glDrawTexxvOES", - (__eglMustCastToProperFunctionPointerType)&glDrawTexxvOES }, - { "glQueryMatrixxOES", - (__eglMustCastToProperFunctionPointerType)&glQueryMatrixxOES }, - { "glEGLImageTargetTexture2DOES", - (__eglMustCastToProperFunctionPointerType)&glEGLImageTargetTexture2DOES }, - { "glEGLImageTargetRenderbufferStorageOES", - (__eglMustCastToProperFunctionPointerType)&glEGLImageTargetRenderbufferStorageOES }, - { "glClipPlanef", - (__eglMustCastToProperFunctionPointerType)&glClipPlanef }, - { "glClipPlanex", - (__eglMustCastToProperFunctionPointerType)&glClipPlanex }, - { "glBindBuffer", - (__eglMustCastToProperFunctionPointerType)&glBindBuffer }, - { "glBufferData", - (__eglMustCastToProperFunctionPointerType)&glBufferData }, - { "glBufferSubData", - (__eglMustCastToProperFunctionPointerType)&glBufferSubData }, - { "glDeleteBuffers", - (__eglMustCastToProperFunctionPointerType)&glDeleteBuffers }, - { "glGenBuffers", - (__eglMustCastToProperFunctionPointerType)&glGenBuffers }, - { "eglCreateImageKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, - { "eglDestroyImageKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, - { "eglCreateSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglCreateSyncKHR }, - { "eglDestroySyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglDestroySyncKHR }, - { "eglClientWaitSyncKHR", - (__eglMustCastToProperFunctionPointerType)&eglClientWaitSyncKHR }, - { "eglGetSyncAttribKHR", - (__eglMustCastToProperFunctionPointerType)&eglGetSyncAttribKHR }, - { "eglSetSwapRectangleANDROID", - (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID }, -}; - -/* - * In the lists below, attributes names MUST be sorted. - * Additionally, all configs must be sorted according to - * the EGL specification. - */ - -static config_pair_t const config_base_attribute_list[] = { - { EGL_STENCIL_SIZE, 0 }, - { EGL_CONFIG_CAVEAT, EGL_SLOW_CONFIG }, - { EGL_LEVEL, 0 }, - { EGL_MAX_PBUFFER_HEIGHT, GGL_MAX_VIEWPORT_DIMS }, - { EGL_MAX_PBUFFER_PIXELS, - GGL_MAX_VIEWPORT_DIMS*GGL_MAX_VIEWPORT_DIMS }, - { EGL_MAX_PBUFFER_WIDTH, GGL_MAX_VIEWPORT_DIMS }, - { EGL_NATIVE_RENDERABLE, EGL_TRUE }, - { EGL_NATIVE_VISUAL_ID, 0 }, - { EGL_NATIVE_VISUAL_TYPE, GGL_PIXEL_FORMAT_RGB_565 }, - { EGL_SAMPLES, 0 }, - { EGL_SAMPLE_BUFFERS, 0 }, - { EGL_TRANSPARENT_TYPE, EGL_NONE }, - { EGL_TRANSPARENT_BLUE_VALUE, 0 }, - { EGL_TRANSPARENT_GREEN_VALUE, 0 }, - { EGL_TRANSPARENT_RED_VALUE, 0 }, - { EGL_BIND_TO_TEXTURE_RGBA, EGL_FALSE }, - { EGL_BIND_TO_TEXTURE_RGB, EGL_FALSE }, - { EGL_MIN_SWAP_INTERVAL, 1 }, - { EGL_MAX_SWAP_INTERVAL, 1 }, - { EGL_LUMINANCE_SIZE, 0 }, - { EGL_ALPHA_MASK_SIZE, 0 }, - { EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER }, - { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT }, - { EGL_CONFORMANT, 0 } -}; - -// These configs can override the base attribute list -// NOTE: when adding a config here, don't forget to update eglCreate*Surface() - -// 565 configs -static config_pair_t const config_0_attribute_list[] = { - { EGL_BUFFER_SIZE, 16 }, - { EGL_ALPHA_SIZE, 0 }, - { EGL_BLUE_SIZE, 5 }, - { EGL_GREEN_SIZE, 6 }, - { EGL_RED_SIZE, 5 }, - { EGL_DEPTH_SIZE, 0 }, - { EGL_CONFIG_ID, 0 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGB_565 }, - { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -static config_pair_t const config_1_attribute_list[] = { - { EGL_BUFFER_SIZE, 16 }, - { EGL_ALPHA_SIZE, 0 }, - { EGL_BLUE_SIZE, 5 }, - { EGL_GREEN_SIZE, 6 }, - { EGL_RED_SIZE, 5 }, - { EGL_DEPTH_SIZE, 16 }, - { EGL_CONFIG_ID, 1 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGB_565 }, - { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -// RGB 888 configs -static config_pair_t const config_2_attribute_list[] = { - { EGL_BUFFER_SIZE, 32 }, - { EGL_ALPHA_SIZE, 0 }, - { EGL_BLUE_SIZE, 8 }, - { EGL_GREEN_SIZE, 8 }, - { EGL_RED_SIZE, 8 }, - { EGL_DEPTH_SIZE, 0 }, - { EGL_CONFIG_ID, 6 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBX_8888 }, - { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -static config_pair_t const config_3_attribute_list[] = { - { EGL_BUFFER_SIZE, 32 }, - { EGL_ALPHA_SIZE, 0 }, - { EGL_BLUE_SIZE, 8 }, - { EGL_GREEN_SIZE, 8 }, - { EGL_RED_SIZE, 8 }, - { EGL_DEPTH_SIZE, 16 }, - { EGL_CONFIG_ID, 7 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBX_8888 }, - { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -// 8888 configs -static config_pair_t const config_4_attribute_list[] = { - { EGL_BUFFER_SIZE, 32 }, - { EGL_ALPHA_SIZE, 8 }, - { EGL_BLUE_SIZE, 8 }, - { EGL_GREEN_SIZE, 8 }, - { EGL_RED_SIZE, 8 }, - { EGL_DEPTH_SIZE, 0 }, - { EGL_CONFIG_ID, 2 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBA_8888 }, - { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -static config_pair_t const config_5_attribute_list[] = { - { EGL_BUFFER_SIZE, 32 }, - { EGL_ALPHA_SIZE, 8 }, - { EGL_BLUE_SIZE, 8 }, - { EGL_GREEN_SIZE, 8 }, - { EGL_RED_SIZE, 8 }, - { EGL_DEPTH_SIZE, 16 }, - { EGL_CONFIG_ID, 3 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBA_8888 }, - { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -// A8 configs -static config_pair_t const config_6_attribute_list[] = { - { EGL_BUFFER_SIZE, 8 }, - { EGL_ALPHA_SIZE, 8 }, - { EGL_BLUE_SIZE, 0 }, - { EGL_GREEN_SIZE, 0 }, - { EGL_RED_SIZE, 0 }, - { EGL_DEPTH_SIZE, 0 }, - { EGL_CONFIG_ID, 4 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_A_8 }, - { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -static config_pair_t const config_7_attribute_list[] = { - { EGL_BUFFER_SIZE, 8 }, - { EGL_ALPHA_SIZE, 8 }, - { EGL_BLUE_SIZE, 0 }, - { EGL_GREEN_SIZE, 0 }, - { EGL_RED_SIZE, 0 }, - { EGL_DEPTH_SIZE, 16 }, - { EGL_CONFIG_ID, 5 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_A_8 }, - { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -// BGRA 8888 config -static config_pair_t const config_8_attribute_list[] = { - { EGL_BUFFER_SIZE, 32 }, - { EGL_ALPHA_SIZE, 8 }, - { EGL_BLUE_SIZE, 8 }, - { EGL_GREEN_SIZE, 8 }, - { EGL_RED_SIZE, 8 }, - { EGL_DEPTH_SIZE, 0 }, - { EGL_CONFIG_ID, 8 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_BGRA_8888 }, - { EGL_SURFACE_TYPE, EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -static configs_t const gConfigs[] = { - { config_0_attribute_list, NELEM(config_0_attribute_list) }, - { config_1_attribute_list, NELEM(config_1_attribute_list) }, - { config_2_attribute_list, NELEM(config_2_attribute_list) }, - { config_3_attribute_list, NELEM(config_3_attribute_list) }, - { config_4_attribute_list, NELEM(config_4_attribute_list) }, - { config_5_attribute_list, NELEM(config_5_attribute_list) }, - { config_6_attribute_list, NELEM(config_6_attribute_list) }, - { config_7_attribute_list, NELEM(config_7_attribute_list) }, - { config_8_attribute_list, NELEM(config_8_attribute_list) }, -}; - -static config_management_t const gConfigManagement[] = { - { EGL_BUFFER_SIZE, config_management_t::atLeast }, - { EGL_ALPHA_SIZE, config_management_t::atLeast }, - { EGL_BLUE_SIZE, config_management_t::atLeast }, - { EGL_GREEN_SIZE, config_management_t::atLeast }, - { EGL_RED_SIZE, config_management_t::atLeast }, - { EGL_DEPTH_SIZE, config_management_t::atLeast }, - { EGL_STENCIL_SIZE, config_management_t::atLeast }, - { EGL_CONFIG_CAVEAT, config_management_t::exact }, - { EGL_CONFIG_ID, config_management_t::exact }, - { EGL_LEVEL, config_management_t::exact }, - { EGL_MAX_PBUFFER_HEIGHT, config_management_t::ignore }, - { EGL_MAX_PBUFFER_PIXELS, config_management_t::ignore }, - { EGL_MAX_PBUFFER_WIDTH, config_management_t::ignore }, - { EGL_NATIVE_RENDERABLE, config_management_t::exact }, - { EGL_NATIVE_VISUAL_ID, config_management_t::ignore }, - { EGL_NATIVE_VISUAL_TYPE, config_management_t::exact }, - { EGL_SAMPLES, config_management_t::exact }, - { EGL_SAMPLE_BUFFERS, config_management_t::exact }, - { EGL_SURFACE_TYPE, config_management_t::mask }, - { EGL_TRANSPARENT_TYPE, config_management_t::exact }, - { EGL_TRANSPARENT_BLUE_VALUE, config_management_t::exact }, - { EGL_TRANSPARENT_GREEN_VALUE, config_management_t::exact }, - { EGL_TRANSPARENT_RED_VALUE, config_management_t::exact }, - { EGL_BIND_TO_TEXTURE_RGBA, config_management_t::exact }, - { EGL_BIND_TO_TEXTURE_RGB, config_management_t::exact }, - { EGL_MIN_SWAP_INTERVAL, config_management_t::exact }, - { EGL_MAX_SWAP_INTERVAL, config_management_t::exact }, - { EGL_LUMINANCE_SIZE, config_management_t::atLeast }, - { EGL_ALPHA_MASK_SIZE, config_management_t::atLeast }, - { EGL_COLOR_BUFFER_TYPE, config_management_t::exact }, - { EGL_RENDERABLE_TYPE, config_management_t::mask }, - { EGL_CONFORMANT, config_management_t::mask } -}; - - -static config_pair_t const config_defaults[] = { - // attributes that are not specified are simply ignored, if a particular - // one needs not be ignored, it must be specified here, eg: - // { EGL_SURFACE_TYPE, EGL_WINDOW_BIT }, -}; - -// ---------------------------------------------------------------------------- - -static status_t getConfigFormatInfo(EGLint configID, - int32_t& pixelFormat, int32_t& depthFormat) -{ - switch(configID) { - case 0: - pixelFormat = GGL_PIXEL_FORMAT_RGB_565; - depthFormat = 0; - break; - case 1: - pixelFormat = GGL_PIXEL_FORMAT_RGB_565; - depthFormat = GGL_PIXEL_FORMAT_Z_16; - break; - case 2: - pixelFormat = GGL_PIXEL_FORMAT_RGBX_8888; - depthFormat = 0; - break; - case 3: - pixelFormat = GGL_PIXEL_FORMAT_RGBX_8888; - depthFormat = GGL_PIXEL_FORMAT_Z_16; - break; - case 4: - pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; - depthFormat = 0; - break; - case 5: - pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; - depthFormat = GGL_PIXEL_FORMAT_Z_16; - break; - case 6: - pixelFormat = GGL_PIXEL_FORMAT_A_8; - depthFormat = 0; - break; - case 7: - pixelFormat = GGL_PIXEL_FORMAT_A_8; - depthFormat = GGL_PIXEL_FORMAT_Z_16; - break; - case 8: - pixelFormat = GGL_PIXEL_FORMAT_BGRA_8888; - depthFormat = 0; - break; - default: - return NAME_NOT_FOUND; - } - return NO_ERROR; -} - -// ---------------------------------------------------------------------------- - -template<typename T> -static int binarySearch(T const sortedArray[], int first, int last, EGLint key) -{ - while (first <= last) { - int mid = (first + last) / 2; - if (key > sortedArray[mid].key) { - first = mid + 1; - } else if (key < sortedArray[mid].key) { - last = mid - 1; - } else { - return mid; - } - } - return -1; -} - -static int isAttributeMatching(int i, EGLint attr, EGLint val) -{ - // look for the attribute in all of our configs - config_pair_t const* configFound = gConfigs[i].array; - int index = binarySearch<config_pair_t>( - gConfigs[i].array, - 0, gConfigs[i].size-1, - attr); - if (index < 0) { - configFound = config_base_attribute_list; - index = binarySearch<config_pair_t>( - config_base_attribute_list, - 0, NELEM(config_base_attribute_list)-1, - attr); - } - if (index >= 0) { - // attribute found, check if this config could match - int cfgMgtIndex = binarySearch<config_management_t>( - gConfigManagement, - 0, NELEM(gConfigManagement)-1, - attr); - if (cfgMgtIndex >= 0) { - bool match = gConfigManagement[cfgMgtIndex].match( - val, configFound[index].value); - if (match) { - // this config matches - return 1; - } - } else { - // attribute not found. this should NEVER happen. - } - } else { - // error, this attribute doesn't exist - } - return 0; -} - -static int makeCurrent(ogles_context_t* gl) -{ - ogles_context_t* current = (ogles_context_t*)getGlThreadSpecific(); - if (gl) { - egl_context_t* c = egl_context_t::context(gl); - if (c->flags & egl_context_t::IS_CURRENT) { - if (current != gl) { - // it is an error to set a context current, if it's already - // current to another thread - return -1; - } - } else { - if (current) { - // mark the current context as not current, and flush - glFlush(); - egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT; - } - } - if (!(c->flags & egl_context_t::IS_CURRENT)) { - // The context is not current, make it current! - setGlThreadSpecific(gl); - c->flags |= egl_context_t::IS_CURRENT; - } - } else { - if (current) { - // mark the current context as not current, and flush - glFlush(); - egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT; - } - // this thread has no context attached to it - setGlThreadSpecific(0); - } - return 0; -} - -static EGLBoolean getConfigAttrib(EGLDisplay /*dpy*/, EGLConfig config, - EGLint attribute, EGLint *value) -{ - size_t numConfigs = NELEM(gConfigs); - int index = (int)(uintptr_t)config; - if (uint32_t(index) >= numConfigs) - return setError(EGL_BAD_CONFIG, EGL_FALSE); - - int attrIndex; - attrIndex = binarySearch<config_pair_t>( - gConfigs[index].array, - 0, gConfigs[index].size-1, - attribute); - if (attrIndex>=0) { - *value = gConfigs[index].array[attrIndex].value; - return EGL_TRUE; - } - - attrIndex = binarySearch<config_pair_t>( - config_base_attribute_list, - 0, NELEM(config_base_attribute_list)-1, - attribute); - if (attrIndex>=0) { - *value = config_base_attribute_list[attrIndex].value; - return EGL_TRUE; - } - return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); -} - -static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config, - NativeWindowType window, const EGLint* /*attrib_list*/) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); - if (window == 0) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - EGLint surfaceType; - if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) - return EGL_FALSE; - - if (!(surfaceType & EGL_WINDOW_BIT)) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - if (static_cast<ANativeWindow*>(window)->common.magic != - ANDROID_NATIVE_WINDOW_MAGIC) { - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); - } - - EGLint configID; - if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) - return EGL_FALSE; - - int32_t depthFormat; - int32_t pixelFormat; - if (getConfigFormatInfo(configID, pixelFormat, depthFormat) != NO_ERROR) { - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - } - - // FIXME: we don't have access to the pixelFormat here just yet. - // (it's possible that the surface is not fully initialized) - // maybe this should be done after the page-flip - //if (EGLint(info.format) != pixelFormat) - // return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - egl_surface_t* surface; - surface = new egl_window_surface_v2_t(dpy, config, depthFormat, - static_cast<ANativeWindow*>(window)); - - if (!surface->initCheck()) { - // there was a problem in the ctor, the error - // flag has been set. - delete surface; - surface = 0; - } - return surface; -} - -static EGLSurface createPixmapSurface(EGLDisplay dpy, EGLConfig config, - NativePixmapType pixmap, const EGLint* /*attrib_list*/) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); - if (pixmap == 0) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - EGLint surfaceType; - if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) - return EGL_FALSE; - - if (!(surfaceType & EGL_PIXMAP_BIT)) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - if (static_cast<egl_native_pixmap_t*>(pixmap)->version != - sizeof(egl_native_pixmap_t)) { - return setError(EGL_BAD_NATIVE_PIXMAP, EGL_NO_SURFACE); - } - - EGLint configID; - if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) - return EGL_FALSE; - - int32_t depthFormat; - int32_t pixelFormat; - if (getConfigFormatInfo(configID, pixelFormat, depthFormat) != NO_ERROR) { - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - } - - if (pixmap->format != pixelFormat) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - egl_surface_t* surface = - new egl_pixmap_surface_t(dpy, config, depthFormat, - static_cast<egl_native_pixmap_t*>(pixmap)); - - if (!surface->initCheck()) { - // there was a problem in the ctor, the error - // flag has been set. - delete surface; - surface = 0; - } - return surface; -} - -static EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config, - const EGLint *attrib_list) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); - - EGLint surfaceType; - if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) - return EGL_FALSE; - - if (!(surfaceType & EGL_PBUFFER_BIT)) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - EGLint configID; - if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) - return EGL_FALSE; - - int32_t depthFormat; - int32_t pixelFormat; - if (getConfigFormatInfo(configID, pixelFormat, depthFormat) != NO_ERROR) { - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - } - - int32_t w = 0; - int32_t h = 0; - while (attrib_list[0] != EGL_NONE) { - if (attrib_list[0] == EGL_WIDTH) w = attrib_list[1]; - if (attrib_list[0] == EGL_HEIGHT) h = attrib_list[1]; - attrib_list+=2; - } - - egl_surface_t* surface = - new egl_pbuffer_surface_t(dpy, config, depthFormat, w, h, pixelFormat); - - if (!surface->initCheck()) { - // there was a problem in the ctor, the error - // flag has been set. - delete surface; - surface = 0; - } - return surface; -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- - -using namespace android; - -// ---------------------------------------------------------------------------- -// Initialization -// ---------------------------------------------------------------------------- - -EGLDisplay eglGetDisplay(NativeDisplayType display) -{ -#ifndef __ANDROID__ - // this just needs to be done once - if (gGLKey == -1) { - pthread_mutex_lock(&gInitMutex); - if (gGLKey == -1) - pthread_key_create(&gGLKey, NULL); - pthread_mutex_unlock(&gInitMutex); - } -#endif - if (display == EGL_DEFAULT_DISPLAY) { - EGLDisplay dpy = (EGLDisplay)1; - egl_display_t& d = egl_display_t::get_display(dpy); - d.type = display; - return dpy; - } - return EGL_NO_DISPLAY; -} - -EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - EGLBoolean res = EGL_TRUE; - egl_display_t& d = egl_display_t::get_display(dpy); - - if (d.initialized.fetch_add(1, std::memory_order_acquire) == 0) { - // initialize stuff here if needed - //pthread_mutex_lock(&gInitMutex); - //pthread_mutex_unlock(&gInitMutex); - } - - if (res == EGL_TRUE) { - if (major != NULL) *major = VERSION_MAJOR; - if (minor != NULL) *minor = VERSION_MINOR; - } - return res; -} - -EGLBoolean eglTerminate(EGLDisplay dpy) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - EGLBoolean res = EGL_TRUE; - egl_display_t& d = egl_display_t::get_display(dpy); - if (d.initialized.fetch_sub(1, std::memory_order_release) == 1) { - std::atomic_thread_fence(std::memory_order_acquire); - // TODO: destroy all resources (surfaces, contexts, etc...) - //pthread_mutex_lock(&gInitMutex); - //pthread_mutex_unlock(&gInitMutex); - } - return res; -} - -// ---------------------------------------------------------------------------- -// configuration -// ---------------------------------------------------------------------------- - -EGLBoolean eglGetConfigs( EGLDisplay dpy, - EGLConfig *configs, - EGLint config_size, EGLint *num_config) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - if (ggl_unlikely(num_config==NULL)) - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - - GLint numConfigs = NELEM(gConfigs); - if (!configs) { - *num_config = numConfigs; - return EGL_TRUE; - } - GLint i; - for (i=0 ; i<numConfigs && i<config_size ; i++) { - *configs++ = (EGLConfig)(uintptr_t)i; - } - *num_config = i; - return EGL_TRUE; -} - -EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, - EGLConfig *configs, EGLint config_size, - EGLint *num_config) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - if (ggl_unlikely(num_config==NULL)) { - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - } - - if (ggl_unlikely(attrib_list==0)) { - /* - * A NULL attrib_list should be treated as though it was an empty - * one (terminated with EGL_NONE) as defined in - * section 3.4.1 "Querying Configurations" in the EGL specification. - */ - static const EGLint dummy = EGL_NONE; - attrib_list = &dummy; - } - - int numAttributes = 0; - int numConfigs = NELEM(gConfigs); - uint32_t possibleMatch = (1<<numConfigs)-1; - while(possibleMatch && *attrib_list != EGL_NONE) { - numAttributes++; - EGLint attr = *attrib_list++; - EGLint val = *attrib_list++; - for (int i=0 ; possibleMatch && i<numConfigs ; i++) { - if (!(possibleMatch & (1<<i))) - continue; - if (isAttributeMatching(i, attr, val) == 0) { - possibleMatch &= ~(1<<i); - } - } - } - - // now, handle the attributes which have a useful default value - for (size_t j=0 ; possibleMatch && j<NELEM(config_defaults) ; j++) { - // see if this attribute was specified, if not, apply its - // default value - if (binarySearch<config_pair_t>( - (config_pair_t const*)attrib_list, - 0, numAttributes-1, - config_defaults[j].key) < 0) - { - for (int i=0 ; possibleMatch && i<numConfigs ; i++) { - if (!(possibleMatch & (1<<i))) - continue; - if (isAttributeMatching(i, - config_defaults[j].key, - config_defaults[j].value) == 0) - { - possibleMatch &= ~(1<<i); - } - } - } - } - - // return the configurations found - int n=0; - if (possibleMatch) { - if (configs) { - for (int i=0 ; config_size && i<numConfigs ; i++) { - if (possibleMatch & (1<<i)) { - *configs++ = (EGLConfig)(uintptr_t)i; - config_size--; - n++; - } - } - } else { - for (int i=0 ; i<numConfigs ; i++) { - if (possibleMatch & (1<<i)) { - n++; - } - } - } - } - *num_config = n; - return EGL_TRUE; -} - -EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, - EGLint attribute, EGLint *value) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - return getConfigAttrib(dpy, config, attribute, value); -} - -// ---------------------------------------------------------------------------- -// surfaces -// ---------------------------------------------------------------------------- - -EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, - NativeWindowType window, - const EGLint *attrib_list) -{ - return createWindowSurface(dpy, config, window, attrib_list); -} - -EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, - NativePixmapType pixmap, - const EGLint *attrib_list) -{ - return createPixmapSurface(dpy, config, pixmap, attrib_list); -} - -EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, - const EGLint *attrib_list) -{ - return createPbufferSurface(dpy, config, attrib_list); -} - -EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface eglSurface) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - if (eglSurface != EGL_NO_SURFACE) { - egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) ); - if (!surface->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (surface->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - if (surface->ctx) { - // defer disconnect/delete until no longer current - surface->zombie = true; - } else { - surface->disconnect(); - delete surface; - } - } - return EGL_TRUE; -} - -EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface eglSurface, - EGLint attribute, EGLint *value) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - egl_surface_t* surface = static_cast<egl_surface_t*>(eglSurface); - if (!surface->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (surface->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - EGLBoolean ret = EGL_TRUE; - switch (attribute) { - case EGL_CONFIG_ID: - ret = getConfigAttrib(dpy, surface->config, EGL_CONFIG_ID, value); - break; - case EGL_WIDTH: - *value = surface->getWidth(); - break; - case EGL_HEIGHT: - *value = surface->getHeight(); - break; - case EGL_LARGEST_PBUFFER: - // not modified for a window or pixmap surface - break; - case EGL_TEXTURE_FORMAT: - *value = EGL_NO_TEXTURE; - break; - case EGL_TEXTURE_TARGET: - *value = EGL_NO_TEXTURE; - break; - case EGL_MIPMAP_TEXTURE: - *value = EGL_FALSE; - break; - case EGL_MIPMAP_LEVEL: - *value = 0; - break; - case EGL_RENDER_BUFFER: - // TODO: return the real RENDER_BUFFER here - *value = EGL_BACK_BUFFER; - break; - case EGL_HORIZONTAL_RESOLUTION: - // pixel/mm * EGL_DISPLAY_SCALING - *value = surface->getHorizontalResolution(); - break; - case EGL_VERTICAL_RESOLUTION: - // pixel/mm * EGL_DISPLAY_SCALING - *value = surface->getVerticalResolution(); - break; - case EGL_PIXEL_ASPECT_RATIO: { - // w/h * EGL_DISPLAY_SCALING - int wr = surface->getHorizontalResolution(); - int hr = surface->getVerticalResolution(); - *value = (wr * EGL_DISPLAY_SCALING) / hr; - } break; - case EGL_SWAP_BEHAVIOR: - *value = surface->getSwapBehavior(); - break; - default: - ret = setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); - } - return ret; -} - -EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, - EGLContext /*share_list*/, const EGLint* /*attrib_list*/) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); - - ogles_context_t* gl = ogles_init(sizeof(egl_context_t)); - if (!gl) return setError(EGL_BAD_ALLOC, EGL_NO_CONTEXT); - - egl_context_t* c = static_cast<egl_context_t*>(gl->rasterizer.base); - c->flags = egl_context_t::NEVER_CURRENT; - c->dpy = dpy; - c->config = config; - c->read = 0; - c->draw = 0; - return (EGLContext)gl; -} - -EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - egl_context_t* c = egl_context_t::context(ctx); - if (c->flags & egl_context_t::IS_CURRENT) - setGlThreadSpecific(0); - ogles_uninit((ogles_context_t*)ctx); - return EGL_TRUE; -} - -EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, - EGLSurface read, EGLContext ctx) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - if (draw) { - egl_surface_t* s = (egl_surface_t*)draw; - if (!s->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (s->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: check that draw is compatible with the context - } - if (read && read!=draw) { - egl_surface_t* s = (egl_surface_t*)read; - if (!s->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (s->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: check that read is compatible with the context - } - - EGLContext current_ctx = EGL_NO_CONTEXT; - - if ((read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE) && (ctx != EGL_NO_CONTEXT)) - return setError(EGL_BAD_MATCH, EGL_FALSE); - - if ((read != EGL_NO_SURFACE || draw != EGL_NO_SURFACE) && (ctx == EGL_NO_CONTEXT)) - return setError(EGL_BAD_MATCH, EGL_FALSE); - - if (ctx == EGL_NO_CONTEXT) { - // if we're detaching, we need the current context - current_ctx = (EGLContext)getGlThreadSpecific(); - } else { - egl_surface_t* d = (egl_surface_t*)draw; - egl_surface_t* r = (egl_surface_t*)read; - if ((d && d->ctx && d->ctx != ctx) || - (r && r->ctx && r->ctx != ctx)) { - // one of the surface is bound to a context in another thread - return setError(EGL_BAD_ACCESS, EGL_FALSE); - } - } - - ogles_context_t* gl = (ogles_context_t*)ctx; - if (makeCurrent(gl) == 0) { - if (ctx) { - egl_context_t* c = egl_context_t::context(ctx); - egl_surface_t* d = (egl_surface_t*)draw; - egl_surface_t* r = (egl_surface_t*)read; - - if (c->draw) { - egl_surface_t* s = reinterpret_cast<egl_surface_t*>(c->draw); - s->disconnect(); - s->ctx = EGL_NO_CONTEXT; - if (s->zombie) - delete s; - } - if (c->read) { - // FIXME: unlock/disconnect the read surface too - } - - c->draw = draw; - c->read = read; - - if (c->flags & egl_context_t::NEVER_CURRENT) { - c->flags &= ~egl_context_t::NEVER_CURRENT; - GLint w = 0; - GLint h = 0; - if (draw) { - w = d->getWidth(); - h = d->getHeight(); - } - ogles_surfaceport(gl, 0, 0); - ogles_viewport(gl, 0, 0, w, h); - ogles_scissor(gl, 0, 0, w, h); - } - if (d) { - if (d->connect() == EGL_FALSE) { - return EGL_FALSE; - } - d->ctx = ctx; - d->bindDrawSurface(gl); - } - if (r) { - // FIXME: lock/connect the read surface too - r->ctx = ctx; - r->bindReadSurface(gl); - } - } else { - // if surfaces were bound to the context bound to this thread - // mark then as unbound. - if (current_ctx) { - egl_context_t* c = egl_context_t::context(current_ctx); - egl_surface_t* d = (egl_surface_t*)c->draw; - egl_surface_t* r = (egl_surface_t*)c->read; - if (d) { - c->draw = 0; - d->disconnect(); - d->ctx = EGL_NO_CONTEXT; - if (d->zombie) - delete d; - } - if (r) { - c->read = 0; - r->ctx = EGL_NO_CONTEXT; - // FIXME: unlock/disconnect the read surface too - } - } - } - return EGL_TRUE; - } - return setError(EGL_BAD_ACCESS, EGL_FALSE); -} - -EGLContext eglGetCurrentContext(void) -{ - // eglGetCurrentContext returns the current EGL rendering context, - // as specified by eglMakeCurrent. If no context is current, - // EGL_NO_CONTEXT is returned. - return (EGLContext)getGlThreadSpecific(); -} - -EGLSurface eglGetCurrentSurface(EGLint readdraw) -{ - // eglGetCurrentSurface returns the read or draw surface attached - // to the current EGL rendering context, as specified by eglMakeCurrent. - // If no context is current, EGL_NO_SURFACE is returned. - EGLContext ctx = (EGLContext)getGlThreadSpecific(); - if (ctx == EGL_NO_CONTEXT) return EGL_NO_SURFACE; - egl_context_t* c = egl_context_t::context(ctx); - if (readdraw == EGL_READ) { - return c->read; - } else if (readdraw == EGL_DRAW) { - return c->draw; - } - return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); -} - -EGLDisplay eglGetCurrentDisplay(void) -{ - // eglGetCurrentDisplay returns the current EGL display connection - // for the current EGL rendering context, as specified by eglMakeCurrent. - // If no context is current, EGL_NO_DISPLAY is returned. - EGLContext ctx = (EGLContext)getGlThreadSpecific(); - if (ctx == EGL_NO_CONTEXT) return EGL_NO_DISPLAY; - egl_context_t* c = egl_context_t::context(ctx); - return c->dpy; -} - -EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, - EGLint attribute, EGLint *value) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - egl_context_t* c = egl_context_t::context(ctx); - switch (attribute) { - case EGL_CONFIG_ID: - // Returns the ID of the EGL frame buffer configuration with - // respect to which the context was created - return getConfigAttrib(dpy, c->config, EGL_CONFIG_ID, value); - } - return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); -} - -EGLBoolean eglWaitGL(void) -{ - return EGL_TRUE; -} - -EGLBoolean eglWaitNative(EGLint /*engine*/) -{ - return EGL_TRUE; -} - -EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - egl_surface_t* d = static_cast<egl_surface_t*>(draw); - if (!d->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (d->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - // post the surface - d->swapBuffers(); - - // if it's bound to a context, update the buffer - if (d->ctx != EGL_NO_CONTEXT) { - d->bindDrawSurface((ogles_context_t*)d->ctx); - // if this surface is also the read surface of the context - // it is bound to, make sure to update the read buffer as well. - // The EGL spec is a little unclear about this. - egl_context_t* c = egl_context_t::context(d->ctx); - if (c->read == draw) { - d->bindReadSurface((ogles_context_t*)d->ctx); - } - } - - return EGL_TRUE; -} - -EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface /*surface*/, - NativePixmapType /*target*/) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: eglCopyBuffers() - return EGL_FALSE; -} - -EGLint eglGetError(void) -{ - return getError(); -} - -const char* eglQueryString(EGLDisplay dpy, EGLint name) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, (const char*)0); - - switch (name) { - case EGL_VENDOR: - return gVendorString; - case EGL_VERSION: - return gVersionString; - case EGL_EXTENSIONS: - return gExtensionsString; - case EGL_CLIENT_APIS: - return gClientApiString; - } - return setError(EGL_BAD_PARAMETER, (const char *)0); -} - -// ---------------------------------------------------------------------------- -// EGL 1.1 -// ---------------------------------------------------------------------------- - -EGLBoolean eglSurfaceAttrib( - EGLDisplay dpy, EGLSurface /*surface*/, EGLint /*attribute*/, EGLint /*value*/) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: eglSurfaceAttrib() - return setError(EGL_BAD_PARAMETER, EGL_FALSE); -} - -EGLBoolean eglBindTexImage( - EGLDisplay dpy, EGLSurface /*surface*/, EGLint /*buffer*/) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: eglBindTexImage() - return setError(EGL_BAD_PARAMETER, EGL_FALSE); -} - -EGLBoolean eglReleaseTexImage( - EGLDisplay dpy, EGLSurface /*surface*/, EGLint /*buffer*/) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: eglReleaseTexImage() - return setError(EGL_BAD_PARAMETER, EGL_FALSE); -} - -EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint /*interval*/) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: eglSwapInterval() - return EGL_TRUE; -} - -// ---------------------------------------------------------------------------- -// EGL 1.2 -// ---------------------------------------------------------------------------- - -EGLBoolean eglBindAPI(EGLenum api) -{ - if (api != EGL_OPENGL_ES_API) - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - return EGL_TRUE; -} - -EGLenum eglQueryAPI(void) -{ - return EGL_OPENGL_ES_API; -} - -EGLBoolean eglWaitClient(void) -{ - glFinish(); - return EGL_TRUE; -} - -EGLBoolean eglReleaseThread(void) -{ - // TODO: eglReleaseThread() - return EGL_TRUE; -} - -EGLSurface eglCreatePbufferFromClientBuffer( - EGLDisplay dpy, EGLenum /*buftype*/, EGLClientBuffer /*buffer*/, - EGLConfig /*config*/, const EGLint* /*attrib_list*/) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); - // TODO: eglCreatePbufferFromClientBuffer() - return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); -} - -// ---------------------------------------------------------------------------- -// EGL_EGLEXT_VERSION 3 -// ---------------------------------------------------------------------------- - -void (*eglGetProcAddress (const char *procname))() -{ - extention_map_t const * const map = gExtentionMap; - for (uint32_t i=0 ; i<NELEM(gExtentionMap) ; i++) { - if (!strcmp(procname, map[i].name)) { - return map[i].address; - } - } - return NULL; -} - -EGLBoolean eglLockSurfaceKHR(EGLDisplay /*dpy*/, EGLSurface /*surface*/, - const EGLint* /*attrib_list*/) -{ - EGLBoolean result = EGL_FALSE; - return result; -} - -EGLBoolean eglUnlockSurfaceKHR(EGLDisplay /*dpy*/, EGLSurface /*surface*/) -{ - EGLBoolean result = EGL_FALSE; - return result; -} - -EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, - EGLClientBuffer buffer, const EGLint* /*attrib_list*/) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) { - return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE_KHR); - } - if (ctx != EGL_NO_CONTEXT) { - return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR); - } - if (target != EGL_NATIVE_BUFFER_ANDROID) { - return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); - } - - ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)buffer; - - if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) - return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); - - if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) - return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); - - switch (native_buffer->format) { - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - case HAL_PIXEL_FORMAT_RGB_888: - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_BGRA_8888: - break; - default: - return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); - } - - native_buffer->common.incRef(&native_buffer->common); - return (EGLImageKHR)native_buffer; -} - -EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) { - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - } - - ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)img; - - if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - - if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - - native_buffer->common.decRef(&native_buffer->common); - - return EGL_TRUE; -} - -// ---------------------------------------------------------------------------- -// EGL_KHR_fence_sync -// ---------------------------------------------------------------------------- - -#define FENCE_SYNC_HANDLE ((EGLSyncKHR)0xFE4CE) - -EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, - const EGLint *attrib_list) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) { - return setError(EGL_BAD_DISPLAY, EGL_NO_SYNC_KHR); - } - - if (type != EGL_SYNC_FENCE_KHR || - (attrib_list != NULL && attrib_list[0] != EGL_NONE)) { - return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SYNC_KHR); - } - - if (eglGetCurrentContext() == EGL_NO_CONTEXT) { - return setError(EGL_BAD_MATCH, EGL_NO_SYNC_KHR); - } - - // AGL is synchronous; nothing to do here. - - return FENCE_SYNC_HANDLE; -} - -EGLBoolean eglDestroySyncKHR(EGLDisplay /*dpy*/, EGLSyncKHR sync) -{ - if (sync != FENCE_SYNC_HANDLE) { - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - } - - return EGL_TRUE; -} - -EGLint eglClientWaitSyncKHR(EGLDisplay /*dpy*/, EGLSyncKHR sync, EGLint /*flags*/, - EGLTimeKHR /*timeout*/) -{ - if (sync != FENCE_SYNC_HANDLE) { - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - } - - return EGL_CONDITION_SATISFIED_KHR; -} - -EGLBoolean eglGetSyncAttribKHR(EGLDisplay /*dpy*/, EGLSyncKHR sync, - EGLint attribute, EGLint *value) -{ - if (sync != FENCE_SYNC_HANDLE) { - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - } - - switch (attribute) { - case EGL_SYNC_TYPE_KHR: - *value = EGL_SYNC_FENCE_KHR; - return EGL_TRUE; - case EGL_SYNC_STATUS_KHR: - *value = EGL_SIGNALED_KHR; - return EGL_TRUE; - case EGL_SYNC_CONDITION_KHR: - *value = EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR; - return EGL_TRUE; - default: - return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); - } -} - -// ---------------------------------------------------------------------------- -// ANDROID extensions -// ---------------------------------------------------------------------------- - -EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, - EGLint left, EGLint top, EGLint width, EGLint height) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - egl_surface_t* d = static_cast<egl_surface_t*>(draw); - if (!d->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (d->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - // post the surface - d->setSwapRectangle(left, top, width, height); - - return EGL_TRUE; -} diff --git a/opengl/libagl/fixed_asm.S b/opengl/libagl/fixed_asm.S deleted file mode 100644 index 5e08856d0a..0000000000 --- a/opengl/libagl/fixed_asm.S +++ /dev/null @@ -1,67 +0,0 @@ -/* libs/opengles/fixed_asm.S -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - - - .text - .align 2 - - .global gglFloatToFixed - .type gglFloatToFixed, %function - .global gglFloatToFixedFast - .type gglFloatToFixedFast, %function - - -/* - * Converts a float to a s15.16 fixed-point number. - * this doesn't handle floats out of the [-32768, +32768[ range - * and doesn't performs round-to-nearest. - * however, it's very fast :-) - */ - -gglFloatToFixedFast: - movs r1, r0, lsl #1 /* remove bit sign */ - mov r2, #0x8E /* 127 + 15 */ - sub r1, r2, r1, lsr #24 /* compute shift */ - mov r2, r0, lsl #8 /* mantissa<<8 */ - orr r2, r2, #0x80000000 /* add the missing 1 */ - mov r0, r2, lsr r1 /* scale to 16.16 */ - rsbcs r0, r0, #0 /* negate if needed */ - bx lr - -/* - * this version rounds-to-nearest and saturates numbers - * outside the range (but not NaNs). - */ - -gglFloatToFixed: - mov r1, r0, lsl #1 /* remove bit sign */ - mov r2, #0x8E /* 127 + 15 */ - subs r1, r2, r1, lsr #24 /* compute shift */ - bls 0f /* too big */ - mov r2, r0, lsl #8 /* mantissa<<8 */ - orr r2, r2, #0x80000000 /* add the missing 1 */ - mov r3, r0 - movs r0, r2, lsr r1 /* scale to 16.16 */ - addcs r0, r0, #1 /* round-to-nearest */ - tst r3, #0x80000000 /* negative? */ - rsbne r0, r0, #0 /* negate if needed */ - bx lr - -0: ands r0, r0, #0x80000000 /* keep only the sign bit */ - moveq r0, #0x7fffffff /* positive, maximum value */ - bx lr - diff --git a/opengl/libagl/fp.cpp b/opengl/libagl/fp.cpp deleted file mode 100644 index a7a4f7b102..0000000000 --- a/opengl/libagl/fp.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* libs/opengles/fp.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include "fp.h" - -// ---------------------------------------------------------------------------- - -#if !(defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6)) -GGLfixed gglFloatToFixed(float v) { - return GGLfixed(floorf(v * 65536.0f + 0.5f)); -} -#endif - -// ---------------------------------------------------------------------------- - -namespace android { - -namespace gl { - -GLfloat fixedToFloat(GLfixed x) -{ -#if DEBUG_USE_FLOATS - return x / 65536.0f; -#else - if (!x) return 0; - const uint32_t s = x & 0x80000000; - union { - uint32_t i; - float f; - }; - i = s ? -x : x; - const int c = gglClz(i) - 8; - i = (c>=0) ? (i<<c) : (i>>-c); - const uint32_t e = 134 - c; - i &= ~0x800000; - i |= e<<23; - i |= s; - return f; -#endif -} - -float sinef(float x) -{ - const float A = 1.0f / (2.0f*M_PI); - const float B = -16.0f; - const float C = 8.0f; - - // scale angle for easy argument reduction - x *= A; - - if (fabsf(x) >= 0.5f) { - // Argument reduction - x = x - ceilf(x + 0.5f) + 1.0f; - } - - const float y = B*x*fabsf(x) + C*x; - return 0.2215f * (y*fabsf(y) - y) + y; -} - -float cosinef(float x) -{ - return sinef(x + float(M_PI/2)); -} - -void sincosf(GLfloat angle, GLfloat* s, GLfloat* c) { - *s = sinef(angle); - *c = cosinef(angle); -} - -}; // namespace fp_utils - -// ---------------------------------------------------------------------------- -}; // namespace android diff --git a/opengl/libagl/fp.h b/opengl/libagl/fp.h deleted file mode 100644 index 6d0c18339f..0000000000 --- a/opengl/libagl/fp.h +++ /dev/null @@ -1,243 +0,0 @@ -/* libs/opengles/fp.h -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_FP_H -#define ANDROID_OPENGLES_FP_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> -#include <math.h> - -#include <private/pixelflinger/ggl_context.h> - -#include <GLES/gl.h> - -#define DEBUG_USE_FLOATS 0 - -// ---------------------------------------------------------------------------- - -extern "C" GLfixed gglFloatToFixed(float f) __attribute__((const)); - -// ---------------------------------------------------------------------------- -namespace android { - -namespace gl { - - GLfloat fixedToFloat(GLfixed) CONST; - - void sincosf(GLfloat angle, GLfloat* s, GLfloat* c); - float sinef(GLfloat x) CONST; - float cosinef(GLfloat x) CONST; - -inline bool cmpf(GLfloat a, GLfloat b) CONST; -inline bool isZerof(GLfloat) CONST; -inline bool isOnef(GLfloat) CONST; - -inline int isZeroOrNegativef(GLfloat) CONST; - -inline int exponent(GLfloat) CONST; -inline int32_t mantissa(GLfloat) CONST; -inline GLfloat clampToZerof(GLfloat) CONST; -inline GLfloat reciprocalf(GLfloat) CONST; -inline GLfloat rsqrtf(GLfloat) CONST; -inline GLfloat sqrf(GLfloat) CONST; -inline GLfloat addExpf(GLfloat v, int e) CONST; -inline GLfloat mul2f(GLfloat v) CONST; -inline GLfloat div2f(GLfloat v) CONST; -inline GLfloat absf(GLfloat v) CONST; - - -/* - * float fastexpf(float) : a fast approximation of expf(x) - * give somewhat accurate results for -88 <= x <= 88 - * - * exp(x) = 2^(x/ln(2)) - * we use the properties of float encoding - * to get a fast 2^ and linear interpolation - * - */ - -inline float fastexpf(float y) __attribute__((const)); - -inline float fastexpf(float y) -{ - union { - float r; - int32_t i; - } u; - - // 127*ln(2) = 88 - if (y < -88.0f) { - u.r = 0.0f; - } else if (y > 88.0f) { - u.r = INFINITY; - } else { - const float kOneOverLogTwo = (1L<<23) / M_LN2; - const int32_t kExponentBias = 127L<<23; - const int32_t e = int32_t(y*kOneOverLogTwo); - u.i = e + kExponentBias; - } - - return u.r; -} - - -bool cmpf(GLfloat a, GLfloat b) { -#if DEBUG_USE_FLOATS - return a == b; -#else - union { - float f; - uint32_t i; - } ua, ub; - ua.f = a; - ub.f = b; - return ua.i == ub.i; -#endif -} - -bool isZerof(GLfloat v) { -#if DEBUG_USE_FLOATS - return v == 0; -#else - union { - float f; - int32_t i; - }; - f = v; - return (i<<1) == 0; -#endif -} - -bool isOnef(GLfloat v) { - return cmpf(v, 1.0f); -} - -int isZeroOrNegativef(GLfloat v) { -#if DEBUG_USE_FLOATS - return v <= 0; -#else - union { - float f; - int32_t i; - }; - f = v; - return isZerof(v) | (i>>31); -#endif -} - -int exponent(GLfloat v) { - union { - float f; - uint32_t i; - }; - f = v; - return ((i << 1) >> 24) - 127; -} - -int32_t mantissa(GLfloat v) { - union { - float f; - uint32_t i; - }; - f = v; - if (!(i&0x7F800000)) return 0; - const int s = i >> 31; - i |= (1L<<23); - i &= ~0xFF000000; - return s ? -i : i; -} - -GLfloat clampToZerof(GLfloat v) { -#if DEBUG_USE_FLOATS - return v<0 ? 0 : (v>1 ? 1 : v); -#else - union { - float f; - int32_t i; - }; - f = v; - i &= ~(i>>31); - return f; -#endif -} - -GLfloat reciprocalf(GLfloat v) { - // XXX: do better - return 1.0f / v; -} - -GLfloat rsqrtf(GLfloat v) { - // XXX: do better - return 1.0f / sqrtf(v); -} - -GLfloat sqrf(GLfloat v) { - // XXX: do better - return v*v; -} - -GLfloat addExpf(GLfloat v, int e) { - union { - float f; - int32_t i; - }; - f = v; - if (i<<1) { // XXX: deal with over/underflow - i += int32_t(e)<<23; - } - return f; -} - -GLfloat mul2f(GLfloat v) { -#if DEBUG_USE_FLOATS - return v*2; -#else - return addExpf(v, 1); -#endif -} - -GLfloat div2f(GLfloat v) { -#if DEBUG_USE_FLOATS - return v*0.5f; -#else - return addExpf(v, -1); -#endif -} - -GLfloat absf(GLfloat v) { -#if DEBUG_USE_FLOATS - return v<0 ? -v : v; -#else - union { - float f; - int32_t i; - }; - f = v; - i &= ~0x80000000; - return f; -#endif -} - -}; // namespace gl - -// ---------------------------------------------------------------------------- -}; // namespace android - -#endif // ANDROID_OPENGLES_FP_H - diff --git a/opengl/libagl/iterators.S b/opengl/libagl/iterators.S deleted file mode 100644 index 8fe9039088..0000000000 --- a/opengl/libagl/iterators.S +++ /dev/null @@ -1,89 +0,0 @@ -/* libs/opengles/iterators.S -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - - - .text - .align 2 - .arm - - .global iterators0032 - .type iterators0032, %function - -/* - * iterators0032 - * - * MUST BE CALLED FROM ARM CODE - * - * r0: const compute_iterators_t* (this) - * r0 + 0: m_dx01 - * r0 + 4: m_dy10 - * r0 + 8: m_dx20 - * r0 +12: m_dy02 - * r0 +16: m_x0 - * r0 +20: m_y0 - * r0 +24: m_area - * r0 +28: m_scale - * r0 +29: m_area_scale; - * r1: int32_t* (out) - * r1 + 0: c - * r1 + 4: dcdx - * r1 + 8: dcdy - * r2: c0 - * r3: c1 - * [sp]: c2 - */ - -iterators0032: - stmfd sp!, {r4, r5, r6, r7, r8, lr} - ldr r4, [sp, #4*6] - - ldrb r12, [r0, #29] - sub r3, r3, r2 - sub r4, r4, r2 - sub r12, r12, #16 - mov r3, r3, asr r12 - mov r4, r4, asr r12 - - ldr r5, [r0, #0] - ldr r12, [r0, #4] - smull r8, lr, r4, r5 - ldr r5, [r0, #8] - smull r6, r7, r4, r12 - ldr r12, [r0, #12] - smlal r8, lr, r3, r5 - smlal r6, r7, r3, r12 - - ldr r3, [r0, #16] // m_x0 - ldr r4, [r0, #20] // m_x1 - - str r6, [r1, #4] - str r8, [r1, #8] - - umull r6, r5, r3, r6 - umull r8, r0, r4, r8 - mla r7, r3, r7, r5 - mla lr, r4, lr, r0 - adds r6, r6, r8 - adc r7, r7, lr - - movs r6, r6, lsr #4 - adc r6, r6, r7, lsl #28 - rsb r6, r6, r2, lsl #16 - str r6, [r1, #0] - - ldmfd sp!, {r4, r5, r6, r7, r8, pc} - diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp deleted file mode 100644 index 216c725128..0000000000 --- a/opengl/libagl/light.cpp +++ /dev/null @@ -1,882 +0,0 @@ -/* libs/opengles/light.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdio.h> -#include "context.h" -#include "fp.h" -#include "light.h" -#include "state.h" -#include "matrix.h" - - -#if defined(__arm__) && defined(__thumb__) -#warning "light.cpp should not be compiled in thumb on ARM." -#endif - -namespace android { - -// ---------------------------------------------------------------------------- - -static void invalidate_lighting(ogles_context_t* c); -static void lightVertexValidate(ogles_context_t* c, vertex_t* v); -static void lightVertexNop(ogles_context_t* c, vertex_t* v); -static void lightVertex(ogles_context_t* c, vertex_t* v); -static void lightVertexMaterial(ogles_context_t* c, vertex_t* v); - -static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s); - -static __attribute__((noinline)) -void vnorm3(GLfixed* d, const GLfixed* a); - -static inline void vsa3(GLfixed* d, - const GLfixed* m, GLfixed s, const GLfixed* a); -static inline void vss3(GLfixed* d, - const GLfixed* m, GLfixed s, const GLfixed* a); -static inline void vmla3(GLfixed* d, - const GLfixed* m0, const GLfixed* m1, const GLfixed* a); -static inline void vmul3(GLfixed* d, - const GLfixed* m0, const GLfixed* m1); - -static GLfixed fog_linear(ogles_context_t* c, GLfixed z); -static GLfixed fog_exp(ogles_context_t* c, GLfixed z); -static GLfixed fog_exp2(ogles_context_t* c, GLfixed z); - - -// ---------------------------------------------------------------------------- - -static void init_white(vec4_t& c) { - c.r = c.g = c.b = c.a = 0x10000; -} - -void ogles_init_light(ogles_context_t* c) -{ - for (unsigned int i=0 ; i<OGLES_MAX_LIGHTS ; i++) { - c->lighting.lights[i].ambient.a = 0x10000; - c->lighting.lights[i].position.z = 0x10000; - c->lighting.lights[i].spotDir.z = -0x10000; - c->lighting.lights[i].spotCutoff = gglIntToFixed(180); - c->lighting.lights[i].attenuation[0] = 0x10000; - } - init_white(c->lighting.lights[0].diffuse); - init_white(c->lighting.lights[0].specular); - - c->lighting.front.ambient.r = - c->lighting.front.ambient.g = - c->lighting.front.ambient.b = gglFloatToFixed(0.2f); - c->lighting.front.ambient.a = 0x10000; - c->lighting.front.diffuse.r = - c->lighting.front.diffuse.g = - c->lighting.front.diffuse.b = gglFloatToFixed(0.8f); - c->lighting.front.diffuse.a = 0x10000; - c->lighting.front.specular.a = 0x10000; - c->lighting.front.emission.a = 0x10000; - - c->lighting.lightModel.ambient.r = - c->lighting.lightModel.ambient.g = - c->lighting.lightModel.ambient.b = gglFloatToFixed(0.2f); - c->lighting.lightModel.ambient.a = 0x10000; - - c->lighting.colorMaterial.face = GL_FRONT_AND_BACK; - c->lighting.colorMaterial.mode = GL_AMBIENT_AND_DIFFUSE; - - c->fog.mode = GL_EXP; - c->fog.fog = fog_exp; - c->fog.density = 0x10000; - c->fog.end = 0x10000; - c->fog.invEndMinusStart = 0x10000; - - invalidate_lighting(c); - - c->rasterizer.procs.shadeModel(c, GL_SMOOTH); - c->lighting.shadeModel = GL_SMOOTH; -} - -void ogles_uninit_light(ogles_context_t* /*c*/) -{ -} - -static inline int32_t clampF(GLfixed f) CONST; -int32_t clampF(GLfixed f) { - f = (f & ~(f>>31)); - if (f >= 0x10000) - f = 0x10000; - return f; -} - -static GLfixed fog_linear(ogles_context_t* c, GLfixed z) { - return clampF(gglMulx((c->fog.end - ((z<0)?-z:z)), c->fog.invEndMinusStart)); -} - -static GLfixed fog_exp(ogles_context_t* c, GLfixed z) { - const float e = fixedToFloat(gglMulx(c->fog.density, ((z<0)?-z:z))); - return clampF(gglFloatToFixed(fastexpf(-e))); -} - -static GLfixed fog_exp2(ogles_context_t* c, GLfixed z) { - const float e = fixedToFloat(gglMulx(c->fog.density, z)); - return clampF(gglFloatToFixed(fastexpf(-e*e))); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark math helpers -#endif - -static inline -void vscale3(GLfixed* d, const GLfixed* m, GLfixed s) { - d[0] = gglMulx(m[0], s); - d[1] = gglMulx(m[1], s); - d[2] = gglMulx(m[2], s); -} - -static inline -void vsa3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) { - d[0] = gglMulAddx(m[0], s, a[0]); - d[1] = gglMulAddx(m[1], s, a[1]); - d[2] = gglMulAddx(m[2], s, a[2]); -} - -static inline -void vss3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) { - d[0] = gglMulSubx(m[0], s, a[0]); - d[1] = gglMulSubx(m[1], s, a[1]); - d[2] = gglMulSubx(m[2], s, a[2]); -} - -static inline -void vmla3(GLfixed* d, - const GLfixed* m0, const GLfixed* m1, const GLfixed* a) -{ - d[0] = gglMulAddx(m0[0], m1[0], a[0]); - d[1] = gglMulAddx(m0[1], m1[1], a[1]); - d[2] = gglMulAddx(m0[2], m1[2], a[2]); -} - -static inline -void vmul3(GLfixed* d, const GLfixed* m0, const GLfixed* m1) { - d[0] = gglMulx(m0[0], m1[0]); - d[1] = gglMulx(m0[1], m1[1]); - d[2] = gglMulx(m0[2], m1[2]); -} - -void vnorm3(GLfixed* d, const GLfixed* a) -{ - // we must take care of overflows when normalizing a vector - GLfixed n; - int32_t x = a[0]; x = x>=0 ? x : -x; - int32_t y = a[1]; y = y>=0 ? y : -y; - int32_t z = a[2]; z = z>=0 ? z : -z; - if (ggl_likely(x<=0x6800 && y<=0x6800 && z<= 0x6800)) { - // in this case this will all fit on 32 bits - n = x*x + y*y + z*z; - n = gglSqrtRecipx(n); - n <<= 8; - } else { - // here norm^2 is at least 0x7EC00000 (>>32 == 0.495117) - n = vsquare3(x, y, z); - n = gglSqrtRecipx(n); - } - vscale3(d, a, n); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark lighting equations -#endif - -static inline void light_picker(ogles_context_t* c) -{ - if (ggl_likely(!c->lighting.enable)) { - c->lighting.lightVertex = lightVertexNop; - return; - } - if (c->lighting.colorMaterial.enable) { - c->lighting.lightVertex = lightVertexMaterial; - } else { - c->lighting.lightVertex = lightVertex; - } -} - -static inline void validate_light_mvi(ogles_context_t* c) -{ - uint32_t en = c->lighting.enabledLights; - // Vector from object to viewer, in eye coordinates - while (en) { - const int i = 31 - gglClz(en); - en &= ~(1<<i); - light_t& l = c->lighting.lights[i]; -#if OBJECT_SPACE_LIGHTING - c->transforms.mvui.point4(&c->transforms.mvui, - &l.objPosition, &l.position); -#else - l.objPosition = l.position; -#endif - vnorm3(l.normalizedObjPosition.v, l.objPosition.v); - } - const vec4_t eyeViewer = {{{ 0, 0, 0x10000, 0 }}}; -#if OBJECT_SPACE_LIGHTING - c->transforms.mvui.point3(&c->transforms.mvui, - &c->lighting.objViewer, &eyeViewer); - vnorm3(c->lighting.objViewer.v, c->lighting.objViewer.v); -#else - c->lighting.objViewer = eyeViewer; -#endif -} - -static inline void validate_light(ogles_context_t* c) -{ - // if colorMaterial is enabled, we get the color from the vertex - if (!c->lighting.colorMaterial.enable) { - material_t& material = c->lighting.front; - uint32_t en = c->lighting.enabledLights; - while (en) { - const int i = 31 - gglClz(en); - en &= ~(1<<i); - light_t& l = c->lighting.lights[i]; - vmul3(l.implicitAmbient.v, material.ambient.v, l.ambient.v); - vmul3(l.implicitDiffuse.v, material.diffuse.v, l.diffuse.v); - vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v); - - // this is just a flag to tell if we have a specular component - l.implicitSpecular.v[3] = - l.implicitSpecular.r | - l.implicitSpecular.g | - l.implicitSpecular.b; - - l.rConstAttenuation = (l.attenuation[1] | l.attenuation[2])==0; - if (l.rConstAttenuation) - l.rConstAttenuation = gglRecipFast(l.attenuation[0]); - } - // emission and ambient for the whole scene - vmla3( c->lighting.implicitSceneEmissionAndAmbient.v, - c->lighting.lightModel.ambient.v, - material.ambient.v, - material.emission.v); - c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a; - } - validate_light_mvi(c); -} - -void invalidate_lighting(ogles_context_t* c) -{ - // TODO: pick lightVertexValidate or lightVertexValidateMVI - // instead of systematically the heavier lightVertexValidate() - c->lighting.lightVertex = lightVertexValidate; -} - -void ogles_invalidate_lighting_mvui(ogles_context_t* c) -{ - invalidate_lighting(c); -} - -void lightVertexNop(ogles_context_t*, vertex_t* /*v*/) -{ - // we should never end-up here -} - -void lightVertexValidateMVI(ogles_context_t* c, vertex_t* v) -{ - validate_light_mvi(c); - light_picker(c); - c->lighting.lightVertex(c, v); -} - -void lightVertexValidate(ogles_context_t* c, vertex_t* v) -{ - validate_light(c); - light_picker(c); - c->lighting.lightVertex(c, v); -} - -void lightVertexMaterial(ogles_context_t* c, vertex_t* v) -{ - // fetch the material color - const GLvoid* cp = c->arrays.color.element( - v->index & vertex_cache_t::INDEX_MASK); - c->arrays.color.fetch(c, v->color.v, cp); - - // acquire the color-material from the vertex - material_t& material = c->lighting.front; - material.ambient = - material.diffuse = v->color; - // implicit arguments need to be computed per/vertex - uint32_t en = c->lighting.enabledLights; - while (en) { - const int i = 31 - gglClz(en); - en &= ~(1<<i); - light_t& l = c->lighting.lights[i]; - vmul3(l.implicitAmbient.v, material.ambient.v, l.ambient.v); - vmul3(l.implicitDiffuse.v, material.diffuse.v, l.diffuse.v); - vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v); - // this is just a flag to tell if we have a specular component - l.implicitSpecular.v[3] = - l.implicitSpecular.r | - l.implicitSpecular.g | - l.implicitSpecular.b; - } - // emission and ambient for the whole scene - vmla3( c->lighting.implicitSceneEmissionAndAmbient.v, - c->lighting.lightModel.ambient.v, - material.ambient.v, - material.emission.v); - c->lighting.implicitSceneEmissionAndAmbient.a = material.diffuse.a; - - // now we can light our vertex as usual - lightVertex(c, v); -} - -void lightVertex(ogles_context_t* c, vertex_t* v) -{ - // emission and ambient for the whole scene - vec4_t r = c->lighting.implicitSceneEmissionAndAmbient; - const vec4_t objViewer = c->lighting.objViewer; - - uint32_t en = c->lighting.enabledLights; - if (ggl_likely(en)) { - // since we do the lighting in object-space, we don't need to - // transform each normal. However, we might still have to normalize - // it if GL_NORMALIZE is enabled. - vec4_t n; - c->arrays.normal.fetch(c, n.v, - c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK)); - -#if !OBJECT_SPACE_LIGHTING - c->transforms.mvui.point3(&c->transforms.mvui, &n, &n); -#endif - - // TODO: right now we handle GL_RESCALE_NORMALS as if it were - // GL_NORMALIZE. We could optimize this by scaling mvui - // appropriately instead. - if (c->transforms.rescaleNormals) - vnorm3(n.v, n.v); - - const material_t& material = c->lighting.front; - const int twoSide = c->lighting.lightModel.twoSide; - - while (en) { - const int i = 31 - gglClz(en); - en &= ~(1<<i); - const light_t& l = c->lighting.lights[i]; - - vec4_t d, t; - GLfixed s; - GLfixed sqDist = 0x10000; - - // compute vertex-to-light vector - if (ggl_unlikely(l.position.w)) { - // lightPos/1.0 - vertex/vertex.w == lightPos*vertex.w - vertex -#if !OBJECT_SPACE_LIGHTING - vec4_t o; - const transform_t& mv = c->transforms.modelview.transform; - mv.point4(&mv, &o, &v->obj); - vss3(d.v, l.objPosition.v, o.w, o.v); -#else - vss3(d.v, l.objPosition.v, v->obj.w, v->obj.v); -#endif - sqDist = dot3(d.v, d.v); - vscale3(d.v, d.v, gglSqrtRecipx(sqDist)); - } else { - // TODO: avoid copy here - d = l.normalizedObjPosition; - } - - // ambient & diffuse - s = dot3(n.v, d.v); - s = (s<0) ? (twoSide?(-s):0) : s; - vsa3(t.v, l.implicitDiffuse.v, s, l.implicitAmbient.v); - - // specular - if (ggl_unlikely(s && l.implicitSpecular.v[3])) { - vec4_t h; - h.x = d.x + objViewer.x; - h.y = d.y + objViewer.y; - h.z = d.z + objViewer.z; - vnorm3(h.v, h.v); - s = dot3(n.v, h.v); - s = (s<0) ? (twoSide?(-s):0) : s; - if (s > 0) { - s = gglPowx(s, material.shininess); - vsa3(t.v, l.implicitSpecular.v, s, t.v); - } - } - - // spot - if (ggl_unlikely(l.spotCutoff != gglIntToFixed(180))) { - GLfixed spotAtt = -dot3(l.normalizedSpotDir.v, d.v); - if (spotAtt >= l.spotCutoffCosine) { - vscale3(t.v, t.v, gglPowx(spotAtt, l.spotExp)); - } - } - - // attenuation - if (ggl_unlikely(l.position.w)) { - if (l.rConstAttenuation) { - s = l.rConstAttenuation; - } else { - s = gglMulAddx(sqDist, l.attenuation[2], l.attenuation[0]); - if (l.attenuation[1]) - s = gglMulAddx(gglSqrtx(sqDist), l.attenuation[1], s); - s = gglRecipFast(s); - } - vscale3(t.v, t.v, s); - } - - r.r += t.r; - r.g += t.g; - r.b += t.b; - } - } - v->color.r = gglClampx(r.r); - v->color.g = gglClampx(r.g); - v->color.b = gglClampx(r.b); - v->color.a = gglClampx(r.a); - v->flags |= vertex_t::LIT; -} - -static void lightModelx(GLenum pname, GLfixed param, ogles_context_t* c) -{ - if (ggl_unlikely(pname != GL_LIGHT_MODEL_TWO_SIDE)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->lighting.lightModel.twoSide = param ? GL_TRUE : GL_FALSE; - invalidate_lighting(c); -} - -static void lightx(GLenum i, GLenum pname, GLfixed param, ogles_context_t* c) -{ - if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - - light_t& light = c->lighting.lights[i-GL_LIGHT0]; - switch (pname) { - case GL_SPOT_EXPONENT: - if (GGLfixed(param) >= gglIntToFixed(128)) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - light.spotExp = param; - break; - case GL_SPOT_CUTOFF: - if (param!=gglIntToFixed(180) && GGLfixed(param)>=gglIntToFixed(90)) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - light.spotCutoff = param; - light.spotCutoffCosine = - gglFloatToFixed(cosinef((M_PI/(180.0f*65536.0f))*param)); - break; - case GL_CONSTANT_ATTENUATION: - if (param < 0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - light.attenuation[0] = param; - break; - case GL_LINEAR_ATTENUATION: - if (param < 0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - light.attenuation[1] = param; - break; - case GL_QUADRATIC_ATTENUATION: - if (param < 0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - light.attenuation[2] = param; - break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - invalidate_lighting(c); -} - -static void lightxv(GLenum i, GLenum pname, const GLfixed *params, ogles_context_t* c) -{ - if (ggl_unlikely(uint32_t(i-GL_LIGHT0) >= OGLES_MAX_LIGHTS)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - - GLfixed* what; - light_t& light = c->lighting.lights[i-GL_LIGHT0]; - switch (pname) { - case GL_AMBIENT: - what = light.ambient.v; - break; - case GL_DIFFUSE: - what = light.diffuse.v; - break; - case GL_SPECULAR: - what = light.specular.v; - break; - case GL_POSITION: { - ogles_validate_transform(c, transform_state_t::MODELVIEW); - transform_t& mv = c->transforms.modelview.transform; - mv.point4(&mv, &light.position, reinterpret_cast<vec4_t const*>(params)); - invalidate_lighting(c); - return; - } - case GL_SPOT_DIRECTION: { -#if OBJECT_SPACE_LIGHTING - ogles_validate_transform(c, transform_state_t::MVUI); - transform_t& mvui = c->transforms.mvui; - mvui.point3(&mvui, &light.spotDir, reinterpret_cast<vec4_t const*>(params)); -#else - light.spotDir = *reinterpret_cast<vec4_t const*>(params); -#endif - vnorm3(light.normalizedSpotDir.v, light.spotDir.v); - invalidate_lighting(c); - return; - } - default: - lightx(i, pname, params[0], c); - return; - } - what[0] = params[0]; - what[1] = params[1]; - what[2] = params[2]; - what[3] = params[3]; - invalidate_lighting(c); -} - -static void materialx(GLenum face, GLenum pname, GLfixed param, ogles_context_t* c) -{ - if (ggl_unlikely(face != GL_FRONT_AND_BACK)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (ggl_unlikely(pname != GL_SHININESS)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->lighting.front.shininess = param; - invalidate_lighting(c); -} - -static void fogx(GLenum pname, GLfixed param, ogles_context_t* c) -{ - switch (pname) { - case GL_FOG_DENSITY: - if (param >= 0) { - c->fog.density = param; - break; - } - ogles_error(c, GL_INVALID_VALUE); - break; - case GL_FOG_START: - c->fog.start = param; - c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start); - break; - case GL_FOG_END: - c->fog.end = param; - c->fog.invEndMinusStart = gglRecip(c->fog.end - c->fog.start); - break; - case GL_FOG_MODE: - switch (param) { - case GL_LINEAR: - c->fog.mode = param; - c->fog.fog = fog_linear; - break; - case GL_EXP: - c->fog.mode = param; - c->fog.fog = fog_exp; - break; - case GL_EXP2: - c->fog.mode = param; - c->fog.fog = fog_exp2; - break; - default: - ogles_error(c, GL_INVALID_ENUM); - break; - } - break; - default: - ogles_error(c, GL_INVALID_ENUM); - break; - } -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- - -using namespace android; - -#if 0 -#pragma mark - -#pragma mark lighting APIs -#endif - -void glShadeModel(GLenum mode) -{ - ogles_context_t* c = ogles_context_t::get(); - if (ggl_unlikely(mode != GL_SMOOTH && mode != GL_FLAT)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->lighting.shadeModel = mode; -} - -void glLightModelf(GLenum pname, GLfloat param) -{ - ogles_context_t* c = ogles_context_t::get(); - lightModelx(pname, gglFloatToFixed(param), c); -} - -void glLightModelx(GLenum pname, GLfixed param) -{ - ogles_context_t* c = ogles_context_t::get(); - lightModelx(pname, param, c); -} - -void glLightModelfv(GLenum pname, const GLfloat *params) -{ - ogles_context_t* c = ogles_context_t::get(); - if (pname == GL_LIGHT_MODEL_TWO_SIDE) { - lightModelx(pname, gglFloatToFixed(params[0]), c); - return; - } - - if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - - c->lighting.lightModel.ambient.r = gglFloatToFixed(params[0]); - c->lighting.lightModel.ambient.g = gglFloatToFixed(params[1]); - c->lighting.lightModel.ambient.b = gglFloatToFixed(params[2]); - c->lighting.lightModel.ambient.a = gglFloatToFixed(params[3]); - invalidate_lighting(c); -} - -void glLightModelxv(GLenum pname, const GLfixed *params) -{ - ogles_context_t* c = ogles_context_t::get(); - if (pname == GL_LIGHT_MODEL_TWO_SIDE) { - lightModelx(pname, params[0], c); - return; - } - - if (ggl_unlikely(pname != GL_LIGHT_MODEL_AMBIENT)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - - c->lighting.lightModel.ambient.r = params[0]; - c->lighting.lightModel.ambient.g = params[1]; - c->lighting.lightModel.ambient.b = params[2]; - c->lighting.lightModel.ambient.a = params[3]; - invalidate_lighting(c); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -void glLightf(GLenum i, GLenum pname, GLfloat param) -{ - ogles_context_t* c = ogles_context_t::get(); - lightx(i, pname, gglFloatToFixed(param), c); -} - -void glLightx(GLenum i, GLenum pname, GLfixed param) -{ - ogles_context_t* c = ogles_context_t::get(); - lightx(i, pname, param, c); -} - -void glLightfv(GLenum i, GLenum pname, const GLfloat *params) -{ - ogles_context_t* c = ogles_context_t::get(); - switch (pname) { - case GL_SPOT_EXPONENT: - case GL_SPOT_CUTOFF: - case GL_CONSTANT_ATTENUATION: - case GL_LINEAR_ATTENUATION: - case GL_QUADRATIC_ATTENUATION: - lightx(i, pname, gglFloatToFixed(params[0]), c); - return; - } - - GLfixed paramsx[4]; - paramsx[0] = gglFloatToFixed(params[0]); - paramsx[1] = gglFloatToFixed(params[1]); - paramsx[2] = gglFloatToFixed(params[2]); - if (pname != GL_SPOT_DIRECTION) - paramsx[3] = gglFloatToFixed(params[3]); - - lightxv(i, pname, paramsx, c); -} - -void glLightxv(GLenum i, GLenum pname, const GLfixed *params) -{ - ogles_context_t* c = ogles_context_t::get(); - lightxv(i, pname, params, c); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -void glMaterialf(GLenum face, GLenum pname, GLfloat param) -{ - ogles_context_t* c = ogles_context_t::get(); - materialx(face, pname, gglFloatToFixed(param), c); -} - -void glMaterialx(GLenum face, GLenum pname, GLfixed param) -{ - ogles_context_t* c = ogles_context_t::get(); - materialx(face, pname, param, c); -} - -void glMaterialfv( - GLenum face, GLenum pname, const GLfloat *params) -{ - ogles_context_t* c = ogles_context_t::get(); - if (ggl_unlikely(face != GL_FRONT_AND_BACK)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - GLfixed* what=0; - GLfixed* other=0; - switch (pname) { - case GL_AMBIENT: what = c->lighting.front.ambient.v; break; - case GL_DIFFUSE: what = c->lighting.front.diffuse.v; break; - case GL_SPECULAR: what = c->lighting.front.specular.v; break; - case GL_EMISSION: what = c->lighting.front.emission.v; break; - case GL_AMBIENT_AND_DIFFUSE: - what = c->lighting.front.ambient.v; - other = c->lighting.front.diffuse.v; - break; - case GL_SHININESS: - c->lighting.front.shininess = gglFloatToFixed(params[0]); - invalidate_lighting(c); - return; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - what[0] = gglFloatToFixed(params[0]); - what[1] = gglFloatToFixed(params[1]); - what[2] = gglFloatToFixed(params[2]); - what[3] = gglFloatToFixed(params[3]); - if (other) { - other[0] = what[0]; - other[1] = what[1]; - other[2] = what[2]; - other[3] = what[3]; - } - invalidate_lighting(c); -} - -void glMaterialxv( - GLenum face, GLenum pname, const GLfixed *params) -{ - ogles_context_t* c = ogles_context_t::get(); - if (ggl_unlikely(face != GL_FRONT_AND_BACK)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - GLfixed* what=0; - GLfixed* other=0; - switch (pname) { - case GL_AMBIENT: what = c->lighting.front.ambient.v; break; - case GL_DIFFUSE: what = c->lighting.front.diffuse.v; break; - case GL_SPECULAR: what = c->lighting.front.specular.v; break; - case GL_EMISSION: what = c->lighting.front.emission.v; break; - case GL_AMBIENT_AND_DIFFUSE: - what = c->lighting.front.ambient.v; - other = c->lighting.front.diffuse.v; - break; - case GL_SHININESS: - c->lighting.front.shininess = gglFloatToFixed(params[0]); - invalidate_lighting(c); - return; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - what[0] = params[0]; - what[1] = params[1]; - what[2] = params[2]; - what[3] = params[3]; - if (other) { - other[0] = what[0]; - other[1] = what[1]; - other[2] = what[2]; - other[3] = what[3]; - } - invalidate_lighting(c); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark fog -#endif - -void glFogf(GLenum pname, GLfloat param) { - ogles_context_t* c = ogles_context_t::get(); - GLfixed paramx = (GLfixed)param; - if (pname != GL_FOG_MODE) - paramx = gglFloatToFixed(param); - fogx(pname, paramx, c); -} - -void glFogx(GLenum pname, GLfixed param) { - ogles_context_t* c = ogles_context_t::get(); - fogx(pname, param, c); -} - -void glFogfv(GLenum pname, const GLfloat *params) -{ - ogles_context_t* c = ogles_context_t::get(); - if (pname != GL_FOG_COLOR) { - GLfixed paramx = (GLfixed)params[0]; - if (pname != GL_FOG_MODE) - paramx = gglFloatToFixed(params[0]); - fogx(pname, paramx, c); - return; - } - GLfixed paramsx[4]; - paramsx[0] = gglFloatToFixed(params[0]); - paramsx[1] = gglFloatToFixed(params[1]); - paramsx[2] = gglFloatToFixed(params[2]); - paramsx[3] = gglFloatToFixed(params[3]); - c->rasterizer.procs.fogColor3xv(c, paramsx); -} - -void glFogxv(GLenum pname, const GLfixed *params) -{ - ogles_context_t* c = ogles_context_t::get(); - if (pname != GL_FOG_COLOR) { - fogx(pname, params[0], c); - return; - } - c->rasterizer.procs.fogColor3xv(c, params); -} diff --git a/opengl/libagl/light.h b/opengl/libagl/light.h deleted file mode 100644 index 39e3309d0d..0000000000 --- a/opengl/libagl/light.h +++ /dev/null @@ -1,45 +0,0 @@ -/* libs/opengles/light.h -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_LIGHT_H -#define ANDROID_OPENGLES_LIGHT_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> - - -// Set to 1 for object-space lighting evaluation. -// There are still some bugs with object-space lighting, -// especially visible in the San Angeles demo. -#define OBJECT_SPACE_LIGHTING 0 - - -namespace android { - -namespace gl { -struct ogles_context_t; -}; - -void ogles_init_light(ogles_context_t* c); -void ogles_uninit_light(ogles_context_t* c); -void ogles_invalidate_lighting_mvui(ogles_context_t* c); - -}; // namespace android - -#endif // ANDROID_OPENGLES_LIGHT_H - diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp deleted file mode 100644 index edd474d30c..0000000000 --- a/opengl/libagl/matrix.cpp +++ /dev/null @@ -1,1123 +0,0 @@ -/* libs/opengles/matrix.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdlib.h> -#include <stdio.h> - -#include "context.h" -#include "fp.h" -#include "state.h" -#include "matrix.h" -#include "vertex.h" -#include "light.h" - -#if defined(__arm__) && defined(__thumb__) -#warning "matrix.cpp should not be compiled in thumb on ARM." -#endif - -#define I(_i, _j) ((_j)+ 4*(_i)) - -namespace android { - -// ---------------------------------------------------------------------------- - -static const GLfloat gIdentityf[16] = { 1,0,0,0, - 0,1,0,0, - 0,0,1,0, - 0,0,0,1 }; - -static const matrixx_t gIdentityx = { - { 0x10000,0,0,0, - 0,0x10000,0,0, - 0,0,0x10000,0, - 0,0,0,0x10000 - } - }; - -static void point2__nop(transform_t const*, vec4_t* c, vec4_t const* o); -static void point3__nop(transform_t const*, vec4_t* c, vec4_t const* o); -static void point4__nop(transform_t const*, vec4_t* c, vec4_t const* o); -static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o); -static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o); -static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o); -static void point3__mvui(transform_t const*, vec4_t* c, vec4_t const* o); -static void point4__mvui(transform_t const*, vec4_t* c, vec4_t const* o); - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -void ogles_init_matrix(ogles_context_t* c) -{ - c->transforms.modelview.init(OGLES_MODELVIEW_STACK_DEPTH); - c->transforms.projection.init(OGLES_PROJECTION_STACK_DEPTH); - for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) - c->transforms.texture[i].init(OGLES_TEXTURE_STACK_DEPTH); - - c->transforms.current = &c->transforms.modelview; - c->transforms.matrixMode = GL_MODELVIEW; - c->transforms.dirty = transform_state_t::VIEWPORT | - transform_state_t::MVUI | - transform_state_t::MVIT | - transform_state_t::MVP; - c->transforms.mvp.loadIdentity(); - c->transforms.mvp4.loadIdentity(); - c->transforms.mvit4.loadIdentity(); - c->transforms.mvui.loadIdentity(); - c->transforms.vpt.loadIdentity(); - c->transforms.vpt.zNear = 0.0f; - c->transforms.vpt.zFar = 1.0f; -} - -void ogles_uninit_matrix(ogles_context_t* c) -{ - c->transforms.modelview.uninit(); - c->transforms.projection.uninit(); - for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) - c->transforms.texture[i].uninit(); -} - -static void validate_perspective(ogles_context_t* c, vertex_t* v) -{ - const uint32_t enables = c->rasterizer.state.enables; - c->arrays.perspective = (c->clipPlanes.enable) ? - ogles_vertex_clipAllPerspective3D : ogles_vertex_perspective3D; - if (enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) { - c->arrays.perspective = ogles_vertex_perspective3DZ; - if (c->clipPlanes.enable || (enables&GGL_ENABLE_FOG)) - c->arrays.perspective = ogles_vertex_clipAllPerspective3DZ; - } - if ((c->arrays.vertex.size != 4) && - (c->transforms.mvp4.flags & transform_t::FLAGS_2D_PROJECTION)) { - c->arrays.perspective = ogles_vertex_perspective2D; - } - c->arrays.perspective(c, v); -} - -void ogles_invalidate_perspective(ogles_context_t* c) -{ - c->arrays.perspective = validate_perspective; -} - -void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want) -{ - int dirty = c->transforms.dirty & want; - - // Validate the modelview - if (dirty & transform_state_t::MODELVIEW) { - c->transforms.modelview.validate(); - } - - // Validate the projection stack (in fact, it's never needed) - if (dirty & transform_state_t::PROJECTION) { - c->transforms.projection.validate(); - } - - // Validate the viewport transformation - if (dirty & transform_state_t::VIEWPORT) { - vp_transform_t& vpt = c->transforms.vpt; - vpt.transform.matrix.load(vpt.matrix); - vpt.transform.picker(); - } - - // We need to update the mvp (used to transform each vertex) - if (dirty & transform_state_t::MVP) { - c->transforms.update_mvp(); - // invalidate perspective (divide by W) and view volume clipping - ogles_invalidate_perspective(c); - } - - // Validate the mvui (for normal transformation) - if (dirty & transform_state_t::MVUI) { - c->transforms.update_mvui(); - ogles_invalidate_lighting_mvui(c); - } - - // Validate the texture stack - if (dirty & transform_state_t::TEXTURE) { - for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) - c->transforms.texture[i].validate(); - } - - // Validate the mvit4 (user-clip planes) - if (dirty & transform_state_t::MVIT) { - c->transforms.update_mvit(); - } - - c->transforms.dirty &= ~want; -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark transform_t -#endif - -void transform_t::loadIdentity() { - matrix = gIdentityx; - flags = 0; - ops = OP_IDENTITY; - point2 = point2__nop; - point3 = point3__nop; - point4 = point4__nop; -} - - -static inline -int notZero(GLfixed v) { - return abs(v) & ~0x3; -} - -static inline -int notOne(GLfixed v) { - return notZero(v - 0x10000); -} - -void transform_t::picker() -{ - const GLfixed* const m = matrix.m; - - // XXX: picker needs to be smarter - flags = 0; - ops = OP_ALL; - point2 = point2__generic; - point3 = point3__generic; - point4 = point4__generic; - - // find out if this is a 2D projection - if (!(notZero(m[3]) | notZero(m[7]) | notZero(m[11]) | notOne(m[15]))) { - flags |= FLAGS_2D_PROJECTION; - } -} - -void mvui_transform_t::picker() -{ - flags = 0; - ops = OP_ALL; - point3 = point3__mvui; - point4 = point4__mvui; -} - -void transform_t::dump(const char* what) -{ - GLfixed const * const m = matrix.m; - ALOGD("%s:", what); - for (int i=0 ; i<4 ; i++) - ALOGD("[%08x %08x %08x %08x] [%f %f %f %f]\n", - m[I(0,i)], m[I(1,i)], m[I(2,i)], m[I(3,i)], - fixedToFloat(m[I(0,i)]), - fixedToFloat(m[I(1,i)]), - fixedToFloat(m[I(2,i)]), - fixedToFloat(m[I(3,i)])); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark matrixx_t -#endif - -void matrixx_t::load(const matrixf_t& rhs) { - GLfixed* xp = m; - GLfloat const* fp = rhs.elements(); - unsigned int i = 16; - do { - const GLfloat f = *fp++; - *xp++ = isZerof(f) ? 0 : gglFloatToFixed(f); - } while (--i); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark matrixf_t -#endif - -void matrixf_t::multiply(matrixf_t& r, const matrixf_t& lhs, const matrixf_t& rhs) -{ - GLfloat const* const m = lhs.m; - for (int i=0 ; i<4 ; i++) { - const float rhs_i0 = rhs.m[ I(i,0) ]; - float ri0 = m[ I(0,0) ] * rhs_i0; - float ri1 = m[ I(0,1) ] * rhs_i0; - float ri2 = m[ I(0,2) ] * rhs_i0; - float ri3 = m[ I(0,3) ] * rhs_i0; - for (int j=1 ; j<4 ; j++) { - const float rhs_ij = rhs.m[ I(i,j) ]; - ri0 += m[ I(j,0) ] * rhs_ij; - ri1 += m[ I(j,1) ] * rhs_ij; - ri2 += m[ I(j,2) ] * rhs_ij; - ri3 += m[ I(j,3) ] * rhs_ij; - } - r.m[ I(i,0) ] = ri0; - r.m[ I(i,1) ] = ri1; - r.m[ I(i,2) ] = ri2; - r.m[ I(i,3) ] = ri3; - } -} - -void matrixf_t::dump(const char* what) { - ALOGD("%s", what); - ALOGD("[ %9f %9f %9f %9f ]", m[I(0,0)], m[I(1,0)], m[I(2,0)], m[I(3,0)]); - ALOGD("[ %9f %9f %9f %9f ]", m[I(0,1)], m[I(1,1)], m[I(2,1)], m[I(3,1)]); - ALOGD("[ %9f %9f %9f %9f ]", m[I(0,2)], m[I(1,2)], m[I(2,2)], m[I(3,2)]); - ALOGD("[ %9f %9f %9f %9f ]", m[I(0,3)], m[I(1,3)], m[I(2,3)], m[I(3,3)]); -} - -void matrixf_t::loadIdentity() { - memcpy(m, gIdentityf, sizeof(m)); -} - -void matrixf_t::set(const GLfixed* rhs) { - load(rhs); -} - -void matrixf_t::set(const GLfloat* rhs) { - load(rhs); -} - -void matrixf_t::load(const GLfixed* rhs) { - GLfloat* fp = m; - unsigned int i = 16; - do { - *fp++ = fixedToFloat(*rhs++); - } while (--i); -} - -void matrixf_t::load(const GLfloat* rhs) { - memcpy(m, rhs, sizeof(m)); -} - -void matrixf_t::load(const matrixf_t& rhs) { - operator = (rhs); -} - -void matrixf_t::multiply(const matrixf_t& rhs) { - matrixf_t r; - multiply(r, *this, rhs); - operator = (r); -} - -void matrixf_t::translate(GLfloat x, GLfloat y, GLfloat z) { - for (int i=0 ; i<4 ; i++) { - m[12+i] += m[i]*x + m[4+i]*y + m[8+i]*z; - } -} - -void matrixf_t::scale(GLfloat x, GLfloat y, GLfloat z) { - for (int i=0 ; i<4 ; i++) { - m[ i] *= x; - m[4+i] *= y; - m[8+i] *= z; - } -} - -void matrixf_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z) -{ - matrixf_t rotation; - GLfloat* r = rotation.m; - GLfloat c, s; - r[3] = 0; r[7] = 0; r[11]= 0; - r[12]= 0; r[13]= 0; r[14]= 0; r[15]= 1; - a *= GLfloat(M_PI / 180.0f); - sincosf(a, &s, &c); - if (isOnef(x) && isZerof(y) && isZerof(z)) { - r[5] = c; r[10]= c; - r[6] = s; r[9] = -s; - r[1] = 0; r[2] = 0; - r[4] = 0; r[8] = 0; - r[0] = 1; - } else if (isZerof(x) && isOnef(y) && isZerof(z)) { - r[0] = c; r[10]= c; - r[8] = s; r[2] = -s; - r[1] = 0; r[4] = 0; - r[6] = 0; r[9] = 0; - r[5] = 1; - } else if (isZerof(x) && isZerof(y) && isOnef(z)) { - r[0] = c; r[5] = c; - r[1] = s; r[4] = -s; - r[2] = 0; r[6] = 0; - r[8] = 0; r[9] = 0; - r[10]= 1; - } else { - const GLfloat len = sqrtf(x*x + y*y + z*z); - if (!isOnef(len)) { - const GLfloat recipLen = reciprocalf(len); - x *= recipLen; - y *= recipLen; - z *= recipLen; - } - const GLfloat nc = 1.0f - c; - const GLfloat xy = x * y; - const GLfloat yz = y * z; - const GLfloat zx = z * x; - const GLfloat xs = x * s; - const GLfloat ys = y * s; - const GLfloat zs = z * s; - r[ 0] = x*x*nc + c; r[ 4] = xy*nc - zs; r[ 8] = zx*nc + ys; - r[ 1] = xy*nc + zs; r[ 5] = y*y*nc + c; r[ 9] = yz*nc - xs; - r[ 2] = zx*nc - ys; r[ 6] = yz*nc + xs; r[10] = z*z*nc + c; - } - multiply(rotation); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark matrix_stack_t -#endif - -void matrix_stack_t::init(int depth) { - stack = new matrixf_t[depth]; - ops = new uint8_t[depth]; - maxDepth = depth; - depth = 0; - dirty = 0; - loadIdentity(); -} - -void matrix_stack_t::uninit() { - delete [] stack; - delete [] ops; -} - -void matrix_stack_t::loadIdentity() { - transform.loadIdentity(); - stack[depth].loadIdentity(); - ops[depth] = OP_IDENTITY; -} - -void matrix_stack_t::load(const GLfixed* rhs) -{ - memcpy(transform.matrix.m, rhs, sizeof(transform.matrix.m)); - stack[depth].load(rhs); - ops[depth] = OP_ALL; // TODO: we should look at the matrix -} - -void matrix_stack_t::load(const GLfloat* rhs) -{ - stack[depth].load(rhs); - ops[depth] = OP_ALL; // TODO: we should look at the matrix -} - -void matrix_stack_t::multiply(const matrixf_t& rhs) -{ - stack[depth].multiply(rhs); - ops[depth] = OP_ALL; // TODO: we should look at the matrix -} - -void matrix_stack_t::translate(GLfloat x, GLfloat y, GLfloat z) -{ - stack[depth].translate(x,y,z); - ops[depth] |= OP_TRANSLATE; -} - -void matrix_stack_t::scale(GLfloat x, GLfloat y, GLfloat z) -{ - stack[depth].scale(x,y,z); - if (x==y && y==z) { - ops[depth] |= OP_UNIFORM_SCALE; - } else { - ops[depth] |= OP_SCALE; - } -} - -void matrix_stack_t::rotate(GLfloat a, GLfloat x, GLfloat y, GLfloat z) -{ - stack[depth].rotate(a,x,y,z); - ops[depth] |= OP_ROTATE; -} - -void matrix_stack_t::validate() -{ - if (dirty & DO_FLOAT_TO_FIXED) { - transform.matrix.load(top()); - } - if (dirty & DO_PICKER) { - transform.picker(); - } - dirty = 0; -} - -GLint matrix_stack_t::push() -{ - if (depth >= (maxDepth-1)) { - return GL_STACK_OVERFLOW; - } - stack[depth+1] = stack[depth]; - ops[depth+1] = ops[depth]; - depth++; - return 0; -} - -GLint matrix_stack_t::pop() -{ - if (depth == 0) { - return GL_STACK_UNDERFLOW; - } - depth--; - return 0; -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark vp_transform_t -#endif - -void vp_transform_t::loadIdentity() { - transform.loadIdentity(); - matrix.loadIdentity(); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark transform_state_t -#endif - -void transform_state_t::invalidate() -{ - switch (matrixMode) { - case GL_MODELVIEW: dirty |= MODELVIEW | MVP | MVUI | MVIT; break; - case GL_PROJECTION: dirty |= PROJECTION | MVP; break; - case GL_TEXTURE: dirty |= TEXTURE | MVP; break; - } - current->dirty = matrix_stack_t::DO_PICKER | - matrix_stack_t::DO_FLOAT_TO_FIXED; -} - -void transform_state_t::update_mvp() -{ - matrixf_t temp_mvp; - matrixf_t::multiply(temp_mvp, projection.top(), modelview.top()); - mvp4.matrix.load(temp_mvp); - mvp4.picker(); - - if (mvp4.flags & transform_t::FLAGS_2D_PROJECTION) { - // the mvp matrix doesn't transform W, in this case we can - // premultiply it with the viewport transformation. In addition to - // being more efficient, this is also much more accurate and in fact - // is needed for 2D drawing with a resulting 1:1 mapping. - matrixf_t mvpv; - matrixf_t::multiply(mvpv, vpt.matrix, temp_mvp); - mvp.matrix.load(mvpv); - mvp.picker(); - } else { - mvp = mvp4; - } -} - -static __attribute__((noinline)) -void invert(GLfloat* inverse, const GLfloat* src) -{ - double t; - int i, j, k, swap; - GLfloat tmp[4][4]; - - memcpy(inverse, gIdentityf, sizeof(gIdentityf)); - memcpy(tmp, src, sizeof(GLfloat)*16); - - for (i = 0; i < 4; i++) { - // look for largest element in column - swap = i; - for (j = i + 1; j < 4; j++) { - if (fabs(tmp[j][i]) > fabs(tmp[i][i])) { - swap = j; - } - } - - if (swap != i) { - /* swap rows. */ - for (k = 0; k < 4; k++) { - t = tmp[i][k]; - tmp[i][k] = tmp[swap][k]; - tmp[swap][k] = t; - - t = inverse[i*4+k]; - inverse[i*4+k] = inverse[swap*4+k]; - inverse[swap*4+k] = t; - } - } - - t = 1.0f / tmp[i][i]; - for (k = 0; k < 4; k++) { - tmp[i][k] *= t; - inverse[i*4+k] *= t; - } - for (j = 0; j < 4; j++) { - if (j != i) { - t = tmp[j][i]; - for (k = 0; k < 4; k++) { - tmp[j][k] -= tmp[i][k]*t; - inverse[j*4+k] -= inverse[i*4+k]*t; - } - } - } - } -} - -void transform_state_t::update_mvit() -{ - GLfloat r[16]; - const GLfloat* const mv = modelview.top().elements(); - invert(r, mv); - // convert to fixed-point and transpose - GLfixed* const x = mvit4.matrix.m; - for (int i=0 ; i<4 ; i++) - for (int j=0 ; j<4 ; j++) - x[I(i,j)] = gglFloatToFixed(r[I(j,i)]); - mvit4.picker(); -} - -void transform_state_t::update_mvui() -{ - GLfloat r[16]; - const GLfloat* const mv = modelview.top().elements(); - - /* - When evaluating the lighting equation in eye-space, normals - are transformed by the upper 3x3 modelview inverse-transpose. - http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html - - (note that inverse-transpose is distributive). - Also note that: - l(obj) = inv(modelview).l(eye) for local light - l(obj) = tr(modelview).l(eye) for infinite light - */ - - invert(r, mv); - - GLfixed* const x = mvui.matrix.m; - -#if OBJECT_SPACE_LIGHTING - for (int i=0 ; i<4 ; i++) - for (int j=0 ; j<4 ; j++) - x[I(i,j)] = gglFloatToFixed(r[I(i,j)]); -#else - for (int i=0 ; i<4 ; i++) - for (int j=0 ; j<4 ; j++) - x[I(i,j)] = gglFloatToFixed(r[I(j,i)]); -#endif - - mvui.picker(); -} - - -// ---------------------------------------------------------------------------- -// transformation and matrices API -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark transformation and matrices API -#endif - -int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y) -{ - c->viewport.surfaceport.x = x; - c->viewport.surfaceport.y = y; - - ogles_viewport(c, - c->viewport.x, - c->viewport.y, - c->viewport.w, - c->viewport.h); - - ogles_scissor(c, - c->viewport.scissor.x, - c->viewport.scissor.y, - c->viewport.scissor.w, - c->viewport.scissor.h); - - return 0; -} - -void ogles_scissor(ogles_context_t* c, - GLint x, GLint y, GLsizei w, GLsizei h) -{ - if ((w|h) < 0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - c->viewport.scissor.x = x; - c->viewport.scissor.y = y; - c->viewport.scissor.w = w; - c->viewport.scissor.h = h; - - x += c->viewport.surfaceport.x; - y += c->viewport.surfaceport.y; - - y = c->rasterizer.state.buffers.color.height - (y + h); - c->rasterizer.procs.scissor(c, x, y, w, h); -} - -void ogles_viewport(ogles_context_t* c, - GLint x, GLint y, GLsizei w, GLsizei h) -{ - if ((w|h)<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - c->viewport.x = x; - c->viewport.y = y; - c->viewport.w = w; - c->viewport.h = h; - - x += c->viewport.surfaceport.x; - y += c->viewport.surfaceport.y; - - GLint H = c->rasterizer.state.buffers.color.height; - GLfloat sx = div2f(w); - GLfloat ox = sx + x; - GLfloat sy = div2f(h); - GLfloat oy = sy - y + (H - h); - - GLfloat near = c->transforms.vpt.zNear; - GLfloat far = c->transforms.vpt.zFar; - GLfloat A = div2f(far - near); - GLfloat B = div2f(far + near); - - // compute viewport matrix - GLfloat* const f = c->transforms.vpt.matrix.editElements(); - f[0] = sx; f[4] = 0; f[ 8] = 0; f[12] = ox; - f[1] = 0; f[5] =-sy; f[ 9] = 0; f[13] = oy; - f[2] = 0; f[6] = 0; f[10] = A; f[14] = B; - f[3] = 0; f[7] = 0; f[11] = 0; f[15] = 1; - c->transforms.dirty |= transform_state_t::VIEWPORT; - if (c->transforms.mvp4.flags & transform_t::FLAGS_2D_PROJECTION) - c->transforms.dirty |= transform_state_t::MVP; -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark matrix * vertex -#endif - -void point2__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { - const GLfixed* const m = mx->matrix.m; - const GLfixed rx = rhs->x; - const GLfixed ry = rhs->y; - lhs->x = mla2a(rx, m[ 0], ry, m[ 4], m[12]); - lhs->y = mla2a(rx, m[ 1], ry, m[ 5], m[13]); - lhs->z = mla2a(rx, m[ 2], ry, m[ 6], m[14]); - lhs->w = mla2a(rx, m[ 3], ry, m[ 7], m[15]); -} - -void point3__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { - const GLfixed* const m = mx->matrix.m; - const GLfixed rx = rhs->x; - const GLfixed ry = rhs->y; - const GLfixed rz = rhs->z; - lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]); - lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]); - lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]); - lhs->w = mla3a(rx, m[ 3], ry, m[ 7], rz, m[11], m[15]); -} - -void point4__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { - const GLfixed* const m = mx->matrix.m; - const GLfixed rx = rhs->x; - const GLfixed ry = rhs->y; - const GLfixed rz = rhs->z; - const GLfixed rw = rhs->w; - lhs->x = mla4(rx, m[ 0], ry, m[ 4], rz, m[ 8], rw, m[12]); - lhs->y = mla4(rx, m[ 1], ry, m[ 5], rz, m[ 9], rw, m[13]); - lhs->z = mla4(rx, m[ 2], ry, m[ 6], rz, m[10], rw, m[14]); - lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]); -} - -void point3__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { - // this is used for transforming light positions back to object space. - // w is used as a switch for directional lights, so we need - // to preserve it. - const GLfixed* const m = mx->matrix.m; - const GLfixed rx = rhs->x; - const GLfixed ry = rhs->y; - const GLfixed rz = rhs->z; - lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]); - lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]); - lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]); - lhs->w = 0; -} - -void point4__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) { - // this is used for transforming light positions back to object space. - // w is used as a switch for directional lights, so we need - // to preserve it. - const GLfixed* const m = mx->matrix.m; - const GLfixed rx = rhs->x; - const GLfixed ry = rhs->y; - const GLfixed rz = rhs->z; - const GLfixed rw = rhs->w; - lhs->x = mla4(rx, m[ 0], ry, m[ 4], rz, m[ 8], rw, m[12]); - lhs->y = mla4(rx, m[ 1], ry, m[ 5], rz, m[ 9], rw, m[13]); - lhs->z = mla4(rx, m[ 2], ry, m[ 6], rz, m[10], rw, m[14]); - lhs->w = rw; -} - -void point2__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { - lhs->z = 0; - lhs->w = 0x10000; - if (lhs != rhs) { - lhs->x = rhs->x; - lhs->y = rhs->y; - } -} - -void point3__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { - lhs->w = 0x10000; - if (lhs != rhs) { - lhs->x = rhs->x; - lhs->y = rhs->y; - lhs->z = rhs->z; - } -} - -void point4__nop(transform_t const*, vec4_t* lhs, vec4_t const* rhs) { - if (lhs != rhs) - *lhs = *rhs; -} - - -static void frustumf( - GLfloat left, GLfloat right, - GLfloat bottom, GLfloat top, - GLfloat zNear, GLfloat zFar, - ogles_context_t* c) - { - if (cmpf(left,right) || - cmpf(top, bottom) || - cmpf(zNear, zFar) || - isZeroOrNegativef(zNear) || - isZeroOrNegativef(zFar)) - { - ogles_error(c, GL_INVALID_VALUE); - return; - } - const GLfloat r_width = reciprocalf(right - left); - const GLfloat r_height = reciprocalf(top - bottom); - const GLfloat r_depth = reciprocalf(zNear - zFar); - const GLfloat x = mul2f(zNear * r_width); - const GLfloat y = mul2f(zNear * r_height); - const GLfloat A = mul2f((right + left) * r_width); - const GLfloat B = (top + bottom) * r_height; - const GLfloat C = (zFar + zNear) * r_depth; - const GLfloat D = mul2f(zFar * zNear * r_depth); - GLfloat f[16]; - f[ 0] = x; - f[ 5] = y; - f[ 8] = A; - f[ 9] = B; - f[10] = C; - f[14] = D; - f[11] = -1.0f; - f[ 1] = f[ 2] = f[ 3] = - f[ 4] = f[ 6] = f[ 7] = - f[12] = f[13] = f[15] = 0.0f; - - matrixf_t rhs; - rhs.set(f); - c->transforms.current->multiply(rhs); - c->transforms.invalidate(); -} - -static void orthof( - GLfloat left, GLfloat right, - GLfloat bottom, GLfloat top, - GLfloat zNear, GLfloat zFar, - ogles_context_t* c) -{ - if (cmpf(left,right) || - cmpf(top, bottom) || - cmpf(zNear, zFar)) - { - ogles_error(c, GL_INVALID_VALUE); - return; - } - const GLfloat r_width = reciprocalf(right - left); - const GLfloat r_height = reciprocalf(top - bottom); - const GLfloat r_depth = reciprocalf(zFar - zNear); - const GLfloat x = mul2f(r_width); - const GLfloat y = mul2f(r_height); - const GLfloat z = -mul2f(r_depth); - const GLfloat tx = -(right + left) * r_width; - const GLfloat ty = -(top + bottom) * r_height; - const GLfloat tz = -(zFar + zNear) * r_depth; - GLfloat f[16]; - f[ 0] = x; - f[ 5] = y; - f[10] = z; - f[12] = tx; - f[13] = ty; - f[14] = tz; - f[15] = 1.0f; - f[ 1] = f[ 2] = f[ 3] = - f[ 4] = f[ 6] = f[ 7] = - f[ 8] = f[ 9] = f[11] = 0.0f; - matrixf_t rhs; - rhs.set(f); - c->transforms.current->multiply(rhs); - c->transforms.invalidate(); -} - -static void depthRangef(GLclampf zNear, GLclampf zFar, ogles_context_t* c) -{ - zNear = clampToZerof(zNear > 1 ? 1 : zNear); - zFar = clampToZerof(zFar > 1 ? 1 : zFar); - GLfloat* const f = c->transforms.vpt.matrix.editElements(); - f[10] = div2f(zFar - zNear); - f[14] = div2f(zFar + zNear); - c->transforms.dirty |= transform_state_t::VIEWPORT; - c->transforms.vpt.zNear = zNear; - c->transforms.vpt.zFar = zFar; -} - - -// ---------------------------------------------------------------------------- -}; // namespace android - -using namespace android; - -void glMatrixMode(GLenum mode) -{ - ogles_context_t* c = ogles_context_t::get(); - matrix_stack_t* stack = 0; - switch (mode) { - case GL_MODELVIEW: - stack = &c->transforms.modelview; - break; - case GL_PROJECTION: - stack = &c->transforms.projection; - break; - case GL_TEXTURE: - stack = &c->transforms.texture[c->textures.active]; - break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->transforms.matrixMode = mode; - c->transforms.current = stack; -} - -void glLoadIdentity() -{ - ogles_context_t* c = ogles_context_t::get(); - c->transforms.current->loadIdentity(); // also loads the GLfixed transform - c->transforms.invalidate(); - c->transforms.current->dirty = 0; -} - -void glLoadMatrixf(const GLfloat* m) -{ - ogles_context_t* c = ogles_context_t::get(); - c->transforms.current->load(m); - c->transforms.invalidate(); -} - -void glLoadMatrixx(const GLfixed* m) -{ - ogles_context_t* c = ogles_context_t::get(); - c->transforms.current->load(m); // also loads the GLfixed transform - c->transforms.invalidate(); - c->transforms.current->dirty &= ~matrix_stack_t::DO_FLOAT_TO_FIXED; -} - -void glMultMatrixf(const GLfloat* m) -{ - ogles_context_t* c = ogles_context_t::get(); - matrixf_t rhs; - rhs.set(m); - c->transforms.current->multiply(rhs); - c->transforms.invalidate(); -} - -void glMultMatrixx(const GLfixed* m) -{ - ogles_context_t* c = ogles_context_t::get(); - matrixf_t rhs; - rhs.set(m); - c->transforms.current->multiply(rhs); - c->transforms.invalidate(); -} - -void glPopMatrix() -{ - ogles_context_t* c = ogles_context_t::get(); - GLint err = c->transforms.current->pop(); - if (ggl_unlikely(err)) { - ogles_error(c, err); - return; - } - c->transforms.invalidate(); -} - -void glPushMatrix() -{ - ogles_context_t* c = ogles_context_t::get(); - GLint err = c->transforms.current->push(); - if (ggl_unlikely(err)) { - ogles_error(c, err); - return; - } - c->transforms.invalidate(); -} - -void glFrustumf( - GLfloat left, GLfloat right, - GLfloat bottom, GLfloat top, - GLfloat zNear, GLfloat zFar) -{ - ogles_context_t* c = ogles_context_t::get(); - frustumf(left, right, bottom, top, zNear, zFar, c); -} - -void glFrustumx( - GLfixed left, GLfixed right, - GLfixed bottom, GLfixed top, - GLfixed zNear, GLfixed zFar) -{ - ogles_context_t* c = ogles_context_t::get(); - frustumf( fixedToFloat(left), fixedToFloat(right), - fixedToFloat(bottom), fixedToFloat(top), - fixedToFloat(zNear), fixedToFloat(zFar), - c); -} - -void glOrthof( - GLfloat left, GLfloat right, - GLfloat bottom, GLfloat top, - GLfloat zNear, GLfloat zFar) -{ - ogles_context_t* c = ogles_context_t::get(); - orthof(left, right, bottom, top, zNear, zFar, c); -} - -void glOrthox( - GLfixed left, GLfixed right, - GLfixed bottom, GLfixed top, - GLfixed zNear, GLfixed zFar) -{ - ogles_context_t* c = ogles_context_t::get(); - orthof( fixedToFloat(left), fixedToFloat(right), - fixedToFloat(bottom), fixedToFloat(top), - fixedToFloat(zNear), fixedToFloat(zFar), - c); -} - -void glRotatef(GLfloat a, GLfloat x, GLfloat y, GLfloat z) -{ - ogles_context_t* c = ogles_context_t::get(); - c->transforms.current->rotate(a, x, y, z); - c->transforms.invalidate(); -} - -void glRotatex(GLfixed a, GLfixed x, GLfixed y, GLfixed z) -{ - ogles_context_t* c = ogles_context_t::get(); - c->transforms.current->rotate( - fixedToFloat(a), fixedToFloat(x), - fixedToFloat(y), fixedToFloat(z)); - c->transforms.invalidate(); -} - -void glScalef(GLfloat x, GLfloat y, GLfloat z) -{ - ogles_context_t* c = ogles_context_t::get(); - c->transforms.current->scale(x, y, z); - c->transforms.invalidate(); -} - -void glScalex(GLfixed x, GLfixed y, GLfixed z) -{ - ogles_context_t* c = ogles_context_t::get(); - c->transforms.current->scale( - fixedToFloat(x), fixedToFloat(y), fixedToFloat(z)); - c->transforms.invalidate(); -} - -void glTranslatef(GLfloat x, GLfloat y, GLfloat z) -{ - ogles_context_t* c = ogles_context_t::get(); - c->transforms.current->translate(x, y, z); - c->transforms.invalidate(); -} - -void glTranslatex(GLfixed x, GLfixed y, GLfixed z) -{ - ogles_context_t* c = ogles_context_t::get(); - c->transforms.current->translate( - fixedToFloat(x), fixedToFloat(y), fixedToFloat(z)); - c->transforms.invalidate(); -} - -void glScissor(GLint x, GLint y, GLsizei w, GLsizei h) -{ - ogles_context_t* c = ogles_context_t::get(); - ogles_scissor(c, x, y, w, h); -} - -void glViewport(GLint x, GLint y, GLsizei w, GLsizei h) -{ - ogles_context_t* c = ogles_context_t::get(); - ogles_viewport(c, x, y, w, h); -} - -void glDepthRangef(GLclampf zNear, GLclampf zFar) -{ - ogles_context_t* c = ogles_context_t::get(); - depthRangef(zNear, zFar, c); -} - -void glDepthRangex(GLclampx zNear, GLclampx zFar) -{ - ogles_context_t* c = ogles_context_t::get(); - depthRangef(fixedToFloat(zNear), fixedToFloat(zFar), c); -} - -void glPolygonOffsetx(GLfixed factor, GLfixed units) -{ - ogles_context_t* c = ogles_context_t::get(); - c->polygonOffset.factor = factor; - c->polygonOffset.units = units; -} - -void glPolygonOffset(GLfloat factor, GLfloat units) -{ - ogles_context_t* c = ogles_context_t::get(); - c->polygonOffset.factor = gglFloatToFixed(factor); - c->polygonOffset.units = gglFloatToFixed(units); -} - -GLbitfield glQueryMatrixxOES(GLfixed* m, GLint* e) -{ - ogles_context_t* c = ogles_context_t::get(); - GLbitfield status = 0; - GLfloat const* f = c->transforms.current->top().elements(); - for (int i=0 ; i<16 ; i++) { - if (isnan(f[i]) || isinf(f[i])) { - status |= 1<<i; - continue; - } - e[i] = exponent(f[i]) - 7; - m[i] = mantissa(f[i]); - } - return status; -} diff --git a/opengl/libagl/matrix.h b/opengl/libagl/matrix.h deleted file mode 100644 index cafc11905c..0000000000 --- a/opengl/libagl/matrix.h +++ /dev/null @@ -1,399 +0,0 @@ -/* libs/opengles/matrix.h -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_MATRIX_H -#define ANDROID_OPENGLES_MATRIX_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> -#include <utils/Log.h> - -#include <private/pixelflinger/ggl_context.h> - -#include <GLES/gl.h> - -namespace android { - -const int OGLES_MODELVIEW_STACK_DEPTH = 16; -const int OGLES_PROJECTION_STACK_DEPTH = 2; -const int OGLES_TEXTURE_STACK_DEPTH = 2; - -void ogles_init_matrix(ogles_context_t*); -void ogles_uninit_matrix(ogles_context_t*); -void ogles_invalidate_perspective(ogles_context_t* c); -void ogles_validate_transform_impl(ogles_context_t* c, uint32_t want); - -int ogles_surfaceport(ogles_context_t* c, GLint x, GLint y); - -void ogles_scissor(ogles_context_t* c, - GLint x, GLint y, GLsizei w, GLsizei h); - -void ogles_viewport(ogles_context_t* c, - GLint x, GLint y, GLsizei w, GLsizei h); - -inline void ogles_validate_transform( - ogles_context_t* c, uint32_t want) -{ - if (c->transforms.dirty & want) - ogles_validate_transform_impl(c, want); -} - -// ---------------------------------------------------------------------------- - -inline -GLfixed vsquare3(GLfixed a, GLfixed b, GLfixed c) -{ -#if defined(__arm__) && !defined(__thumb__) - - GLfixed r; - int32_t t; - asm( - "smull %0, %1, %2, %2 \n" - "smlal %0, %1, %3, %3 \n" - "smlal %0, %1, %4, %4 \n" - "movs %0, %0, lsr #16 \n" - "adc %0, %0, %1, lsl #16 \n" - : "=&r"(r), "=&r"(t) - : "%r"(a), "r"(b), "r"(c) - : "cc" - ); - return r; - -#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 - - GLfixed res; - int32_t t1,t2,t3; - asm( - "mult %[a], %[a] \r\n" - "li %[res],0x8000 \r\n" - "madd %[b],%[b] \r\n" - "move %[t3],$zero \r\n" - "madd %[c],%[c] \r\n" - "mflo %[t1]\r\n" - "mfhi %[t2]\r\n" - "addu %[t1],%[res],%[t1]\r\n" /*add 0x8000*/ - "sltu %[t3],%[t1],%[res]\r\n" - "addu %[t2],%[t2],%[t3]\r\n" - "srl %[res],%[t1],16\r\n" - "sll %[t2],%[t2],16\r\n" - "or %[res],%[res],%[t2]\r\n" - : [res]"=&r"(res),[t1]"=&r"(t1),[t2]"=&r"(t2),[t3]"=&r"(t3) - : [a] "r" (a),[b] "r" (b),[c] "r" (c) - : "%hi","%lo" - ); - return res; - -#else - - return (( int64_t(a)*a + - int64_t(b)*b + - int64_t(c)*c + 0x8000)>>16); - -#endif -} - -static inline GLfixed mla2a( GLfixed a0, GLfixed b0, - GLfixed a1, GLfixed b1, - GLfixed c) -{ -#if defined(__arm__) && !defined(__thumb__) - - GLfixed r; - int32_t t; - asm( - "smull %0, %1, %2, %3 \n" - "smlal %0, %1, %4, %5 \n" - "add %0, %6, %0, lsr #16 \n" - "add %0, %0, %1, lsl #16 \n" - : "=&r"(r), "=&r"(t) - : "%r"(a0), "r"(b0), - "%r"(a1), "r"(b1), - "r"(c) - : - ); - return r; - -#else - - return (( int64_t(a0)*b0 + - int64_t(a1)*b1)>>16) + c; - -#endif -} - -static inline GLfixed mla3a( GLfixed a0, GLfixed b0, - GLfixed a1, GLfixed b1, - GLfixed a2, GLfixed b2, - GLfixed c) -{ -#if defined(__arm__) && !defined(__thumb__) - - GLfixed r; - int32_t t; - asm( - "smull %0, %1, %2, %3 \n" - "smlal %0, %1, %4, %5 \n" - "smlal %0, %1, %6, %7 \n" - "add %0, %8, %0, lsr #16 \n" - "add %0, %0, %1, lsl #16 \n" - : "=&r"(r), "=&r"(t) - : "%r"(a0), "r"(b0), - "%r"(a1), "r"(b1), - "%r"(a2), "r"(b2), - "r"(c) - : - ); - return r; - -#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6 - - GLfixed res; - int32_t t1,t2; - asm( - "mult %[a0],%[b0] \r\n" - "madd %[a1],%[b1] \r\n" - "madd %[a2],%[b2] \r\n" - "mflo %[t2]\r\n" - "mfhi %[t1]\r\n" - "srl %[t2],%[t2],16\r\n" - "sll %[t1],%[t1],16\r\n" - "or %[t2],%[t2],%[t1]\r\n" - "addu %[res],%[t2],%[c]" - : [res]"=&r"(res),[t1]"=&r"(t1),[t2]"=&r"(t2) - : [a0] "r" (a0),[b0] "r" (b0),[a1] "r" (a1),[b1] "r" (b1),[a2] "r" (a2),[b2] "r" (b2),[c] "r" (c) - : "%hi","%lo" - ); - return res; - -#else - - return (( int64_t(a0)*b0 + - int64_t(a1)*b1 + - int64_t(a2)*b2)>>16) + c; - -#endif -} - -// b0, b1, b2 are signed 16-bit quanities -// that have been shifted right by 'shift' bits relative to normal -// S16.16 fixed point -static inline GLfixed mla3a16( GLfixed a0, int32_t b1b0, - GLfixed a1, - GLfixed a2, int32_t b2, - GLint shift, - GLfixed c) -{ -#if defined(__arm__) && !defined(__thumb__) - - GLfixed r; - asm( - "smulwb %0, %1, %2 \n" - "smlawt %0, %3, %2, %0 \n" - "smlawb %0, %4, %5, %0 \n" - "add %0, %7, %0, lsl %6 \n" - : "=&r"(r) - : "r"(a0), "r"(b1b0), - "r"(a1), - "r"(a2), "r"(b2), - "r"(shift), - "r"(c) - : - ); - return r; - -#else - - int32_t accum; - int16_t b0 = b1b0 & 0xffff; - int16_t b1 = (b1b0 >> 16) & 0xffff; - accum = int64_t(a0)*int16_t(b0) >> 16; - accum += int64_t(a1)*int16_t(b1) >> 16; - accum += int64_t(a2)*int16_t(b2) >> 16; - accum = (accum << shift) + c; - return accum; - -#endif -} - - -static inline GLfixed mla3a16_btb( GLfixed a0, - GLfixed a1, - GLfixed a2, - int32_t b1b0, int32_t xxb2, - GLint shift, - GLfixed c) -{ -#if defined(__arm__) && !defined(__thumb__) - - GLfixed r; - asm( - "smulwb %0, %1, %4 \n" - "smlawt %0, %2, %4, %0 \n" - "smlawb %0, %3, %5, %0 \n" - "add %0, %7, %0, lsl %6 \n" - : "=&r"(r) - : "r"(a0), - "r"(a1), - "r"(a2), - "r"(b1b0), "r"(xxb2), - "r"(shift), - "r"(c) - : - ); - return r; - -#else - - int32_t accum; - int16_t b0 = b1b0 & 0xffff; - int16_t b1 = (b1b0 >> 16) & 0xffff; - int16_t b2 = xxb2 & 0xffff; - accum = int64_t(a0)*int16_t(b0) >> 16; - accum += int64_t(a1)*int16_t(b1) >> 16; - accum += int64_t(a2)*int16_t(b2) >> 16; - accum = (accum << shift) + c; - return accum; - -#endif -} - -static inline GLfixed mla3a16_btt( GLfixed a0, - GLfixed a1, - GLfixed a2, - int32_t b1b0, int32_t b2xx, - GLint shift, - GLfixed c) -{ -#if defined(__arm__) && !defined(__thumb__) - - GLfixed r; - asm( - "smulwb %0, %1, %4 \n" - "smlawt %0, %2, %4, %0 \n" - "smlawt %0, %3, %5, %0 \n" - "add %0, %7, %0, lsl %6 \n" - : "=&r"(r) - : "r"(a0), - "r"(a1), - "r"(a2), - "r"(b1b0), "r"(b2xx), - "r"(shift), - "r"(c) - : - ); - return r; - -#else - - int32_t accum; - int16_t b0 = b1b0 & 0xffff; - int16_t b1 = (b1b0 >> 16) & 0xffff; - int16_t b2 = (b2xx >> 16) & 0xffff; - accum = int64_t(a0)*int16_t(b0) >> 16; - accum += int64_t(a1)*int16_t(b1) >> 16; - accum += int64_t(a2)*int16_t(b2) >> 16; - accum = (accum << shift) + c; - return accum; - -#endif -} - -static inline GLfixed mla3( GLfixed a0, GLfixed b0, - GLfixed a1, GLfixed b1, - GLfixed a2, GLfixed b2) -{ -#if defined(__arm__) && !defined(__thumb__) - - GLfixed r; - int32_t t; - asm( - "smull %0, %1, %2, %3 \n" - "smlal %0, %1, %4, %5 \n" - "smlal %0, %1, %6, %7 \n" - "movs %0, %0, lsr #16 \n" - "adc %0, %0, %1, lsl #16 \n" - : "=&r"(r), "=&r"(t) - : "%r"(a0), "r"(b0), - "%r"(a1), "r"(b1), - "%r"(a2), "r"(b2) - : "cc" - ); - return r; - -#else - - return (( int64_t(a0)*b0 + - int64_t(a1)*b1 + - int64_t(a2)*b2 + 0x8000)>>16); - -#endif -} - -static inline GLfixed mla4( GLfixed a0, GLfixed b0, - GLfixed a1, GLfixed b1, - GLfixed a2, GLfixed b2, - GLfixed a3, GLfixed b3) -{ -#if defined(__arm__) && !defined(__thumb__) - - GLfixed r; - int32_t t; - asm( - "smull %0, %1, %2, %3 \n" - "smlal %0, %1, %4, %5 \n" - "smlal %0, %1, %6, %7 \n" - "smlal %0, %1, %8, %9 \n" - "movs %0, %0, lsr #16 \n" - "adc %0, %0, %1, lsl #16 \n" - : "=&r"(r), "=&r"(t) - : "%r"(a0), "r"(b0), - "%r"(a1), "r"(b1), - "%r"(a2), "r"(b2), - "%r"(a3), "r"(b3) - : "cc" - ); - return r; - -#else - - return (( int64_t(a0)*b0 + - int64_t(a1)*b1 + - int64_t(a2)*b2 + - int64_t(a3)*b3 + 0x8000)>>16); - -#endif -} - -inline -GLfixed dot4(const GLfixed* a, const GLfixed* b) -{ - return mla4(a[0], b[0], a[1], b[1], a[2], b[2], a[3], b[3]); -} - - -inline -GLfixed dot3(const GLfixed* a, const GLfixed* b) -{ - return mla3(a[0], b[0], a[1], b[1], a[2], b[2]); -} - - -}; // namespace android - -#endif // ANDROID_OPENGLES_MATRIX_H - diff --git a/opengl/libagl/mipmap.cpp b/opengl/libagl/mipmap.cpp deleted file mode 100644 index e142a58d00..0000000000 --- a/opengl/libagl/mipmap.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* libs/opengles/mipmap.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdio.h> -#include <stdlib.h> - -#include "context.h" -#include "state.h" -#include "texture.h" -#include "TextureObjectManager.h" - -namespace android { - -// ---------------------------------------------------------------------------- - -status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex) -{ - int level = 0; - const GGLSurface* base = &tex->surface; - const GGLFormat& pixelFormat(c->rasterizer.formats[base->format]); - - int w = base->width; - int h = base->height; - if ((w&h) == 1) - return NO_ERROR; - - w = (w>>1) ? : 1; - h = (h>>1) ? : 1; - - while(true) { - ++level; - const int bpr = w * pixelFormat.size; - if (tex->reallocate(level, w, h, w, - base->format, base->compressedFormat, bpr) != NO_ERROR) { - return NO_MEMORY; - } - - int stride = w; - int bs = base->stride; - GGLSurface& cur = tex->editMip(level); - - if (base->format == GGL_PIXEL_FORMAT_RGB_565) - { - uint16_t const * src = (uint16_t const *)base->data; - uint16_t* dst = (uint16_t*)cur.data; - const uint32_t mask = 0x07E0F81F; - for (int y=0 ; y<h ; y++) { - size_t offset = (y*2) * bs; - for (int x=0 ; x<w ; x++) { - uint32_t p00 = src[offset]; - uint32_t p10 = src[offset+1]; - uint32_t p01 = src[offset+bs]; - uint32_t p11 = src[offset+bs+1]; - p00 = (p00 | (p00 << 16)) & mask; - p01 = (p01 | (p01 << 16)) & mask; - p10 = (p10 | (p10 << 16)) & mask; - p11 = (p11 | (p11 << 16)) & mask; - uint32_t grb = ((p00 + p10 + p01 + p11) >> 2) & mask; - uint32_t rgb = (grb & 0xFFFF) | (grb >> 16); - dst[x + y*stride] = rgb; - offset += 2; - } - } - } - else if (base->format == GGL_PIXEL_FORMAT_RGBA_5551) - { - uint16_t const * src = (uint16_t const *)base->data; - uint16_t* dst = (uint16_t*)cur.data; - for (int y=0 ; y<h ; y++) { - size_t offset = (y*2) * bs; - for (int x=0 ; x<w ; x++) { - uint32_t p00 = src[offset]; - uint32_t p10 = src[offset+1]; - uint32_t p01 = src[offset+bs]; - uint32_t p11 = src[offset+bs+1]; - uint32_t r = ((p00>>11)+(p10>>11)+(p01>>11)+(p11>>11)+2)>>2; - uint32_t g = (((p00>>6)+(p10>>6)+(p01>>6)+(p11>>6)+2)>>2)&0x3F; - uint32_t b = ((p00&0x3E)+(p10&0x3E)+(p01&0x3E)+(p11&0x3E)+4)>>3; - uint32_t a = ((p00&1)+(p10&1)+(p01&1)+(p11&1)+2)>>2; - dst[x + y*stride] = (r<<11)|(g<<6)|(b<<1)|a; - offset += 2; - } - } - } - else if (base->format == GGL_PIXEL_FORMAT_RGBA_8888) - { - uint32_t const * src = (uint32_t const *)base->data; - uint32_t* dst = (uint32_t*)cur.data; - for (int y=0 ; y<h ; y++) { - size_t offset = (y*2) * bs; - for (int x=0 ; x<w ; x++) { - uint32_t p00 = src[offset]; - uint32_t p10 = src[offset+1]; - uint32_t p01 = src[offset+bs]; - uint32_t p11 = src[offset+bs+1]; - uint32_t rb00 = p00 & 0x00FF00FF; - uint32_t rb01 = p01 & 0x00FF00FF; - uint32_t rb10 = p10 & 0x00FF00FF; - uint32_t rb11 = p11 & 0x00FF00FF; - uint32_t ga00 = (p00 >> 8) & 0x00FF00FF; - uint32_t ga01 = (p01 >> 8) & 0x00FF00FF; - uint32_t ga10 = (p10 >> 8) & 0x00FF00FF; - uint32_t ga11 = (p11 >> 8) & 0x00FF00FF; - uint32_t rb = (rb00 + rb01 + rb10 + rb11)>>2; - uint32_t ga = (ga00 + ga01 + ga10 + ga11)>>2; - uint32_t rgba = (rb & 0x00FF00FF) | ((ga & 0x00FF00FF)<<8); - dst[x + y*stride] = rgba; - offset += 2; - } - } - } - else if ((base->format == GGL_PIXEL_FORMAT_RGB_888) || - (base->format == GGL_PIXEL_FORMAT_LA_88) || - (base->format == GGL_PIXEL_FORMAT_A_8) || - (base->format == GGL_PIXEL_FORMAT_L_8)) - { - int skip; - switch (base->format) { - case GGL_PIXEL_FORMAT_RGB_888: skip = 3; break; - case GGL_PIXEL_FORMAT_LA_88: skip = 2; break; - default: skip = 1; break; - } - uint8_t const * src = (uint8_t const *)base->data; - uint8_t* dst = (uint8_t*)cur.data; - bs *= skip; - stride *= skip; - for (int y=0 ; y<h ; y++) { - size_t offset = (y*2) * bs; - for (int x=0 ; x<w ; x++) { - for (int c=0 ; c<skip ; c++) { - uint32_t p00 = src[c+offset]; - uint32_t p10 = src[c+offset+skip]; - uint32_t p01 = src[c+offset+bs]; - uint32_t p11 = src[c+offset+bs+skip]; - dst[x + y*stride + c] = (p00 + p10 + p01 + p11) >> 2; - } - offset += 2*skip; - } - } - } - else if (base->format == GGL_PIXEL_FORMAT_RGBA_4444) - { - uint16_t const * src = (uint16_t const *)base->data; - uint16_t* dst = (uint16_t*)cur.data; - for (int y=0 ; y<h ; y++) { - size_t offset = (y*2) * bs; - for (int x=0 ; x<w ; x++) { - uint32_t p00 = src[offset]; - uint32_t p10 = src[offset+1]; - uint32_t p01 = src[offset+bs]; - uint32_t p11 = src[offset+bs+1]; - p00 = ((p00 << 12) & 0x0F0F0000) | (p00 & 0x0F0F); - p10 = ((p10 << 12) & 0x0F0F0000) | (p10 & 0x0F0F); - p01 = ((p01 << 12) & 0x0F0F0000) | (p01 & 0x0F0F); - p11 = ((p11 << 12) & 0x0F0F0000) | (p11 & 0x0F0F); - uint32_t rbga = (p00 + p10 + p01 + p11) >> 2; - uint32_t rgba = (rbga & 0x0F0F) | ((rbga>>12) & 0xF0F0); - dst[x + y*stride] = rgba; - offset += 2; - } - } - } else { - ALOGE("Unsupported format (%d)", base->format); - return BAD_TYPE; - } - - // exit condition: we just processed the 1x1 LODs - if ((w&h) == 1) - break; - - base = &cur; - w = (w>>1) ? : 1; - h = (h>>1) ? : 1; - } - return NO_ERROR; -} - -}; // namespace android diff --git a/opengl/libagl/primitives.cpp b/opengl/libagl/primitives.cpp deleted file mode 100644 index d3b19e8e6d..0000000000 --- a/opengl/libagl/primitives.cpp +++ /dev/null @@ -1,1112 +0,0 @@ -/* libs/opengles/primitives.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdio.h> -#include <stdlib.h> -#include <math.h> - -#include "context.h" -#include "primitives.h" -#include "light.h" -#include "matrix.h" -#include "vertex.h" -#include "fp.h" -#include "TextureObjectManager.h" - -extern "C" void iterators0032(const void* that, - int32_t* it, int32_t c0, int32_t c1, int32_t c2); - -namespace android { - -// ---------------------------------------------------------------------------- - -static void primitive_point(ogles_context_t* c, vertex_t* v); -static void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1); -static void primitive_clip_triangle(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2); - -static void primitive_nop_point(ogles_context_t* c, vertex_t* v); -static void primitive_nop_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1); -static void primitive_nop_triangle(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2); - -static inline bool cull_triangle(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2); - -static void lerp_triangle(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2); - -static void lerp_texcoords(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2); - -static void lerp_texcoords_w(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2); - -static void triangle(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2); - -static void clip_triangle(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2); - -static unsigned int clip_line(ogles_context_t* c, - vertex_t* s, vertex_t* p); - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -static void lightTriangleDarkSmooth(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - if (!(v0->flags & vertex_t::LIT)) { - v0->flags |= vertex_t::LIT; - const GLvoid* cp = c->arrays.color.element( - v0->index & vertex_cache_t::INDEX_MASK); - c->arrays.color.fetch(c, v0->color.v, cp); - } - if (!(v1->flags & vertex_t::LIT)) { - v1->flags |= vertex_t::LIT; - const GLvoid* cp = c->arrays.color.element( - v1->index & vertex_cache_t::INDEX_MASK); - c->arrays.color.fetch(c, v1->color.v, cp); - } - if(!(v2->flags & vertex_t::LIT)) { - v2->flags |= vertex_t::LIT; - const GLvoid* cp = c->arrays.color.element( - v2->index & vertex_cache_t::INDEX_MASK); - c->arrays.color.fetch(c, v2->color.v, cp); - } -} - -static void lightTriangleDarkFlat(ogles_context_t* c, - vertex_t* /*v0*/, vertex_t* /*v1*/, vertex_t* v2) -{ - if (!(v2->flags & vertex_t::LIT)) { - v2->flags |= vertex_t::LIT; - const GLvoid* cp = c->arrays.color.element( - v2->index & vertex_cache_t::INDEX_MASK); - c->arrays.color.fetch(c, v2->color.v, cp); - } - // configure the rasterizer here, before we clip - c->rasterizer.procs.color4xv(c, v2->color.v); -} - -static void lightTriangleSmooth(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - if (!(v0->flags & vertex_t::LIT)) - c->lighting.lightVertex(c, v0); - if (!(v1->flags & vertex_t::LIT)) - c->lighting.lightVertex(c, v1); - if(!(v2->flags & vertex_t::LIT)) - c->lighting.lightVertex(c, v2); -} - -static void lightTriangleFlat(ogles_context_t* c, - vertex_t* /*v0*/, vertex_t* /*v1*/, vertex_t* v2) -{ - if (!(v2->flags & vertex_t::LIT)) - c->lighting.lightVertex(c, v2); - // configure the rasterizer here, before we clip - c->rasterizer.procs.color4xv(c, v2->color.v); -} - -// The fog versions... - -static inline -void lightVertexDarkSmoothFog(ogles_context_t* c, vertex_t* v) -{ - if (!(v->flags & vertex_t::LIT)) { - v->flags |= vertex_t::LIT; - v->fog = c->fog.fog(c, v->eye.z); - const GLvoid* cp = c->arrays.color.element( - v->index & vertex_cache_t::INDEX_MASK); - c->arrays.color.fetch(c, v->color.v, cp); - } -} -static inline -void lightVertexDarkFlatFog(ogles_context_t* c, vertex_t* v) -{ - if (!(v->flags & vertex_t::LIT)) { - v->flags |= vertex_t::LIT; - v->fog = c->fog.fog(c, v->eye.z); - } -} -static inline -void lightVertexSmoothFog(ogles_context_t* c, vertex_t* v) -{ - if (!(v->flags & vertex_t::LIT)) { - v->fog = c->fog.fog(c, v->eye.z); - c->lighting.lightVertex(c, v); - } -} - -static void lightTriangleDarkSmoothFog(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - lightVertexDarkSmoothFog(c, v0); - lightVertexDarkSmoothFog(c, v1); - lightVertexDarkSmoothFog(c, v2); -} - -static void lightTriangleDarkFlatFog(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - lightVertexDarkFlatFog(c, v0); - lightVertexDarkFlatFog(c, v1); - lightVertexDarkSmoothFog(c, v2); - // configure the rasterizer here, before we clip - c->rasterizer.procs.color4xv(c, v2->color.v); -} - -static void lightTriangleSmoothFog(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - lightVertexSmoothFog(c, v0); - lightVertexSmoothFog(c, v1); - lightVertexSmoothFog(c, v2); -} - -static void lightTriangleFlatFog(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - lightVertexDarkFlatFog(c, v0); - lightVertexDarkFlatFog(c, v1); - lightVertexSmoothFog(c, v2); - // configure the rasterizer here, before we clip - c->rasterizer.procs.color4xv(c, v2->color.v); -} - - - -typedef void (*light_primitive_t)(ogles_context_t*, - vertex_t*, vertex_t*, vertex_t*); - -// fog 0x4, light 0x2, smooth 0x1 -static const light_primitive_t lightPrimitive[8] = { - lightTriangleDarkFlat, // no fog | dark | flat - lightTriangleDarkSmooth, // no fog | dark | smooth - lightTriangleFlat, // no fog | light | flat - lightTriangleSmooth, // no fog | light | smooth - lightTriangleDarkFlatFog, // fog | dark | flat - lightTriangleDarkSmoothFog, // fog | dark | smooth - lightTriangleFlatFog, // fog | light | flat - lightTriangleSmoothFog // fog | light | smooth -}; - -void ogles_validate_primitives(ogles_context_t* c) -{ - const uint32_t enables = c->rasterizer.state.enables; - - // set up the lighting/shading/smoothing/fogging function - int index = enables & GGL_ENABLE_SMOOTH ? 0x1 : 0; - index |= c->lighting.enable ? 0x2 : 0; - index |= enables & GGL_ENABLE_FOG ? 0x4 : 0; - c->lighting.lightTriangle = lightPrimitive[index]; - - // set up the primitive renderers - if (ggl_likely(c->arrays.vertex.enable)) { - c->prims.renderPoint = primitive_point; - c->prims.renderLine = primitive_line; - c->prims.renderTriangle = primitive_clip_triangle; - } else { - c->prims.renderPoint = primitive_nop_point; - c->prims.renderLine = primitive_nop_line; - c->prims.renderTriangle = primitive_nop_triangle; - } -} - -// ---------------------------------------------------------------------------- - -void compute_iterators_t::initTriangle( - vertex_t const* v0, vertex_t const* v1, vertex_t const* v2) -{ - m_dx01 = v1->window.x - v0->window.x; - m_dy10 = v0->window.y - v1->window.y; - m_dx20 = v0->window.x - v2->window.x; - m_dy02 = v2->window.y - v0->window.y; - m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20; - (void)m_reserved; // suppress unused warning -} - -void compute_iterators_t::initLine( - vertex_t const* v0, vertex_t const* v1) -{ - m_dx01 = m_dy02 = v1->window.x - v0->window.x; - m_dy10 = m_dx20 = v0->window.y - v1->window.y; - m_area = m_dx01*m_dy02 + (-m_dy10)*m_dx20; -} - -void compute_iterators_t::initLerp(vertex_t const* v0, uint32_t enables) -{ - m_x0 = v0->window.x; - m_y0 = v0->window.y; - const GGLcoord area = (m_area + TRI_HALF) >> TRI_FRACTION_BITS; - const GGLcoord minArea = 2; // cannot be inverted - // triangles with an area smaller than 1.0 are not smooth-shaded - - int q=0, s=0, d=0; - if (abs(area) >= minArea) { - // Here we do some voodoo magic, to compute a suitable scale - // factor for deltas/area: - - // First compute the 1/area with full 32-bits precision, - // gglRecipQNormalized returns a number [-0.5, 0.5[ and an exponent. - d = gglRecipQNormalized(area, &q); - - // Then compute the minimum left-shift to not overflow the muls - // below. - s = 32 - gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20)); - - // We'll keep 16-bits of precision for deltas/area. So we need - // to shift everything left an extra 15 bits. - s += 15; - - // make sure all final shifts are not > 32, because gglMulx - // can't handle it. - if (s < q) s = q; - if (s > 32) { - d >>= 32-s; - s = 32; - } - } - - m_dx01 = gglMulx(m_dx01, d, s); - m_dy10 = gglMulx(m_dy10, d, s); - m_dx20 = gglMulx(m_dx20, d, s); - m_dy02 = gglMulx(m_dy02, d, s); - m_area_scale = 32 + q - s; - m_scale = 0; - - if (enables & GGL_ENABLE_TMUS) { - const int A = gglClz(abs(m_dy02)|abs(m_dy10)|abs(m_dx01)|abs(m_dx20)); - const int B = gglClz(abs(m_x0)|abs(m_y0)); - m_scale = max(0, 32 - (A + 16)) + - max(0, 32 - (B + TRI_FRACTION_BITS)) + 1; - } -} - -int compute_iterators_t::iteratorsScale(GGLfixed* it, - int32_t c0, int32_t c1, int32_t c2) const -{ - int32_t dc01 = c1 - c0; - int32_t dc02 = c2 - c0; - const int A = gglClz(abs(c0)); - const int B = gglClz(abs(dc01)|abs(dc02)); - const int scale = min(A, B - m_scale) - 2; - if (scale >= 0) { - c0 <<= scale; - dc01 <<= scale; - dc02 <<= scale; - } else { - c0 >>= -scale; - dc01 >>= -scale; - dc02 >>= -scale; - } - const int s = m_area_scale; - int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s); - int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s); - int32_t c = c0 - (gglMulAddx(dcdx, m_x0, - gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS)); - it[0] = c; - it[1] = dcdx; - it[2] = dcdy; - return scale; -} - -void compute_iterators_t::iterators1616(GGLfixed* it, - GGLfixed c0, GGLfixed c1, GGLfixed c2) const -{ - const GGLfixed dc01 = c1 - c0; - const GGLfixed dc02 = c2 - c0; - // 16.16 x 16.16 == 32.32 --> 16.16 - const int s = m_area_scale; - int32_t dcdx = gglMulAddx(dc01, m_dy02, gglMulx(dc02, m_dy10, s), s); - int32_t dcdy = gglMulAddx(dc02, m_dx01, gglMulx(dc01, m_dx20, s), s); - int32_t c = c0 - (gglMulAddx(dcdx, m_x0, - gglMulx(dcdy, m_y0, TRI_FRACTION_BITS), TRI_FRACTION_BITS)); - it[0] = c; - it[1] = dcdx; - it[2] = dcdy; -} - -void compute_iterators_t::iterators0032(int64_t* it, - int32_t c0, int32_t c1, int32_t c2) const -{ - const int s = m_area_scale - 16; - int32_t dc01 = (c1 - c0)>>s; - int32_t dc02 = (c2 - c0)>>s; - // 16.16 x 16.16 == 32.32 - int64_t dcdx = gglMulii(dc01, m_dy02) + gglMulii(dc02, m_dy10); - int64_t dcdy = gglMulii(dc02, m_dx01) + gglMulii(dc01, m_dx20); - it[ 0] = (c0<<16) - ((dcdx*m_x0 + dcdy*m_y0)>>4); - it[ 1] = dcdx; - it[ 2] = dcdy; -} - -#if defined(__arm__) && !defined(__thumb__) -inline void compute_iterators_t::iterators0032(int32_t* it, - int32_t c0, int32_t c1, int32_t c2) const -{ - ::iterators0032(this, it, c0, c1, c2); -} -#else -void compute_iterators_t::iterators0032(int32_t* it, - int32_t c0, int32_t c1, int32_t c2) const -{ - int64_t it64[3]; - iterators0032(it64, c0, c1, c2); - it[0] = it64[0]; - it[1] = it64[1]; - it[2] = it64[2]; -} -#endif - -// ---------------------------------------------------------------------------- - -static inline int32_t clampZ(GLfixed z) CONST; -int32_t clampZ(GLfixed z) { - z = (z & ~(z>>31)); - if (z >= 0x10000) - z = 0xFFFF; - return z; -} - -static __attribute__((noinline)) -void fetch_texcoord_impl(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - vertex_t* const vtx[3] = { v0, v1, v2 }; - array_t const * const texcoordArray = c->arrays.texture; - - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - if (!(c->rasterizer.state.texture[i].enable)) - continue; - - for (int j=0 ; j<3 ; j++) { - vertex_t* const v = vtx[j]; - if (v->flags & vertex_t::TT) - continue; - - // NOTE: here we could compute automatic texgen - // such as sphere/cube maps, instead of fetching them - // from the textcoord array. - - vec4_t& coords = v->texture[i]; - const GLubyte* tp = texcoordArray[i].element( - v->index & vertex_cache_t::INDEX_MASK); - texcoordArray[i].fetch(c, coords.v, tp); - - // transform texture coordinates... - coords.Q = 0x10000; - const transform_t& tr = c->transforms.texture[i].transform; - if (ggl_unlikely(tr.ops)) { - c->arrays.tex_transform[i](&tr, &coords, &coords); - } - - // divide by Q - const GGLfixed q = coords.Q; - if (ggl_unlikely(q != 0x10000)) { - const int32_t qinv = gglRecip28(q); - coords.S = gglMulx(coords.S, qinv, 28); - coords.T = gglMulx(coords.T, qinv, 28); - } - } - } - v0->flags |= vertex_t::TT; - v1->flags |= vertex_t::TT; - v2->flags |= vertex_t::TT; -} - -inline void fetch_texcoord(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - const uint32_t enables = c->rasterizer.state.enables; - if (!(enables & GGL_ENABLE_TMUS)) - return; - - // Fetch & transform texture coordinates... - if (ggl_likely(v0->flags & v1->flags & v2->flags & vertex_t::TT)) { - // already done for all three vertices, bail... - return; - } - fetch_texcoord_impl(c, v0, v1, v2); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Point -#endif - -void primitive_nop_point(ogles_context_t*, vertex_t*) { -} - -void primitive_point(ogles_context_t* c, vertex_t* v) -{ - // lighting & clamping... - const uint32_t enables = c->rasterizer.state.enables; - - if (ggl_unlikely(!(v->flags & vertex_t::LIT))) { - if (c->lighting.enable) { - c->lighting.lightVertex(c, v); - } else { - v->flags |= vertex_t::LIT; - const GLvoid* cp = c->arrays.color.element( - v->index & vertex_cache_t::INDEX_MASK); - c->arrays.color.fetch(c, v->color.v, cp); - } - if (enables & GGL_ENABLE_FOG) { - v->fog = c->fog.fog(c, v->eye.z); - } - } - - // XXX: we don't need to do that each-time - // if color array and lighting not enabled - c->rasterizer.procs.color4xv(c, v->color.v); - - // XXX: look into ES point-sprite extension - if (enables & GGL_ENABLE_TMUS) { - fetch_texcoord(c, v,v,v); - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - if (!c->rasterizer.state.texture[i].enable) - continue; - int32_t itt[8]; - itt[1] = itt[2] = itt[4] = itt[5] = 0; - itt[6] = itt[7] = 16; // XXX: check that - if (c->rasterizer.state.texture[i].s_wrap == GGL_CLAMP) { - int width = c->textures.tmu[i].texture->surface.width; - itt[0] = v->texture[i].S * width; - itt[6] = 0; - } - if (c->rasterizer.state.texture[i].t_wrap == GGL_CLAMP) { - int height = c->textures.tmu[i].texture->surface.height; - itt[3] = v->texture[i].T * height; - itt[7] = 0; - } - c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); - } - } - - if (enables & GGL_ENABLE_DEPTH_TEST) { - int32_t itz[3]; - itz[0] = clampZ(v->window.z) * 0x00010001; - itz[1] = itz[2] = 0; - c->rasterizer.procs.zGrad3xv(c, itz); - } - - if (enables & GGL_ENABLE_FOG) { - GLfixed itf[3]; - itf[0] = v->fog; - itf[1] = itf[2] = 0; - c->rasterizer.procs.fogGrad3xv(c, itf); - } - - // Render our point... - c->rasterizer.procs.pointx(c, v->window.v, c->point.size); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Line -#endif - -void primitive_nop_line(ogles_context_t*, vertex_t*, vertex_t*) { -} - -void primitive_line(ogles_context_t* c, vertex_t* v0, vertex_t* v1) -{ - // get texture coordinates - fetch_texcoord(c, v0, v1, v1); - - // light/shade the vertices first (they're copied below) - c->lighting.lightTriangle(c, v0, v1, v1); - - // clip the line if needed - if (ggl_unlikely((v0->flags | v1->flags) & vertex_t::CLIP_ALL)) { - unsigned int count = clip_line(c, v0, v1); - if (ggl_unlikely(count == 0)) - return; - } - - // compute iterators... - const uint32_t enables = c->rasterizer.state.enables; - const uint32_t mask = GGL_ENABLE_TMUS | - GGL_ENABLE_SMOOTH | - GGL_ENABLE_W | - GGL_ENABLE_FOG | - GGL_ENABLE_DEPTH_TEST; - - if (ggl_unlikely(enables & mask)) { - c->lerp.initLine(v0, v1); - lerp_triangle(c, v0, v1, v0); - } - - // render our line - c->rasterizer.procs.linex(c, v0->window.v, v1->window.v, c->line.width); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Triangle -#endif - -void primitive_nop_triangle(ogles_context_t* /*c*/, - vertex_t* /*v0*/, vertex_t* /*v1*/, vertex_t* /*v2*/) { -} - -void primitive_clip_triangle(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - uint32_t cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL; - if (ggl_likely(!cc)) { - // code below must be as optimized as possible, this is the - // common code path. - - // This triangle is not clipped, test if it's culled - // unclipped triangle... - c->lerp.initTriangle(v0, v1, v2); - if (cull_triangle(c, v0, v1, v2)) - return; // culled! - - // Fetch all texture coordinates if needed - fetch_texcoord(c, v0, v1, v2); - - // light (or shade) our triangle! - c->lighting.lightTriangle(c, v0, v1, v2); - - triangle(c, v0, v1, v2); - return; - } - - // The assumption here is that we're not going to clip very often, - // and even more rarely will we clip a triangle that ends up - // being culled out. So it's okay to light the vertices here, even though - // in a few cases we won't render the triangle (if culled). - - // Fetch texture coordinates... - fetch_texcoord(c, v0, v1, v2); - - // light (or shade) our triangle! - c->lighting.lightTriangle(c, v0, v1, v2); - - clip_triangle(c, v0, v1, v2); -} - -// ----------------------------------------------------------------------- - -void triangle(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - // compute iterators... - const uint32_t enables = c->rasterizer.state.enables; - const uint32_t mask = GGL_ENABLE_TMUS | - GGL_ENABLE_SMOOTH | - GGL_ENABLE_W | - GGL_ENABLE_FOG | - GGL_ENABLE_DEPTH_TEST; - - if (ggl_likely(enables & mask)) - lerp_triangle(c, v0, v1, v2); - - c->rasterizer.procs.trianglex(c, v0->window.v, v1->window.v, v2->window.v); -} - -void lerp_triangle(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - const uint32_t enables = c->rasterizer.state.enables; - c->lerp.initLerp(v0, enables); - - // set up texture iterators - if (enables & GGL_ENABLE_TMUS) { - if (enables & GGL_ENABLE_W) { - lerp_texcoords_w(c, v0, v1, v2); - } else { - lerp_texcoords(c, v0, v1, v2); - } - } - - // set up the color iterators - const compute_iterators_t& lerp = c->lerp; - if (enables & GGL_ENABLE_SMOOTH) { - GLfixed itc[12]; - for (int i=0 ; i<4 ; i++) { - const GGLcolor c0 = v0->color.v[i] * 255; - const GGLcolor c1 = v1->color.v[i] * 255; - const GGLcolor c2 = v2->color.v[i] * 255; - lerp.iterators1616(&itc[i*3], c0, c1, c2); - } - c->rasterizer.procs.colorGrad12xv(c, itc); - } - - if (enables & GGL_ENABLE_DEPTH_TEST) { - int32_t itz[3]; - const int32_t v0z = clampZ(v0->window.z); - const int32_t v1z = clampZ(v1->window.z); - const int32_t v2z = clampZ(v2->window.z); - if (ggl_unlikely(c->polygonOffset.enable)) { - const int32_t units = (c->polygonOffset.units << 16); - const GLfixed factor = c->polygonOffset.factor; - if (factor) { - int64_t itz64[3]; - lerp.iterators0032(itz64, v0z, v1z, v2z); - int64_t maxDepthSlope = max(itz64[1], itz64[2]); - itz[0] = uint32_t(itz64[0]) - + uint32_t((maxDepthSlope*factor)>>16) + units; - itz[1] = uint32_t(itz64[1]); - itz[2] = uint32_t(itz64[2]); - } else { - lerp.iterators0032(itz, v0z, v1z, v2z); - itz[0] += units; - } - } else { - lerp.iterators0032(itz, v0z, v1z, v2z); - } - c->rasterizer.procs.zGrad3xv(c, itz); - } - - if (ggl_unlikely(enables & GGL_ENABLE_FOG)) { - GLfixed itf[3]; - lerp.iterators1616(itf, v0->fog, v1->fog, v2->fog); - c->rasterizer.procs.fogGrad3xv(c, itf); - } -} - - -static inline -int compute_lod(ogles_context_t* c, int i, - int32_t s0, int32_t t0, int32_t s1, int32_t t1, int32_t s2, int32_t t2) -{ - // Compute mipmap level / primitive - // rho = sqrt( texelArea / area ) - // lod = log2( rho ) - // lod = log2( texelArea / area ) / 2 - // lod = (log2( texelArea ) - log2( area )) / 2 - const compute_iterators_t& lerp = c->lerp; - const GGLcoord area = abs(lerp.area()); - const int w = c->textures.tmu[i].texture->surface.width; - const int h = c->textures.tmu[i].texture->surface.height; - const int shift = 16 + (16 - TRI_FRACTION_BITS); - int32_t texelArea = abs( gglMulx(s1-s0, t2-t0, shift) - - gglMulx(s2-s0, t1-t0, shift) )*w*h; - int log2TArea = (32-TRI_FRACTION_BITS -1) - gglClz(texelArea); - int log2Area = (32-TRI_FRACTION_BITS*2-1) - gglClz(area); - int lod = (log2TArea - log2Area + 1) >> 1; - return lod; -} - -void lerp_texcoords(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - const compute_iterators_t& lerp = c->lerp; - int32_t itt[8] __attribute__((aligned(16))); - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - const texture_t& tmu = c->rasterizer.state.texture[i]; - if (!tmu.enable) - continue; - - // compute the jacobians using block floating-point - int32_t s0 = v0->texture[i].S; - int32_t t0 = v0->texture[i].T; - int32_t s1 = v1->texture[i].S; - int32_t t1 = v1->texture[i].T; - int32_t s2 = v2->texture[i].S; - int32_t t2 = v2->texture[i].T; - - const GLenum min_filter = c->textures.tmu[i].texture->min_filter; - if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) { - int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2); - c->rasterizer.procs.bindTextureLod(c, i, - &c->textures.tmu[i].texture->mip(lod)); - } - - // premultiply (s,t) when clampling - if (tmu.s_wrap == GGL_CLAMP) { - const int width = tmu.surface.width; - s0 *= width; - s1 *= width; - s2 *= width; - } - if (tmu.t_wrap == GGL_CLAMP) { - const int height = tmu.surface.height; - t0 *= height; - t1 *= height; - t2 *= height; - } - itt[6] = -lerp.iteratorsScale(itt+0, s0, s1, s2); - itt[7] = -lerp.iteratorsScale(itt+3, t0, t1, t2); - c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); - } -} - -void lerp_texcoords_w(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - const compute_iterators_t& lerp = c->lerp; - int32_t itt[8] __attribute__((aligned(16))); - int32_t itw[3]; - - // compute W's scale to 2.30 - int32_t w0 = v0->window.w; - int32_t w1 = v1->window.w; - int32_t w2 = v2->window.w; - int wscale = 32 - gglClz(w0|w1|w2); - - // compute the jacobian using block floating-point - int sc = lerp.iteratorsScale(itw, w0, w1, w2); - sc += wscale - 16; - c->rasterizer.procs.wGrad3xv(c, itw); - - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - const texture_t& tmu = c->rasterizer.state.texture[i]; - if (!tmu.enable) - continue; - - // compute the jacobians using block floating-point - int32_t s0 = v0->texture[i].S; - int32_t t0 = v0->texture[i].T; - int32_t s1 = v1->texture[i].S; - int32_t t1 = v1->texture[i].T; - int32_t s2 = v2->texture[i].S; - int32_t t2 = v2->texture[i].T; - - const GLenum min_filter = c->textures.tmu[i].texture->min_filter; - if (ggl_unlikely(min_filter >= GL_NEAREST_MIPMAP_NEAREST)) { - int lod = compute_lod(c, i, s0, t0, s1, t1, s2, t2); - c->rasterizer.procs.bindTextureLod(c, i, - &c->textures.tmu[i].texture->mip(lod)); - } - - // premultiply (s,t) when clampling - if (tmu.s_wrap == GGL_CLAMP) { - const int width = tmu.surface.width; - s0 *= width; - s1 *= width; - s2 *= width; - } - if (tmu.t_wrap == GGL_CLAMP) { - const int height = tmu.surface.height; - t0 *= height; - t1 *= height; - t2 *= height; - } - - s0 = gglMulx(s0, w0, wscale); - t0 = gglMulx(t0, w0, wscale); - s1 = gglMulx(s1, w1, wscale); - t1 = gglMulx(t1, w1, wscale); - s2 = gglMulx(s2, w2, wscale); - t2 = gglMulx(t2, w2, wscale); - - itt[6] = sc - lerp.iteratorsScale(itt+0, s0, s1, s2); - itt[7] = sc - lerp.iteratorsScale(itt+3, t0, t1, t2); - c->rasterizer.procs.texCoordGradScale8xv(c, i, itt); - } -} - - -static inline -bool cull_triangle(ogles_context_t* c, vertex_t* /*v0*/, vertex_t* /*v1*/, vertex_t* /*v2*/) -{ - if (ggl_likely(c->cull.enable)) { - const GLenum winding = (c->lerp.area() > 0) ? GL_CW : GL_CCW; - const GLenum face = (winding == c->cull.frontFace) ? GL_FRONT : GL_BACK; - if (face == c->cull.cullFace) - return true; // culled! - } - return false; -} - -static inline -GLfixed frustumPlaneDist(int plane, const vec4_t& s) -{ - const GLfixed d = s.v[ plane >> 1 ]; - return ((plane & 1) ? (s.w - d) : (s.w + d)); -} - -static inline -int32_t clipDivide(GLfixed a, GLfixed b) { - // returns a 4.28 fixed-point - return gglMulDivi(1LU<<28, a, b); -} - -void clip_triangle(ogles_context_t* c, - vertex_t* v0, vertex_t* v1, vertex_t* v2) -{ - uint32_t all_cc = (v0->flags | v1->flags | v2->flags) & vertex_t::CLIP_ALL; - - vertex_t *p0, *p1, *p2; - const int MAX_CLIPPING_PLANES = 6 + OGLES_MAX_CLIP_PLANES; - const int MAX_VERTICES = 3; - - // Temporary buffer to hold the new vertices. Each plane can add up to - // two new vertices (because the polygon is convex). - // We need one extra element, to handle an overflow case when - // the polygon degenerates into something non convex. - vertex_t buffer[MAX_CLIPPING_PLANES * 2 + 1]; // ~3KB - vertex_t* buf = buffer; - - // original list of vertices (polygon to clip, in fact this - // function works with an arbitrary polygon). - vertex_t* in[3] = { v0, v1, v2 }; - - // output lists (we need 2, which we use back and forth) - // (maximum outpout list's size is MAX_CLIPPING_PLANES + MAX_VERTICES) - // 2 more elements for overflow when non convex polygons. - vertex_t* out[2][MAX_CLIPPING_PLANES + MAX_VERTICES + 2]; - unsigned int outi = 0; - - // current input list - vertex_t** ivl = in; - - // 3 input vertices, 0 in the output list, first plane - unsigned int ic = 3; - - // User clip-planes first, the clipping is always done in eye-coordinate - // this is basically the same algorithm than for the view-volume - // clipping, except for the computation of the distance (vertex, plane) - // and the fact that we need to compute the eye-coordinates of each - // new vertex we create. - - if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL)) - { - unsigned int plane = 0; - uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8; - do { - if (cc & 1) { - // pointers to our output list (head and current) - vertex_t** const ovl = &out[outi][0]; - vertex_t** output = ovl; - unsigned int oc = 0; - unsigned int sentinel = 0; - // previous vertex, compute distance to the plane - vertex_t* s = ivl[ic-1]; - const vec4_t& equation = c->clipPlanes.plane[plane].equation; - GLfixed sd = dot4(equation.v, s->eye.v); - // clip each vertex against this plane... - for (unsigned int i=0 ; i<ic ; i++) { - vertex_t* p = ivl[i]; - const GLfixed pd = dot4(equation.v, p->eye.v); - if (sd >= 0) { - if (pd >= 0) { - // both inside - *output++ = p; - oc++; - } else { - // s inside, p outside (exiting) - const GLfixed t = clipDivide(sd, sd-pd); - c->arrays.clipEye(c, buf, t, p, s); - *output++ = buf++; - oc++; - if (++sentinel >= 3) - return; // non-convex polygon! - } - } else { - if (pd >= 0) { - // s outside (entering) - if (pd) { - const GLfixed t = clipDivide(pd, pd-sd); - c->arrays.clipEye(c, buf, t, s, p); - *output++ = buf++; - oc++; - if (++sentinel >= 3) - return; // non-convex polygon! - } - *output++ = p; - oc++; - } else { - // both outside - } - } - s = p; - sd = pd; - } - // output list become the new input list - if (oc<3) - return; // less than 3 vertices left? we're done! - ivl = ovl; - ic = oc; - outi = 1-outi; - } - cc >>= 1; - plane++; - } while (cc); - } - - // frustum clip-planes - if (all_cc & vertex_t::FRUSTUM_CLIP_ALL) - { - unsigned int plane = 0; - uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL; - do { - if (cc & 1) { - // pointers to our output list (head and current) - vertex_t** const ovl = &out[outi][0]; - vertex_t** output = ovl; - unsigned int oc = 0; - unsigned int sentinel = 0; - // previous vertex, compute distance to the plane - vertex_t* s = ivl[ic-1]; - GLfixed sd = frustumPlaneDist(plane, s->clip); - // clip each vertex against this plane... - for (unsigned int i=0 ; i<ic ; i++) { - vertex_t* p = ivl[i]; - const GLfixed pd = frustumPlaneDist(plane, p->clip); - if (sd >= 0) { - if (pd >= 0) { - // both inside - *output++ = p; - oc++; - } else { - // s inside, p outside (exiting) - const GLfixed t = clipDivide(sd, sd-pd); - c->arrays.clipVertex(c, buf, t, p, s); - *output++ = buf++; - oc++; - if (++sentinel >= 3) - return; // non-convex polygon! - } - } else { - if (pd >= 0) { - // s outside (entering) - if (pd) { - const GLfixed t = clipDivide(pd, pd-sd); - c->arrays.clipVertex(c, buf, t, s, p); - *output++ = buf++; - oc++; - if (++sentinel >= 3) - return; // non-convex polygon! - } - *output++ = p; - oc++; - } else { - // both outside - } - } - s = p; - sd = pd; - } - // output list become the new input list - if (oc<3) - return; // less than 3 vertices left? we're done! - ivl = ovl; - ic = oc; - outi = 1-outi; - } - cc >>= 1; - plane++; - } while (cc); - } - - // finally we can render our triangles... - p0 = ivl[0]; - p1 = ivl[1]; - for (unsigned int i=2 ; i<ic ; i++) { - p2 = ivl[i]; - c->lerp.initTriangle(p0, p1, p2); - if (cull_triangle(c, p0, p1, p2)) { - p1 = p2; - continue; // culled! - } - triangle(c, p0, p1, p2); - p1 = p2; - } -} - -unsigned int clip_line(ogles_context_t* c, vertex_t* s, vertex_t* p) -{ - const uint32_t all_cc = (s->flags | p->flags) & vertex_t::CLIP_ALL; - - if (ggl_unlikely(all_cc & vertex_t::USER_CLIP_ALL)) - { - unsigned int plane = 0; - uint32_t cc = (all_cc & vertex_t::USER_CLIP_ALL) >> 8; - do { - if (cc & 1) { - const vec4_t& equation = c->clipPlanes.plane[plane].equation; - const GLfixed sd = dot4(equation.v, s->eye.v); - const GLfixed pd = dot4(equation.v, p->eye.v); - if (sd >= 0) { - if (pd >= 0) { - // both inside - } else { - // s inside, p outside (exiting) - const GLfixed t = clipDivide(sd, sd-pd); - c->arrays.clipEye(c, p, t, p, s); - } - } else { - if (pd >= 0) { - // s outside (entering) - if (pd) { - const GLfixed t = clipDivide(pd, pd-sd); - c->arrays.clipEye(c, s, t, s, p); - } - } else { - // both outside - return 0; - } - } - } - cc >>= 1; - plane++; - } while (cc); - } - - // frustum clip-planes - if (all_cc & vertex_t::FRUSTUM_CLIP_ALL) - { - unsigned int plane = 0; - uint32_t cc = all_cc & vertex_t::FRUSTUM_CLIP_ALL; - do { - if (cc & 1) { - const GLfixed sd = frustumPlaneDist(plane, s->clip); - const GLfixed pd = frustumPlaneDist(plane, p->clip); - if (sd >= 0) { - if (pd >= 0) { - // both inside - } else { - // s inside, p outside (exiting) - const GLfixed t = clipDivide(sd, sd-pd); - c->arrays.clipVertex(c, p, t, p, s); - } - } else { - if (pd >= 0) { - // s outside (entering) - if (pd) { - const GLfixed t = clipDivide(pd, pd-sd); - c->arrays.clipVertex(c, s, t, s, p); - } - } else { - // both outside - return 0; - } - } - } - cc >>= 1; - plane++; - } while (cc); - } - - return 2; -} - - -}; // namespace android diff --git a/opengl/libagl/primitives.h b/opengl/libagl/primitives.h deleted file mode 100644 index 1bef604eb6..0000000000 --- a/opengl/libagl/primitives.h +++ /dev/null @@ -1,37 +0,0 @@ -/* libs/opengles/primitives.h -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_PRIMITIVES_H -#define ANDROID_OPENGLES_PRIMITIVES_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> - - -namespace android { - -namespace gl { -struct ogles_context_t; -}; - -void ogles_validate_primitives(ogles_context_t* c); - -}; // namespace android - -#endif // ANDROID_OPENGLES_PRIMITIVES_H - diff --git a/opengl/libagl/state.cpp b/opengl/libagl/state.cpp deleted file mode 100644 index 8bb7e83820..0000000000 --- a/opengl/libagl/state.cpp +++ /dev/null @@ -1,598 +0,0 @@ -/* libs/opengles/state.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdlib.h> - -#include "context.h" -#include "fp.h" -#include "state.h" -#include "array.h" -#include "matrix.h" -#include "vertex.h" -#include "light.h" -#include "texture.h" -#include "BufferObjectManager.h" -#include "TextureObjectManager.h" - -namespace android { - -// ---------------------------------------------------------------------------- - -static char const * const gVendorString = "Android"; -static char const * const gRendererString = "Android PixelFlinger 1.4"; -static char const * const gVersionString = "OpenGL ES-CM 1.0"; -static char const * const gExtensionsString = - "GL_OES_byte_coordinates " // OK - "GL_OES_fixed_point " // OK - "GL_OES_single_precision " // OK - "GL_OES_read_format " // OK - "GL_OES_compressed_paletted_texture " // OK - "GL_OES_draw_texture " // OK - "GL_OES_matrix_get " // OK - "GL_OES_query_matrix " // OK - // "GL_OES_point_size_array " // TODO - // "GL_OES_point_sprite " // TODO - "GL_OES_EGL_image " // OK - "GL_OES_EGL_sync " // OK -#ifdef GL_OES_compressed_ETC1_RGB8_texture - "GL_OES_compressed_ETC1_RGB8_texture " // OK -#endif - "GL_ARB_texture_compression " // OK - "GL_ARB_texture_non_power_of_two " // OK - "GL_ANDROID_user_clip_plane " // OK - "GL_ANDROID_vertex_buffer_object " // OK - "GL_ANDROID_generate_mipmap " // OK - ; - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -ogles_context_t *ogles_init(size_t extra) -{ - void* const base = malloc(extra + sizeof(ogles_context_t) + 32); - if (!base) return 0; - - ogles_context_t *c = - (ogles_context_t *)((ptrdiff_t(base) + extra + 31) & ~0x1FL); - memset(c, 0, sizeof(ogles_context_t)); - ggl_init_context(&(c->rasterizer)); - - // XXX: this should be passed as an argument - sp<EGLSurfaceManager> smgr(new EGLSurfaceManager()); - c->surfaceManager = smgr.get(); - c->surfaceManager->incStrong(c); - - sp<EGLBufferObjectManager> bomgr(new EGLBufferObjectManager()); - c->bufferObjectManager = bomgr.get(); - c->bufferObjectManager->incStrong(c); - - ogles_init_array(c); - ogles_init_matrix(c); - ogles_init_vertex(c); - ogles_init_light(c); - ogles_init_texture(c); - - c->rasterizer.base = base; - c->point.size = TRI_ONE; - c->line.width = TRI_ONE; - - // in OpenGL, writing to the depth buffer is enabled by default. - c->rasterizer.procs.depthMask(c, 1); - - // OpenGL enables dithering by default - c->rasterizer.procs.enable(c, GL_DITHER); - - return c; -} - -void ogles_uninit(ogles_context_t* c) -{ - ogles_uninit_array(c); - ogles_uninit_matrix(c); - ogles_uninit_vertex(c); - ogles_uninit_light(c); - ogles_uninit_texture(c); - c->surfaceManager->decStrong(c); - c->bufferObjectManager->decStrong(c); - ggl_uninit_context(&(c->rasterizer)); - free(c->rasterizer.base); -} - -void _ogles_error(ogles_context_t* c, GLenum error) -{ - if (c->error == GL_NO_ERROR) - c->error = error; -} - -static bool stencilop_valid(GLenum op) { - switch (op) { - case GL_KEEP: - case GL_ZERO: - case GL_REPLACE: - case GL_INCR: - case GL_DECR: - case GL_INVERT: - return true; - } - return false; -} - -static void enable_disable(ogles_context_t* c, GLenum cap, int enabled) -{ - if ((cap >= GL_LIGHT0) && (cap<GL_LIGHT0+OGLES_MAX_LIGHTS)) { - c->lighting.lights[cap-GL_LIGHT0].enable = enabled; - c->lighting.enabledLights &= ~(1<<(cap-GL_LIGHT0)); - c->lighting.enabledLights |= (enabled<<(cap-GL_LIGHT0)); - return; - } - - switch (cap) { - case GL_POINT_SMOOTH: - c->point.smooth = enabled; - break; - case GL_LINE_SMOOTH: - c->line.smooth = enabled; - break; - case GL_POLYGON_OFFSET_FILL: - c->polygonOffset.enable = enabled; - break; - case GL_CULL_FACE: - c->cull.enable = enabled; - break; - case GL_LIGHTING: - c->lighting.enable = enabled; - break; - case GL_COLOR_MATERIAL: - c->lighting.colorMaterial.enable = enabled; - break; - case GL_NORMALIZE: - case GL_RESCALE_NORMAL: - c->transforms.rescaleNormals = enabled ? cap : 0; - // XXX: invalidate mvit - break; - - case GL_CLIP_PLANE0: - case GL_CLIP_PLANE1: - case GL_CLIP_PLANE2: - case GL_CLIP_PLANE3: - case GL_CLIP_PLANE4: - case GL_CLIP_PLANE5: - c->clipPlanes.enable &= ~(1<<(cap-GL_CLIP_PLANE0)); - c->clipPlanes.enable |= (enabled<<(cap-GL_CLIP_PLANE0)); - ogles_invalidate_perspective(c); - break; - - case GL_FOG: - case GL_DEPTH_TEST: - ogles_invalidate_perspective(c); - [[fallthrough]]; - case GL_BLEND: - case GL_SCISSOR_TEST: - case GL_ALPHA_TEST: - case GL_COLOR_LOGIC_OP: - case GL_DITHER: - case GL_STENCIL_TEST: - case GL_TEXTURE_2D: - // these need to fall through into the rasterizer - c->rasterizer.procs.enableDisable(c, cap, enabled); - break; - case GL_TEXTURE_EXTERNAL_OES: - c->rasterizer.procs.enableDisable(c, GL_TEXTURE_2D, enabled); - break; - - case GL_MULTISAMPLE: - case GL_SAMPLE_ALPHA_TO_COVERAGE: - case GL_SAMPLE_ALPHA_TO_ONE: - case GL_SAMPLE_COVERAGE: - // not supported in this implementation - break; - - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- -using namespace android; - -#if 0 -#pragma mark - -#endif - -// These ones are super-easy, we're not supporting those features! -void glSampleCoverage(GLclampf /*value*/, GLboolean /*invert*/) { -} -void glSampleCoveragex(GLclampx /*value*/, GLboolean /*invert*/) { -} -void glStencilFunc(GLenum func, GLint /*ref*/, GLuint /*mask*/) { - ogles_context_t* c = ogles_context_t::get(); - if (func < GL_NEVER || func > GL_ALWAYS) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - // from OpenGL|ES 1.0 sepcification: - // If there is no stencil buffer, no stencil modification can occur - // and it is as if the stencil test always passes. -} - -void glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) { - ogles_context_t* c = ogles_context_t::get(); - if ((stencilop_valid(fail) & - stencilop_valid(zfail) & - stencilop_valid(zpass)) == 0) { - ogles_error(c, GL_INVALID_ENUM); - return; - } -} - -// ---------------------------------------------------------------------------- - -void glAlphaFunc(GLenum func, GLclampf ref) -{ - glAlphaFuncx(func, gglFloatToFixed(ref)); -} - -void glCullFace(GLenum mode) -{ - ogles_context_t* c = ogles_context_t::get(); - switch (mode) { - case GL_FRONT: - case GL_BACK: - case GL_FRONT_AND_BACK: - break; - default: - ogles_error(c, GL_INVALID_ENUM); - } - c->cull.cullFace = mode; -} - -void glFrontFace(GLenum mode) -{ - ogles_context_t* c = ogles_context_t::get(); - switch (mode) { - case GL_CW: - case GL_CCW: - break; - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->cull.frontFace = mode; -} - -void glHint(GLenum target, GLenum mode) -{ - ogles_context_t* c = ogles_context_t::get(); - switch (target) { - case GL_FOG_HINT: - case GL_GENERATE_MIPMAP_HINT: - case GL_LINE_SMOOTH_HINT: - break; - case GL_POINT_SMOOTH_HINT: - c->rasterizer.procs.enableDisable(c, - GGL_POINT_SMOOTH_NICE, mode==GL_NICEST); - break; - case GL_PERSPECTIVE_CORRECTION_HINT: - c->perspective = (mode == GL_NICEST) ? 1 : 0; - break; - default: - ogles_error(c, GL_INVALID_ENUM); - } -} - -void glEnable(GLenum cap) { - ogles_context_t* c = ogles_context_t::get(); - enable_disable(c, cap, 1); -} -void glDisable(GLenum cap) { - ogles_context_t* c = ogles_context_t::get(); - enable_disable(c, cap, 0); -} - -void glFinish() -{ // nothing to do for our software implementation -} - -void glFlush() -{ // nothing to do for our software implementation -} - -GLenum glGetError() -{ - // From OpenGL|ES 1.0 specification: - // If more than one flag has recorded an error, glGetError returns - // and clears an arbitrary error flag value. Thus, glGetError should - // always be called in a loop, until it returns GL_NO_ERROR, - // if all error flags are to be reset. - - ogles_context_t* c = ogles_context_t::get(); - if (c->error) { - const GLenum ret(c->error); - c->error = 0; - return ret; - } - - if (c->rasterizer.error) { - const GLenum ret(c->rasterizer.error); - c->rasterizer.error = 0; - return ret; - } - - return GL_NO_ERROR; -} - -const GLubyte* glGetString(GLenum string) -{ - switch (string) { - case GL_VENDOR: return (const GLubyte*)gVendorString; - case GL_RENDERER: return (const GLubyte*)gRendererString; - case GL_VERSION: return (const GLubyte*)gVersionString; - case GL_EXTENSIONS: return (const GLubyte*)gExtensionsString; - } - ogles_context_t* c = ogles_context_t::get(); - ogles_error(c, GL_INVALID_ENUM); - return 0; -} - -void glGetIntegerv(GLenum pname, GLint *params) -{ - int i; - ogles_context_t* c = ogles_context_t::get(); - switch (pname) { - case GL_ALIASED_POINT_SIZE_RANGE: - params[0] = 0; - params[1] = GGL_MAX_ALIASED_POINT_SIZE; - break; - case GL_ALIASED_LINE_WIDTH_RANGE: - params[0] = 0; - params[1] = GGL_MAX_ALIASED_POINT_SIZE; - break; - case GL_ALPHA_BITS: { - int index = c->rasterizer.state.buffers.color.format; - GGLFormat const * formats = gglGetPixelFormatTable(); - params[0] = formats[index].ah - formats[index].al; - break; - } - case GL_RED_BITS: { - int index = c->rasterizer.state.buffers.color.format; - GGLFormat const * formats = gglGetPixelFormatTable(); - params[0] = formats[index].rh - formats[index].rl; - break; - } - case GL_GREEN_BITS: { - int index = c->rasterizer.state.buffers.color.format; - GGLFormat const * formats = gglGetPixelFormatTable(); - params[0] = formats[index].gh - formats[index].gl; - break; - } - case GL_BLUE_BITS: { - int index = c->rasterizer.state.buffers.color.format; - GGLFormat const * formats = gglGetPixelFormatTable(); - params[0] = formats[index].bh - formats[index].bl; - break; - } - case GL_COMPRESSED_TEXTURE_FORMATS: - params[ 0] = GL_PALETTE4_RGB8_OES; - params[ 1] = GL_PALETTE4_RGBA8_OES; - params[ 2] = GL_PALETTE4_R5_G6_B5_OES; - params[ 3] = GL_PALETTE4_RGBA4_OES; - params[ 4] = GL_PALETTE4_RGB5_A1_OES; - params[ 5] = GL_PALETTE8_RGB8_OES; - params[ 6] = GL_PALETTE8_RGBA8_OES; - params[ 7] = GL_PALETTE8_R5_G6_B5_OES; - params[ 8] = GL_PALETTE8_RGBA4_OES; - params[ 9] = GL_PALETTE8_RGB5_A1_OES; - i = 10; -#ifdef GL_OES_compressed_ETC1_RGB8_texture - params[i++] = GL_ETC1_RGB8_OES; -#endif - break; - case GL_DEPTH_BITS: - params[0] = c->rasterizer.state.buffers.depth.format ? 0 : 16; - break; - case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES: - params[0] = GL_RGB; - break; - case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES: - params[0] = GL_UNSIGNED_SHORT_5_6_5; - break; - case GL_MAX_LIGHTS: - params[0] = OGLES_MAX_LIGHTS; - break; - case GL_MAX_CLIP_PLANES: - params[0] = OGLES_MAX_CLIP_PLANES; - break; - case GL_MAX_MODELVIEW_STACK_DEPTH: - params[0] = OGLES_MODELVIEW_STACK_DEPTH; - break; - case GL_MAX_PROJECTION_STACK_DEPTH: - params[0] = OGLES_PROJECTION_STACK_DEPTH; - break; - case GL_MAX_TEXTURE_STACK_DEPTH: - params[0] = OGLES_TEXTURE_STACK_DEPTH; - break; - case GL_MAX_TEXTURE_SIZE: - params[0] = GGL_MAX_TEXTURE_SIZE; - break; - case GL_MAX_TEXTURE_UNITS: - params[0] = GGL_TEXTURE_UNIT_COUNT; - break; - case GL_MAX_VIEWPORT_DIMS: - params[0] = GGL_MAX_VIEWPORT_DIMS; - params[1] = GGL_MAX_VIEWPORT_DIMS; - break; - case GL_NUM_COMPRESSED_TEXTURE_FORMATS: - params[0] = OGLES_NUM_COMPRESSED_TEXTURE_FORMATS; - break; - case GL_SMOOTH_LINE_WIDTH_RANGE: - params[0] = 0; - params[1] = GGL_MAX_SMOOTH_LINE_WIDTH; - break; - case GL_SMOOTH_POINT_SIZE_RANGE: - params[0] = 0; - params[1] = GGL_MAX_SMOOTH_POINT_SIZE; - break; - case GL_STENCIL_BITS: - params[0] = 0; - break; - case GL_SUBPIXEL_BITS: - params[0] = GGL_SUBPIXEL_BITS; - break; - - case GL_MODELVIEW_MATRIX_FLOAT_AS_INT_BITS_OES: - memcpy( params, - c->transforms.modelview.top().elements(), - 16*sizeof(GLint)); - break; - case GL_PROJECTION_MATRIX_FLOAT_AS_INT_BITS_OES: - memcpy( params, - c->transforms.projection.top().elements(), - 16*sizeof(GLint)); - break; - case GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES: - memcpy( params, - c->transforms.texture[c->textures.active].top().elements(), - 16*sizeof(GLint)); - break; - - default: - ogles_error(c, GL_INVALID_ENUM); - break; - } -} - -// ---------------------------------------------------------------------------- - -void glPointSize(GLfloat size) -{ - ogles_context_t* c = ogles_context_t::get(); - if (size <= 0) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->point.size = TRI_FROM_FIXED(gglFloatToFixed(size)); -} - -void glPointSizex(GLfixed size) -{ - ogles_context_t* c = ogles_context_t::get(); - if (size <= 0) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->point.size = TRI_FROM_FIXED(size); -} - -// ---------------------------------------------------------------------------- - -void glLineWidth(GLfloat width) -{ - ogles_context_t* c = ogles_context_t::get(); - if (width <= 0) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->line.width = TRI_FROM_FIXED(gglFloatToFixed(width)); -} - -void glLineWidthx(GLfixed width) -{ - ogles_context_t* c = ogles_context_t::get(); - if (width <= 0) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->line.width = TRI_FROM_FIXED(width); -} - -// ---------------------------------------------------------------------------- - -void glColorMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.colorMask(c, r, g, b, a); -} - -void glDepthMask(GLboolean flag) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.depthMask(c, flag); -} - -void glStencilMask(GLuint mask) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.stencilMask(c, mask); -} - -void glDepthFunc(GLenum func) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.depthFunc(c, func); -} - -void glLogicOp(GLenum opcode) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.logicOp(c, opcode); -} - -void glAlphaFuncx(GLenum func, GLclampx ref) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.alphaFuncx(c, func, ref); -} - -void glBlendFunc(GLenum sfactor, GLenum dfactor) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.blendFunc(c, sfactor, dfactor); -} - -void glClear(GLbitfield mask) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.clear(c, mask); -} - -void glClearColorx(GLclampx red, GLclampx green, GLclampx blue, GLclampx alpha) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.clearColorx(c, red, green, blue, alpha); -} - -void glClearColor(GLclampf r, GLclampf g, GLclampf b, GLclampf a) -{ - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.clearColorx(c, - gglFloatToFixed(r), - gglFloatToFixed(g), - gglFloatToFixed(b), - gglFloatToFixed(a)); -} - -void glClearDepthx(GLclampx depth) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.clearDepthx(c, depth); -} - -void glClearDepthf(GLclampf depth) -{ - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.clearDepthx(c, gglFloatToFixed(depth)); -} - -void glClearStencil(GLint s) { - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.clearStencil(c, s); -} diff --git a/opengl/libagl/state.h b/opengl/libagl/state.h deleted file mode 100644 index 55a5ccbd11..0000000000 --- a/opengl/libagl/state.h +++ /dev/null @@ -1,54 +0,0 @@ -/* libs/opengles/state.h -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_STATE_H -#define ANDROID_OPENGLES_STATE_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> - -#include <private/pixelflinger/ggl_context.h> - -#include <GLES/gl.h> - -#include <stdio.h> - -namespace android { - -ogles_context_t *ogles_init(size_t extra); -void ogles_uninit(ogles_context_t* c); -void _ogles_error(ogles_context_t* c, GLenum error); - -#ifndef TRACE_GL_ERRORS -#define TRACE_GL_ERRORS 0 -#endif - -#if TRACE_GL_ERRORS -#define ogles_error(c, error) \ -do { \ - printf("ogles_error at file %s line %d\n", __FILE__, __LINE__); \ - _ogles_error(c, error); \ -} while (0) -#else /* !TRACE_GL_ERRORS */ -#define ogles_error(c, error) _ogles_error((c), (error)) -#endif - -}; // namespace android - -#endif // ANDROID_OPENGLES_STATE_H - diff --git a/opengl/libagl/texture.cpp b/opengl/libagl/texture.cpp deleted file mode 100644 index 4c5f3e93d3..0000000000 --- a/opengl/libagl/texture.cpp +++ /dev/null @@ -1,1643 +0,0 @@ -/* libs/opengles/texture.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdio.h> -#include <stdlib.h> -#include "context.h" -#include "fp.h" -#include "state.h" -#include "texture.h" -#include "TextureObjectManager.h" - -#include <ETC1/etc1.h> - -#include <ui/GraphicBufferMapper.h> -#include <ui/Rect.h> - -namespace android { - -// ---------------------------------------------------------------------------- - -static void bindTextureTmu( - ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex); - -static __attribute__((noinline)) -void generateMipmap(ogles_context_t* c, GLint level); - -// ---------------------------------------------------------------------------- - -#if 0 -#pragma mark - -#pragma mark Init -#endif - -void ogles_init_texture(ogles_context_t* c) -{ - c->textures.packAlignment = 4; - c->textures.unpackAlignment = 4; - - // each context has a default named (0) texture (not shared) - c->textures.defaultTexture = new EGLTextureObject(); - c->textures.defaultTexture->incStrong(c); - - // bind the default texture to each texture unit - for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - bindTextureTmu(c, i, 0, c->textures.defaultTexture); - memset(c->current.texture[i].v, 0, sizeof(vec4_t)); - c->current.texture[i].Q = 0x10000; - } -} - -void ogles_uninit_texture(ogles_context_t* c) -{ - if (c->textures.ggl) - gglUninit(c->textures.ggl); - c->textures.defaultTexture->decStrong(c); - for (int i=0; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - if (c->textures.tmu[i].texture) - c->textures.tmu[i].texture->decStrong(c); - } -} - -static __attribute__((noinline)) -void validate_tmu(ogles_context_t* c, int i) -{ - texture_unit_t& u(c->textures.tmu[i]); - if (u.dirty) { - u.dirty = 0; - c->rasterizer.procs.activeTexture(c, i); - c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); - c->rasterizer.procs.texGeni(c, GGL_S, - GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); - c->rasterizer.procs.texGeni(c, GGL_T, - GGL_TEXTURE_GEN_MODE, GGL_AUTOMATIC); - c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, - GGL_TEXTURE_WRAP_S, u.texture->wraps); - c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, - GGL_TEXTURE_WRAP_T, u.texture->wrapt); - c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, - GGL_TEXTURE_MIN_FILTER, u.texture->min_filter); - c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, - GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter); - - // disable this texture unit if it's not complete - if (!u.texture->isComplete()) { - c->rasterizer.procs.disable(c, GGL_TEXTURE_2D); - } - } -} - -void ogles_validate_texture(ogles_context_t* c) -{ - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - if (c->rasterizer.state.texture[i].enable) - validate_tmu(c, i); - } - c->rasterizer.procs.activeTexture(c, c->textures.active); -} - -static -void invalidate_texture(ogles_context_t* c, int tmu, uint8_t flags = 0xFF) { - c->textures.tmu[tmu].dirty = flags; -} - -/* - * If the active textures are EGLImage, they need to be locked before - * they can be used. - * - * FIXME: code below is far from being optimal - * - */ - -void ogles_lock_textures(ogles_context_t* c) -{ - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - if (c->rasterizer.state.texture[i].enable) { - texture_unit_t& u(c->textures.tmu[i]); - ANativeWindowBuffer* native_buffer = u.texture->buffer; - if (native_buffer) { - c->rasterizer.procs.activeTexture(c, i); - - auto& mapper = GraphicBufferMapper::get(); - void* vaddr; - mapper.lock(native_buffer->handle, GRALLOC_USAGE_SW_READ_OFTEN, - Rect(native_buffer->width, native_buffer->height), - &vaddr); - - u.texture->setImageBits(vaddr); - c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); - } - } - } -} - -void ogles_unlock_textures(ogles_context_t* c) -{ - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - if (c->rasterizer.state.texture[i].enable) { - texture_unit_t& u(c->textures.tmu[i]); - ANativeWindowBuffer* native_buffer = u.texture->buffer; - if (native_buffer) { - c->rasterizer.procs.activeTexture(c, i); - - auto& mapper = GraphicBufferMapper::get(); - mapper.unlock(native_buffer->handle); - - u.texture->setImageBits(NULL); - c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); - } - } - } - c->rasterizer.procs.activeTexture(c, c->textures.active); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Format conversion -#endif - -static uint32_t gl2format_table[6][4] = { - // BYTE, 565, 4444, 5551 - { GGL_PIXEL_FORMAT_A_8, - 0, 0, 0 }, // GL_ALPHA - { GGL_PIXEL_FORMAT_RGB_888, - GGL_PIXEL_FORMAT_RGB_565, - 0, 0 }, // GL_RGB - { GGL_PIXEL_FORMAT_RGBA_8888, - 0, - GGL_PIXEL_FORMAT_RGBA_4444, - GGL_PIXEL_FORMAT_RGBA_5551 }, // GL_RGBA - { GGL_PIXEL_FORMAT_L_8, - 0, 0, 0 }, // GL_LUMINANCE - { GGL_PIXEL_FORMAT_LA_88, - 0, 0, 0 }, // GL_LUMINANCE_ALPHA -}; - -static int32_t convertGLPixelFormat(GLint format, GLenum type) -{ - int32_t fi = -1; - int32_t ti = -1; - switch (format) { - case GL_ALPHA: fi = 0; break; - case GL_RGB: fi = 1; break; - case GL_RGBA: fi = 2; break; - case GL_LUMINANCE: fi = 3; break; - case GL_LUMINANCE_ALPHA: fi = 4; break; - } - switch (type) { - case GL_UNSIGNED_BYTE: ti = 0; break; - case GL_UNSIGNED_SHORT_5_6_5: ti = 1; break; - case GL_UNSIGNED_SHORT_4_4_4_4: ti = 2; break; - case GL_UNSIGNED_SHORT_5_5_5_1: ti = 3; break; - } - if (fi==-1 || ti==-1) - return 0; - return gl2format_table[fi][ti]; -} - -// ---------------------------------------------------------------------------- - -static GLenum validFormatType(ogles_context_t* c, GLenum format, GLenum type) -{ - GLenum error = 0; - if (format<GL_ALPHA || format>GL_LUMINANCE_ALPHA) { - error = GL_INVALID_ENUM; - } - if (type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT_4_4_4_4 && - type != GL_UNSIGNED_SHORT_5_5_5_1 && type != GL_UNSIGNED_SHORT_5_6_5) { - error = GL_INVALID_ENUM; - } - if (type == GL_UNSIGNED_SHORT_5_6_5 && format != GL_RGB) { - error = GL_INVALID_OPERATION; - } - if ((type == GL_UNSIGNED_SHORT_4_4_4_4 || - type == GL_UNSIGNED_SHORT_5_5_5_1) && format != GL_RGBA) { - error = GL_INVALID_OPERATION; - } - if (error) { - ogles_error(c, error); - } - return error; -} - -// ---------------------------------------------------------------------------- - -GGLContext* getRasterizer(ogles_context_t* c) -{ - GGLContext* ggl = c->textures.ggl; - if (ggl_unlikely(!ggl)) { - // this is quite heavy the first time... - gglInit(&ggl); - if (!ggl) { - return 0; - } - GGLfixed colors[4] = { 0, 0, 0, 0x10000 }; - c->textures.ggl = ggl; - ggl->activeTexture(ggl, 0); - ggl->enable(ggl, GGL_TEXTURE_2D); - ggl->texEnvi(ggl, GGL_TEXTURE_ENV, GGL_TEXTURE_ENV_MODE, GGL_REPLACE); - ggl->disable(ggl, GGL_DITHER); - ggl->shadeModel(ggl, GGL_FLAT); - ggl->color4xv(ggl, colors); - } - return ggl; -} - -static __attribute__((noinline)) -int copyPixels( - ogles_context_t* c, - const GGLSurface& dst, - GLint xoffset, GLint yoffset, - const GGLSurface& src, - GLint x, GLint y, GLsizei w, GLsizei h) -{ - if ((dst.format == src.format) && - (dst.stride == src.stride) && - (dst.width == src.width) && - (dst.height == src.height) && - (dst.stride > 0) && - ((x|y) == 0) && - ((xoffset|yoffset) == 0)) - { - // this is a common case... - const GGLFormat& pixelFormat(c->rasterizer.formats[src.format]); - const size_t size = src.height * src.stride * pixelFormat.size; - memcpy(dst.data, src.data, size); - return 0; - } - - // use pixel-flinger to handle all the conversions - GGLContext* ggl = getRasterizer(c); - if (!ggl) { - // the only reason this would fail is because we ran out of memory - return GL_OUT_OF_MEMORY; - } - - ggl->colorBuffer(ggl, &dst); - ggl->bindTexture(ggl, &src); - ggl->texCoord2i(ggl, x-xoffset, y-yoffset); - ggl->recti(ggl, xoffset, yoffset, xoffset+w, yoffset+h); - return 0; -} - -// ---------------------------------------------------------------------------- - -static __attribute__((noinline)) -sp<EGLTextureObject> getAndBindActiveTextureObject(ogles_context_t* c) -{ - sp<EGLTextureObject> tex; - const int active = c->textures.active; - const GLuint name = c->textures.tmu[active].name; - - // free the reference to the previously bound object - texture_unit_t& u(c->textures.tmu[active]); - if (u.texture) - u.texture->decStrong(c); - - if (name == 0) { - // 0 is our local texture object, not shared with anyone. - // But it affects all bound TMUs immediately. - // (we need to invalidate all units bound to this texture object) - tex = c->textures.defaultTexture; - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - if (c->textures.tmu[i].texture == tex.get()) - invalidate_texture(c, i); - } - } else { - // get a new texture object for that name - tex = c->surfaceManager->replaceTexture(name); - } - - // bind this texture to the current active texture unit - // and add a reference to this texture object - u.texture = tex.get(); - u.texture->incStrong(c); - u.name = name; - invalidate_texture(c, active); - return tex; -} - -void bindTextureTmu( - ogles_context_t* c, int tmu, GLuint texture, const sp<EGLTextureObject>& tex) -{ - if (tex.get() == c->textures.tmu[tmu].texture) - return; - - // free the reference to the previously bound object - texture_unit_t& u(c->textures.tmu[tmu]); - if (u.texture) - u.texture->decStrong(c); - - // bind this texture to the current active texture unit - // and add a reference to this texture object - u.texture = tex.get(); - u.texture->incStrong(c); - u.name = texture; - invalidate_texture(c, tmu); -} - -int createTextureSurface(ogles_context_t* c, - GGLSurface** outSurface, int32_t* outSize, GLint level, - GLenum format, GLenum type, GLsizei width, GLsizei height, - GLenum compressedFormat = 0) -{ - // convert the pixelformat to one we can handle - const int32_t formatIdx = convertGLPixelFormat(format, type); - if (formatIdx == 0) { // we don't know what to do with this - return GL_INVALID_OPERATION; - } - - // figure out the size we need as well as the stride - const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); - const int32_t align = c->textures.unpackAlignment-1; - const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; - const size_t size = bpr * height; - const int32_t stride = bpr / pixelFormat.size; - - if (level > 0) { - const int active = c->textures.active; - EGLTextureObject* tex = c->textures.tmu[active].texture; - status_t err = tex->reallocate(level, - width, height, stride, formatIdx, compressedFormat, bpr); - if (err != NO_ERROR) - return GL_OUT_OF_MEMORY; - GGLSurface& surface = tex->editMip(level); - *outSurface = &surface; - *outSize = size; - return 0; - } - - sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c); - status_t err = tex->reallocate(level, - width, height, stride, formatIdx, compressedFormat, bpr); - if (err != NO_ERROR) - return GL_OUT_OF_MEMORY; - - tex->internalformat = format; - *outSurface = &tex->surface; - *outSize = size; - return 0; -} - -static GLsizei dataSizePalette4(int numLevels, int width, int height, int format) -{ - int indexBits = 8; - int entrySize = 0; - switch (format) { - case GL_PALETTE4_RGB8_OES: - indexBits = 4; - [[fallthrough]]; - case GL_PALETTE8_RGB8_OES: - entrySize = 3; - break; - - case GL_PALETTE4_RGBA8_OES: - indexBits = 4; - [[fallthrough]]; - case GL_PALETTE8_RGBA8_OES: - entrySize = 4; - break; - - case GL_PALETTE4_R5_G6_B5_OES: - case GL_PALETTE4_RGBA4_OES: - case GL_PALETTE4_RGB5_A1_OES: - indexBits = 4; - [[fallthrough]]; - case GL_PALETTE8_R5_G6_B5_OES: - case GL_PALETTE8_RGBA4_OES: - case GL_PALETTE8_RGB5_A1_OES: - entrySize = 2; - break; - } - - size_t size = (1 << indexBits) * entrySize; // palette size - - for (int i=0 ; i< numLevels ; i++) { - int w = (width >> i) ? : 1; - int h = (height >> i) ? : 1; - int levelSize = h * ((w * indexBits) / 8) ? : 1; - size += levelSize; - } - - return size; -} - -static void decodePalette4(const GLvoid *data, int level, int width, int height, - void *surface, int stride, int format) - -{ - int indexBits = 8; - int entrySize = 0; - switch (format) { - case GL_PALETTE4_RGB8_OES: - indexBits = 4; - [[fallthrough]]; - case GL_PALETTE8_RGB8_OES: - entrySize = 3; - break; - - case GL_PALETTE4_RGBA8_OES: - indexBits = 4; - [[fallthrough]]; - case GL_PALETTE8_RGBA8_OES: - entrySize = 4; - break; - - case GL_PALETTE4_R5_G6_B5_OES: - case GL_PALETTE4_RGBA4_OES: - case GL_PALETTE4_RGB5_A1_OES: - indexBits = 4; - [[fallthrough]]; - case GL_PALETTE8_R5_G6_B5_OES: - case GL_PALETTE8_RGBA4_OES: - case GL_PALETTE8_RGB5_A1_OES: - entrySize = 2; - break; - } - - const int paletteSize = (1 << indexBits) * entrySize; - - uint8_t const* pixels = (uint8_t *)data + paletteSize; - for (int i=0 ; i<level ; i++) { - int w = (width >> i) ? : 1; - int h = (height >> i) ? : 1; - pixels += h * ((w * indexBits) / 8); - } - width = (width >> level) ? : 1; - height = (height >> level) ? : 1; - - if (entrySize == 2) { - uint8_t const* const palette = (uint8_t*)data; - for (int y=0 ; y<height ; y++) { - uint8_t* p = (uint8_t*)surface + y*stride*2; - if (indexBits == 8) { - for (int x=0 ; x<width ; x++) { - int index = 2 * (*pixels++); - *p++ = palette[index + 0]; - *p++ = palette[index + 1]; - } - } else { - for (int x=0 ; x<width ; x+=2) { - int v = *pixels++; - int index = 2 * (v >> 4); - *p++ = palette[index + 0]; - *p++ = palette[index + 1]; - if (x+1 < width) { - index = 2 * (v & 0xF); - *p++ = palette[index + 0]; - *p++ = palette[index + 1]; - } - } - } - } - } else if (entrySize == 3) { - uint8_t const* const palette = (uint8_t*)data; - for (int y=0 ; y<height ; y++) { - uint8_t* p = (uint8_t*)surface + y*stride*3; - if (indexBits == 8) { - for (int x=0 ; x<width ; x++) { - int index = 3 * (*pixels++); - *p++ = palette[index + 0]; - *p++ = palette[index + 1]; - *p++ = palette[index + 2]; - } - } else { - for (int x=0 ; x<width ; x+=2) { - int v = *pixels++; - int index = 3 * (v >> 4); - *p++ = palette[index + 0]; - *p++ = palette[index + 1]; - *p++ = palette[index + 2]; - if (x+1 < width) { - index = 3 * (v & 0xF); - *p++ = palette[index + 0]; - *p++ = palette[index + 1]; - *p++ = palette[index + 2]; - } - } - } - } - } else if (entrySize == 4) { - uint8_t const* const palette = (uint8_t*)data; - for (int y=0 ; y<height ; y++) { - uint8_t* p = (uint8_t*)surface + y*stride*4; - if (indexBits == 8) { - for (int x=0 ; x<width ; x++) { - int index = 4 * (*pixels++); - *p++ = palette[index + 0]; - *p++ = palette[index + 1]; - *p++ = palette[index + 2]; - *p++ = palette[index + 3]; - } - } else { - for (int x=0 ; x<width ; x+=2) { - int v = *pixels++; - int index = 4 * (v >> 4); - *p++ = palette[index + 0]; - *p++ = palette[index + 1]; - *p++ = palette[index + 2]; - *p++ = palette[index + 3]; - if (x+1 < width) { - index = 4 * (v & 0xF); - *p++ = palette[index + 0]; - *p++ = palette[index + 1]; - *p++ = palette[index + 2]; - *p++ = palette[index + 3]; - } - } - } - } - } -} - - - -static __attribute__((noinline)) -void set_depth_and_fog(ogles_context_t* c, GGLfixed z) -{ - const uint32_t enables = c->rasterizer.state.enables; - // we need to compute Zw - int32_t iterators[3]; - iterators[1] = iterators[2] = 0; - GGLfixed Zw; - GGLfixed n = gglFloatToFixed(c->transforms.vpt.zNear); - GGLfixed f = gglFloatToFixed(c->transforms.vpt.zFar); - if (z<=0) Zw = n; - else if (z>=0x10000) Zw = f; - else Zw = gglMulAddx(z, (f-n), n); - if (enables & GGL_ENABLE_FOG) { - // set up fog if needed... - iterators[0] = c->fog.fog(c, Zw); - c->rasterizer.procs.fogGrad3xv(c, iterators); - } - if (enables & GGL_ENABLE_DEPTH_TEST) { - // set up z-test if needed... - int32_t z = (Zw & ~(Zw>>31)); - if (z >= 0x10000) - z = 0xFFFF; - iterators[0] = (z << 16) | z; - c->rasterizer.procs.zGrad3xv(c, iterators); - } -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark Generate mimaps -#endif - -extern status_t buildAPyramid(ogles_context_t* c, EGLTextureObject* tex); - -void generateMipmap(ogles_context_t* c, GLint level) -{ - if (level == 0) { - const int active = c->textures.active; - EGLTextureObject* tex = c->textures.tmu[active].texture; - if (tex->generate_mipmap) { - if (buildAPyramid(c, tex) != NO_ERROR) { - ogles_error(c, GL_OUT_OF_MEMORY); - return; - } - } - } -} - - -static void texParameterx( - GLenum target, GLenum pname, GLfixed param, ogles_context_t* c) -{ - if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - - EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture; - switch (pname) { - case GL_TEXTURE_WRAP_S: - if ((param == GL_REPEAT) || - (param == GL_CLAMP_TO_EDGE)) { - textureObject->wraps = param; - } else { - goto invalid_enum; - } - break; - case GL_TEXTURE_WRAP_T: - if ((param == GL_REPEAT) || - (param == GL_CLAMP_TO_EDGE)) { - textureObject->wrapt = param; - } else { - goto invalid_enum; - } - break; - case GL_TEXTURE_MIN_FILTER: - if ((param == GL_NEAREST) || - (param == GL_LINEAR) || - (param == GL_NEAREST_MIPMAP_NEAREST) || - (param == GL_LINEAR_MIPMAP_NEAREST) || - (param == GL_NEAREST_MIPMAP_LINEAR) || - (param == GL_LINEAR_MIPMAP_LINEAR)) { - textureObject->min_filter = param; - } else { - goto invalid_enum; - } - break; - case GL_TEXTURE_MAG_FILTER: - if ((param == GL_NEAREST) || - (param == GL_LINEAR)) { - textureObject->mag_filter = param; - } else { - goto invalid_enum; - } - break; - case GL_GENERATE_MIPMAP: - textureObject->generate_mipmap = param; - break; - default: -invalid_enum: - ogles_error(c, GL_INVALID_ENUM); - return; - } - invalidate_texture(c, c->textures.active); -} - - - -static void drawTexxOESImp(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h, - ogles_context_t* c) -{ - ogles_lock_textures(c); - - const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; - y = gglIntToFixed(cbSurface.height) - (y + h); - w >>= FIXED_BITS; - h >>= FIXED_BITS; - - // set up all texture units - for (int i=0 ; i<GGL_TEXTURE_UNIT_COUNT ; i++) { - if (!c->rasterizer.state.texture[i].enable) - continue; - - int32_t texcoords[8]; - texture_unit_t& u(c->textures.tmu[i]); - - // validate this tmu (bind, wrap, filter) - validate_tmu(c, i); - // we CLAMP here, which works with premultiplied (s,t) - c->rasterizer.procs.texParameteri(c, - GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_S, GGL_CLAMP); - c->rasterizer.procs.texParameteri(c, - GGL_TEXTURE_2D, GGL_TEXTURE_WRAP_T, GGL_CLAMP); - u.dirty = 0xFF; // XXX: should be more subtle - - EGLTextureObject* textureObject = u.texture; - const GLint Ucr = textureObject->crop_rect[0] << 16; - const GLint Vcr = textureObject->crop_rect[1] << 16; - const GLint Wcr = textureObject->crop_rect[2] << 16; - const GLint Hcr = textureObject->crop_rect[3] << 16; - - // computes texture coordinates (pre-multiplied) - int32_t dsdx = Wcr / w; // dsdx = ((Wcr/w)/Wt)*Wt - int32_t dtdy =-Hcr / h; // dtdy = -((Hcr/h)/Ht)*Ht - int32_t s0 = Ucr - gglMulx(dsdx, x); // s0 = Ucr - x * dsdx - int32_t t0 = (Vcr+Hcr) - gglMulx(dtdy, y); // t0 = (Vcr+Hcr) - y*dtdy - texcoords[0] = s0; - texcoords[1] = dsdx; - texcoords[2] = 0; - texcoords[3] = t0; - texcoords[4] = 0; - texcoords[5] = dtdy; - texcoords[6] = 0; - texcoords[7] = 0; - c->rasterizer.procs.texCoordGradScale8xv(c, i, texcoords); - } - - const uint32_t enables = c->rasterizer.state.enables; - if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG))) - set_depth_and_fog(c, z); - - c->rasterizer.procs.activeTexture(c, c->textures.active); - c->rasterizer.procs.color4xv(c, c->currentColorClamped.v); - c->rasterizer.procs.disable(c, GGL_W_LERP); - c->rasterizer.procs.disable(c, GGL_AA); - c->rasterizer.procs.shadeModel(c, GL_FLAT); - c->rasterizer.procs.recti(c, - gglFixedToIntRound(x), - gglFixedToIntRound(y), - gglFixedToIntRound(x)+w, - gglFixedToIntRound(y)+h); - - ogles_unlock_textures(c); -} - -static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h, - ogles_context_t* c) -{ - // quickly reject empty rects - if ((w|h) <= 0) - return; - - drawTexxOESImp(x, y, z, w, h, c); -} - -static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c) -{ - // All coordinates are integer, so if we have only one - // texture unit active and no scaling is required - // THEN, we can use our special 1:1 mapping - // which is a lot faster. - - if (ggl_likely(c->rasterizer.state.enabled_tmu == 1)) { - const int tmu = 0; - texture_unit_t& u(c->textures.tmu[tmu]); - EGLTextureObject* textureObject = u.texture; - const GLint Wcr = textureObject->crop_rect[2]; - const GLint Hcr = textureObject->crop_rect[3]; - - if ((w == Wcr) && (h == -Hcr)) { - if ((w|h) <= 0) return; // quickly reject empty rects - - if (u.dirty) { - c->rasterizer.procs.activeTexture(c, tmu); - c->rasterizer.procs.bindTexture(c, &(u.texture->surface)); - c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, - GGL_TEXTURE_MIN_FILTER, u.texture->min_filter); - c->rasterizer.procs.texParameteri(c, GGL_TEXTURE_2D, - GGL_TEXTURE_MAG_FILTER, u.texture->mag_filter); - } - c->rasterizer.procs.texGeni(c, GGL_S, - GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - c->rasterizer.procs.texGeni(c, GGL_T, - GGL_TEXTURE_GEN_MODE, GGL_ONE_TO_ONE); - u.dirty = 0xFF; // XXX: should be more subtle - c->rasterizer.procs.activeTexture(c, c->textures.active); - - const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; - y = cbSurface.height - (y + h); - const GLint Ucr = textureObject->crop_rect[0]; - const GLint Vcr = textureObject->crop_rect[1]; - const GLint s0 = Ucr - x; - const GLint t0 = (Vcr + Hcr) - y; - - const GLuint tw = textureObject->surface.width; - const GLuint th = textureObject->surface.height; - if ((uint32_t(s0+x+w) > tw) || (uint32_t(t0+y+h) > th)) { - // The GL spec is unclear about what should happen - // in this case, so we just use the slow case, which - // at least won't crash - goto slow_case; - } - - ogles_lock_textures(c); - - c->rasterizer.procs.texCoord2i(c, s0, t0); - const uint32_t enables = c->rasterizer.state.enables; - if (ggl_unlikely(enables & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG))) - set_depth_and_fog(c, gglIntToFixed(z)); - - c->rasterizer.procs.color4xv(c, c->currentColorClamped.v); - c->rasterizer.procs.disable(c, GGL_W_LERP); - c->rasterizer.procs.disable(c, GGL_AA); - c->rasterizer.procs.shadeModel(c, GL_FLAT); - c->rasterizer.procs.recti(c, x, y, x+w, y+h); - - ogles_unlock_textures(c); - - return; - } - } - -slow_case: - drawTexxOESImp( - gglIntToFixed(x), gglIntToFixed(y), gglIntToFixed(z), - gglIntToFixed(w), gglIntToFixed(h), - c); -} - - -}; // namespace android -// ---------------------------------------------------------------------------- - -using namespace android; - - -#if 0 -#pragma mark - -#pragma mark Texture API -#endif - -void glActiveTexture(GLenum texture) -{ - ogles_context_t* c = ogles_context_t::get(); - if (uint32_t(texture-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - c->textures.active = texture - GL_TEXTURE0; - c->rasterizer.procs.activeTexture(c, c->textures.active); -} - -void glBindTexture(GLenum target, GLuint texture) -{ - ogles_context_t* c = ogles_context_t::get(); - if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - - // Bind or create a texture - sp<EGLTextureObject> tex; - if (texture == 0) { - // 0 is our local texture object - tex = c->textures.defaultTexture; - } else { - tex = c->surfaceManager->texture(texture); - if (ggl_unlikely(tex == 0)) { - tex = c->surfaceManager->createTexture(texture); - if (tex == 0) { - ogles_error(c, GL_OUT_OF_MEMORY); - return; - } - } - } - bindTextureTmu(c, c->textures.active, texture, tex); -} - -void glGenTextures(GLsizei n, GLuint *textures) -{ - ogles_context_t* c = ogles_context_t::get(); - if (n<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - // generate unique (shared) texture names - c->surfaceManager->getToken(n, textures); -} - -void glDeleteTextures(GLsizei n, const GLuint *textures) -{ - ogles_context_t* c = ogles_context_t::get(); - if (n<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - // If deleting a bound texture, bind this unit to 0 - for (int t=0 ; t<GGL_TEXTURE_UNIT_COUNT ; t++) { - if (c->textures.tmu[t].name == 0) - continue; - for (int i=0 ; i<n ; i++) { - if (textures[i] && (textures[i] == c->textures.tmu[t].name)) { - // bind this tmu to texture 0 - sp<EGLTextureObject> tex(c->textures.defaultTexture); - bindTextureTmu(c, t, 0, tex); - } - } - } - c->surfaceManager->deleteTextures(n, textures); - c->surfaceManager->recycleTokens(n, textures); -} - -void glMultiTexCoord4f( - GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q) -{ - ogles_context_t* c = ogles_context_t::get(); - if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - const int tmu = target-GL_TEXTURE0; - c->current.texture[tmu].S = gglFloatToFixed(s); - c->current.texture[tmu].T = gglFloatToFixed(t); - c->current.texture[tmu].R = gglFloatToFixed(r); - c->current.texture[tmu].Q = gglFloatToFixed(q); -} - -void glMultiTexCoord4x( - GLenum target, GLfixed s, GLfixed t, GLfixed r, GLfixed q) -{ - ogles_context_t* c = ogles_context_t::get(); - if (uint32_t(target-GL_TEXTURE0) > uint32_t(GGL_TEXTURE_UNIT_COUNT)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - const int tmu = target-GL_TEXTURE0; - c->current.texture[tmu].S = s; - c->current.texture[tmu].T = t; - c->current.texture[tmu].R = r; - c->current.texture[tmu].Q = q; -} - -void glPixelStorei(GLenum pname, GLint param) -{ - ogles_context_t* c = ogles_context_t::get(); - if ((pname != GL_PACK_ALIGNMENT) && (pname != GL_UNPACK_ALIGNMENT)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if ((param<=0 || param>8) || (param & (param-1))) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - if (pname == GL_PACK_ALIGNMENT) - c->textures.packAlignment = param; - if (pname == GL_UNPACK_ALIGNMENT) - c->textures.unpackAlignment = param; -} - -void glTexEnvf(GLenum target, GLenum pname, GLfloat param) -{ - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.texEnvi(c, target, pname, GLint(param)); -} - -void glTexEnvfv( - GLenum target, GLenum pname, const GLfloat *params) -{ - ogles_context_t* c = ogles_context_t::get(); - if (pname == GL_TEXTURE_ENV_MODE) { - c->rasterizer.procs.texEnvi(c, target, pname, GLint(*params)); - return; - } - if (pname == GL_TEXTURE_ENV_COLOR) { - GGLfixed fixed[4]; - for (int i=0 ; i<4 ; i++) - fixed[i] = gglFloatToFixed(params[i]); - c->rasterizer.procs.texEnvxv(c, target, pname, fixed); - return; - } - ogles_error(c, GL_INVALID_ENUM); -} - -void glTexEnvx(GLenum target, GLenum pname, GLfixed param) -{ - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.texEnvi(c, target, pname, param); -} - -void glTexEnvxv( - GLenum target, GLenum pname, const GLfixed *params) -{ - ogles_context_t* c = ogles_context_t::get(); - c->rasterizer.procs.texEnvxv(c, target, pname, params); -} - -void glTexParameteriv( - GLenum target, GLenum pname, const GLint* params) -{ - ogles_context_t* c = ogles_context_t::get(); - if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - - EGLTextureObject* textureObject = c->textures.tmu[c->textures.active].texture; - switch (pname) { - case GL_TEXTURE_CROP_RECT_OES: - memcpy(textureObject->crop_rect, params, 4*sizeof(GLint)); - break; - default: - texParameterx(target, pname, GLfixed(params[0]), c); - return; - } -} - -void glTexParameterf( - GLenum target, GLenum pname, GLfloat param) -{ - ogles_context_t* c = ogles_context_t::get(); - texParameterx(target, pname, GLfixed(param), c); -} - -void glTexParameterx( - GLenum target, GLenum pname, GLfixed param) -{ - ogles_context_t* c = ogles_context_t::get(); - texParameterx(target, pname, param, c); -} - -void glTexParameteri( - GLenum target, GLenum pname, GLint param) -{ - ogles_context_t* c = ogles_context_t::get(); - texParameterx(target, pname, GLfixed(param), c); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -void glCompressedTexImage2D( - GLenum target, GLint level, GLenum internalformat, - GLsizei width, GLsizei height, GLint border, - GLsizei imageSize, const GLvoid *data) -{ - ogles_context_t* c = ogles_context_t::get(); - if (target != GL_TEXTURE_2D) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (width<0 || height<0 || border!=0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - // "uncompress" the texture since pixelflinger doesn't support - // any compressed texture format natively. - GLenum format; - GLenum type; - switch (internalformat) { - case GL_PALETTE8_RGB8_OES: - case GL_PALETTE4_RGB8_OES: - format = GL_RGB; - type = GL_UNSIGNED_BYTE; - break; - case GL_PALETTE8_RGBA8_OES: - case GL_PALETTE4_RGBA8_OES: - format = GL_RGBA; - type = GL_UNSIGNED_BYTE; - break; - case GL_PALETTE8_R5_G6_B5_OES: - case GL_PALETTE4_R5_G6_B5_OES: - format = GL_RGB; - type = GL_UNSIGNED_SHORT_5_6_5; - break; - case GL_PALETTE8_RGBA4_OES: - case GL_PALETTE4_RGBA4_OES: - format = GL_RGBA; - type = GL_UNSIGNED_SHORT_4_4_4_4; - break; - case GL_PALETTE8_RGB5_A1_OES: - case GL_PALETTE4_RGB5_A1_OES: - format = GL_RGBA; - type = GL_UNSIGNED_SHORT_5_5_5_1; - break; -#ifdef GL_OES_compressed_ETC1_RGB8_texture - case GL_ETC1_RGB8_OES: - format = GL_RGB; - type = GL_UNSIGNED_BYTE; - break; -#endif - default: - ogles_error(c, GL_INVALID_ENUM); - return; - } - - if (!data || !width || !height) { - // unclear if this is an error or not... - return; - } - - int32_t size; - GGLSurface* surface; - -#ifdef GL_OES_compressed_ETC1_RGB8_texture - if (internalformat == GL_ETC1_RGB8_OES) { - GLsizei compressedSize = etc1_get_encoded_data_size(width, height); - if (compressedSize > imageSize) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - int error = createTextureSurface(c, &surface, &size, - level, format, type, width, height); - if (error) { - ogles_error(c, error); - return; - } - if (etc1_decode_image( - (const etc1_byte*)data, - (etc1_byte*)surface->data, - width, height, 3, surface->stride*3) != 0) { - ogles_error(c, GL_INVALID_OPERATION); - } - return; - } -#endif - - // all mipmap levels are specified at once. - const int numLevels = level<0 ? -level : 1; - - if (dataSizePalette4(numLevels, width, height, format) > imageSize) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - for (int i=0 ; i<numLevels ; i++) { - int lod_w = (width >> i) ? : 1; - int lod_h = (height >> i) ? : 1; - int error = createTextureSurface(c, &surface, &size, - i, format, type, lod_w, lod_h); - if (error) { - ogles_error(c, error); - return; - } - decodePalette4(data, i, width, height, - surface->data, surface->stride, internalformat); - } -} - - -void glTexImage2D( - GLenum target, GLint level, GLint internalformat, - GLsizei width, GLsizei height, GLint border, - GLenum format, GLenum type, const GLvoid *pixels) -{ - ogles_context_t* c = ogles_context_t::get(); - if (target != GL_TEXTURE_2D) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (width<0 || height<0 || border!=0 || level < 0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - if (format != (GLenum)internalformat) { - ogles_error(c, GL_INVALID_OPERATION); - return; - } - if (validFormatType(c, format, type)) { - return; - } - - int32_t size = 0; - GGLSurface* surface = 0; - int error = createTextureSurface(c, &surface, &size, - level, format, type, width, height); - if (error) { - ogles_error(c, error); - return; - } - - if (pixels) { - const int32_t formatIdx = convertGLPixelFormat(format, type); - const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); - const int32_t align = c->textures.unpackAlignment-1; - const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; - const int32_t stride = bpr / pixelFormat.size; - - GGLSurface userSurface; - userSurface.version = sizeof(userSurface); - userSurface.width = width; - userSurface.height = height; - userSurface.stride = stride; - userSurface.format = formatIdx; - userSurface.compressedFormat = 0; - userSurface.data = (GLubyte*)pixels; - - int err = copyPixels(c, *surface, 0, 0, userSurface, 0, 0, width, height); - if (err) { - ogles_error(c, err); - return; - } - generateMipmap(c, level); - } -} - -// ---------------------------------------------------------------------------- - -void glCompressedTexSubImage2D( - GLenum /*target*/, GLint /*level*/, GLint /*xoffset*/, - GLint /*yoffset*/, GLsizei /*width*/, GLsizei /*height*/, - GLenum /*format*/, GLsizei /*imageSize*/, - const GLvoid* /*data*/) -{ - ogles_context_t* c = ogles_context_t::get(); - ogles_error(c, GL_INVALID_ENUM); -} - -void glTexSubImage2D( - GLenum target, GLint level, GLint xoffset, - GLint yoffset, GLsizei width, GLsizei height, - GLenum format, GLenum type, const GLvoid *pixels) -{ - ogles_context_t* c = ogles_context_t::get(); - if (target != GL_TEXTURE_2D) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - if (validFormatType(c, format, type)) { - return; - } - - // find out which texture is bound to the current unit - const int active = c->textures.active; - EGLTextureObject* tex = c->textures.tmu[active].texture; - const GGLSurface& surface(tex->mip(level)); - - if (!tex->internalformat || tex->direct) { - ogles_error(c, GL_INVALID_OPERATION); - return; - } - - if (format != tex->internalformat) { - ogles_error(c, GL_INVALID_OPERATION); - return; - } - if ((xoffset + width > GLsizei(surface.width)) || - (yoffset + height > GLsizei(surface.height))) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - if (!width || !height) { - return; // okay, but no-op. - } - - // figure out the size we need as well as the stride - const int32_t formatIdx = convertGLPixelFormat(format, type); - if (formatIdx == 0) { // we don't know what to do with this - ogles_error(c, GL_INVALID_OPERATION); - return; - } - - const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); - const int32_t align = c->textures.unpackAlignment-1; - const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; - const int32_t stride = bpr / pixelFormat.size; - GGLSurface userSurface; - userSurface.version = sizeof(userSurface); - userSurface.width = width; - userSurface.height = height; - userSurface.stride = stride; - userSurface.format = formatIdx; - userSurface.compressedFormat = 0; - userSurface.data = (GLubyte*)pixels; - - int err = copyPixels(c, - surface, xoffset, yoffset, - userSurface, 0, 0, width, height); - if (err) { - ogles_error(c, err); - return; - } - - generateMipmap(c, level); - - // since we only changed the content of the texture, we don't need - // to call bindTexture on the main rasterizer. -} - -// ---------------------------------------------------------------------------- - -void glCopyTexImage2D( - GLenum target, GLint level, GLenum internalformat, - GLint x, GLint y, GLsizei width, GLsizei height, - GLint border) -{ - ogles_context_t* c = ogles_context_t::get(); - if (target != GL_TEXTURE_2D) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (internalformat<GL_ALPHA || internalformat>GL_LUMINANCE_ALPHA) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (width<0 || height<0 || border!=0 || level<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - GLenum format = 0; - GLenum type = GL_UNSIGNED_BYTE; - const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; - const int cbFormatIdx = cbSurface.format; - switch (cbFormatIdx) { - case GGL_PIXEL_FORMAT_RGB_565: - type = GL_UNSIGNED_SHORT_5_6_5; - break; - case GGL_PIXEL_FORMAT_RGBA_5551: - type = GL_UNSIGNED_SHORT_5_5_5_1; - break; - case GGL_PIXEL_FORMAT_RGBA_4444: - type = GL_UNSIGNED_SHORT_4_4_4_4; - break; - } - switch (internalformat) { - case GL_ALPHA: - case GL_LUMINANCE_ALPHA: - case GL_LUMINANCE: - type = GL_UNSIGNED_BYTE; - break; - } - - // figure out the format to use for the new texture - switch (cbFormatIdx) { - case GGL_PIXEL_FORMAT_RGBA_8888: - case GGL_PIXEL_FORMAT_A_8: - case GGL_PIXEL_FORMAT_RGBA_5551: - case GGL_PIXEL_FORMAT_RGBA_4444: - format = internalformat; - break; - case GGL_PIXEL_FORMAT_RGBX_8888: - case GGL_PIXEL_FORMAT_RGB_888: - case GGL_PIXEL_FORMAT_RGB_565: - case GGL_PIXEL_FORMAT_L_8: - switch (internalformat) { - case GL_LUMINANCE: - case GL_RGB: - format = internalformat; - break; - } - break; - } - - if (format == 0) { - // invalid combination - ogles_error(c, GL_INVALID_ENUM); - return; - } - - // create the new texture... - int32_t size; - GGLSurface* surface; - int error = createTextureSurface(c, &surface, &size, - level, format, type, width, height); - if (error) { - ogles_error(c, error); - return; - } - - // The bottom row is stored first in textures - GGLSurface txSurface(*surface); - txSurface.stride = -txSurface.stride; - - // (x,y) is the lower-left corner of colorBuffer - y = cbSurface.height - (y + height); - - /* The GLES spec says: - * If any of the pixels within the specified rectangle are outside - * the framebuffer associated with the current rendering context, - * then the values obtained for those pixels are undefined. - */ - if (x+width > GLint(cbSurface.width)) - width = cbSurface.width - x; - - if (y+height > GLint(cbSurface.height)) - height = cbSurface.height - y; - - int err = copyPixels(c, - txSurface, 0, 0, - cbSurface, x, y, width, height); - if (err) { - ogles_error(c, err); - } - - generateMipmap(c, level); -} - -void glCopyTexSubImage2D( - GLenum target, GLint level, GLint xoffset, GLint yoffset, - GLint x, GLint y, GLsizei width, GLsizei height) -{ - ogles_context_t* c = ogles_context_t::get(); - if (target != GL_TEXTURE_2D) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (xoffset<0 || yoffset<0 || width<0 || height<0 || level<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - if (!width || !height) { - return; // okay, but no-op. - } - - // find out which texture is bound to the current unit - const int active = c->textures.active; - EGLTextureObject* tex = c->textures.tmu[active].texture; - const GGLSurface& surface(tex->mip(level)); - - if (!tex->internalformat) { - ogles_error(c, GL_INVALID_OPERATION); - return; - } - if ((xoffset + width > GLsizei(surface.width)) || - (yoffset + height > GLsizei(surface.height))) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - // The bottom row is stored first in textures - GGLSurface txSurface(surface); - txSurface.stride = -txSurface.stride; - - // (x,y) is the lower-left corner of colorBuffer - const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; - y = cbSurface.height - (y + height); - - /* The GLES spec says: - * If any of the pixels within the specified rectangle are outside - * the framebuffer associated with the current rendering context, - * then the values obtained for those pixels are undefined. - */ - if (x+width > GLint(cbSurface.width)) - width = cbSurface.width - x; - - if (y+height > GLint(cbSurface.height)) - height = cbSurface.height - y; - - int err = copyPixels(c, - txSurface, xoffset, yoffset, - cbSurface, x, y, width, height); - if (err) { - ogles_error(c, err); - return; - } - - generateMipmap(c, level); -} - -void glReadPixels( - GLint x, GLint y, GLsizei width, GLsizei height, - GLenum format, GLenum type, GLvoid *pixels) -{ - ogles_context_t* c = ogles_context_t::get(); - if ((format != GL_RGBA) && (format != GL_RGB)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if ((type != GL_UNSIGNED_BYTE) && (type != GL_UNSIGNED_SHORT_5_6_5)) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - if (width<0 || height<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - if (x<0 || y<0) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - int32_t formatIdx = GGL_PIXEL_FORMAT_NONE; - if ((format == GL_RGBA) && (type == GL_UNSIGNED_BYTE)) { - formatIdx = GGL_PIXEL_FORMAT_RGBA_8888; - } else if ((format == GL_RGB) && (type == GL_UNSIGNED_SHORT_5_6_5)) { - formatIdx = GGL_PIXEL_FORMAT_RGB_565; - } else { - ogles_error(c, GL_INVALID_OPERATION); - return; - } - - const GGLSurface& readSurface = c->rasterizer.state.buffers.read.s; - if ((x+width > GLint(readSurface.width)) || - (y+height > GLint(readSurface.height))) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - const GGLFormat& pixelFormat(c->rasterizer.formats[formatIdx]); - const int32_t align = c->textures.packAlignment-1; - const int32_t bpr = ((width * pixelFormat.size) + align) & ~align; - const int32_t stride = bpr / pixelFormat.size; - - GGLSurface userSurface; - userSurface.version = sizeof(userSurface); - userSurface.width = width; - userSurface.height = height; - userSurface.stride = -stride; // bottom row is transfered first - userSurface.format = formatIdx; - userSurface.compressedFormat = 0; - userSurface.data = (GLubyte*)pixels; - - // use pixel-flinger to handle all the conversions - GGLContext* ggl = getRasterizer(c); - if (!ggl) { - // the only reason this would fail is because we ran out of memory - ogles_error(c, GL_OUT_OF_MEMORY); - return; - } - - ggl->colorBuffer(ggl, &userSurface); // destination is user buffer - ggl->bindTexture(ggl, &readSurface); // source is read-buffer - ggl->texCoord2i(ggl, x, readSurface.height - (y + height)); - ggl->recti(ggl, 0, 0, width, height); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark DrawTexture Extension -#endif - -void glDrawTexsvOES(const GLshort* coords) { - ogles_context_t* c = ogles_context_t::get(); - drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); -} -void glDrawTexivOES(const GLint* coords) { - ogles_context_t* c = ogles_context_t::get(); - drawTexiOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); -} -void glDrawTexsOES(GLshort x , GLshort y, GLshort z, GLshort w, GLshort h) { - ogles_context_t* c = ogles_context_t::get(); - drawTexiOES(x, y, z, w, h, c); -} -void glDrawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h) { - ogles_context_t* c = ogles_context_t::get(); - drawTexiOES(x, y, z, w, h, c); -} - -void glDrawTexfvOES(const GLfloat* coords) { - ogles_context_t* c = ogles_context_t::get(); - drawTexxOES( - gglFloatToFixed(coords[0]), - gglFloatToFixed(coords[1]), - gglFloatToFixed(coords[2]), - gglFloatToFixed(coords[3]), - gglFloatToFixed(coords[4]), - c); -} -void glDrawTexxvOES(const GLfixed* coords) { - ogles_context_t* c = ogles_context_t::get(); - drawTexxOES(coords[0], coords[1], coords[2], coords[3], coords[4], c); -} -void glDrawTexfOES(GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLfloat h){ - ogles_context_t* c = ogles_context_t::get(); - drawTexxOES( - gglFloatToFixed(x), gglFloatToFixed(y), gglFloatToFixed(z), - gglFloatToFixed(w), gglFloatToFixed(h), - c); -} -void glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h) { - ogles_context_t* c = ogles_context_t::get(); - drawTexxOES(x, y, z, w, h, c); -} - -// ---------------------------------------------------------------------------- -#if 0 -#pragma mark - -#pragma mark EGL Image Extension -#endif - -void glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) -{ - ogles_context_t* c = ogles_context_t::get(); - if (target != GL_TEXTURE_2D && target != GL_TEXTURE_EXTERNAL_OES) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - - if (image == EGL_NO_IMAGE_KHR) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)image; - if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - // bind it to the texture unit - sp<EGLTextureObject> tex = getAndBindActiveTextureObject(c); - tex->setImage(native_buffer); -} - -void glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES image) -{ - ogles_context_t* c = ogles_context_t::get(); - if (target != GL_RENDERBUFFER_OES) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - - if (image == EGL_NO_IMAGE_KHR) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)image; - if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) { - ogles_error(c, GL_INVALID_VALUE); - return; - } - - // well, we're not supporting this extension anyways -} diff --git a/opengl/libagl/texture.h b/opengl/libagl/texture.h deleted file mode 100644 index 98f75509db..0000000000 --- a/opengl/libagl/texture.h +++ /dev/null @@ -1,41 +0,0 @@ -/* libs/opengles/texture.h -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_TEXTURE_H -#define ANDROID_OPENGLES_TEXTURE_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> - -#include <private/pixelflinger/ggl_context.h> - -#include <GLES/gl.h> - -#include "context.h" - -namespace android { - -void ogles_init_texture(ogles_context_t* c); -void ogles_uninit_texture(ogles_context_t* c); -void ogles_validate_texture(ogles_context_t* c); -void ogles_lock_textures(ogles_context_t* c); -void ogles_unlock_textures(ogles_context_t* c); - -}; // namespace android - -#endif // ANDROID_OPENGLES_TEXTURE_H diff --git a/opengl/libagl/vertex.cpp b/opengl/libagl/vertex.cpp deleted file mode 100644 index 9aacdb3c89..0000000000 --- a/opengl/libagl/vertex.cpp +++ /dev/null @@ -1,247 +0,0 @@ -/* libs/opengles/vertex.cpp -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#include <stdio.h> -#include <stdlib.h> -#include "context.h" -#include "fp.h" -#include "vertex.h" -#include "state.h" -#include "matrix.h" - -namespace android { - -// ---------------------------------------------------------------------------- - -void ogles_init_vertex(ogles_context_t* c) -{ - c->cull.enable = GL_FALSE; - c->cull.cullFace = GL_BACK; - c->cull.frontFace = GL_CCW; - - c->current.color.r = 0x10000; - c->current.color.g = 0x10000; - c->current.color.b = 0x10000; - c->current.color.a = 0x10000; - - c->currentNormal.z = 0x10000; -} - -void ogles_uninit_vertex(ogles_context_t* /*c*/) -{ -} - -// ---------------------------------------------------------------------------- -// vertex processing -// ---------------------------------------------------------------------------- - -// Divides a vertex clip coordinates by W -static inline -void perspective(ogles_context_t* c, vertex_t* v, uint32_t enables) -{ - // [x,y,z]window = vpt * ([x,y,z]clip / clip.w) - // [w]window = 1/w - - // With a regular projection generated by glFrustum(), - // we have w=-z, therefore, w is in [zNear, zFar]. - // Also, zNear and zFar are stricly positive, - // and 1/w (window.w) is in [1/zFar, 1/zNear], usually this - // means ]0, +inf[ -- however, it is always recommended - // to use as large values as possible for zNear. - // All in all, w is usually smaller than 1.0 (assuming - // zNear is at least 1.0); and even if zNear is smaller than 1.0 - // values of w won't be too big. - - const int32_t rw = gglRecip28(v->clip.w); - const GLfixed* const m = c->transforms.vpt.transform.matrix.m; - v->window.w = rw; - v->window.x = gglMulAddx(gglMulx(v->clip.x, rw, 16), m[ 0], m[12], 28); - v->window.y = gglMulAddx(gglMulx(v->clip.y, rw, 16), m[ 5], m[13], 28); - v->window.x = TRI_FROM_FIXED(v->window.x); - v->window.y = TRI_FROM_FIXED(v->window.y); - if (enables & GGL_ENABLE_DEPTH_TEST) { - v->window.z = gglMulAddx(gglMulx(v->clip.z, rw, 16), m[10], m[14], 28); - } -} - -// frustum clipping and W-divide -static inline -void clipFrustumPerspective(ogles_context_t* c, vertex_t* v, uint32_t enables) -{ - // ndc = clip / W - // window = ncd * viewport - - // clip to the view-volume - uint32_t clip = v->flags & vertex_t::CLIP_ALL; - const GLfixed w = v->clip.w; - if (v->clip.x < -w) clip |= vertex_t::CLIP_L; - if (v->clip.x > w) clip |= vertex_t::CLIP_R; - if (v->clip.y < -w) clip |= vertex_t::CLIP_B; - if (v->clip.y > w) clip |= vertex_t::CLIP_T; - if (v->clip.z < -w) clip |= vertex_t::CLIP_N; - if (v->clip.z > w) clip |= vertex_t::CLIP_F; - - v->flags |= clip; - c->arrays.cull &= clip; - - if (ggl_likely(!clip)) { - // if the vertex is clipped, we don't do the perspective - // divide, since we don't need its window coordinates. - perspective(c, v, enables); - } -} - -// frustum clipping, user clipping and W-divide -static inline -void clipAllPerspective(ogles_context_t* c, vertex_t* v, uint32_t enables) -{ - // compute eye coordinates - c->arrays.mv_transform( - &c->transforms.modelview.transform, &v->eye, &v->obj); - v->flags |= vertex_t::EYE; - - // clip this vertex against each user clip plane - uint32_t clip = 0; - int planes = c->clipPlanes.enable; - while (planes) { - const int i = 31 - gglClz(planes); - planes &= ~(1<<i); - // XXX: we should have a special dot() for 2,3,4 coords vertices - GLfixed d = dot4(c->clipPlanes.plane[i].equation.v, v->eye.v); - if (d < 0) { - clip |= 0x100<<i; - } - } - v->flags |= clip; - - clipFrustumPerspective(c, v, enables); -} - -// ---------------------------------------------------------------------------- - -void ogles_vertex_project(ogles_context_t* c, vertex_t* v) { - perspective(c, v, c->rasterizer.state.enables); -} - -void ogles_vertex_perspective2D(ogles_context_t* c, vertex_t* v) -{ - // here we assume w=1.0 and the viewport transformation - // has been applied already. - c->arrays.cull = 0; - v->window.x = TRI_FROM_FIXED(v->clip.x); - v->window.y = TRI_FROM_FIXED(v->clip.y); - v->window.z = v->clip.z; - v->window.w = v->clip.w << 12; -} - -void ogles_vertex_perspective3DZ(ogles_context_t* c, vertex_t* v) { - clipFrustumPerspective(c, v, GGL_ENABLE_DEPTH_TEST); -} -void ogles_vertex_perspective3D(ogles_context_t* c, vertex_t* v) { - clipFrustumPerspective(c, v, 0); -} -void ogles_vertex_clipAllPerspective3DZ(ogles_context_t* c, vertex_t* v) { - clipAllPerspective(c, v, GGL_ENABLE_DEPTH_TEST); -} -void ogles_vertex_clipAllPerspective3D(ogles_context_t* c, vertex_t* v) { - clipAllPerspective(c, v, 0); -} - -static void clipPlanex(GLenum plane, const GLfixed* equ, ogles_context_t* c) -{ - const int p = plane - GL_CLIP_PLANE0; - if (ggl_unlikely(uint32_t(p) > (GL_CLIP_PLANE5 - GL_CLIP_PLANE0))) { - ogles_error(c, GL_INVALID_ENUM); - return; - } - - vec4_t& equation = c->clipPlanes.plane[p].equation; - memcpy(equation.v, equ, sizeof(vec4_t)); - - ogles_validate_transform(c, transform_state_t::MVIT); - transform_t& mvit = c->transforms.mvit4; - mvit.point4(&mvit, &equation, &equation); -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- - -using namespace android; - - -void glColor4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a) -{ - ogles_context_t* c = ogles_context_t::get(); - c->current.color.r = gglFloatToFixed(r); - c->currentColorClamped.r = gglClampx(c->current.color.r); - c->current.color.g = gglFloatToFixed(g); - c->currentColorClamped.g = gglClampx(c->current.color.g); - c->current.color.b = gglFloatToFixed(b); - c->currentColorClamped.b = gglClampx(c->current.color.b); - c->current.color.a = gglFloatToFixed(a); - c->currentColorClamped.a = gglClampx(c->current.color.a); -} - -void glColor4x(GLfixed r, GLfixed g, GLfixed b, GLfixed a) -{ - ogles_context_t* c = ogles_context_t::get(); - c->current.color.r = r; - c->current.color.g = g; - c->current.color.b = b; - c->current.color.a = a; - c->currentColorClamped.r = gglClampx(r); - c->currentColorClamped.g = gglClampx(g); - c->currentColorClamped.b = gglClampx(b); - c->currentColorClamped.a = gglClampx(a); -} - -void glNormal3f(GLfloat x, GLfloat y, GLfloat z) -{ - ogles_context_t* c = ogles_context_t::get(); - c->currentNormal.x = gglFloatToFixed(x); - c->currentNormal.y = gglFloatToFixed(y); - c->currentNormal.z = gglFloatToFixed(z); -} - -void glNormal3x(GLfixed x, GLfixed y, GLfixed z) -{ - ogles_context_t* c = ogles_context_t::get(); - c->currentNormal.x = x; - c->currentNormal.y = y; - c->currentNormal.z = z; -} - -// ---------------------------------------------------------------------------- - -void glClipPlanef(GLenum plane, const GLfloat* equ) -{ - const GLfixed equx[4] = { - gglFloatToFixed(equ[0]), - gglFloatToFixed(equ[1]), - gglFloatToFixed(equ[2]), - gglFloatToFixed(equ[3]) - }; - ogles_context_t* c = ogles_context_t::get(); - clipPlanex(plane, equx, c); -} - -void glClipPlanex(GLenum plane, const GLfixed* equ) -{ - ogles_context_t* c = ogles_context_t::get(); - clipPlanex(plane, equ, c); -} diff --git a/opengl/libagl/vertex.h b/opengl/libagl/vertex.h deleted file mode 100644 index 55e62137c1..0000000000 --- a/opengl/libagl/vertex.h +++ /dev/null @@ -1,48 +0,0 @@ -/* libs/opengles/vertex.h -** -** Copyright 2006, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef ANDROID_OPENGLES_VERTEX_H -#define ANDROID_OPENGLES_VERTEX_H - -#include <stdint.h> -#include <stddef.h> -#include <sys/types.h> - -namespace android { - -namespace gl { -struct vertex_t; -struct ogles_context_t; -}; - -void ogles_init_vertex(ogles_context_t* c); -void ogles_uninit_vertex(ogles_context_t* c); - -void ogles_vertex_perspective2D(ogles_context_t*, vertex_t*); - -void ogles_vertex_perspective3D(ogles_context_t*, vertex_t*); -void ogles_vertex_perspective3DZ(ogles_context_t*, vertex_t*); -void ogles_vertex_clipAllPerspective3D(ogles_context_t*, vertex_t*); -void ogles_vertex_clipAllPerspective3DZ(ogles_context_t*, vertex_t*); - - -void ogles_vertex_project(ogles_context_t* c, vertex_t*); - -}; // namespace android - -#endif // ANDROID_OPENGLES_VERTEX_H - diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index eb90c8b45b..48a68afc8e 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -159,7 +159,7 @@ cc_library_shared { "libEGL_getProcAddress", "libEGL_blobCache", ], - ldflags: ["-Wl,--exclude-libs=ALL"], + ldflags: ["-Wl,--exclude-libs=ALL,--Bsymbolic-functions"], export_include_dirs: ["EGL/include"], } diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 038a432337..e1432608c9 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -51,12 +51,6 @@ namespace android { * /vendor/lib/egl/libGLESv1_CM.so * /vendor/lib/egl/libGLESv2.so * - * The software renderer for the emulator must be provided as a single - * library at: - * - * /system/lib/egl/libGLES_android.so - * - * * For backward compatibility and to facilitate the transition to * this new naming scheme, the loader will additionally look for: * @@ -146,38 +140,6 @@ static void* load_wrapper(const char* path) { #endif #endif -static void setEmulatorGlesValue(void) { - char prop[PROPERTY_VALUE_MAX]; - property_get("ro.kernel.qemu", prop, "0"); - if (atoi(prop) != 1) return; - - property_get("ro.kernel.qemu.gles",prop,"0"); - if (atoi(prop) == 1) { - ALOGD("Emulator has host GPU support, qemu.gles is set to 1."); - property_set("qemu.gles", "1"); - return; - } - - // for now, checking the following - // directory is good enough for emulator system images - const char* vendor_lib_path = -#if defined(__LP64__) - "/vendor/lib64/egl"; -#else - "/vendor/lib/egl"; -#endif - - const bool has_vendor_lib = (access(vendor_lib_path, R_OK) == 0); - if (has_vendor_lib) { - ALOGD("Emulator has vendor provided software renderer, qemu.gles is set to 2."); - property_set("qemu.gles", "2"); - } else { - ALOGD("Emulator without GPU support detected. " - "Fallback to legacy software renderer, qemu.gles is set to 0."); - property_set("qemu.gles", "0"); - } -} - static const char* DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl"; static const char* HAL_SUBNAME_KEY_PROPERTIES[2] = { @@ -260,8 +222,6 @@ void* Loader::open(egl_connection_t* cnx) return cnx->dso; } - setEmulatorGlesValue(); - // Check if we should use ANGLE early, so loading each driver doesn't require repeated queries. if (android::GraphicsEnv::getInstance().shouldUseAngle()) { cnx->shouldUseAngle = true; @@ -311,7 +271,7 @@ void* Loader::open(egl_connection_t* cnx) } if (!hnd) { - android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, + android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL, false, systemTime() - openTime); } @@ -330,7 +290,7 @@ void* Loader::open(egl_connection_t* cnx) } if (!cnx->libEgl || !cnx->libGles2 || !cnx->libGles1) { - android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, + android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL, false, systemTime() - openTime); } @@ -340,7 +300,7 @@ void* Loader::open(egl_connection_t* cnx) LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1, "couldn't load system OpenGL ES wrapper libraries"); - android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, true, + android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL, true, systemTime() - openTime); return (void*)hnd; @@ -637,7 +597,7 @@ Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) { return nullptr; } - android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::ANGLE); + android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE); driver_t* hnd = nullptr; // ANGLE doesn't ship with GLES library, and thus we skip GLES driver. @@ -666,7 +626,7 @@ Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) } ALOGD("Load updated gl driver."); - android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::GL_UPDATED); + android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL_UPDATED); driver_t* hnd = nullptr; void* dso = load_updated_driver("GLES", ns); if (dso) { @@ -697,7 +657,7 @@ Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, const bool exact) { ATRACE_CALL(); - android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::GL); + android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL); driver_t* hnd = nullptr; void* dso = load_system_driver("GLES", suffix, exact); if (dso) { diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index e996be6853..d1b4a4ec33 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -1379,6 +1379,9 @@ EGLBoolean eglSwapBuffersWithDamageKHRImpl(EGLDisplay dpy, EGLSurface draw, if (!_s.get()) return setError(EGL_BAD_SURFACE, (EGLBoolean)EGL_FALSE); + if (n_rects < 0 || (n_rects > 0 && rects == NULL)) + return setError(EGL_BAD_PARAMETER, (EGLBoolean)EGL_FALSE); + egl_surface_t* const s = get_surface(draw); if (CC_UNLIKELY(dp->traceGpuCompletion)) { diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index c8253e05c2..55a0c0a639 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -28,7 +28,12 @@ #include <utils/String8.h> #include <utils/Trace.h> +#include <array> +#include <fstream> +#include <sstream> +#include <sys/types.h> #include <vkjson.h> +#include <unistd.h> #include "gpustats/GpuStats.h" @@ -40,6 +45,7 @@ namespace { status_t cmdHelp(int out); status_t cmdVkjson(int out, int err); void dumpGameDriverInfo(std::string* result); +void dumpMemoryInfo(std::string* result, const GpuMemoryMap& memories, uint32_t pid); } // namespace const String16 sDump("android.permission.DUMP"); @@ -51,7 +57,7 @@ GpuService::GpuService() : mGpuStats(std::make_unique<GpuStats>()){}; void GpuService::setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, - const int32_t vulkanVersion, GraphicsEnv::Driver driver, + const int32_t vulkanVersion, GpuStatsInfo::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) { mGpuStats->insert(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime, appPackageName, vulkanVersion, driver, isDriverLoaded, driverLoadingTime); @@ -68,10 +74,151 @@ status_t GpuService::getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) } void GpuService::setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, - const GraphicsEnv::Stats stats, const uint64_t value) { + const GpuStatsInfo::Stats stats, const uint64_t value) { mGpuStats->insertTargetStats(appPackageName, driverVersionCode, stats, value); } +bool isExpectedFormat(const char* str) { + // Should match in order: + // gpuaddr useraddr size id flags type usage sglen mapsize eglsrf eglimg + std::istringstream iss; + iss.str(str); + + std::string word; + iss >> word; + if (word != "gpuaddr") { return false; } + iss >> word; + if (word != "useraddr") { return false; } + iss >> word; + if (word != "size") { return false; } + iss >> word; + if (word != "id") { return false; } + iss >> word; + if (word != "flags") { return false; } + iss >> word; + if (word != "type") { return false; } + iss >> word; + if (word != "usage") { return false; } + iss >> word; + if (word != "sglen") { return false; } + iss >> word; + if (word != "mapsize") { return false; } + iss >> word; + if (word != "eglsrf") { return false; } + iss >> word; + if (word != "eglimg") { return false; } + return true; +} + + +// Queries gpu memory via Qualcomm's /d/kgsl/proc/*/mem interface. +status_t GpuService::getQCommGpuMemoryInfo(GpuMemoryMap* memories, std::string* result, int32_t dumpPid) const { + const std::string kDirectoryPath = "/d/kgsl/proc"; + DIR* directory = opendir(kDirectoryPath.c_str()); + if (!directory) { return PERMISSION_DENIED; } + + // File Format: + // gpuaddr useraddr size id flags type usage sglen mapsize eglsrf eglimg + // 0000000000000000 0000000000000000 8359936 23 --w--pY-- gpumem VK/others( 38) 0 0 0 0 + // 0000000000000000 0000000000000000 16293888 24 --wL--N-- ion surface 41 0 0 1 + + const bool dumpAll = dumpPid == 0; + static constexpr size_t kMaxLineLength = 1024; + static char line[kMaxLineLength]; + while(dirent* subdir = readdir(directory)) { + // Skip "." and ".." in directory. + if (strcmp(subdir->d_name, ".") == 0 || strcmp(subdir->d_name, "..") == 0 ) { continue; } + + std::string pid_str(subdir->d_name); + const uint32_t pid(stoi(pid_str)); + + if (!dumpAll && dumpPid != pid) { + continue; + } + + std::string filepath(kDirectoryPath + "/" + pid_str + "/mem"); + std::ifstream file(filepath); + + // Check first line + file.getline(line, kMaxLineLength); + if (!isExpectedFormat(line)) { + continue; + } + + if (result) { + StringAppendF(result, "%d:\n%s\n", pid, line); + } + + while( file.getline(line, kMaxLineLength) ) { + if (result) { + StringAppendF(result, "%s\n", line); + } + + std::istringstream iss; + iss.str(line); + + // Skip gpuaddr, useraddr. + const char delimiter = ' '; + iss >> std::ws; + iss.ignore(kMaxLineLength, delimiter); + iss >> std::ws; + iss.ignore(kMaxLineLength, delimiter); + + // Get size. + int64_t memsize; + iss >> memsize; + + // Skip id, flags. + iss >> std::ws; + iss.ignore(kMaxLineLength, delimiter); + iss >> std::ws; + iss.ignore(kMaxLineLength, delimiter); + + // Get type, usage. + std::string memtype; + std::string usage; + iss >> memtype >> usage; + + // Adjust for the space in VK/others( #) + if (usage == "VK/others(") { + std::string vkTypeEnd; + iss >> vkTypeEnd; + usage.append(vkTypeEnd); + } + + // Skip sglen. + iss >> std::ws; + iss.ignore(kMaxLineLength, delimiter); + + // Get mapsize. + int64_t mapsize; + iss >> mapsize; + + if (memsize == 0 && mapsize == 0) { + continue; + } + + if (memtype == "gpumem") { + (*memories)[pid][usage].gpuMemory += memsize; + } else { + (*memories)[pid][usage].ionMemory += memsize; + } + + if (mapsize > 0) { + (*memories)[pid][usage].mappedMemory += mapsize; + } + } + + if (result) { + StringAppendF(result, "\n"); + } + } + + closedir(directory); + + return OK; +} + status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) { ATRACE_CALL(); @@ -99,24 +246,44 @@ status_t GpuService::doDump(int fd, const Vector<String16>& args, bool /*asProto StringAppendF(&result, "Permission Denial: can't dump gpu from pid=%d, uid=%d\n", pid, uid); } else { bool dumpAll = true; - size_t index = 0; + bool dumpDriverInfo = false; + bool dumpStats = false; + bool dumpMemory = false; size_t numArgs = args.size(); + int32_t pid = 0; if (numArgs) { - if ((index < numArgs) && (args[index] == String16("--gpustats"))) { - index++; - mGpuStats->dump(args, &result); - dumpAll = false; + dumpAll = false; + for (size_t index = 0; index < numArgs; ++index) { + if (args[index] == String16("--gpustats")) { + dumpStats = true; + } else if (args[index] == String16("--gpudriverinfo")) { + dumpDriverInfo = true; + } else if (args[index] == String16("--gpumem")) { + dumpMemory = true; + } else if (args[index].startsWith(String16("--gpumem="))) { + dumpMemory = true; + pid = atoi(String8(&args[index][9])); + } } } - if (dumpAll) { + if (dumpAll || dumpDriverInfo) { dumpGameDriverInfo(&result); result.append("\n"); - - mGpuStats->dump(Vector<String16>(), &result); + } + if (dumpAll || dumpStats) { + mGpuStats->dump(args, &result); result.append("\n"); } + if (dumpAll || dumpMemory) { + GpuMemoryMap memories; + // Currently only queries Qualcomm gpu memory. More will be added later. + if (getQCommGpuMemoryInfo(&memories, &result, pid) == OK) { + dumpMemoryInfo(&result, memories, pid); + result.append("\n"); + } + } } write(fd, result.c_str(), result.size()); @@ -168,6 +335,34 @@ void dumpGameDriverInfo(std::string* result) { StringAppendF(result, "Pre-release Game Driver: %s\n", preReleaseGameDriver); } +// Read and print all memory info for each process from /d/kgsl/proc/<pid>/mem. +void dumpMemoryInfo(std::string* result, const GpuMemoryMap& memories, uint32_t pid) { + if (!result) return; + + // Write results. + StringAppendF(result, "GPU Memory Summary:\n"); + for(auto& mem : memories) { + uint32_t process = mem.first; + if (pid != 0 && pid != process) { + continue; + } + + StringAppendF(result, "%d:\n", process); + for(auto& memStruct : mem.second) { + StringAppendF(result, " %s", memStruct.first.c_str()); + + if(memStruct.second.gpuMemory > 0) + StringAppendF(result, ", GPU memory = %" PRId64, memStruct.second.gpuMemory); + if(memStruct.second.mappedMemory > 0) + StringAppendF(result, ", Mapped memory = %" PRId64, memStruct.second.mappedMemory); + if(memStruct.second.ionMemory > 0) + StringAppendF(result, ", Ion memory = %" PRId64, memStruct.second.ionMemory); + + StringAppendF(result, "\n"); + } + } +} + } // anonymous namespace } // namespace android diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h index 7d44a35814..b3dc2e2718 100644 --- a/services/gpuservice/GpuService.h +++ b/services/gpuservice/GpuService.h @@ -25,11 +25,22 @@ #include <mutex> #include <vector> +#include <unordered_map> namespace android { class GpuStats; +struct MemoryStruct { + int64_t gpuMemory; + int64_t mappedMemory; + int64_t ionMemory; +}; + +// A map that keeps track of how much memory of each type is allocated by every process. +// Format: map[pid][memoryType] = MemoryStruct()' +using GpuMemoryMap = std::unordered_map<int32_t, std::unordered_map<std::string, MemoryStruct>>; + class GpuService : public BnGpuService, public PriorityDumper { public: static const char* const SERVICE_NAME ANDROID_API; @@ -46,12 +57,12 @@ private: void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, const int32_t vulkanVersion, - GraphicsEnv::Driver driver, bool isDriverLoaded, + GpuStatsInfo::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) override; status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const override; status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const override; void setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, - const GraphicsEnv::Stats stats, const uint64_t value) override; + const GpuStatsInfo::Stats stats, const uint64_t value) override; /* * IBinder interface @@ -71,6 +82,8 @@ private: status_t doDump(int fd, const Vector<String16>& args, bool asProto); + status_t getQCommGpuMemoryInfo(GpuMemoryMap* memories, std::string* result, int32_t dumpPid) const; + /* * Attributes */ diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp index 5d27e7281d..67babd496f 100644 --- a/services/gpuservice/gpustats/GpuStats.cpp +++ b/services/gpuservice/gpustats/GpuStats.cpp @@ -27,20 +27,20 @@ namespace android { -static void addLoadingCount(GraphicsEnv::Driver driver, bool isDriverLoaded, +static void addLoadingCount(GpuStatsInfo::Driver driver, bool isDriverLoaded, GpuStatsGlobalInfo* const outGlobalInfo) { switch (driver) { - case GraphicsEnv::Driver::GL: - case GraphicsEnv::Driver::GL_UPDATED: + case GpuStatsInfo::Driver::GL: + case GpuStatsInfo::Driver::GL_UPDATED: outGlobalInfo->glLoadingCount++; if (!isDriverLoaded) outGlobalInfo->glLoadingFailureCount++; break; - case GraphicsEnv::Driver::VULKAN: - case GraphicsEnv::Driver::VULKAN_UPDATED: + case GpuStatsInfo::Driver::VULKAN: + case GpuStatsInfo::Driver::VULKAN_UPDATED: outGlobalInfo->vkLoadingCount++; if (!isDriverLoaded) outGlobalInfo->vkLoadingFailureCount++; break; - case GraphicsEnv::Driver::ANGLE: + case GpuStatsInfo::Driver::ANGLE: outGlobalInfo->angleLoadingCount++; if (!isDriverLoaded) outGlobalInfo->angleLoadingFailureCount++; break; @@ -49,22 +49,22 @@ static void addLoadingCount(GraphicsEnv::Driver driver, bool isDriverLoaded, } } -static void addLoadingTime(GraphicsEnv::Driver driver, int64_t driverLoadingTime, +static void addLoadingTime(GpuStatsInfo::Driver driver, int64_t driverLoadingTime, GpuStatsAppInfo* const outAppInfo) { switch (driver) { - case GraphicsEnv::Driver::GL: - case GraphicsEnv::Driver::GL_UPDATED: + case GpuStatsInfo::Driver::GL: + case GpuStatsInfo::Driver::GL_UPDATED: if (outAppInfo->glDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) { outAppInfo->glDriverLoadingTime.emplace_back(driverLoadingTime); } break; - case GraphicsEnv::Driver::VULKAN: - case GraphicsEnv::Driver::VULKAN_UPDATED: + case GpuStatsInfo::Driver::VULKAN: + case GpuStatsInfo::Driver::VULKAN_UPDATED: if (outAppInfo->vkDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) { outAppInfo->vkDriverLoadingTime.emplace_back(driverLoadingTime); } break; - case GraphicsEnv::Driver::ANGLE: + case GpuStatsInfo::Driver::ANGLE: if (outAppInfo->angleDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) { outAppInfo->angleDriverLoadingTime.emplace_back(driverLoadingTime); } @@ -77,7 +77,7 @@ static void addLoadingTime(GraphicsEnv::Driver driver, int64_t driverLoadingTime void GpuStats::insert(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, const int32_t vulkanVersion, - GraphicsEnv::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) { + GpuStatsInfo::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mLock); @@ -127,7 +127,7 @@ void GpuStats::insert(const std::string& driverPackageName, const std::string& d } void GpuStats::insertTargetStats(const std::string& appPackageName, - const uint64_t driverVersionCode, const GraphicsEnv::Stats stats, + const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats, const uint64_t /*value*/) { ATRACE_CALL(); @@ -139,9 +139,12 @@ void GpuStats::insertTargetStats(const std::string& appPackageName, } switch (stats) { - case GraphicsEnv::Stats::CPU_VULKAN_IN_USE: + case GpuStatsInfo::Stats::CPU_VULKAN_IN_USE: mAppStats[appStatsKey].cpuVulkanInUse = true; break; + case GpuStatsInfo::Stats::FALSE_PREROTATION: + mAppStats[appStatsKey].falsePrerotation = true; + break; default: break; } diff --git a/services/gpuservice/gpustats/GpuStats.h b/services/gpuservice/gpustats/GpuStats.h index 378f7f470d..656b181464 100644 --- a/services/gpuservice/gpustats/GpuStats.h +++ b/services/gpuservice/gpustats/GpuStats.h @@ -36,10 +36,10 @@ public: void insert(const std::string& driverPackageName, const std::string& driverVersionName, uint64_t driverVersionCode, int64_t driverBuildTime, const std::string& appPackageName, const int32_t vulkanVersion, - GraphicsEnv::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime); + GpuStatsInfo::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime); // Insert target stats into app stats or potentially global stats as well. void insertTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, - const GraphicsEnv::Stats stats, const uint64_t value); + const GpuStatsInfo::Stats stats, const uint64_t value); // dumpsys interface void dump(const Vector<String16>& args, std::string* result); // Pull gpu global stats diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index 11578c393e..f6b5935bcf 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -44,6 +44,7 @@ cc_library_shared { "libhidlbase", "libinput", "liblog", + "libstatslog", "libutils", "libui", "server_configurable_flags", diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 6a7f2797f4..7c061c5857 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -276,7 +276,7 @@ void MotionClassifier::enqueueEvent(ClassifierEvent&& event) { bool eventAdded = mEvents.push(std::move(event)); if (!eventAdded) { // If the queue is full, suspect the HAL is slow in processing the events. - ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime); + ALOGE("Could not add the event to the queue. Resetting"); reset(); } } diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp index f82c8ef1fd..fc8c7c39f9 100644 --- a/services/inputflinger/InputClassifierConverter.cpp +++ b/services/inputflinger/InputClassifierConverter.cpp @@ -358,6 +358,7 @@ common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArg event.displayId = args.displayId; event.downTime = args.downTime; event.eventTime = args.eventTime; + event.deviceTimestamp = 0; event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK); event.actionIndex = getActionIndex(args.action); event.actionButton = getActionButton(args.actionButton); @@ -375,7 +376,6 @@ common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArg event.pointerProperties = pointerProperties; event.pointerCoords = pointerCoords; - event.deviceTimestamp = args.deviceTimestamp; event.frames = convertVideoFrames(args.videoFrames); return event; diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 423b69cff3..de639772a8 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -21,6 +21,7 @@ #include "InputListener.h" #include <android/log.h> +#include <math.h> namespace android { @@ -87,21 +88,32 @@ void NotifyKeyArgs::notify(const sp<InputListenerInterface>& listener) const { // --- NotifyMotionArgs --- -NotifyMotionArgs::NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, - uint32_t source, int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, - int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xPrecision, float yPrecision, nsecs_t downTime, - const std::vector<TouchVideoFrame>& videoFrames) : - NotifyArgs(sequenceNum, eventTime), deviceId(deviceId), source(source), - displayId(displayId), policyFlags(policyFlags), - action(action), actionButton(actionButton), - flags(flags), metaState(metaState), buttonState(buttonState), - classification(classification), edgeFlags(edgeFlags), deviceTimestamp(deviceTimestamp), +NotifyMotionArgs::NotifyMotionArgs( + uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, + int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xPrecision, float yPrecision, + float xCursorPosition, float yCursorPosition, nsecs_t downTime, + const std::vector<TouchVideoFrame>& videoFrames) + : NotifyArgs(sequenceNum, eventTime), + deviceId(deviceId), + source(source), + displayId(displayId), + policyFlags(policyFlags), + action(action), + actionButton(actionButton), + flags(flags), + metaState(metaState), + buttonState(buttonState), + classification(classification), + edgeFlags(edgeFlags), pointerCount(pointerCount), - xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime), + xPrecision(xPrecision), + yPrecision(yPrecision), + xCursorPosition(xCursorPosition), + yCursorPosition(yCursorPosition), + downTime(downTime), videoFrames(videoFrames) { for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); @@ -109,14 +121,25 @@ NotifyMotionArgs::NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int3 } } -NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) : - NotifyArgs(other.sequenceNum, other.eventTime), deviceId(other.deviceId), - source(other.source), displayId(other.displayId), policyFlags(other.policyFlags), - action(other.action), actionButton(other.actionButton), flags(other.flags), - metaState(other.metaState), buttonState(other.buttonState), - classification(other.classification), edgeFlags(other.edgeFlags), - deviceTimestamp(other.deviceTimestamp), pointerCount(other.pointerCount), - xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime), +NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) + : NotifyArgs(other.sequenceNum, other.eventTime), + deviceId(other.deviceId), + source(other.source), + displayId(other.displayId), + policyFlags(other.policyFlags), + action(other.action), + actionButton(other.actionButton), + flags(other.flags), + metaState(other.metaState), + buttonState(other.buttonState), + classification(other.classification), + edgeFlags(other.edgeFlags), + pointerCount(other.pointerCount), + xPrecision(other.xPrecision), + yPrecision(other.yPrecision), + xCursorPosition(other.xCursorPosition), + yCursorPosition(other.yCursorPosition), + downTime(other.downTime), videoFrames(other.videoFrames) { for (uint32_t i = 0; i < pointerCount; i++) { pointerProperties[i].copyFrom(other.pointerProperties[i]); @@ -124,28 +147,23 @@ NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) : } } +static inline bool isCursorPositionEqual(float lhs, float rhs) { + return (isnan(lhs) && isnan(rhs)) || lhs == rhs; +} + bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { - bool equal = - sequenceNum == rhs.sequenceNum - && eventTime == rhs.eventTime - && deviceId == rhs.deviceId - && source == rhs.source - && displayId == rhs.displayId - && policyFlags == rhs.policyFlags - && action == rhs.action - && actionButton == rhs.actionButton - && flags == rhs.flags - && metaState == rhs.metaState - && buttonState == rhs.buttonState - && classification == rhs.classification - && edgeFlags == rhs.edgeFlags - && deviceTimestamp == rhs.deviceTimestamp - && pointerCount == rhs.pointerCount + bool equal = sequenceNum == rhs.sequenceNum && eventTime == rhs.eventTime && + deviceId == rhs.deviceId && source == rhs.source && displayId == rhs.displayId && + policyFlags == rhs.policyFlags && action == rhs.action && + actionButton == rhs.actionButton && flags == rhs.flags && metaState == rhs.metaState && + buttonState == rhs.buttonState && classification == rhs.classification && + edgeFlags == rhs.edgeFlags && + pointerCount == rhs.pointerCount // PointerProperties and PointerCoords are compared separately below - && xPrecision == rhs.xPrecision - && yPrecision == rhs.yPrecision - && downTime == rhs.downTime - && videoFrames == rhs.videoFrames; + && xPrecision == rhs.xPrecision && yPrecision == rhs.yPrecision && + isCursorPositionEqual(xCursorPosition, rhs.xCursorPosition) && + isCursorPositionEqual(yCursorPosition, rhs.yCursorPosition) && + downTime == rhs.downTime && videoFrames == rhs.videoFrames; if (!equal) { return false; } diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 359325faed..e7640dd6af 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -119,10 +119,6 @@ void InputManager::setInputWindows(const std::vector<InputWindowInfo>& infos, } } -void InputManager::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) { - mDispatcher->transferTouchFocus(fromToken, toToken); -} - // Used by tests only. void InputManager::registerInputChannel(const sp<InputChannel>& channel) { IPCThreadState* ipc = IPCThreadState::self(); @@ -132,7 +128,7 @@ void InputManager::registerInputChannel(const sp<InputChannel>& channel) { "from non shell/root entity (PID: %d)", ipc->getCallingPid()); return; } - mDispatcher->registerInputChannel(channel, false); + mDispatcher->registerInputChannel(channel); } void InputManager::unregisterInputChannel(const sp<InputChannel>& channel) { diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index f3da324ba4..40f66d82f4 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -96,7 +96,6 @@ public: virtual void setInputWindows(const std::vector<InputWindowInfo>& handles, const sp<ISetInputWindowsListener>& setInputWindowsListener); - virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken); virtual void registerInputChannel(const sp<InputChannel>& channel); virtual void unregisterInputChannel(const sp<InputChannel>& channel); diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index f48a64551e..0422d8342b 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -49,6 +49,44 @@ bool InputReaderThread::threadLoop() { // --- InputReaderConfiguration --- +std::string InputReaderConfiguration::changesToString(uint32_t changes) { + if (changes == 0) { + return "<none>"; + } + std::string result; + if (changes & CHANGE_POINTER_SPEED) { + result += "POINTER_SPEED | "; + } + if (changes & CHANGE_POINTER_GESTURE_ENABLEMENT) { + result += "POINTER_GESTURE_ENABLEMENT | "; + } + if (changes & CHANGE_DISPLAY_INFO) { + result += "DISPLAY_INFO | "; + } + if (changes & CHANGE_SHOW_TOUCHES) { + result += "SHOW_TOUCHES | "; + } + if (changes & CHANGE_KEYBOARD_LAYOUTS) { + result += "KEYBOARD_LAYOUTS | "; + } + if (changes & CHANGE_DEVICE_ALIAS) { + result += "DEVICE_ALIAS | "; + } + if (changes & CHANGE_TOUCH_AFFINE_TRANSFORMATION) { + result += "TOUCH_AFFINE_TRANSFORMATION | "; + } + if (changes & CHANGE_EXTERNAL_STYLUS_PRESENCE) { + result += "EXTERNAL_STYLUS_PRESENCE | "; + } + if (changes & CHANGE_ENABLED_STATE) { + result += "ENABLED_STATE | "; + } + if (changes & CHANGE_MUST_REOPEN) { + result += "MUST_REOPEN | "; + } + return result; +} + std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByUniqueId( const std::string& uniqueDisplayId) const { if (uniqueDisplayId.empty()) { @@ -76,8 +114,10 @@ std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportByTyp std::optional<DisplayViewport> result = std::nullopt; for (const DisplayViewport& currentViewport : mDisplays) { // Return the first match - if (currentViewport.type == type && !result) { - result = std::make_optional(currentViewport); + if (currentViewport.type == type) { + if (!result) { + result = std::make_optional(currentViewport); + } count++; } } diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index b8c3a808f1..9185e00272 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -34,6 +34,7 @@ cc_library_static { "libinputreporter", "libinputflinger_base", "liblog", + "libstatslog", "libui", "libutils", ], diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index ef7f6509e4..6f82f4fd10 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -52,13 +52,13 @@ const char* Connection::getStatusLabel() const { } } -DispatchEntry* Connection::findWaitQueueEntry(uint32_t seq) { - for (DispatchEntry* entry = waitQueue.head; entry != nullptr; entry = entry->next) { - if (entry->seq == seq) { - return entry; +std::deque<DispatchEntry*>::iterator Connection::findWaitQueueEntry(uint32_t seq) { + for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) { + if ((*it)->seq == seq) { + return it; } } - return nullptr; + return waitQueue.end(); } } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index ed4eebdff4..8423010502 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -18,7 +18,6 @@ #define _UI_INPUT_INPUTDISPATCHER_CONNECTION_H #include "InputState.h" -#include "Queue.h" #include <input/InputTransport.h> #include <deque> @@ -53,11 +52,11 @@ public: bool inputPublisherBlocked; // Queue of events that need to be published to the connection. - Queue<DispatchEntry> outboundQueue; + std::deque<DispatchEntry*> outboundQueue; // Queue of events that have been published to the connection but that have not // yet received a "finished" response from the application. - Queue<DispatchEntry> waitQueue; + std::deque<DispatchEntry*> waitQueue; explicit Connection(const sp<InputChannel>& inputChannel, bool monitor); @@ -66,7 +65,7 @@ public: const std::string getWindowName() const; const char* getStatusLabel() const; - DispatchEntry* findWaitQueueEntry(uint32_t seq); + std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq); }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 8d05640b38..930c7c77e7 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -18,10 +18,12 @@ #include "Connection.h" +#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <cutils/atomic.h> #include <inttypes.h> +using android::base::GetBoolProperty; using android::base::StringPrintf; namespace android::inputdispatcher { @@ -133,7 +135,11 @@ KeyEntry::KeyEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, ui KeyEntry::~KeyEntry() {} void KeyEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("KeyEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " + msg += StringPrintf("KeyEvent"); + if (!GetBoolProperty("ro.debuggable", false)) { + return; + } + msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, " "flags=0x%08x, keyCode=%d, scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", deviceId, source, displayId, keyActionToString(action).c_str(), flags, @@ -155,7 +161,8 @@ MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t device int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, nsecs_t downTime, + int32_t edgeFlags, float xPrecision, float yPrecision, + float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xOffset, float yOffset) : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags), @@ -172,6 +179,8 @@ MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t device edgeFlags(edgeFlags), xPrecision(xPrecision), yPrecision(yPrecision), + xCursorPosition(xCursorPosition), + yCursorPosition(yCursorPosition), downTime(downTime), pointerCount(pointerCount) { for (uint32_t i = 0; i < pointerCount; i++) { @@ -186,15 +195,19 @@ MotionEntry::MotionEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t device MotionEntry::~MotionEntry() {} void MotionEntry::appendDescription(std::string& msg) const { - msg += StringPrintf("MotionEvent(deviceId=%d, source=0x%08x, displayId=%" PRId32 + msg += StringPrintf("MotionEvent"); + if (!GetBoolProperty("ro.debuggable", false)) { + return; + } + msg += StringPrintf("(deviceId=%d, source=0x%08x, displayId=%" PRId32 ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " "buttonState=0x%08x, " "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " - "pointers=[", + "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", deviceId, source, displayId, motionActionToString(action).c_str(), actionButton, flags, metaState, buttonState, motionClassificationToString(classification), edgeFlags, xPrecision, - yPrecision); + yPrecision, xCursorPosition, yCursorPosition); for (uint32_t i = 0; i < pointerCount; i++) { if (i) { diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index b904caf671..28c2799477 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -29,16 +29,10 @@ namespace android::inputdispatcher { -template <typename T> -struct Link { - T* next; - T* prev; +// Sequence number for synthesized or injected events. +constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; -protected: - inline Link() : next(nullptr), prev(nullptr) {} -}; - -struct EventEntry : Link<EventEntry> { +struct EventEntry { enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION }; uint32_t sequenceNum; @@ -50,8 +44,23 @@ struct EventEntry : Link<EventEntry> { bool dispatchInProgress; // initially false, set to true while dispatching + /** + * Injected keys are events from an external (probably untrusted) application + * and are not related to real hardware state. They come in via + * InputDispatcher::injectInputEvent, which sets policy flag POLICY_FLAG_INJECTED. + */ inline bool isInjected() const { return injectionState != nullptr; } + /** + * Synthesized events are either injected events, or events that come + * from real hardware, but aren't directly attributable to a specific hardware event. + * Key repeat is a synthesized event, because it is related to an actual hardware state + * (a key is currently pressed), but the repeat itself is generated by the framework. + */ + inline bool isSynthesized() const { + return isInjected() || sequenceNum == SYNTHESIZED_EVENT_SEQUENCE_NUM; + } + void release(); virtual void appendDescription(std::string& msg) const = 0; @@ -128,6 +137,8 @@ struct MotionEntry : EventEntry { int32_t edgeFlags; float xPrecision; float yPrecision; + float xCursorPosition; + float yCursorPosition; nsecs_t downTime; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; @@ -137,9 +148,9 @@ struct MotionEntry : EventEntry { int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification, int32_t edgeFlags, float xPrecision, - float yPrecision, nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset); + float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, + uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xOffset, float yOffset); virtual void appendDescription(std::string& msg) const; protected: @@ -147,7 +158,7 @@ protected: }; // Tracks the progress of dispatching a particular event to a particular connection. -struct DispatchEntry : Link<DispatchEntry> { +struct DispatchEntry { const uint32_t seq; // unique sequence number, never 0 EventEntry* eventEntry; // the event to dispatch @@ -195,10 +206,10 @@ class InputDispatcher; // // Commands are implicitly 'LockedInterruptible'. struct CommandEntry; -typedef void (InputDispatcher::*Command)(CommandEntry* commandEntry); +typedef std::function<void(InputDispatcher&, CommandEntry*)> Command; class Connection; -struct CommandEntry : Link<CommandEntry> { +struct CommandEntry { explicit CommandEntry(Command command); ~CommandEntry(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 44100086c4..ee8c344a96 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -35,7 +35,7 @@ #define DEBUG_INJECTION 0 // Log debug messages about input focus tracking. -#define DEBUG_FOCUS 0 +static constexpr bool DEBUG_FOCUS = false; // Log debug messages about the app switch latency optimization. #define DEBUG_APP_SWITCH 0 @@ -50,9 +50,11 @@ #include <errno.h> #include <inttypes.h> #include <limits.h> +#include <statslog.h> #include <stddef.h> #include <time.h> #include <unistd.h> +#include <queue> #include <sstream> #include <android-base/chrono_utils.h> @@ -203,10 +205,39 @@ static void dumpRegion(std::string& dump, const Region& region) { } } -template <typename T, typename U> -static T getValueByKey(std::unordered_map<U, T>& map, U key) { - typename std::unordered_map<U, T>::const_iterator it = map.find(key); - return it != map.end() ? it->second : T{}; +/** + * Find the entry in std::unordered_map by key, and return it. + * If the entry is not found, return a default constructed entry. + * + * Useful when the entries are vectors, since an empty vector will be returned + * if the entry is not found. + * Also useful when the entries are sp<>. If an entry is not found, nullptr is returned. + */ +template <typename K, typename V> +static V getValueByKey(const std::unordered_map<K, V>& map, K key) { + auto it = map.find(key); + return it != map.end() ? it->second : V{}; +} + +/** + * Find the entry in std::unordered_map by value, and remove it. + * If more than one entry has the same value, then all matching + * key-value pairs will be removed. + * + * Return true if at least one value has been removed. + */ +template <typename K, typename V> +static bool removeByValue(std::unordered_map<K, V>& map, const V& value) { + bool removed = false; + for (auto it = map.begin(); it != map.end();) { + if (it->second == value) { + it = map.erase(it); + removed = true; + } else { + it++; + } + } + return removed; } // --- InputDispatcher --- @@ -240,8 +271,9 @@ InputDispatcher::~InputDispatcher() { drainInboundQueueLocked(); } - while (mConnectionsByFd.size() != 0) { - unregisterInputChannel(mConnectionsByFd.valueAt(0)->inputChannel); + while (!mConnectionsByFd.empty()) { + sp<Connection> connection = mConnectionsByFd.begin()->second; + unregisterInputChannel(connection->inputChannel); } } @@ -282,9 +314,9 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // If dispatching is frozen, do not process timeouts or try to deliver any new events. if (mDispatchFrozen) { -#if DEBUG_FOCUS - ALOGD("Dispatch frozen. Waiting some more."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Dispatch frozen. Waiting some more."); + } return; } @@ -299,7 +331,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // Ready to start a new event. // If we don't already have a pending event, go grab one. if (!mPendingEvent) { - if (mInboundQueue.isEmpty()) { + if (mInboundQueue.empty()) { if (isAppSwitchDue) { // The inbound queue is empty so the app switch key we were waiting // for will never arrive. Stop waiting for it. @@ -324,7 +356,8 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } } else { // Inbound queue has at least one entry. - mPendingEvent = mInboundQueue.dequeueAtHead(); + mPendingEvent = mInboundQueue.front(); + mInboundQueue.pop_front(); traceInboundQueueLengthLocked(); } @@ -420,8 +453,8 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { - bool needWake = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(entry); + bool needWake = mInboundQueue.empty(); + mInboundQueue.push_back(entry); traceInboundQueueLengthLocked(); switch (entry->type) { @@ -482,9 +515,10 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { void InputDispatcher::addRecentEventLocked(EventEntry* entry) { entry->refCount += 1; - mRecentQueue.enqueueAtTail(entry); - if (mRecentQueue.count() > RECENT_QUEUE_MAX_SIZE) { - mRecentQueue.dequeueAtHead()->release(); + mRecentQueue.push_back(entry); + if (mRecentQueue.size() > RECENT_QUEUE_MAX_SIZE) { + mRecentQueue.front()->release(); + mRecentQueue.pop_front(); } } @@ -643,35 +677,33 @@ bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) { } bool InputDispatcher::haveCommandsLocked() const { - return !mCommandQueue.isEmpty(); + return !mCommandQueue.empty(); } bool InputDispatcher::runCommandsLockedInterruptible() { - if (mCommandQueue.isEmpty()) { + if (mCommandQueue.empty()) { return false; } do { - CommandEntry* commandEntry = mCommandQueue.dequeueAtHead(); - + std::unique_ptr<CommandEntry> commandEntry = std::move(mCommandQueue.front()); + mCommandQueue.pop_front(); Command command = commandEntry->command; - (this->*command)(commandEntry); // commands are implicitly 'LockedInterruptible' + command(*this, commandEntry.get()); // commands are implicitly 'LockedInterruptible' commandEntry->connection.clear(); - delete commandEntry; - } while (!mCommandQueue.isEmpty()); + } while (!mCommandQueue.empty()); return true; } -CommandEntry* InputDispatcher::postCommandLocked(Command command) { - CommandEntry* commandEntry = new CommandEntry(command); - mCommandQueue.enqueueAtTail(commandEntry); - return commandEntry; +void InputDispatcher::postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) { + mCommandQueue.push_back(std::move(commandEntry)); } void InputDispatcher::drainInboundQueueLocked() { - while (!mInboundQueue.isEmpty()) { - EventEntry* entry = mInboundQueue.dequeueAtHead(); + while (!mInboundQueue.empty()) { + EventEntry* entry = mInboundQueue.front(); + mInboundQueue.pop_front(); releaseInboundEventLocked(entry); } traceInboundQueueLengthLocked(); @@ -750,9 +782,10 @@ bool InputDispatcher::dispatchConfigurationChangedLocked(nsecs_t currentTime, resetKeyRepeatLocked(); // Enqueue a command to run outside the lock to tell the policy that the configuration changed. - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); + std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( + &InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); commandEntry->eventTime = entry->eventTime; + postCommandLocked(std::move(commandEntry)); return true; } @@ -821,7 +854,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, // Give the policy a chance to intercept the key. if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { if (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) { - CommandEntry* commandEntry = postCommandLocked( + std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( &InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); sp<InputWindowHandle> focusedWindowHandle = getValueByKey(mFocusedWindowHandlesByDisplay, getTargetDisplayId(entry)); @@ -829,6 +862,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, commandEntry->inputChannel = getInputChannelLocked(focusedWindowHandle->getToken()); } commandEntry->keyEntry = entry; + postCommandLocked(std::move(commandEntry)); entry->refCount += 1; return false; // wait for the command to run } else { @@ -1004,16 +1038,15 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event pokeUserActivityLocked(eventEntry); for (const InputTarget& inputTarget : inputTargets) { - ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); + sp<Connection> connection = getConnectionLocked(inputTarget.inputChannel); + if (connection != nullptr) { prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } else { -#if DEBUG_FOCUS - ALOGD("Dropping event delivery to target with channel '%s' because it " - "is no longer registered with the input dispatcher.", - inputTarget.inputChannel->getName().c_str()); -#endif + if (DEBUG_FOCUS) { + ALOGD("Dropping event delivery to target with channel '%s' because it " + "is no longer registered with the input dispatcher.", + inputTarget.inputChannel->getName().c_str()); + } } } } @@ -1024,9 +1057,9 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( const sp<InputWindowHandle>& windowHandle, nsecs_t* nextWakeupTime, const char* reason) { if (applicationHandle == nullptr && windowHandle == nullptr) { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { -#if DEBUG_FOCUS - ALOGD("Waiting for system to become ready for input. Reason: %s", reason); -#endif + if (DEBUG_FOCUS) { + ALOGD("Waiting for system to become ready for input. Reason: %s", reason); + } mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; mInputTargetWaitStartTime = currentTime; mInputTargetWaitTimeoutTime = LONG_LONG_MAX; @@ -1035,10 +1068,10 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked( } } else { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { -#if DEBUG_FOCUS - ALOGD("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); -#endif + if (DEBUG_FOCUS) { + ALOGD("Waiting for application to become ready for input: %s. Reason: %s", + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); + } nsecs_t timeout; if (windowHandle != nullptr) { timeout = windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); @@ -1102,21 +1135,18 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked( mInputTargetWaitTimeoutExpired = true; // Input state will not be realistic. Mark it out of sync. - if (inputChannel.get()) { - ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); - sp<IBinder> token = connection->inputChannel->getToken(); - - if (token != nullptr) { - removeWindowByTokenLocked(token); - } + sp<Connection> connection = getConnectionLocked(inputChannel); + if (connection != nullptr) { + sp<IBinder> token = connection->inputChannel->getToken(); - if (connection->status == Connection::STATUS_NORMAL) { - CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, - "application not responding"); - synthesizeCancelationEventsForConnectionLocked(connection, options); - } + if (token != nullptr) { + removeWindowByTokenLocked(token); + } + + if (connection->status == Connection::STATUS_NORMAL) { + CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, + "application not responding"); + synthesizeCancelationEventsForConnectionLocked(connection, options); } } } @@ -1130,9 +1160,9 @@ nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationLocked(nsecs_t current } void InputDispatcher::resetANRTimeoutsLocked() { -#if DEBUG_FOCUS - ALOGD("Resetting ANR timeouts."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Resetting ANR timeouts."); + } // Reset input target wait timeout. mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; @@ -1186,10 +1216,8 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, handleTargetsNotReadyLocked(currentTime, entry, focusedApplicationHandle, nullptr, nextWakeupTime, "Waiting because no window has focus but there is " - "a " - "focused application that may eventually add a " - "window " - "when it finishes starting up."); + "a focused application that may eventually add a " + "window when it finishes starting up."); goto Unresponsive; } @@ -1226,11 +1254,11 @@ Failed: Unresponsive: nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); -#if DEBUG_FOCUS - ALOGD("findFocusedWindow finished: injectionResult=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, timeSpentWaitingForApplication / 1000000.0); -#endif + if (DEBUG_FOCUS) { + ALOGD("findFocusedWindow finished: injectionResult=%d, " + "timeSpentWaitingForApplication=%0.1fms", + injectionResult, timeSpentWaitingForApplication / 1000000.0); + } return injectionResult; } @@ -1276,15 +1304,16 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction); + const bool isFromMouse = entry->source == AINPUT_SOURCE_MOUSE; bool wrongDevice = false; if (newGesture) { bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN; if (switchedDevice && mTempTouchState.down && !down && !isHoverAction) { -#if DEBUG_FOCUS - ALOGD("Dropping event because a pointer for a different device is already down " - "in display %" PRId32, - displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Dropping event because a pointer for a different device is already down " + "in display %" PRId32, + displayId); + } // TODO: test multiple simultaneous input streams. injectionResult = INPUT_EVENT_INJECTION_FAILED; switchedDevice = false; @@ -1298,11 +1327,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, mTempTouchState.displayId = displayId; isSplit = false; } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) { -#if DEBUG_FOCUS - ALOGI("Dropping move event because a pointer for a different device is already active " - "in display %" PRId32, - displayId); -#endif + if (DEBUG_FOCUS) { + ALOGI("Dropping move event because a pointer for a different device is already active " + "in display %" PRId32, + displayId); + } // TODO: test multiple simultaneous input streams. injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; switchedDevice = false; @@ -1313,9 +1342,17 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ + int32_t x; + int32_t y; int32_t pointerIndex = getMotionEventActionPointerIndex(action); - int32_t x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); + // Always dispatch mouse events to cursor position. + if (isFromMouse) { + x = int32_t(entry->xCursorPosition); + y = int32_t(entry->yCursorPosition); + } else { + x = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X)); + y = int32_t(entry->pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)); + } bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN; sp<InputWindowHandle> newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, isDown /*addOutsideTargets*/, @@ -1328,8 +1365,8 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Figure out whether splitting will be allowed for this window. if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->supportsSplitTouch()) { - // New window supports splitting. - isSplit = true; + // New window supports splitting, but we should never split mouse events. + isSplit = !isFromMouse; } else if (isSplit) { // New window does not support splitting but we have already split events. // Ignore the new window. @@ -1384,11 +1421,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // If the pointer is not currently down, then ignore the event. if (!mTempTouchState.down) { -#if DEBUG_FOCUS - ALOGD("Dropping event because the pointer is not down or we previously " - "dropped the pointer down event in display %" PRId32, - displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Dropping event because the pointer is not down or we previously " + "dropped the pointer down event in display %" PRId32, + displayId); + } injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } @@ -1405,11 +1442,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, findTouchedWindowAtLocked(displayId, x, y); if (oldTouchedWindowHandle != newTouchedWindowHandle && oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) { -#if DEBUG_FOCUS - ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, - oldTouchedWindowHandle->getName().c_str(), - newTouchedWindowHandle->getName().c_str(), displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32, + oldTouchedWindowHandle->getName().c_str(), + newTouchedWindowHandle->getName().c_str(), displayId); + } // Make a slippery exit from the old window. mTempTouchState.addOrUpdateWindow(oldTouchedWindowHandle, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, @@ -1478,11 +1515,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, } bool hasGestureMonitor = !mTempTouchState.gestureMonitors.empty(); if (!haveForegroundWindow && !hasGestureMonitor) { -#if DEBUG_FOCUS - ALOGD("Dropping event because there is no touched foreground window in display %" PRId32 - " or gesture monitor to receive it.", - displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Dropping event because there is no touched foreground window in display " + "%" PRId32 " or gesture monitor to receive it.", + displayId); + } injectionResult = INPUT_EVENT_INJECTION_FAILED; goto Failed; } @@ -1586,18 +1623,19 @@ Failed: if (injectionPermission == INJECTION_PERMISSION_GRANTED) { if (!wrongDevice) { if (switchedDevice) { -#if DEBUG_FOCUS - ALOGD("Conflicting pointer actions: Switched to a different device."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Switched to a different device."); + } *outConflictingPointerActions = true; } if (isHoverAction) { // Started hovering, therefore no longer down. if (oldState && oldState->down) { -#if DEBUG_FOCUS - ALOGD("Conflicting pointer actions: Hover received while pointer was down."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Hover received while pointer was " + "down."); + } *outConflictingPointerActions = true; } mTempTouchState.reset(); @@ -1614,9 +1652,9 @@ Failed: } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { // First pointer went down. if (oldState && oldState->down) { -#if DEBUG_FOCUS - ALOGD("Conflicting pointer actions: Down received while already down."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Conflicting pointer actions: Down received while already down."); + } *outConflictingPointerActions = true; } } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { @@ -1657,9 +1695,9 @@ Failed: mLastHoverWindowHandle = newHoverWindowHandle; } } else { -#if DEBUG_FOCUS - ALOGD("Not updating touch focus because injection was denied."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Not updating touch focus because injection was denied."); + } } Unresponsive: @@ -1668,11 +1706,11 @@ Unresponsive: nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); -#if DEBUG_FOCUS - ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " - "timeSpentWaitingForApplication=%0.1fms", - injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); -#endif + if (DEBUG_FOCUS) { + ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " + "timeSpentWaitingForApplication=%0.1fms", + injectionResult, injectionPermission, timeSpentWaitingForApplication / 1000000.0); + } return injectionResult; } @@ -1790,18 +1828,16 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked( } // If the window's connection is not registered then keep waiting. - ssize_t connectionIndex = - getConnectionIndexLocked(getInputChannelLocked(windowHandle->getToken())); - if (connectionIndex < 0) { + sp<Connection> connection = + getConnectionLocked(getInputChannelLocked(windowHandle->getToken())); + if (connection == nullptr) { return StringPrintf("Waiting because the %s window's input channel is not " "registered with the input dispatcher. The window may be in the " - "process " - "of being removed.", + "process of being removed.", targetType); } // If the connection is dead then keep waiting. - sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); if (connection->status != Connection::STATUS_NORMAL) { return StringPrintf("Waiting because the %s window's input connection is %s." "The window may be in the process of being removed.", @@ -1811,9 +1847,9 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked( // If the connection is backed up then keep waiting. if (connection->inputPublisherBlocked) { return StringPrintf("Waiting because the %s window's input channel is full. " - "Outbound queue length: %d. Wait queue length: %d.", - targetType, connection->outboundQueue.count(), - connection->waitQueue.count()); + "Outbound queue length: %zu. Wait queue length: %zu.", + targetType, connection->outboundQueue.size(), + connection->waitQueue.size()); } // Ensure that the dispatch queues aren't too far backed up for this event. @@ -1829,13 +1865,13 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked( // often anticipate pending UI changes when typing on a keyboard. // To obtain this behavior, we must serialize key events with respect to all // prior input events. - if (!connection->outboundQueue.isEmpty() || !connection->waitQueue.isEmpty()) { + if (!connection->outboundQueue.empty() || !connection->waitQueue.empty()) { return StringPrintf("Waiting to send key event because the %s window has not " "finished processing all of the input events that were previously " - "delivered to it. Outbound queue length: %d. Wait queue length: " - "%d.", - targetType, connection->outboundQueue.count(), - connection->waitQueue.count()); + "delivered to it. Outbound queue length: %zu. Wait queue length: " + "%zu.", + targetType, connection->outboundQueue.size(), + connection->waitQueue.size()); } } else { // Touch events can always be sent to a window immediately because the user intended @@ -1853,16 +1889,17 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked( // The one case where we pause input event delivery is when the wait queue is piling // up with lots of events because the application is not responding. // This condition ensures that ANRs are detected reliably. - if (!connection->waitQueue.isEmpty() && - currentTime >= connection->waitQueue.head->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) { + if (!connection->waitQueue.empty() && + currentTime >= + connection->waitQueue.front()->deliveryTime + STREAM_AHEAD_EVENT_TIMEOUT) { return StringPrintf("Waiting to send non-key event because the %s window has not " "finished processing certain input events that were delivered to " "it over " - "%0.1fms ago. Wait queue length: %d. Wait queue head age: " + "%0.1fms ago. Wait queue length: %zu. Wait queue head age: " "%0.1fms.", targetType, STREAM_AHEAD_EVENT_TIMEOUT * 0.000001f, - connection->waitQueue.count(), - (currentTime - connection->waitQueue.head->deliveryTime) * + connection->waitQueue.size(), + (currentTime - connection->waitQueue.front()->deliveryTime) * 0.000001f); } } @@ -1925,10 +1962,11 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { } } - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doPokeUserActivityLockedInterruptible); + std::unique_ptr<CommandEntry> commandEntry = + std::make_unique<CommandEntry>(&InputDispatcher::doPokeUserActivityLockedInterruptible); commandEntry->eventTime = eventEntry->eventTime; commandEntry->userActivityEventType = eventType; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, @@ -1971,10 +2009,11 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, if (!splitMotionEntry) { return; // split event was dropped } -#if DEBUG_FOCUS - ALOGD("channel '%s' ~ Split motion event.", connection->getInputChannelName().c_str()); - logOutboundMotionDetails(" ", splitMotionEntry); -#endif + if (DEBUG_FOCUS) { + ALOGD("channel '%s' ~ Split motion event.", + connection->getInputChannelName().c_str()); + logOutboundMotionDetails(" ", splitMotionEntry); + } enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget); splitMotionEntry->release(); return; @@ -1997,7 +2036,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, ATRACE_NAME(message.c_str()); } - bool wasEmpty = connection->outboundQueue.isEmpty(); + bool wasEmpty = connection->outboundQueue.empty(); // Enqueue dispatch entries for the requested modes. enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, @@ -2014,7 +2053,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); // If the outbound queue was previously empty, start the dispatch cycle going. - if (wasEmpty && !connection->outboundQueue.isEmpty()) { + if (wasEmpty && !connection->outboundQueue.empty()) { startDispatchCycleLocked(currentTime, connection); } } @@ -2120,7 +2159,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio } // Enqueue the dispatch entry. - connection->outboundQueue.enqueueAtTail(dispatchEntry); + connection->outboundQueue.push_back(dispatchEntry); traceOutboundQueueLength(connection); } @@ -2146,9 +2185,10 @@ void InputDispatcher::dispatchPointerDownOutsideFocus(uint32_t source, int32_t a return; } - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); + std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( + &InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible); commandEntry->newToken = newToken; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, @@ -2162,9 +2202,8 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str()); #endif - while (connection->status == Connection::STATUS_NORMAL && - !connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.head; + while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) { + DispatchEntry* dispatchEntry = connection->outboundQueue.front(); dispatchEntry->deliveryTime = currentTime; // Publish the event. @@ -2232,10 +2271,13 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, motionEntry->buttonState, motionEntry->classification, xOffset, yOffset, motionEntry->xPrecision, - motionEntry->yPrecision, motionEntry->downTime, - motionEntry->eventTime, + motionEntry->yPrecision, + motionEntry->xCursorPosition, + motionEntry->yCursorPosition, + motionEntry->downTime, motionEntry->eventTime, motionEntry->pointerCount, motionEntry->pointerProperties, usingCoords); + reportTouchEventForStatistics(*motionEntry); break; } @@ -2247,7 +2289,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // Check the result. if (status) { if (status == WOULD_BLOCK) { - if (connection->waitQueue.isEmpty()) { + if (connection->waitQueue.empty()) { ALOGE("channel '%s' ~ Could not publish event because the pipe is full. " "This is unexpected because the wait queue is empty, so the pipe " "should be empty and we shouldn't have any problems writing an " @@ -2274,9 +2316,11 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } // Re-enqueue the event on the wait queue. - connection->outboundQueue.dequeue(dispatchEntry); + connection->outboundQueue.erase(std::remove(connection->outboundQueue.begin(), + connection->outboundQueue.end(), + dispatchEntry)); traceOutboundQueueLength(connection); - connection->waitQueue.enqueueAtTail(dispatchEntry); + connection->waitQueue.push_back(dispatchEntry); traceWaitQueueLength(connection); } } @@ -2309,9 +2353,9 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, #endif // Clear the dispatch queues. - drainDispatchQueue(&connection->outboundQueue); + drainDispatchQueue(connection->outboundQueue); traceOutboundQueueLength(connection); - drainDispatchQueue(&connection->waitQueue); + drainDispatchQueue(connection->waitQueue); traceWaitQueueLength(connection); // The connection appears to be unrecoverably broken. @@ -2326,9 +2370,10 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, } } -void InputDispatcher::drainDispatchQueue(Queue<DispatchEntry>* queue) { - while (!queue->isEmpty()) { - DispatchEntry* dispatchEntry = queue->dequeueAtHead(); +void InputDispatcher::drainDispatchQueue(std::deque<DispatchEntry*>& queue) { + while (!queue.empty()) { + DispatchEntry* dispatchEntry = queue.front(); + queue.pop_front(); releaseDispatchEntry(dispatchEntry); } } @@ -2346,8 +2391,7 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { { // acquire lock std::scoped_lock _l(d->mLock); - ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd); - if (connectionIndex < 0) { + if (d->mConnectionsByFd.find(fd) == d->mConnectionsByFd.end()) { ALOGE("Received spurious receive callback for unknown input channel. " "fd=%d, events=0x%x", fd, events); @@ -2355,7 +2399,7 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { } bool notify; - sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex); + sp<Connection> connection = d->mConnectionsByFd[fd]; if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) { if (!(events & ALOOPER_EVENT_INPUT)) { ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " @@ -2409,8 +2453,8 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( const CancelationOptions& options) { - for (size_t i = 0; i < mConnectionsByFd.size(); i++) { - synthesizeCancelationEventsForConnectionLocked(mConnectionsByFd.valueAt(i), options); + for (const auto& pair : mConnectionsByFd) { + synthesizeCancelationEventsForConnectionLocked(pair.second, options); } } @@ -2433,10 +2477,12 @@ void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( const sp<InputChannel>& channel, const CancelationOptions& options) { - ssize_t index = getConnectionIndexLocked(channel); - if (index >= 0) { - synthesizeCancelationEventsForConnectionLocked(mConnectionsByFd.valueAt(index), options); + sp<Connection> connection = getConnectionLocked(channel); + if (connection == nullptr) { + return; } + + synthesizeCancelationEventsForConnectionLocked(connection, options); } void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( @@ -2573,8 +2619,9 @@ MotionEntry* InputDispatcher::splitMotionEvent(const MotionEntry* originalMotion originalMotionEntry->metaState, originalMotionEntry->buttonState, originalMotionEntry->classification, originalMotionEntry->edgeFlags, originalMotionEntry->xPrecision, originalMotionEntry->yPrecision, - originalMotionEntry->downTime, splitPointerCount, - splitPointerProperties, splitPointerCoords, 0, 0); + originalMotionEntry->xCursorPosition, + originalMotionEntry->yCursorPosition, originalMotionEntry->downTime, + splitPointerCount, splitPointerProperties, splitPointerCoords, 0, 0); if (originalMotionEntry->injectionState) { splitMotionEntry->injectionState = originalMotionEntry->injectionState; @@ -2720,11 +2767,13 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("notifyMotion - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x," - "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, + "action=0x%x, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, " + "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, " + "yCursorPosition=%f, downTime=%" PRId64, args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, args->action, args->actionButton, args->flags, args->metaState, args->buttonState, - args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime); + args->edgeFlags, args->xPrecision, args->yPrecision, args->xCursorPosition, + args->yCursorPosition, args->downTime); for (uint32_t i = 0; i < args->pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " "x=%f, y=%f, pressure=%f, size=%f, " @@ -2768,7 +2817,8 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { event.initialize(args->deviceId, args->source, args->displayId, args->action, args->actionButton, args->flags, args->edgeFlags, args->metaState, args->buttonState, args->classification, 0, 0, args->xPrecision, - args->yPrecision, args->downTime, args->eventTime, args->pointerCount, + args->yPrecision, args->xCursorPosition, args->yCursorPosition, + args->downTime, args->eventTime, args->pointerCount, args->pointerProperties, args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; @@ -2785,8 +2835,9 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->displayId, policyFlags, args->action, args->actionButton, args->flags, args->metaState, args->buttonState, args->classification, args->edgeFlags, args->xPrecision, - args->yPrecision, args->downTime, args->pointerCount, - args->pointerProperties, args->pointerCoords, 0, 0); + args->yPrecision, args->xCursorPosition, args->yCursorPosition, + args->downTime, args->pointerCount, args->pointerProperties, + args->pointerCoords, 0, 0); needWake = enqueueInboundEventLocked(newEntry); mLock.unlock(); @@ -2849,8 +2900,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec policyFlags |= POLICY_FLAG_TRUSTED; } - EventEntry* firstInjectedEntry; - EventEntry* lastInjectedEntry; + std::queue<EventEntry*> injectedEntries; switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: { KeyEvent keyEvent; @@ -2884,13 +2934,14 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec } mLock.lock(); - firstInjectedEntry = new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, - keyEvent.getEventTime(), keyEvent.getDeviceId(), - keyEvent.getSource(), keyEvent.getDisplayId(), - policyFlags, action, flags, keyEvent.getKeyCode(), - keyEvent.getScanCode(), keyEvent.getMetaState(), - keyEvent.getRepeatCount(), keyEvent.getDownTime()); - lastInjectedEntry = firstInjectedEntry; + KeyEntry* injectedEntry = + new KeyEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, keyEvent.getEventTime(), + keyEvent.getDeviceId(), keyEvent.getSource(), + keyEvent.getDisplayId(), policyFlags, action, flags, + keyEvent.getKeyCode(), keyEvent.getScanCode(), + keyEvent.getMetaState(), keyEvent.getRepeatCount(), + keyEvent.getDownTime()); + injectedEntries.push(injectedEntry); break; } @@ -2918,17 +2969,20 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec mLock.lock(); const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); - firstInjectedEntry = + MotionEntry* injectedEntry = new MotionEntry(SYNTHESIZED_EVENT_SEQUENCE_NUM, *sampleEventTimes, motionEvent->getDeviceId(), motionEvent->getSource(), motionEvent->getDisplayId(), policyFlags, action, actionButton, motionEvent->getFlags(), motionEvent->getMetaState(), motionEvent->getButtonState(), motionEvent->getClassification(), motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), - motionEvent->getYPrecision(), motionEvent->getDownTime(), - uint32_t(pointerCount), pointerProperties, samplePointerCoords, + motionEvent->getYPrecision(), + motionEvent->getRawXCursorPosition(), + motionEvent->getRawYCursorPosition(), + motionEvent->getDownTime(), uint32_t(pointerCount), + pointerProperties, samplePointerCoords, motionEvent->getXOffset(), motionEvent->getYOffset()); - lastInjectedEntry = firstInjectedEntry; + injectedEntries.push(injectedEntry); for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { sampleEventTimes += 1; samplePointerCoords += pointerCount; @@ -2940,12 +2994,13 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec motionEvent->getMetaState(), motionEvent->getButtonState(), motionEvent->getClassification(), motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), - motionEvent->getYPrecision(), motionEvent->getDownTime(), - uint32_t(pointerCount), pointerProperties, - samplePointerCoords, motionEvent->getXOffset(), - motionEvent->getYOffset()); - lastInjectedEntry->next = nextInjectedEntry; - lastInjectedEntry = nextInjectedEntry; + motionEvent->getYPrecision(), + motionEvent->getRawXCursorPosition(), + motionEvent->getRawYCursorPosition(), + motionEvent->getDownTime(), uint32_t(pointerCount), + pointerProperties, samplePointerCoords, + motionEvent->getXOffset(), motionEvent->getYOffset()); + injectedEntries.push(nextInjectedEntry); } break; } @@ -2961,13 +3016,12 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, int32_t injec } injectionState->refCount += 1; - lastInjectedEntry->injectionState = injectionState; + injectedEntries.back()->injectionState = injectionState; bool needWake = false; - for (EventEntry* entry = firstInjectedEntry; entry != nullptr;) { - EventEntry* nextEntry = entry->next; - needWake |= enqueueInboundEventLocked(entry); - entry = nextEntry; + while (!injectedEntries.empty()) { + needWake |= enqueueInboundEventLocked(injectedEntries.front()); + injectedEntries.pop(); } mLock.unlock(); @@ -3093,14 +3147,7 @@ void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) { std::vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked( int32_t displayId) const { - std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>::const_iterator it = - mWindowHandlesByDisplay.find(displayId); - if (it != mWindowHandlesByDisplay.end()) { - return it->second; - } - - // Return an empty one if nothing found. - return std::vector<sp<InputWindowHandle>>(); + return getValueByKey(mWindowHandlesByDisplay, displayId); } sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( @@ -3142,6 +3189,63 @@ sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token return mInputChannelsByToken.at(token); } +void InputDispatcher::updateWindowHandlesForDisplayLocked( + const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) { + if (inputWindowHandles.empty()) { + // Remove all handles on a display if there are no windows left. + mWindowHandlesByDisplay.erase(displayId); + return; + } + + // Since we compare the pointer of input window handles across window updates, we need + // to make sure the handle object for the same window stays unchanged across updates. + const std::vector<sp<InputWindowHandle>>& oldHandles = getWindowHandlesLocked(displayId); + std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens; + for (const sp<InputWindowHandle>& handle : oldHandles) { + oldHandlesByTokens[handle->getToken()] = handle; + } + + std::vector<sp<InputWindowHandle>> newHandles; + for (const sp<InputWindowHandle>& handle : inputWindowHandles) { + if (!handle->updateInfo()) { + // handle no longer valid + continue; + } + + const InputWindowInfo* info = handle->getInfo(); + if ((getInputChannelLocked(handle->getToken()) == nullptr && + info->portalToDisplayId == ADISPLAY_ID_NONE)) { + const bool noInputChannel = + info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL; + const bool canReceiveInput = + !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) || + !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE); + if (canReceiveInput && !noInputChannel) { + ALOGE("Window handle %s has no registered input channel", + handle->getName().c_str()); + } + continue; + } + + if (info->displayId != displayId) { + ALOGE("Window %s updated by wrong display %d, should belong to display %d", + handle->getName().c_str(), displayId, info->displayId); + continue; + } + + if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { + const sp<InputWindowHandle> oldHandle = oldHandlesByTokens.at(handle->getToken()); + oldHandle->updateFrom(handle); + newHandles.push_back(oldHandle); + } else { + newHandles.push_back(handle); + } + } + + // Insert or replace + mWindowHandlesByDisplay[displayId] = newHandles; +} + /** * Called from InputManagerService, update window handle list by displayId that can receive input. * A window handle contains information about InputChannel, Touch Region, Types, Focused,... @@ -3152,9 +3256,13 @@ sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId, const sp<ISetInputWindowsListener>& setInputWindowsListener) { -#if DEBUG_FOCUS - ALOGD("setInputWindows displayId=%" PRId32, displayId); -#endif + if (DEBUG_FOCUS) { + std::string windowList; + for (const sp<InputWindowHandle>& iwh : inputWindowHandles) { + windowList += iwh->getName() + " "; + } + ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str()); + } { // acquire lock std::scoped_lock _l(mLock); @@ -3162,73 +3270,19 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& const std::vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId); + updateWindowHandlesForDisplayLocked(inputWindowHandles, displayId); + sp<InputWindowHandle> newFocusedWindowHandle = nullptr; bool foundHoveredWindow = false; - - if (inputWindowHandles.empty()) { - // Remove all handles on a display if there are no windows left. - mWindowHandlesByDisplay.erase(displayId); - } else { - // Since we compare the pointer of input window handles across window updates, we need - // to make sure the handle object for the same window stays unchanged across updates. - const std::vector<sp<InputWindowHandle>>& oldHandles = - mWindowHandlesByDisplay[displayId]; - std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens; - for (const sp<InputWindowHandle>& handle : oldHandles) { - oldHandlesByTokens[handle->getToken()] = handle; - } - - std::vector<sp<InputWindowHandle>> newHandles; - for (const sp<InputWindowHandle>& handle : inputWindowHandles) { - if (!handle->updateInfo()) { - // handle no longer valid - continue; - } - const InputWindowInfo* info = handle->getInfo(); - - if ((getInputChannelLocked(handle->getToken()) == nullptr && - info->portalToDisplayId == ADISPLAY_ID_NONE)) { - const bool noInputChannel = - info->inputFeatures & InputWindowInfo::INPUT_FEATURE_NO_INPUT_CHANNEL; - const bool canReceiveInput = - !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_TOUCHABLE) || - !(info->layoutParamsFlags & InputWindowInfo::FLAG_NOT_FOCUSABLE); - if (canReceiveInput && !noInputChannel) { - ALOGE("Window handle %s has no registered input channel", - handle->getName().c_str()); - } - continue; - } - - if (info->displayId != displayId) { - ALOGE("Window %s updated by wrong display %d, should belong to display %d", - handle->getName().c_str(), displayId, info->displayId); - continue; - } - - if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) { - const sp<InputWindowHandle> oldHandle = - oldHandlesByTokens.at(handle->getToken()); - oldHandle->updateFrom(handle); - newHandles.push_back(oldHandle); - } else { - newHandles.push_back(handle); - } + for (const sp<InputWindowHandle>& windowHandle : getWindowHandlesLocked(displayId)) { + // Set newFocusedWindowHandle to the top most focused window instead of the last one + if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && + windowHandle->getInfo()->visible) { + newFocusedWindowHandle = windowHandle; } - - for (const sp<InputWindowHandle>& windowHandle : newHandles) { - // Set newFocusedWindowHandle to the top most focused window instead of the last one - if (!newFocusedWindowHandle && windowHandle->getInfo()->hasFocus && - windowHandle->getInfo()->visible) { - newFocusedWindowHandle = windowHandle; - } - if (windowHandle == mLastHoverWindowHandle) { - foundHoveredWindow = true; - } + if (windowHandle == mLastHoverWindowHandle) { + foundHoveredWindow = true; } - - // Insert or replace - mWindowHandlesByDisplay[displayId] = newHandles; } if (!foundHoveredWindow) { @@ -3240,10 +3294,10 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& if (oldFocusedWindowHandle != newFocusedWindowHandle) { if (oldFocusedWindowHandle != nullptr) { -#if DEBUG_FOCUS - ALOGD("Focus left window: %s in display %" PRId32, - oldFocusedWindowHandle->getName().c_str(), displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Focus left window: %s in display %" PRId32, + oldFocusedWindowHandle->getName().c_str(), displayId); + } sp<InputChannel> focusedInputChannel = getInputChannelLocked(oldFocusedWindowHandle->getToken()); if (focusedInputChannel != nullptr) { @@ -3254,10 +3308,10 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& mFocusedWindowHandlesByDisplay.erase(displayId); } if (newFocusedWindowHandle != nullptr) { -#if DEBUG_FOCUS - ALOGD("Focus entered window: %s in display %" PRId32, - newFocusedWindowHandle->getName().c_str(), displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Focus entered window: %s in display %" PRId32, + newFocusedWindowHandle->getName().c_str(), displayId); + } mFocusedWindowHandlesByDisplay[displayId] = newFocusedWindowHandle; } @@ -3272,10 +3326,10 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& for (size_t i = 0; i < state.windows.size();) { TouchedWindow& touchedWindow = state.windows[i]; if (!hasWindowHandleLocked(touchedWindow.windowHandle)) { -#if DEBUG_FOCUS - ALOGD("Touched window was removed: %s in display %" PRId32, - touchedWindow.windowHandle->getName().c_str(), displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("Touched window was removed: %s in display %" PRId32, + touchedWindow.windowHandle->getName().c_str(), displayId); + } sp<InputChannel> touchedInputChannel = getInputChannelLocked(touchedWindow.windowHandle->getToken()); if (touchedInputChannel != nullptr) { @@ -3297,9 +3351,9 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& // which might not happen until the next GC. for (const sp<InputWindowHandle>& oldWindowHandle : oldWindowHandles) { if (!hasWindowHandleLocked(oldWindowHandle)) { -#if DEBUG_FOCUS - ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); -#endif + if (DEBUG_FOCUS) { + ALOGD("Window went away: %s", oldWindowHandle->getName().c_str()); + } oldWindowHandle->releaseChannel(); } } @@ -3315,9 +3369,10 @@ void InputDispatcher::setInputWindows(const std::vector<sp<InputWindowHandle>>& void InputDispatcher::setFocusedApplication( int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle) { -#if DEBUG_FOCUS - ALOGD("setFocusedApplication displayId=%" PRId32, displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId, + inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>"); + } { // acquire lock std::scoped_lock _l(mLock); @@ -3335,10 +3390,6 @@ void InputDispatcher::setFocusedApplication( oldFocusedApplicationHandle.clear(); mFocusedApplicationHandlesByDisplay.erase(displayId); } - -#if DEBUG_FOCUS - // logDispatchStateLocked(); -#endif } // release lock // Wake up poll loop since it may need to make new input dispatching choices. @@ -3355,9 +3406,9 @@ void InputDispatcher::setFocusedApplication( * display. The display-specified events won't be affected. */ void InputDispatcher::setFocusedDisplay(int32_t displayId) { -#if DEBUG_FOCUS - ALOGD("setFocusedDisplay displayId=%" PRId32, displayId); -#endif + if (DEBUG_FOCUS) { + ALOGD("setFocusedDisplay displayId=%" PRId32, displayId); + } { // acquire lock std::scoped_lock _l(mLock); @@ -3396,9 +3447,9 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { } } -#if DEBUG_FOCUS - logDispatchStateLocked(); -#endif + if (DEBUG_FOCUS) { + logDispatchStateLocked(); + } } // release lock // Wake up poll loop since it may need to make new input dispatching choices. @@ -3406,9 +3457,9 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { } void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { -#if DEBUG_FOCUS - ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); -#endif + if (DEBUG_FOCUS) { + ALOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); + } bool changed; { // acquire lock @@ -3430,9 +3481,9 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { changed = false; } -#if DEBUG_FOCUS - logDispatchStateLocked(); -#endif + if (DEBUG_FOCUS) { + logDispatchStateLocked(); + } } // release lock if (changed) { @@ -3442,9 +3493,9 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { } void InputDispatcher::setInputFilterEnabled(bool enabled) { -#if DEBUG_FOCUS - ALOGD("setInputFilterEnabled: enabled=%d", enabled); -#endif + if (DEBUG_FOCUS) { + ALOGD("setInputFilterEnabled: enabled=%d", enabled); + } { // acquire lock std::scoped_lock _l(mLock); @@ -3463,9 +3514,9 @@ void InputDispatcher::setInputFilterEnabled(bool enabled) { bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) { if (fromToken == toToken) { -#if DEBUG_FOCUS - ALOGD("Trivial transfer to same window."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Trivial transfer to same window."); + } return true; } @@ -3478,14 +3529,14 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< ALOGW("Cannot transfer focus because from or to window not found."); return false; } -#if DEBUG_FOCUS - ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", - fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str()); -#endif + if (DEBUG_FOCUS) { + ALOGD("transferTouchFocus: fromWindowHandle=%s, toWindowHandle=%s", + fromWindowHandle->getName().c_str(), toWindowHandle->getName().c_str()); + } if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) { -#if DEBUG_FOCUS - ALOGD("Cannot transfer focus because windows are on different displays."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Cannot transfer focus because windows are on different displays."); + } return false; } @@ -3513,20 +3564,17 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< Found: if (!found) { -#if DEBUG_FOCUS - ALOGD("Focus transfer failed because from window did not have focus."); -#endif + if (DEBUG_FOCUS) { + ALOGD("Focus transfer failed because from window did not have focus."); + } return false; } sp<InputChannel> fromChannel = getInputChannelLocked(fromToken); sp<InputChannel> toChannel = getInputChannelLocked(toToken); - ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel); - ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel); - if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) { - sp<Connection> fromConnection = mConnectionsByFd.valueAt(fromConnectionIndex); - sp<Connection> toConnection = mConnectionsByFd.valueAt(toConnectionIndex); - + sp<Connection> fromConnection = getConnectionLocked(fromChannel); + sp<Connection> toConnection = getConnectionLocked(toChannel); + if (fromConnection != nullptr && toConnection != nullptr) { fromConnection->inputState.copyPointerStateTo(toConnection->inputState); CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, @@ -3534,9 +3582,9 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< synthesizeCancelationEventsForConnectionLocked(fromConnection, options); } -#if DEBUG_FOCUS - logDispatchStateLocked(); -#endif + if (DEBUG_FOCUS) { + logDispatchStateLocked(); + } } // release lock // Wake up poll loop since it may need to make new input dispatching choices. @@ -3545,9 +3593,9 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< } void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { -#if DEBUG_FOCUS - ALOGD("Resetting and dropping all events (%s).", reason); -#endif + if (DEBUG_FOCUS) { + ALOGD("Resetting and dropping all events (%s).", reason); + } CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, reason); synthesizeCancelationEventsForAllConnectionsLocked(options); @@ -3703,9 +3751,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { nsecs_t currentTime = now(); // Dump recently dispatched or dropped events from oldest to newest. - if (!mRecentQueue.isEmpty()) { - dump += StringPrintf(INDENT "RecentQueue: length=%u\n", mRecentQueue.count()); - for (EventEntry* entry = mRecentQueue.head; entry; entry = entry->next) { + if (!mRecentQueue.empty()) { + dump += StringPrintf(INDENT "RecentQueue: length=%zu\n", mRecentQueue.size()); + for (EventEntry* entry : mRecentQueue) { dump += INDENT2; entry->appendDescription(dump); dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); @@ -3726,9 +3774,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { } // Dump inbound events from oldest to newest. - if (!mInboundQueue.isEmpty()) { - dump += StringPrintf(INDENT "InboundQueue: length=%u\n", mInboundQueue.count()); - for (EventEntry* entry = mInboundQueue.head; entry; entry = entry->next) { + if (!mInboundQueue.empty()) { + dump += StringPrintf(INDENT "InboundQueue: length=%zu\n", mInboundQueue.size()); + for (EventEntry* entry : mInboundQueue) { dump += INDENT2; entry->appendDescription(dump); dump += StringPrintf(", age=%0.1fms\n", (currentTime - entry->eventTime) * 0.000001f); @@ -3749,22 +3797,21 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT "ReplacedKeys: <empty>\n"; } - if (!mConnectionsByFd.isEmpty()) { + if (!mConnectionsByFd.empty()) { dump += INDENT "Connections:\n"; - for (size_t i = 0; i < mConnectionsByFd.size(); i++) { - const sp<Connection>& connection = mConnectionsByFd.valueAt(i); - dump += StringPrintf(INDENT2 "%zu: channelName='%s', windowName='%s', " + for (const auto& pair : mConnectionsByFd) { + const sp<Connection>& connection = pair.second; + dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " "status=%s, monitor=%s, inputPublisherBlocked=%s\n", - i, connection->getInputChannelName().c_str(), + pair.first, connection->getInputChannelName().c_str(), connection->getWindowName().c_str(), connection->getStatusLabel(), toString(connection->monitor), toString(connection->inputPublisherBlocked)); - if (!connection->outboundQueue.isEmpty()) { - dump += StringPrintf(INDENT3 "OutboundQueue: length=%u\n", - connection->outboundQueue.count()); - for (DispatchEntry* entry = connection->outboundQueue.head; entry; - entry = entry->next) { + if (!connection->outboundQueue.empty()) { + dump += StringPrintf(INDENT3 "OutboundQueue: length=%zu\n", + connection->outboundQueue.size()); + for (DispatchEntry* entry : connection->outboundQueue) { dump.append(INDENT4); entry->eventEntry->appendDescription(dump); dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n", @@ -3775,11 +3822,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { dump += INDENT3 "OutboundQueue: <empty>\n"; } - if (!connection->waitQueue.isEmpty()) { - dump += StringPrintf(INDENT3 "WaitQueue: length=%u\n", - connection->waitQueue.count()); - for (DispatchEntry* entry = connection->waitQueue.head; entry; - entry = entry->next) { + if (!connection->waitQueue.empty()) { + dump += StringPrintf(INDENT3 "WaitQueue: length=%zu\n", + connection->waitQueue.size()); + for (DispatchEntry* entry : connection->waitQueue) { dump += INDENT4; entry->eventEntry->appendDescription(dump); dump += StringPrintf(", targetFlags=0x%08x, resolvedAction=%d, " @@ -3819,17 +3865,15 @@ void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor> } } -status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, - int32_t displayId) { +status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) { #if DEBUG_REGISTRATION - ALOGD("channel '%s' ~ registerInputChannel - displayId=%" PRId32, - inputChannel->getName().c_str(), displayId); + ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str()); #endif { // acquire lock std::scoped_lock _l(mLock); - - if (getConnectionIndexLocked(inputChannel) >= 0) { + sp<Connection> existingConnection = getConnectionLocked(inputChannel); + if (existingConnection != nullptr) { ALOGW("Attempted to register already registered input channel '%s'", inputChannel->getName().c_str()); return BAD_VALUE; @@ -3838,7 +3882,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan sp<Connection> connection = new Connection(inputChannel, false /*monitor*/); int fd = inputChannel->getFd(); - mConnectionsByFd.add(fd, connection); + mConnectionsByFd[fd] = connection; mInputChannelsByToken[inputChannel->getToken()] = inputChannel; mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); @@ -3867,7 +3911,7 @@ status_t InputDispatcher::registerInputMonitor(const sp<InputChannel>& inputChan sp<Connection> connection = new Connection(inputChannel, true /*monitor*/); const int fd = inputChannel->getFd(); - mConnectionsByFd.add(fd, connection); + mConnectionsByFd[fd] = connection; mInputChannelsByToken[inputChannel->getToken()] = inputChannel; auto& monitorsByDisplay = @@ -3903,16 +3947,15 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify) { - ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); - if (connectionIndex < 0) { + sp<Connection> connection = getConnectionLocked(inputChannel); + if (connection == nullptr) { ALOGW("Attempted to unregister already unregistered input channel '%s'", inputChannel->getName().c_str()); return BAD_VALUE; } - sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex); - mConnectionsByFd.removeItemsAt(connectionIndex); - + const bool removed = removeByValue(mConnectionsByFd, connection); + ALOG_ASSERT(removed); mInputChannelsByToken.erase(inputChannel->getToken()); if (connection->monitor) { @@ -4012,30 +4055,31 @@ std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked( return std::nullopt; } -ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) { +sp<Connection> InputDispatcher::getConnectionLocked(const sp<InputChannel>& inputChannel) { if (inputChannel == nullptr) { - return -1; + return nullptr; } - for (size_t i = 0; i < mConnectionsByFd.size(); i++) { - sp<Connection> connection = mConnectionsByFd.valueAt(i); + for (const auto& pair : mConnectionsByFd) { + sp<Connection> connection = pair.second; if (connection->inputChannel->getToken() == inputChannel->getToken()) { - return i; + return connection; } } - return -1; + return nullptr; } void InputDispatcher::onDispatchCycleFinishedLocked(nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) { - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doDispatchCycleFinishedLockedInterruptible); + std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( + &InputDispatcher::doDispatchCycleFinishedLockedInterruptible); commandEntry->connection = connection; commandEntry->eventTime = currentTime; commandEntry->seq = seq; commandEntry->handled = handled; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime, @@ -4043,19 +4087,21 @@ void InputDispatcher::onDispatchCycleBrokenLocked(nsecs_t currentTime, ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!", connection->getInputChannelName().c_str()); - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); + std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( + &InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible); commandEntry->connection = connection; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus, const sp<InputWindowHandle>& newFocus) { sp<IBinder> oldToken = oldFocus != nullptr ? oldFocus->getToken() : nullptr; sp<IBinder> newToken = newFocus != nullptr ? newFocus->getToken() : nullptr; - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doNotifyFocusChangedLockedInterruptible); + std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( + &InputDispatcher::doNotifyFocusChangedLockedInterruptible); commandEntry->oldToken = oldToken; commandEntry->newToken = newToken; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::onANRLocked(nsecs_t currentTime, @@ -4086,12 +4132,13 @@ void InputDispatcher::onANRLocked(nsecs_t currentTime, mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason); dumpDispatchStateLocked(mLastANRState); - CommandEntry* commandEntry = - postCommandLocked(&InputDispatcher::doNotifyANRLockedInterruptible); + std::unique_ptr<CommandEntry> commandEntry = + std::make_unique<CommandEntry>(&InputDispatcher::doNotifyANRLockedInterruptible); commandEntry->inputApplicationHandle = applicationHandle; commandEntry->inputChannel = windowHandle != nullptr ? getInputChannelLocked(windowHandle->getToken()) : nullptr; commandEntry->reason = reason; + postCommandLocked(std::move(commandEntry)); } void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) { @@ -4176,53 +4223,58 @@ void InputDispatcher::doOnPointerDownOutsideFocusLockedInterruptible(CommandEntr void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) { sp<Connection> connection = commandEntry->connection; - nsecs_t finishTime = commandEntry->eventTime; + const nsecs_t finishTime = commandEntry->eventTime; uint32_t seq = commandEntry->seq; - bool handled = commandEntry->handled; + const bool handled = commandEntry->handled; // Handle post-event policy actions. - DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq); - if (dispatchEntry) { - nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; - if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - std::string msg = - StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", - connection->getWindowName().c_str(), eventDuration * 0.000001f); - dispatchEntry->eventEntry->appendDescription(msg); - ALOGI("%s", msg.c_str()); - } - - bool restartEvent; - if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { - KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry); - restartEvent = - afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled); - } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { - MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry); - restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, - motionEntry, handled); + std::deque<DispatchEntry*>::iterator dispatchEntryIt = connection->findWaitQueueEntry(seq); + if (dispatchEntryIt == connection->waitQueue.end()) { + return; + } + DispatchEntry* dispatchEntry = *dispatchEntryIt; + + nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; + if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { + std::string msg = + StringPrintf("Window '%s' spent %0.1fms processing the last input event: ", + connection->getWindowName().c_str(), eventDuration * 0.000001f); + dispatchEntry->eventEntry->appendDescription(msg); + ALOGI("%s", msg.c_str()); + } + + bool restartEvent; + if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { + KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry); + restartEvent = + afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled); + } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { + MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry); + restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry, + handled); + } else { + restartEvent = false; + } + + // Dequeue the event and start the next cycle. + // Note that because the lock might have been released, it is possible that the + // contents of the wait queue to have been drained, so we need to double-check + // a few things. + dispatchEntryIt = connection->findWaitQueueEntry(seq); + if (dispatchEntryIt != connection->waitQueue.end()) { + dispatchEntry = *dispatchEntryIt; + connection->waitQueue.erase(dispatchEntryIt); + traceWaitQueueLength(connection); + if (restartEvent && connection->status == Connection::STATUS_NORMAL) { + connection->outboundQueue.push_front(dispatchEntry); + traceOutboundQueueLength(connection); } else { - restartEvent = false; - } - - // Dequeue the event and start the next cycle. - // Note that because the lock might have been released, it is possible that the - // contents of the wait queue to have been drained, so we need to double-check - // a few things. - if (dispatchEntry == connection->findWaitQueueEntry(seq)) { - connection->waitQueue.dequeue(dispatchEntry); - traceWaitQueueLength(connection); - if (restartEvent && connection->status == Connection::STATUS_NORMAL) { - connection->outboundQueue.enqueueAtHead(dispatchEntry); - traceOutboundQueueLength(connection); - } else { - releaseDispatchEntry(dispatchEntry); - } + releaseDispatchEntry(dispatchEntry); } - - // Start the next dispatch cycle for this connection. - startDispatchCycleLocked(now(), connection); } + + // Start the next dispatch cycle for this connection. + startDispatchCycleLocked(now(), connection); } bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection, @@ -4429,9 +4481,37 @@ void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventE // TODO Write some statistics about how long we spend waiting. } +/** + * Report the touch event latency to the statsd server. + * Input events are reported for statistics if: + * - This is a touchscreen event + * - InputFilter is not enabled + * - Event is not injected or synthesized + * + * Statistics should be reported before calling addValue, to prevent a fresh new sample + * from getting aggregated with the "old" data. + */ +void InputDispatcher::reportTouchEventForStatistics(const MotionEntry& motionEntry) + REQUIRES(mLock) { + const bool reportForStatistics = (motionEntry.source == AINPUT_SOURCE_TOUCHSCREEN) && + !(motionEntry.isSynthesized()) && !mInputFilterEnabled; + if (!reportForStatistics) { + return; + } + + if (mTouchStatistics.shouldReport()) { + android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mTouchStatistics.getMin(), + mTouchStatistics.getMax(), mTouchStatistics.getMean(), + mTouchStatistics.getStDev(), mTouchStatistics.getCount()); + mTouchStatistics.reset(); + } + const float latencyMicros = nanoseconds_to_microseconds(now() - motionEntry.eventTime); + mTouchStatistics.addValue(latencyMicros); +} + void InputDispatcher::traceInboundQueueLengthLocked() { if (ATRACE_ENABLED()) { - ATRACE_INT("iq", mInboundQueue.count()); + ATRACE_INT("iq", mInboundQueue.size()); } } @@ -4439,7 +4519,7 @@ void InputDispatcher::traceOutboundQueueLength(const sp<Connection>& connection) if (ATRACE_ENABLED()) { char counterName[40]; snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str()); - ATRACE_INT(counterName, connection->outboundQueue.count()); + ATRACE_INT(counterName, connection->outboundQueue.size()); } } @@ -4447,7 +4527,7 @@ void InputDispatcher::traceWaitQueueLength(const sp<Connection>& connection) { if (ATRACE_ENABLED()) { char counterName[40]; snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str()); - ATRACE_INT(counterName, connection->waitQueue.count()); + ATRACE_INT(counterName, connection->waitQueue.size()); } } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 67bf199cd0..1f906e43f6 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -26,15 +26,14 @@ #include "InputState.h" #include "InputTarget.h" #include "Monitor.h" -#include "Queue.h" #include "TouchState.h" #include "TouchedWindow.h" -#include <cutils/atomic.h> #include <input/Input.h> #include <input/InputApplication.h> #include <input/InputTransport.h> #include <input/InputWindow.h> +#include <input/LatencyStatistics.h> #include <limits.h> #include <stddef.h> #include <ui/Region.h> @@ -107,8 +106,7 @@ public: virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) override; - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, - int32_t displayId) override; + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) override; virtual status_t registerInputMonitor(const sp<InputChannel>& inputChannel, int32_t displayId, bool isGestureMonitor) override; virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel) override; @@ -135,9 +133,9 @@ private: sp<Looper> mLooper; EventEntry* mPendingEvent GUARDED_BY(mLock); - Queue<EventEntry> mInboundQueue GUARDED_BY(mLock); - Queue<EventEntry> mRecentQueue GUARDED_BY(mLock); - Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock); + std::deque<EventEntry*> mInboundQueue GUARDED_BY(mLock); + std::deque<EventEntry*> mRecentQueue GUARDED_BY(mLock); + std::deque<std::unique_ptr<CommandEntry>> mCommandQueue GUARDED_BY(mLock); DropReason mLastDropReason GUARDED_BY(mLock); @@ -172,7 +170,7 @@ private: bool addPortalWindows = false) REQUIRES(mLock); // All registered connections mapped by channel file descriptor. - KeyedVector<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock); + std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock); struct IBinderHash { std::size_t operator()(const sp<IBinder>& b) const { @@ -186,7 +184,7 @@ private: std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock); - ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock); + sp<Connection> getConnectionLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock); // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock); @@ -235,7 +233,7 @@ private: // Deferred command processing. bool haveCommandsLocked() const REQUIRES(mLock); bool runCommandsLockedInterruptible() REQUIRES(mLock); - CommandEntry* postCommandLocked(Command command) REQUIRES(mLock); + void postCommandLocked(std::unique_ptr<CommandEntry> commandEntry) REQUIRES(mLock); // Input filter processing. bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); @@ -261,6 +259,13 @@ private: sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock); bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); + /* + * Validate and update InputWindowHandles for a given display. + */ + void updateWindowHandlesForDisplayLocked( + const std::vector<sp<InputWindowHandle>>& inputWindowHandles, int32_t displayId) + REQUIRES(mLock); + // Focus tracking for keys, trackball, etc. std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay GUARDED_BY(mLock); @@ -379,7 +384,7 @@ private: uint32_t seq, bool handled) REQUIRES(mLock); void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool notify) REQUIRES(mLock); - void drainDispatchQueue(Queue<DispatchEntry>* queue); + void drainDispatchQueue(std::deque<DispatchEntry*>& queue); void releaseDispatchEntry(DispatchEntry* dispatchEntry); static int handleReceiveCallback(int fd, int events, void* data); // The action sent should only be of type AMOTION_EVENT_* @@ -450,6 +455,10 @@ private: void doOnPointerDownOutsideFocusLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); // Statistics gathering. + static constexpr std::chrono::duration TOUCH_STATS_REPORT_PERIOD = 5min; + LatencyStatistics mTouchStatistics{TOUCH_STATS_REPORT_PERIOD}; + + void reportTouchEventForStatistics(const MotionEntry& entry); void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); void traceInboundQueueLengthLocked() REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 7d9b03a70e..c60700e369 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -232,6 +232,8 @@ void InputState::addMotionMemento(const MotionEntry* entry, int32_t flags, bool memento.flags = flags; memento.xPrecision = entry->xPrecision; memento.yPrecision = entry->yPrecision; + memento.xCursorPosition = entry->xCursorPosition; + memento.yCursorPosition = entry->yCursorPosition; memento.downTime = entry->downTime; memento.setPointers(entry); memento.hovering = hovering; @@ -271,7 +273,8 @@ void InputState::synthesizeCancelationEvents(nsecs_t currentTime, 0 /*actionButton*/, memento.flags, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, - memento.yPrecision, memento.downTime, memento.pointerCount, + memento.yPrecision, memento.xCursorPosition, + memento.yCursorPosition, memento.downTime, memento.pointerCount, memento.pointerProperties, memento.pointerCoords, 0 /*xOffset*/, 0 /*yOffset*/)); } diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index 205b64728a..47e9b36219 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -24,9 +24,6 @@ namespace android::inputdispatcher { -// Sequence number for synthesized or injected events. -constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0; - /* Tracks dispatched key and motion event state so that cancellation events can be * synthesized when events are dropped. */ class InputState { @@ -94,6 +91,8 @@ private: int32_t flags; float xPrecision; float yPrecision; + float xCursorPosition; + float yCursorPosition; nsecs_t downTime; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; diff --git a/services/inputflinger/dispatcher/Queue.h b/services/inputflinger/dispatcher/Queue.h deleted file mode 100644 index 0e75821661..0000000000 --- a/services/inputflinger/dispatcher/Queue.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2019 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 _UI_INPUT_INPUTDISPATCHER_QUEUE_H -#define _UI_INPUT_INPUTDISPATCHER_QUEUE_H - -namespace android::inputdispatcher { - -// Generic queue implementation. -template <typename T> -struct Queue { - T* head; - T* tail; - uint32_t entryCount; - - inline Queue() : head(nullptr), tail(nullptr), entryCount(0) {} - - inline bool isEmpty() const { return !head; } - - inline void enqueueAtTail(T* entry) { - entryCount++; - entry->prev = tail; - if (tail) { - tail->next = entry; - } else { - head = entry; - } - entry->next = nullptr; - tail = entry; - } - - inline void enqueueAtHead(T* entry) { - entryCount++; - entry->next = head; - if (head) { - head->prev = entry; - } else { - tail = entry; - } - entry->prev = nullptr; - head = entry; - } - - inline void dequeue(T* entry) { - entryCount--; - if (entry->prev) { - entry->prev->next = entry->next; - } else { - head = entry->next; - } - if (entry->next) { - entry->next->prev = entry->prev; - } else { - tail = entry->prev; - } - } - - inline T* dequeueAtHead() { - entryCount--; - T* entry = head; - head = entry->next; - if (head) { - head->prev = nullptr; - } else { - tail = nullptr; - } - return entry; - } - - uint32_t count() const { return entryCount; } -}; - -} // namespace android::inputdispatcher - -#endif // _UI_INPUT_INPUTDISPATCHER_QUEUE_H diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 18848a0c2f..2baceba582 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -93,15 +93,6 @@ void TouchState::addGestureMonitors(const std::vector<TouchedMonitor>& newMonito std::end(newMonitors)); } -void TouchState::removeWindow(const sp<InputWindowHandle>& windowHandle) { - for (size_t i = 0; i < windows.size(); i++) { - if (windows[i].windowHandle == windowHandle) { - windows.erase(windows.begin() + i); - return; - } - } -} - void TouchState::removeWindowByToken(const sp<IBinder>& token) { for (size_t i = 0; i < windows.size(); i++) { if (windows[i].windowHandle->getToken() == token) { diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 3e0e0eb897..623c6a824f 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -49,7 +49,6 @@ struct TouchState { BitSet32 pointerIds); void addPortalWindow(const sp<android::InputWindowHandle>& windowHandle); void addGestureMonitors(const std::vector<TouchedMonitor>& monitors); - void removeWindow(const sp<android::InputWindowHandle>& windowHandle); void removeWindowByToken(const sp<IBinder>& token); void filterNonAsIsTouchWindows(); void filterNonMonitors(); diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index 9329ca664e..ce7366f475 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -126,8 +126,7 @@ public: * * This method may be called on any thread (usually by the input manager). */ - virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, - int32_t displayId) = 0; + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel) = 0; /* Registers input channels to be used to monitor input events. * diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h index d8b352cbc6..973b4f92fa 100644 --- a/services/inputflinger/host/InputFlinger.h +++ b/services/inputflinger/host/InputFlinger.h @@ -42,7 +42,6 @@ public: virtual status_t dump(int fd, const Vector<String16>& args); void setInputWindows(const std::vector<InputWindowInfo>&, const sp<ISetInputWindowsListener>&) {} - void transferTouchFocus(const sp<IBinder>&, const sp<IBinder>&) {} void registerInputChannel(const sp<InputChannel>&) {} void unregisterInputChannel(const sp<InputChannel>&) {} diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index b51dcb6cad..0dcd2f9c38 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -107,31 +107,32 @@ struct NotifyMotionArgs : public NotifyArgs { */ MotionClassification classification; int32_t edgeFlags; - /** - * A timestamp in the input device's time base, not the platform's. - * The units are microseconds since the last reset. - * This can only be compared to other device timestamps from the same device. - * This value will overflow after a little over an hour. - */ - uint32_t deviceTimestamp; + uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; float xPrecision; float yPrecision; + /** + * Mouse cursor position when this event is reported relative to the origin of the specified + * display. Only valid if this is a mouse event (originates from a mouse or from a trackpad in + * gestures enabled mode. + */ + float xCursorPosition; + float yCursorPosition; nsecs_t downTime; std::vector<TouchVideoFrame> videoFrames; inline NotifyMotionArgs() { } NotifyMotionArgs(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, - int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, uint32_t deviceTimestamp, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xPrecision, float yPrecision, nsecs_t downTime, - const std::vector<TouchVideoFrame>& videoFrames); + int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton, + int32_t flags, int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount, + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xPrecision, float yPrecision, float xCursorPosition, + float yCursorPosition, nsecs_t downTime, + const std::vector<TouchVideoFrame>& videoFrames); NotifyMotionArgs(const NotifyMotionArgs& other); diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 8ad5dd0785..5d576b94f3 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -24,14 +24,13 @@ #include <input/DisplayViewport.h> #include <input/VelocityControl.h> #include <input/VelocityTracker.h> -#include <utils/KeyedVector.h> #include <utils/Thread.h> #include <utils/RefBase.h> -#include <utils/SortedVector.h> -#include <optional> #include <stddef.h> #include <unistd.h> +#include <optional> +#include <set> #include <unordered_map> #include <vector> @@ -250,7 +249,7 @@ struct InputReaderConfiguration { bool pointerCapture; // The set of currently disabled input devices. - SortedVector<int32_t> disabledDevices; + std::set<int32_t> disabledDevices; InputReaderConfiguration() : virtualKeyQuietTime(0), @@ -270,6 +269,8 @@ struct InputReaderConfiguration { pointerGestureZoomSpeedRatio(0.3f), showTouches(false), pointerCapture(false) { } + static std::string changesToString(uint32_t changes); + std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const; std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId) const; diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index 4e97397e2b..3c1607096e 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -50,6 +50,7 @@ cc_library_shared { shared_libs: [ "libbase", + "libcap", "libinputflinger_base", "libcrypto", "libcutils", @@ -58,7 +59,6 @@ cc_library_shared { "libui", "libutils", "libhardware_legacy", - "libstatslog", ], header_libs: [ diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index a5e5415dd7..c8da0ab29f 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -24,11 +24,11 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/capability.h> #include <sys/epoll.h> #include <sys/inotify.h> #include <sys/ioctl.h> #include <sys/limits.h> -#include <sys/utsname.h> #include <unistd.h> #define LOG_TAG "EventHub" @@ -94,14 +94,6 @@ static std::string sha1(const std::string& in) { return out; } -static void getLinuxRelease(int* major, int* minor) { - struct utsname info; - if (uname(&info) || sscanf(info.release, "%d.%d", major, minor) <= 0) { - *major = 0, *minor = 0; - ALOGE("Could not get linux version: %s", strerror(errno)); - } -} - /** * Return true if name matches "v4l-touch*" */ @@ -246,6 +238,47 @@ bool EventHub::Device::hasValidFd() { return !isVirtual && enabled; } +/** + * Get the capabilities for the current process. + * Crashes the system if unable to create / check / destroy the capabilities object. + */ +class Capabilities final { +public: + explicit Capabilities() { + mCaps = cap_get_proc(); + LOG_ALWAYS_FATAL_IF(mCaps == nullptr, "Could not get capabilities of the current process"); + } + + /** + * Check whether the current process has a specific capability + * in the set of effective capabilities. + * Return CAP_SET if the process has the requested capability + * Return CAP_CLEAR otherwise. + */ + cap_flag_value_t checkEffectiveCapability(cap_value_t capability) { + cap_flag_value_t value; + const int result = cap_get_flag(mCaps, capability, CAP_EFFECTIVE, &value); + LOG_ALWAYS_FATAL_IF(result == -1, "Could not obtain the requested capability"); + return value; + } + + ~Capabilities() { + const int result = cap_free(mCaps); + LOG_ALWAYS_FATAL_IF(result == -1, "Could not release the capabilities structure"); + } + +private: + cap_t mCaps; +}; + +static void ensureProcessCanBlockSuspend() { + Capabilities capabilities; + const bool canBlockSuspend = + capabilities.checkEffectiveCapability(CAP_BLOCK_SUSPEND) == CAP_SET; + LOG_ALWAYS_FATAL_IF(!canBlockSuspend, + "Input must be able to block suspend to properly process events"); +} + // --- EventHub --- const int EventHub::EPOLL_MAX_EVENTS; @@ -262,6 +295,7 @@ EventHub::EventHub(void) mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { + ensureProcessCanBlockSuspend(); acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); mEpollFd = epoll_create1(EPOLL_CLOEXEC); @@ -280,9 +314,8 @@ EventHub::EventHub(void) ALOGI("Video device scanning disabled"); } - struct epoll_event eventItem; - memset(&eventItem, 0, sizeof(eventItem)); - eventItem.events = EPOLLIN; + struct epoll_event eventItem = {}; + eventItem.events = EPOLLIN | EPOLLWAKEUP; eventItem.data.fd = mINotifyFd; int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); @@ -306,11 +339,6 @@ EventHub::EventHub(void) result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", errno); - - int major, minor; - getLinuxRelease(&major, &minor); - // EPOLLWAKEUP was introduced in kernel 3.5 - mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5); } EventHub::~EventHub(void) { @@ -1491,27 +1519,13 @@ void EventHub::configureFd(Device* device) { } } - std::string wakeMechanism = "EPOLLWAKEUP"; - if (!mUsingEpollWakeup) { -#ifndef EVIOCSSUSPENDBLOCK - // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels - // will use an epoll flag instead, so as long as we want to support - // this feature, we need to be prepared to define the ioctl ourselves. -#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) -#endif - if (ioctl(device->fd, EVIOCSSUSPENDBLOCK, 1)) { - wakeMechanism = "<none>"; - } else { - wakeMechanism = "EVIOCSSUSPENDBLOCK"; - } - } // Tell the kernel that we want to use the monotonic clock for reporting timestamps // associated with input events. This is important because the input system // uses the timestamps extensively and assumes they were recorded using the monotonic // clock. int clockId = CLOCK_MONOTONIC; bool usingClockIoctl = !ioctl(device->fd, EVIOCSCLOCKID, &clockId); - ALOGI("wakeMechanism=%s, usingClockIoctl=%s", wakeMechanism.c_str(), toString(usingClockIoctl)); + ALOGI("usingClockIoctl=%s", toString(usingClockIoctl)); } void EventHub::openVideoDeviceLocked(const std::string& devicePath) { diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index d0613b0c69..7fed61f09d 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -49,6 +49,13 @@ bool InputDevice::isEnabled() { } void InputDevice::setEnabled(bool enabled, nsecs_t when) { + if (enabled && mAssociatedDisplayPort && !mAssociatedViewport) { + ALOGW("Cannot enable input device %s because it is associated with port %" PRIu8 ", " + "but the corresponding viewport is not found", + getName().c_str(), *mAssociatedDisplayPort); + enabled = false; + } + if (isEnabled() == enabled) { return; } @@ -144,14 +151,15 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config } if (!changes || (changes & InputReaderConfiguration::CHANGE_ENABLED_STATE)) { - ssize_t index = config->disabledDevices.indexOf(mId); - bool enabled = index < 0; + auto it = config->disabledDevices.find(mId); + bool enabled = it == config->disabledDevices.end(); setEnabled(enabled, when); } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { // In most situations, no port will be specified. mAssociatedDisplayPort = std::nullopt; + mAssociatedViewport = std::nullopt; // Find the display port that corresponds to the current input port. const std::string& inputPort = mIdentifier.location; if (!inputPort.empty()) { @@ -161,12 +169,40 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config mAssociatedDisplayPort = std::make_optional(displayPort->second); } } + + // If the device was explicitly disabled by the user, it would be present in the + // "disabledDevices" list. If it is associated with a specific display, and it was not + // explicitly disabled, then enable/disable the device based on whether we can find the + // corresponding viewport. + bool enabled = (config->disabledDevices.find(mId) == config->disabledDevices.end()); + if (mAssociatedDisplayPort) { + mAssociatedViewport = config->getDisplayViewportByPort(*mAssociatedDisplayPort); + if (!mAssociatedViewport) { + ALOGW("Input device %s should be associated with display on port %" PRIu8 ", " + "but the corresponding viewport is not found.", + getName().c_str(), *mAssociatedDisplayPort); + enabled = false; + } + } + + if (changes) { + // For first-time configuration, only allow device to be disabled after mappers have + // finished configuring. This is because we need to read some of the properties from + // the device's open fd. + setEnabled(enabled, when); + } } for (InputMapper* mapper : mMappers) { mapper->configure(when, config, changes); mSources |= mapper->getSources(); } + + // If a device is just plugged but it might be disabled, we need to update some info like + // axis range of touch from each InputMapper first, then disable it. + if (!changes) { + setEnabled(config->disabledDevices.find(mId) == config->disabledDevices.end(), when); + } } } @@ -324,9 +360,15 @@ void InputDevice::notifyReset(nsecs_t when) { mContext->getListener()->notifyDeviceReset(&args); } -std::optional<int32_t> InputDevice::getAssociatedDisplay() { +std::optional<int32_t> InputDevice::getAssociatedDisplayId() { + // Check if we had associated to the specific display. + if (mAssociatedViewport) { + return mAssociatedViewport->displayId; + } + + // No associated display port, check if some InputMapper is associated. for (InputMapper* mapper : mMappers) { - std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplay(); + std::optional<int32_t> associatedDisplayId = mapper->getAssociatedDisplayId(); if (associatedDisplayId) { return associatedDisplayId; } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 27cbf192d3..e57604cbe8 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -48,7 +48,7 @@ using android::base::StringPrintf; namespace android { -InputReader::InputReader(const sp<EventHubInterface>& eventHub, +InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub, const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) : mContext(this), @@ -353,7 +353,8 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); if (changes) { - ALOGI("Reconfiguring input devices. changes=0x%08x", changes); + ALOGI("Reconfiguring input devices, changes=%s", + InputReaderConfiguration::changesToString(changes).c_str()); nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { @@ -607,14 +608,19 @@ bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) { } InputDevice* device = mDevices.valueAt(deviceIndex); - std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplay(); + if (!device->isEnabled()) { + ALOGW("Ignoring disabled device %s", device->getName().c_str()); + return false; + } + + std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId(); // No associated display. By default, can dispatch to all displays. if (!associatedDisplayId) { return true; } if (*associatedDisplayId == ADISPLAY_ID_NONE) { - ALOGW("Device has associated, but no associated display id."); + ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str()); return true; } diff --git a/services/inputflinger/reader/InputReaderFactory.cpp b/services/inputflinger/reader/InputReaderFactory.cpp index 9f73680913..a8971411a4 100644 --- a/services/inputflinger/reader/InputReaderFactory.cpp +++ b/services/inputflinger/reader/InputReaderFactory.cpp @@ -22,7 +22,7 @@ namespace android { sp<InputReaderInterface> createInputReader(const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener) { - return new InputReader(new EventHub(), policy, listener); + return new InputReader(std::make_unique<EventHub>(), policy, listener); } } // namespace android
\ No newline at end of file diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 794396acca..c17f3a184d 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -145,12 +145,11 @@ extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses); * which keys are currently down. Finally, the event hub keeps track of the capabilities of * individual input devices, such as their class and the set of key codes that they support. */ -class EventHubInterface : public virtual RefBase { -protected: +class EventHubInterface { +public: EventHubInterface() {} virtual ~EventHubInterface() {} -public: // Synthetic raw event type codes produced when devices are added or removed. enum { // Sent when a device is added. @@ -261,62 +260,64 @@ class EventHub : public EventHubInterface { public: EventHub(); - virtual uint32_t getDeviceClasses(int32_t deviceId) const; + virtual uint32_t getDeviceClasses(int32_t deviceId) const override; - virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const; + virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const override; - virtual int32_t getDeviceControllerNumber(int32_t deviceId) const; + virtual int32_t getDeviceControllerNumber(int32_t deviceId) const override; - virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const; + virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override; virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, - RawAbsoluteAxisInfo* outAxisInfo) const; + RawAbsoluteAxisInfo* outAxisInfo) const override; - virtual bool hasRelativeAxis(int32_t deviceId, int axis) const; + virtual bool hasRelativeAxis(int32_t deviceId, int axis) const override; - virtual bool hasInputProperty(int32_t deviceId, int property) const; + virtual bool hasInputProperty(int32_t deviceId, int property) const override; virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t metaState, int32_t* outKeycode, int32_t* outMetaState, - uint32_t* outFlags) const; + uint32_t* outFlags) const override; - virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const; + virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, + AxisInfo* outAxisInfo) const override; - virtual void setExcludedDevices(const std::vector<std::string>& devices); + virtual void setExcludedDevices(const std::vector<std::string>& devices) override; - virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const; - virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const; - virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const; - virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* outValue) const; + virtual int32_t getScanCodeState(int32_t deviceId, int32_t scanCode) const override; + virtual int32_t getKeyCodeState(int32_t deviceId, int32_t keyCode) const override; + virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const override; + virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis, + int32_t* outValue) const override; virtual bool markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const int32_t* keyCodes, - uint8_t* outFlags) const; + uint8_t* outFlags) const override; - virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize); - virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId); + virtual size_t getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) override; + virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) override; - virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const; - virtual bool hasLed(int32_t deviceId, int32_t led) const; - virtual void setLedState(int32_t deviceId, int32_t led, bool on); + virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const override; + virtual bool hasLed(int32_t deviceId, int32_t led) const override; + virtual void setLedState(int32_t deviceId, int32_t led, bool on) override; - virtual void getVirtualKeyDefinitions(int32_t deviceId, - std::vector<VirtualKeyDefinition>& outVirtualKeys) const; + virtual void getVirtualKeyDefinitions( + int32_t deviceId, std::vector<VirtualKeyDefinition>& outVirtualKeys) const override; - virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const; - virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& map); + virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const override; + virtual bool setKeyboardLayoutOverlay(int32_t deviceId, + const sp<KeyCharacterMap>& map) override; - virtual void vibrate(int32_t deviceId, nsecs_t duration); - virtual void cancelVibrate(int32_t deviceId); + virtual void vibrate(int32_t deviceId, nsecs_t duration) override; + virtual void cancelVibrate(int32_t deviceId) override; - virtual void requestReopenDevices(); + virtual void requestReopenDevices() override; - virtual void wake(); + virtual void wake() override; - virtual void dump(std::string& dump); - virtual void monitor(); + virtual void dump(std::string& dump) override; + virtual void monitor() override; -protected: - virtual ~EventHub(); + virtual ~EventHub() override; private: struct Device { @@ -385,9 +386,9 @@ private: void configureFd(Device* device); - bool isDeviceEnabled(int32_t deviceId); - status_t enableDevice(int32_t deviceId); - status_t disableDevice(int32_t deviceId); + bool isDeviceEnabled(int32_t deviceId) override; + status_t enableDevice(int32_t deviceId) override; + status_t disableDevice(int32_t deviceId) override; status_t registerFdForEpoll(int fd); status_t unregisterFdFromEpoll(int fd); status_t registerDeviceForEpollLocked(Device* device); @@ -475,8 +476,6 @@ private: size_t mPendingEventCount; size_t mPendingEventIndex; bool mPendingINotify; - - bool mUsingEpollWakeup; }; }; // namespace android diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 57f0b319c8..882407dd20 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -55,7 +55,9 @@ public: inline std::optional<uint8_t> getAssociatedDisplayPort() const { return mAssociatedDisplayPort; } - + inline std::optional<DisplayViewport> getAssociatedViewport() const { + return mAssociatedViewport; + } inline void setMic(bool hasMic) { mHasMic = hasMic; } inline bool hasMic() const { return mHasMic; } @@ -112,7 +114,7 @@ public: return value; } - std::optional<int32_t> getAssociatedDisplay(); + std::optional<int32_t> getAssociatedDisplayId(); private: InputReaderContext* mContext; @@ -128,6 +130,7 @@ private: uint32_t mSources; bool mIsExternal; std::optional<uint8_t> mAssociatedDisplayPort; + std::optional<DisplayViewport> mAssociatedViewport; bool mHasMic; bool mDropUntilNextSync; diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index e29c8f219c..7b4321ea82 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -22,11 +22,9 @@ #include "InputReaderBase.h" #include "InputReaderContext.h" -#include <utils/BitSet.h> #include <utils/Condition.h> #include <utils/KeyedVector.h> #include <utils/Mutex.h> -#include <utils/Timers.h> #include <vector> @@ -49,35 +47,39 @@ struct StylusState; */ class InputReader : public InputReaderInterface { public: - InputReader(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, + InputReader(std::shared_ptr<EventHubInterface> eventHub, + const sp<InputReaderPolicyInterface>& policy, const sp<InputListenerInterface>& listener); virtual ~InputReader(); - virtual void dump(std::string& dump); - virtual void monitor(); + virtual void dump(std::string& dump) override; + virtual void monitor() override; - virtual void loopOnce(); + virtual void loopOnce() override; - virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices); + virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override; - virtual bool isInputDeviceEnabled(int32_t deviceId); + virtual bool isInputDeviceEnabled(int32_t deviceId) override; - virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode); - virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode); - virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw); + virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t scanCode) override; + virtual int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, + int32_t keyCode) override; + virtual int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override; - virtual void toggleCapsLockState(int32_t deviceId); + virtual void toggleCapsLockState(int32_t deviceId) override; virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); + const int32_t* keyCodes, uint8_t* outFlags) override; - virtual void requestRefreshConfiguration(uint32_t changes); + virtual void requestRefreshConfiguration(uint32_t changes) override; virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, - ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t deviceId, int32_t token); + ssize_t repeat, int32_t token) override; + virtual void cancelVibrate(int32_t deviceId, int32_t token) override; + + virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override; - virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId); protected: // These members are protected so they can be instrumented by test cases. virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber, @@ -90,20 +92,20 @@ protected: public: explicit ContextImpl(InputReader* reader); - virtual void updateGlobalMetaState(); - virtual int32_t getGlobalMetaState(); - virtual void disableVirtualKeysUntil(nsecs_t time); + virtual void updateGlobalMetaState() override; + virtual int32_t getGlobalMetaState() override; + virtual void disableVirtualKeysUntil(nsecs_t time) override; virtual bool shouldDropVirtualKey(nsecs_t now, InputDevice* device, int32_t keyCode, - int32_t scanCode); - virtual void fadePointer(); - virtual void requestTimeoutAtTime(nsecs_t when); - virtual int32_t bumpGeneration(); - virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices); - virtual void dispatchExternalStylusState(const StylusState& outState); - virtual InputReaderPolicyInterface* getPolicy(); - virtual InputListenerInterface* getListener(); - virtual EventHubInterface* getEventHub(); - virtual uint32_t getNextSequenceNum(); + int32_t scanCode) override; + virtual void fadePointer() override; + virtual void requestTimeoutAtTime(nsecs_t when) override; + virtual int32_t bumpGeneration() override; + virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override; + virtual void dispatchExternalStylusState(const StylusState& outState) override; + virtual InputReaderPolicyInterface* getPolicy() override; + virtual InputListenerInterface* getListener() override; + virtual EventHubInterface* getEventHub() override; + virtual uint32_t getNextSequenceNum() override; } mContext; friend class ContextImpl; @@ -113,7 +115,10 @@ private: Condition mReaderIsAliveCondition; - sp<EventHubInterface> mEventHub; + // This could be unique_ptr, but due to the way InputReader tests are written, + // it is made shared_ptr here. In the tests, an EventHub reference is retained by the test + // in parallel to passing it to the InputReader. + std::shared_ptr<EventHubInterface> mEventHub; sp<InputReaderPolicyInterface> mPolicy; sp<QueuedInputListener> mQueuedListener; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index da85fda0e9..f69138ea09 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -313,6 +313,8 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerVelocityControl.move(when, &deltaX, &deltaY); int32_t displayId; + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mSource == AINPUT_SOURCE_MOUSE) { if (moved || scrolled || buttonsChanged) { mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); @@ -328,10 +330,9 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); } - float x, y; - mPointerController->getPosition(&x, &y); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); displayId = mPointerController->getDisplayId(); @@ -378,9 +379,9 @@ void CursorInputMapper::sync(nsecs_t when) { mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, downTime, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&releaseArgs); } @@ -389,9 +390,9 @@ void CursorInputMapper::sync(nsecs_t when) { NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - mXPrecision, mYPrecision, downTime, /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); getListener()->notifyMotion(&args); if (buttonsPressed) { @@ -403,9 +404,9 @@ void CursorInputMapper::sync(nsecs_t when) { mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, downTime, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&pressArgs); } @@ -418,10 +419,9 @@ void CursorInputMapper::sync(nsecs_t when) { NotifyMotionArgs hoverArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, downTime, - /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&hoverArgs); } @@ -434,10 +434,9 @@ void CursorInputMapper::sync(nsecs_t when) { mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, downTime, - /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, /* videoFrames */ {}); getListener()->notifyMotion(&scrollArgs); } } @@ -464,7 +463,7 @@ void CursorInputMapper::fadePointer() { } } -std::optional<int32_t> CursorInputMapper::getAssociatedDisplay() { +std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() { if (mParameters.hasAssociatedDisplay) { if (mParameters.mode == Parameters::MODE_POINTER) { return std::make_optional(mPointerController->getDisplayId()); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index eb2ad54c80..77d122af0a 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -56,18 +56,19 @@ public: explicit CursorInputMapper(InputDevice* device); virtual ~CursorInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; - virtual void fadePointer(); + virtual void fadePointer() override; - virtual std::optional<int32_t> getAssociatedDisplay(); + virtual std::optional<int32_t> getAssociatedDisplayId() override; private: // Amount that trackball needs to move in order to generate a key event. diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h index 9764fbb3c1..34f339a3cd 100644 --- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h +++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h @@ -30,13 +30,13 @@ public: explicit ExternalStylusInputMapper(InputDevice* device); virtual ~ExternalStylusInputMapper() = default; - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - virtual void sync(nsecs_t when); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; private: SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; @@ -44,6 +44,8 @@ private: TouchButtonAccumulator mTouchButtonAccumulator; StylusState mStylusState; + + void sync(nsecs_t when); }; } // namespace android diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index cfd207cc4d..a559ef882f 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -73,7 +73,7 @@ public: virtual void updateExternalStylusState(const StylusState& state); virtual void fadePointer(); - virtual std::optional<int32_t> getAssociatedDisplay() { return std::nullopt; } + virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; } protected: InputDevice* mDevice; diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index b493e8368f..50adf73c76 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -334,9 +334,10 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, 0, - /* videoFrames */ {}); + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); getListener()->notifyMotion(&args); } diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h index 1b071d0480..b46d27d2d1 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.h +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h @@ -26,12 +26,13 @@ public: explicit JoystickInputMapper(InputDevice* device); virtual ~JoystickInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; private: struct Axis { diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 0e91c0e2cc..f51d4a0d4a 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -127,6 +127,22 @@ void KeyboardInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); } +std::optional<DisplayViewport> KeyboardInputMapper::findViewport( + nsecs_t when, const InputReaderConfiguration* config) { + const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort(); + if (displayPort) { + // Find the viewport that contains the same port + return mDevice->getAssociatedViewport(); + } + + // No associated display defined, try to find default display if orientationAware. + if (mParameters.orientationAware) { + return config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + } + + return std::nullopt; +} + void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes) { InputMapper::configure(when, config, changes); @@ -137,9 +153,7 @@ void KeyboardInputMapper::configure(nsecs_t when, const InputReaderConfiguration } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - if (mParameters.orientationAware) { - mViewport = config->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); - } + mViewport = findViewport(when, config); } } @@ -409,4 +423,11 @@ void KeyboardInputMapper::updateLedStateForModifier(LedState& ledState, int32_t } } +std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() { + if (mViewport) { + return std::make_optional(mViewport->displayId); + } + return std::nullopt; +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index 7a68fc33f8..de2a377e9a 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -26,20 +26,22 @@ public: KeyboardInputMapper(InputDevice* device, uint32_t source, int32_t keyboardType); virtual ~KeyboardInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); - - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; + + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override; + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); + const int32_t* keyCodes, uint8_t* outFlags) override; - virtual int32_t getMetaState(); - virtual void updateMetaState(int32_t keyCode); + virtual int32_t getMetaState() override; + virtual void updateMetaState(int32_t keyCode) override; + virtual std::optional<int32_t> getAssociatedDisplayId() override; private: // The current viewport. @@ -92,6 +94,8 @@ private: void initializeLedState(LedState& ledState, int32_t led); void updateLedState(bool reset); void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); + std::optional<DisplayViewport> findViewport(nsecs_t when, + const InputReaderConfiguration* config); }; } // namespace android diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index 7460a3130e..c567c8bf80 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -72,7 +72,6 @@ void MultiTouchMotionAccumulator::reset(InputDevice* device) { } else { clearSlots(-1); } - mDeviceTimestamp = 0; } void MultiTouchMotionAccumulator::clearSlots(int32_t initialSlot) { @@ -166,8 +165,6 @@ void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { // MultiTouch Sync: The driver has returned all data for *one* of the pointers. mCurrentSlot += 1; - } else if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) { - mDeviceTimestamp = rawEvent->value; } } @@ -316,7 +313,6 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { outCount += 1; } - outState->deviceTimestamp = mMultiTouchMotionAccumulator.getDeviceTimestamp(); outState->rawPointerData.pointerCount = outCount; mPointerIdBits = newPointerIdBits; diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h index 873dda1aec..a45c3cb12b 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h @@ -78,7 +78,6 @@ public: inline size_t getSlotCount() const { return mSlotCount; } inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } - inline uint32_t getDeviceTimestamp() const { return mDeviceTimestamp; } private: int32_t mCurrentSlot; @@ -86,7 +85,6 @@ private: size_t mSlotCount; bool mUsingSlotsProtocol; bool mHaveStylus; - uint32_t mDeviceTimestamp; void clearSlots(int32_t initialSlot); }; @@ -96,8 +94,8 @@ public: explicit MultiTouchInputMapper(InputDevice* device); virtual ~MultiTouchInputMapper(); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; protected: virtual void syncTouch(nsecs_t when, RawState* outState); diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index 803fdf3656..e113ccad84 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -122,9 +122,9 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { NotifyMotionArgs scrollArgs(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, - 0, 0, 0, /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); getListener()->notifyMotion(&scrollArgs); } diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h index 26488373bd..38c7258f7e 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h @@ -27,12 +27,13 @@ public: explicit RotaryEncoderInputMapper(InputDevice* device); virtual ~RotaryEncoderInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; private: CursorScrollAccumulator mRotaryEncoderScrollAccumulator; diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h index d6b1455b68..8438eee80b 100644 --- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h +++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h @@ -27,8 +27,8 @@ public: explicit SingleTouchInputMapper(InputDevice* device); virtual ~SingleTouchInputMapper(); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; protected: virtual void syncTouch(nsecs_t when, RawState* outState); diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h index dd4bb9ed65..e65d4e2bd5 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.h +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h @@ -26,11 +26,11 @@ public: explicit SwitchInputMapper(InputDevice* device); virtual ~SwitchInputMapper(); - virtual uint32_t getSources(); - virtual void process(const RawEvent* rawEvent); + virtual uint32_t getSources() override; + virtual void process(const RawEvent* rawEvent) override; - virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); - virtual void dump(std::string& dump); + virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override; + virtual void dump(std::string& dump) override; private: uint32_t mSwitchValues; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 32ed97bffe..34603b95f4 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -23,11 +23,6 @@ #include "TouchButtonAccumulator.h" #include "TouchCursorInputMapperCommon.h" -#include <statslog.h> - -// How often to report input event statistics -static constexpr nsecs_t STATISTICS_REPORT_FREQUENCY = seconds_to_nanoseconds(5 * 60); - namespace android { // --- Constants --- @@ -566,15 +561,10 @@ std::optional<DisplayViewport> TouchInputMapper::findViewport() { const std::optional<uint8_t> displayPort = mDevice->getAssociatedDisplayPort(); if (displayPort) { // Find the viewport that contains the same port - std::optional<DisplayViewport> v = mConfig.getDisplayViewportByPort(*displayPort); - if (!v) { - ALOGW("Input device %s should be associated with display on port %" PRIu8 ", " - "but the corresponding viewport is not found.", - getDeviceName().c_str(), *displayPort); - } - return v; + return mDevice->getAssociatedViewport(); } + // Check if uniqueDisplayId is specified in idc file. if (!mParameters.uniqueDisplayId.empty()) { return mConfig.getDisplayViewportByUniqueId(mParameters.uniqueDisplayId); } @@ -598,6 +588,7 @@ std::optional<DisplayViewport> TouchInputMapper::findViewport() { return viewport; } + // No associated display, return a non-display viewport. DisplayViewport newViewport; // Raw width and height in the natural orientation. int32_t rawWidth = mRawPointerAxes.getRawWidth(); @@ -1395,26 +1386,12 @@ void TouchInputMapper::clearStylusDataPendingFlags() { mExternalStylusFusionTimeout = LLONG_MAX; } -void TouchInputMapper::reportEventForStatistics(nsecs_t evdevTime) { - nsecs_t now = systemTime(CLOCK_MONOTONIC); - nsecs_t latency = now - evdevTime; - mStatistics.addValue(nanoseconds_to_microseconds(latency)); - nsecs_t timeSinceLastReport = now - mStatistics.lastReportTime; - if (timeSinceLastReport > STATISTICS_REPORT_FREQUENCY) { - android::util::stats_write(android::util::TOUCH_EVENT_REPORTED, mStatistics.min, - mStatistics.max, mStatistics.mean(), mStatistics.stdev(), - mStatistics.count); - mStatistics.reset(now); - } -} - void TouchInputMapper::process(const RawEvent* rawEvent) { mCursorButtonAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { - reportEventForStatistics(rawEvent->when); sync(rawEvent->when); } } @@ -1848,7 +1825,6 @@ void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { int32_t buttonState = mCurrentCookedState.buttonState; dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, @@ -1869,7 +1845,6 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { // The listener takes care of batching moves so we don't have to deal with that here. dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, currentIdBits, -1, @@ -1901,7 +1876,7 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { uint32_t upId = upIdBits.clearFirstMarkedBit(); dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, - metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp, + metaState, buttonState, 0, mLastCookedState.cookedPointerData.pointerProperties, mLastCookedState.cookedPointerData.pointerCoords, mLastCookedState.cookedPointerData.idToIndex, dispatchedIdBits, upId, @@ -1915,8 +1890,7 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { if (moveNeeded && !moveIdBits.isEmpty()) { ALOG_ASSERT(moveIdBits.value == dispatchedIdBits.value); dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - buttonState, 0, mCurrentCookedState.deviceTimestamp, - mCurrentCookedState.cookedPointerData.pointerProperties, + buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, -1, mOrientedXPrecision, mOrientedYPrecision, mDownTime); @@ -1933,7 +1907,7 @@ void TouchInputMapper::dispatchTouches(nsecs_t when, uint32_t policyFlags) { } dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, - metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp, + metaState, buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, dispatchedIdBits, @@ -1948,7 +1922,7 @@ void TouchInputMapper::dispatchHoverExit(nsecs_t when, uint32_t policyFlags) { !mCurrentCookedState.cookedPointerData.touchingIdBits.isEmpty())) { int32_t metaState = getContext()->getGlobalMetaState(); dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, - mLastCookedState.buttonState, 0, mLastCookedState.deviceTimestamp, + mLastCookedState.buttonState, 0, mLastCookedState.cookedPointerData.pointerProperties, mLastCookedState.cookedPointerData.pointerCoords, mLastCookedState.cookedPointerData.idToIndex, @@ -1965,7 +1939,6 @@ void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFl if (!mSentHoverEnter) { dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, mCurrentRawState.buttonState, 0, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, @@ -1975,7 +1948,7 @@ void TouchInputMapper::dispatchHoverEnterAndMove(nsecs_t when, uint32_t policyFl } dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, 0, mCurrentCookedState.deviceTimestamp, + mCurrentRawState.buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, @@ -1994,7 +1967,6 @@ void TouchInputMapper::dispatchButtonRelease(nsecs_t when, uint32_t policyFlags) buttonState &= ~actionButton; dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, metaState, buttonState, 0, - mCurrentCookedState.deviceTimestamp, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, @@ -2011,7 +1983,7 @@ void TouchInputMapper::dispatchButtonPress(nsecs_t when, uint32_t policyFlags) { int32_t actionButton = BitSet32::valueForBit(pressedButtons.clearFirstMarkedBit()); buttonState |= actionButton; dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, - 0, metaState, buttonState, 0, mCurrentCookedState.deviceTimestamp, + 0, metaState, buttonState, 0, mCurrentCookedState.cookedPointerData.pointerProperties, mCurrentCookedState.cookedPointerData.pointerCoords, mCurrentCookedState.cookedPointerData.idToIndex, idBits, -1, @@ -2030,7 +2002,6 @@ void TouchInputMapper::cookPointerData() { uint32_t currentPointerCount = mCurrentRawState.rawPointerData.pointerCount; mCurrentCookedState.cookedPointerData.clear(); - mCurrentCookedState.deviceTimestamp = mCurrentRawState.deviceTimestamp; mCurrentCookedState.cookedPointerData.pointerCount = currentPointerCount; mCurrentCookedState.cookedPointerData.hoveringIdBits = mCurrentRawState.rawPointerData.hoveringIdBits; @@ -2428,7 +2399,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag if (!dispatchedGestureIdBits.isEmpty()) { if (cancelPreviousGesture) { dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, mPointerGesture.downTime); @@ -2447,7 +2418,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_UP, 0, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, mPointerGesture.lastGestureProperties, + mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, dispatchedGestureIdBits, id, 0, 0, mPointerGesture.downTime); @@ -2460,7 +2431,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag // Send motion events for all pointers that moved. if (moveNeeded) { dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.currentGestureProperties, mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, -1, 0, 0, @@ -2480,8 +2451,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag } dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_POINTER_DOWN, 0, 0, - metaState, buttonState, 0, - /* deviceTimestamp */ 0, mPointerGesture.currentGestureProperties, + metaState, buttonState, 0, mPointerGesture.currentGestureProperties, mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, dispatchedGestureIdBits, id, 0, 0, mPointerGesture.downTime); @@ -2491,7 +2461,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag // Send motion events for hover. if (mPointerGesture.currentGestureMode == PointerGesture::HOVER) { dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.currentGestureProperties, mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex, @@ -2518,9 +2488,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &pointerProperties, &pointerCoords, 0, 0, - mPointerGesture.downTime, /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, + 0, 0, x, y, mPointerGesture.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -2548,7 +2517,7 @@ void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) int32_t metaState = getContext()->getGlobalMetaState(); int32_t buttonState = mCurrentRawState.buttonState; dispatchMotion(when, policyFlags, mSource, AMOTION_EVENT_ACTION_CANCEL, 0, 0, metaState, - buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, + buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, mPointerGesture.lastGestureProperties, mPointerGesture.lastGestureCoords, mPointerGesture.lastGestureIdToIndex, mPointerGesture.lastGestureIdBits, -1, 0, 0, mPointerGesture.downTime); @@ -3418,17 +3387,19 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, int32_t metaState = getContext()->getGlobalMetaState(); int32_t displayId = mViewport.displayId; - if (mPointerController != nullptr) { - if (down || hovering) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); - mPointerController->clearSpots(); - mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); - } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); - } - displayId = mPointerController->getDisplayId(); + if (down || hovering) { + mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->clearSpots(); + mPointerController->setButtonState(mCurrentRawState.buttonState); + mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); } + displayId = mPointerController->getDisplayId(); + + float xCursorPosition; + float yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); if (mPointerSimple.down && !down) { mPointerSimple.down = false; @@ -3437,9 +3408,9 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, - mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -3451,9 +3422,9 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, - mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -3467,11 +3438,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, mPointerSimple.downTime, - /* videoFrames */ {}); + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -3479,10 +3449,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime, - /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -3494,11 +3464,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - /* deviceTimestamp */ 0, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, mPointerSimple.downTime, - /* videoFrames */ {}); + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -3506,10 +3475,10 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime, - /* videoFrames */ {}); + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -3528,9 +3497,9 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(mContext->getNextSequenceNum(), when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, - &mPointerSimple.currentProperties, &pointerCoords, - mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, + &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, + xCursorPosition, yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); getListener()->notifyMotion(&args); } @@ -3554,7 +3523,7 @@ void TouchInputMapper::abortPointerSimple(nsecs_t when, uint32_t policyFlags) { void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, - uint32_t deviceTimestamp, const PointerProperties* properties, + const PointerProperties* properties, const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime) { @@ -3589,16 +3558,21 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 ALOG_ASSERT(false); } } - const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE); + float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; + if (mDeviceMode == DEVICE_MODE_POINTER) { + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + } + const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); const int32_t deviceId = getDeviceId(); std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId); std::for_each(frames.begin(), frames.end(), [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, policyFlags, action, actionButton, flags, metaState, buttonState, - MotionClassification::NONE, edgeFlags, deviceTimestamp, pointerCount, - pointerProperties, pointerCoords, xPrecision, yPrecision, downTime, - std::move(frames)); + MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, + pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, + downTime, std::move(frames)); getListener()->notifyMotion(&args); } @@ -3647,9 +3621,9 @@ bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) { const float scaledX = x * mXScale; const float scaledY = y * mYScale; return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue && - scaledX >= mPhysicalLeft && scaledX <= mPhysicalLeft + mPhysicalWidth && + scaledX >= mSurfaceLeft && scaledX <= mSurfaceLeft + mSurfaceWidth && y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue && - scaledY >= mPhysicalTop && scaledY <= mPhysicalTop + mPhysicalHeight; + scaledY >= mSurfaceTop && scaledY <= mSurfaceTop + mSurfaceHeight; } const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) { @@ -3895,7 +3869,7 @@ bool TouchInputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCode return true; } -std::optional<int32_t> TouchInputMapper::getAssociatedDisplay() { +std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() { if (mParameters.hasAssociatedDisplay) { if (mDeviceMode == DEVICE_MODE_POINTER) { return std::make_optional(mPointerController->getDisplayId()); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index d14812aecd..89c017d84e 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -28,68 +28,6 @@ namespace android { -/** - * Basic statistics information. - * Keep track of min, max, average, and standard deviation of the received samples. - * Used to report latency information about input events. - */ -struct LatencyStatistics { - float min; - float max; - // Sum of all samples - float sum; - // Sum of squares of all samples - float sum2; - // The number of samples - size_t count; - // The last time statistics were reported. - nsecs_t lastReportTime; - - LatencyStatistics() { reset(systemTime(SYSTEM_TIME_MONOTONIC)); } - - inline void addValue(float x) { - if (x < min) { - min = x; - } - if (x > max) { - max = x; - } - sum += x; - sum2 += x * x; - count++; - } - - // Get the average value. Should not be called if no samples have been added. - inline float mean() { - if (count == 0) { - return 0; - } - return sum / count; - } - - // Get the standard deviation. Should not be called if no samples have been added. - inline float stdev() { - if (count == 0) { - return 0; - } - float average = mean(); - return sqrt(sum2 / count - average * average); - } - - /** - * Reset internal state. The variable 'when' is the time when the data collection started. - * Call this to start a new data collection window. - */ - inline void reset(nsecs_t when) { - max = 0; - min = std::numeric_limits<float>::max(); - sum = 0; - sum2 = 0; - count = 0; - lastReportTime = when; - } -}; - /* Raw axis information from the driver. */ struct RawPointerAxes { RawAbsoluteAxisInfo x; @@ -197,23 +135,24 @@ public: explicit TouchInputMapper(InputDevice* device); virtual ~TouchInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void dump(std::string& dump); - virtual void configure(nsecs_t when, const InputReaderConfiguration* config, uint32_t changes); - virtual void reset(nsecs_t when); - virtual void process(const RawEvent* rawEvent); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void dump(std::string& dump) override; + virtual void configure(nsecs_t when, const InputReaderConfiguration* config, + uint32_t changes) override; + virtual void reset(nsecs_t when) override; + virtual void process(const RawEvent* rawEvent) override; - virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); - virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override; + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override; virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, - const int32_t* keyCodes, uint8_t* outFlags); + const int32_t* keyCodes, uint8_t* outFlags) override; - virtual void fadePointer(); - virtual void cancelTouch(nsecs_t when); - virtual void timeoutExpired(nsecs_t when); - virtual void updateExternalStylusState(const StylusState& state); - virtual std::optional<int32_t> getAssociatedDisplay(); + virtual void fadePointer() override; + virtual void cancelTouch(nsecs_t when) override; + virtual void timeoutExpired(nsecs_t when) override; + virtual void updateExternalStylusState(const StylusState& state) override; + virtual std::optional<int32_t> getAssociatedDisplayId() override; protected: CursorButtonAccumulator mCursorButtonAccumulator; @@ -358,7 +297,6 @@ protected: struct RawState { nsecs_t when; - uint32_t deviceTimestamp; // Raw pointer sample data. RawPointerData rawPointerData; @@ -371,7 +309,6 @@ protected: void copyFrom(const RawState& other) { when = other.when; - deviceTimestamp = other.deviceTimestamp; rawPointerData.copyFrom(other.rawPointerData); buttonState = other.buttonState; rawVScroll = other.rawVScroll; @@ -380,7 +317,6 @@ protected: void clear() { when = 0; - deviceTimestamp = 0; rawPointerData.clear(); buttonState = 0; rawVScroll = 0; @@ -389,7 +325,6 @@ protected: }; struct CookedState { - uint32_t deviceTimestamp; // Cooked pointer sample data. CookedPointerData cookedPointerData; @@ -401,7 +336,6 @@ protected: int32_t buttonState; void copyFrom(const CookedState& other) { - deviceTimestamp = other.deviceTimestamp; cookedPointerData.copyFrom(other.cookedPointerData); fingerIdBits = other.fingerIdBits; stylusIdBits = other.stylusIdBits; @@ -410,7 +344,6 @@ protected: } void clear() { - deviceTimestamp = 0; cookedPointerData.clear(); fingerIdBits.clear(); stylusIdBits.clear(); @@ -759,9 +692,6 @@ private: VelocityControl mWheelXVelocityControl; VelocityControl mWheelYVelocityControl; - // Latency statistics for touch events - struct LatencyStatistics mStatistics; - std::optional<DisplayViewport> findViewport(); void resetExternalStylus(); @@ -811,10 +741,9 @@ private: // it is the first / last pointer to go down / up. void dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source, int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState, - int32_t edgeFlags, uint32_t deviceTimestamp, - const PointerProperties* properties, const PointerCoords* coords, - const uint32_t* idToIndex, BitSet32 idBits, int32_t changedId, - float xPrecision, float yPrecision, nsecs_t downTime); + int32_t edgeFlags, const PointerProperties* properties, + const PointerCoords* coords, const uint32_t* idToIndex, BitSet32 idBits, + int32_t changedId, float xPrecision, float yPrecision, nsecs_t downTime); // Updates pointer coords and properties for pointers with specified ids that have moved. // Returns true if any of them changed. @@ -828,11 +757,9 @@ private: static void assignPointerIds(const RawState* last, RawState* current); - void reportEventForStatistics(nsecs_t evdevTime); - const char* modeToString(DeviceMode deviceMode); }; } // namespace android -#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H +#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H
\ No newline at end of file diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h index 6b33f4811e..dc67890a31 100644 --- a/services/inputflinger/reader/mapper/VibratorInputMapper.h +++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h @@ -26,14 +26,15 @@ public: explicit VibratorInputMapper(InputDevice* device); virtual ~VibratorInputMapper(); - virtual uint32_t getSources(); - virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); - virtual void process(const RawEvent* rawEvent); - - virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); - virtual void cancelVibrate(int32_t token); - virtual void timeoutExpired(nsecs_t when); - virtual void dump(std::string& dump); + virtual uint32_t getSources() override; + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; + virtual void process(const RawEvent* rawEvent) override; + + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) override; + virtual void cancelVibrate(int32_t token) override; + virtual void timeoutExpired(nsecs_t when) override; + virtual void dump(std::string& dump) override; private: bool mVibrating; diff --git a/services/inputflinger/tests/InputClassifierConverter_test.cpp b/services/inputflinger/tests/InputClassifierConverter_test.cpp index 813b69edbb..f58b6281df 100644 --- a/services/inputflinger/tests/InputClassifierConverter_test.cpp +++ b/services/inputflinger/tests/InputClassifierConverter_test.cpp @@ -38,12 +38,15 @@ static NotifyMotionArgs generateBasicMotionArgs() { coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 2); coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5); static constexpr nsecs_t downTime = 2; - NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/, - AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, - 0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/, - 1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/, - downTime, {}/*videoFrames*/); + NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/, + AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/, + AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties, + &coords, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, + {} /*videoFrames*/); return motionArgs; } diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index 7cc17a2215..40086ef708 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -38,12 +38,15 @@ static NotifyMotionArgs generateBasicMotionArgs() { coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1); static constexpr nsecs_t downTime = 2; - NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/, - AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN, - 0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/, - 1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/, - downTime, {}/*videoFrames*/); + NotifyMotionArgs motionArgs(1 /*sequenceNum*/, downTime /*eventTime*/, 3 /*deviceId*/, + AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4 /*policyFlags*/, + AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, 0 /*flags*/, + AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1 /*pointerCount*/, &properties, + &coords, 0 /*xPrecision*/, 0 /*yPrecision*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, + {} /*videoFrames*/); return motionArgs; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 551bee18e8..7d69854868 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -251,8 +251,10 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects undefined motion actions. event.initialize(DEVICE_ID, source, DISPLAY_ID, - /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -260,18 +262,24 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects pointer down with invalid index. event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer down index too large."; event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_DOWN | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_DOWN | + (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -279,36 +287,45 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects pointer up with invalid index. event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_UP | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too large."; event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_POINTER_UP | (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), - 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + AMOTION_EVENT_ACTION_POINTER_UP | + (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer up index too small."; // Rejects motion events with invalid number of pointers. - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with 0 pointers."; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, - /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -316,18 +333,22 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with invalid pointer ids. pointerProperties[0].id = -1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) << "Should reject motion events with pointer ids less than 0."; pointerProperties[0].id = MAX_POINTER_ID + 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -336,9 +357,11 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { // Rejects motion events with duplicate pointer ids. pointerProperties[0].id = 1; pointerProperties[1].id = 1; - event.initialize(DEVICE_ID, source, DISPLAY_ID, - AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, 0, 0, 0, 0, - ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); + event.initialize(DEVICE_ID, source, DISPLAY_ID, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, + metaState, 0, classification, 0, 0, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, + ARBITRARY_TIME, ARBITRARY_TIME, + /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(INPUT_EVENT_INJECTION_FAILED, mDispatcher->injectInputEvent( &event, INJECTOR_PID, INJECTOR_UID, INPUT_EVENT_INJECTION_SYNC_NONE, 0, 0)) @@ -448,7 +471,7 @@ public: FakeInputReceiver(dispatcher, name, displayId), mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) { mServerChannel->setToken(new BBinder()); - mDispatcher->registerInputChannel(mServerChannel, displayId); + mDispatcher->registerInputChannel(mServerChannel); inputApplicationHandle->updateInfo(); mInfo.applicationInfo = *inputApplicationHandle->getInfo(); @@ -465,6 +488,7 @@ public: mInfo.frameRight = mFrame.right; mInfo.frameBottom = mFrame.bottom; mInfo.globalScaleFactor = 1.0; + mInfo.touchableRegion.clear(); mInfo.addTouchableRegion(mFrame); mInfo.visible = true; mInfo.canReceiveKeys = true; @@ -523,8 +547,10 @@ static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher, INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } -static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source, - int32_t displayId, int32_t x = 100, int32_t y = 200) { +static int32_t injectMotionEvent(const sp<InputDispatcher>& dispatcher, int32_t action, + int32_t source, int32_t displayId, int32_t x, int32_t y, + int32_t xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION, + int32_t yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION) { MotionEvent event; PointerProperties pointerProperties[1]; PointerCoords pointerCoords[1]; @@ -539,12 +565,11 @@ static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t s nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion down event. - event.initialize(DEVICE_ID, source, displayId, - AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, - AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, - /* yPrecision */ 0, currentTime, currentTime, /*pointerCount*/ 1, pointerProperties, - pointerCoords); + event.initialize(DEVICE_ID, source, displayId, action, /* actionButton */ 0, /* flags */ 0, + /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, + /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0, + /* yPrecision */ 0, xCursorPosition, yCursorPosition, currentTime, currentTime, + /*pointerCount*/ 1, pointerProperties, pointerCoords); // Inject event until dispatch out. return dispatcher->injectInputEvent( @@ -553,6 +578,11 @@ static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t s INJECT_EVENT_TIMEOUT, POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER); } +static int32_t injectMotionDown(const sp<InputDispatcher>& dispatcher, int32_t source, + int32_t displayId, int32_t x = 100, int32_t y = 200) { + return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, x, y); +} + static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) { nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid key event. @@ -578,11 +608,12 @@ static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32 nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); // Define a valid motion event. NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId, - POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0, - AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, /* deviceTimestamp */ 0, 1, pointerProperties, - pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, currentTime, - /* videoFrames */ {}); + POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0, + AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, pointerProperties, pointerCoords, + /* xPrecision */ 0, /* yPrecision */ 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {}); return args; } @@ -706,6 +737,32 @@ TEST_F(InputDispatcherTest, SetInputWindow_InputWindowInfo) { windowSecond->consumeEvent(AINPUT_EVENT_TYPE_KEY, ADISPLAY_ID_NONE); } +TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) { + sp<FakeApplicationHandle> application = new FakeApplicationHandle(); + + sp<FakeWindowHandle> windowLeft = + new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + windowLeft->setFrame(Rect(0, 0, 600, 800)); + windowLeft->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + sp<FakeWindowHandle> windowRight = + new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + windowRight->setFrame(Rect(600, 0, 1200, 800)); + windowRight->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL); + + mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); + + std::vector<sp<InputWindowHandle>> inputWindowHandles{windowLeft, windowRight}; + mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT); + + // Inject an event with coordinate in the area of right window, with mouse cursor in the area of + // left window. This event should be dispatched to the left window. + ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE, + ADISPLAY_ID_DEFAULT, 610, 400, 599, 400)); + windowLeft->consumeEvent(AINPUT_EVENT_TYPE_MOTION, ADISPLAY_ID_DEFAULT); + windowRight->assertNoEvents(); +} + /* Test InputDispatcher for MultiDisplay */ class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest { public: diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index aeb4ad62a8..31b1652806 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -202,6 +202,20 @@ public: mConfig.setDisplayViewports(mViewports); } + bool updateViewport(const DisplayViewport& viewport) { + size_t count = mViewports.size(); + for (size_t i = 0; i < count; i++) { + const DisplayViewport& currentViewport = mViewports[i]; + if (currentViewport.displayId == viewport.displayId) { + mViewports[i] = viewport; + mConfig.setDisplayViewports(mViewports); + return true; + } + } + // no viewport found. + return false; + } + void addExcludedDeviceName(const std::string& deviceName) { mConfig.excludedDeviceNames.push_back(deviceName); } @@ -210,21 +224,9 @@ public: mConfig.portAssociations.insert({inputPort, displayPort}); } - void addDisabledDevice(int32_t deviceId) { - ssize_t index = mConfig.disabledDevices.indexOf(deviceId); - bool currentlyEnabled = index < 0; - if (currentlyEnabled) { - mConfig.disabledDevices.add(deviceId); - } - } + void addDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.insert(deviceId); } - void removeDisabledDevice(int32_t deviceId) { - ssize_t index = mConfig.disabledDevices.indexOf(deviceId); - bool currentlyEnabled = index < 0; - if (!currentlyEnabled) { - mConfig.disabledDevices.remove(deviceId); - } - } + void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); } void setPointerController(int32_t deviceId, const sp<FakePointerController>& controller) { mPointerControllers.add(deviceId, controller); @@ -345,14 +347,13 @@ class FakeEventHub : public EventHubInterface { List<RawEvent> mEvents; std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames; -protected: +public: virtual ~FakeEventHub() { for (size_t i = 0; i < mDevices.size(); i++) { delete mDevices.valueAt(i); } } -public: FakeEventHub() { } void addDevice(int32_t deviceId, const std::string& name, uint32_t classes) { @@ -548,7 +549,7 @@ private: virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const { Device* device = getDevice(deviceId); - if (device) { + if (device && device->enabled) { ssize_t index = device->absoluteAxes.indexOfKey(axis); if (index >= 0) { *outAxisInfo = device->absoluteAxes.valueAt(index); @@ -780,7 +781,7 @@ private: // --- FakeInputReaderContext --- class FakeInputReaderContext : public InputReaderContext { - sp<EventHubInterface> mEventHub; + std::shared_ptr<EventHubInterface> mEventHub; sp<InputReaderPolicyInterface> mPolicy; sp<InputListenerInterface> mListener; int32_t mGlobalMetaState; @@ -789,12 +790,14 @@ class FakeInputReaderContext : public InputReaderContext { uint32_t mNextSequenceNum; public: - FakeInputReaderContext(const sp<EventHubInterface>& eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener) : - mEventHub(eventHub), mPolicy(policy), mListener(listener), - mGlobalMetaState(0), mNextSequenceNum(1) { - } + FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub, + const sp<InputReaderPolicyInterface>& policy, + const sp<InputListenerInterface>& listener) + : mEventHub(eventHub), + mPolicy(policy), + mListener(listener), + mGlobalMetaState(0), + mNextSequenceNum(1) {} virtual ~FakeInputReaderContext() { } @@ -1019,12 +1022,10 @@ class InstrumentedInputReader : public InputReader { InputDevice* mNextDevice; public: - InstrumentedInputReader(const sp<EventHubInterface>& eventHub, - const sp<InputReaderPolicyInterface>& policy, - const sp<InputListenerInterface>& listener) : - InputReader(eventHub, policy, listener), - mNextDevice(nullptr) { - } + InstrumentedInputReader(std::shared_ptr<EventHubInterface> eventHub, + const sp<InputReaderPolicyInterface>& policy, + const sp<InputListenerInterface>& listener) + : InputReader(eventHub, policy, listener), mNextDevice(nullptr) {} virtual ~InstrumentedInputReader() { if (mNextDevice) { @@ -1252,11 +1253,11 @@ class InputReaderTest : public testing::Test { protected: sp<TestInputListener> mFakeListener; sp<FakeInputReaderPolicy> mFakePolicy; - sp<FakeEventHub> mFakeEventHub; + std::shared_ptr<FakeEventHub> mFakeEventHub; sp<InstrumentedInputReader> mReader; virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); + mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); @@ -1268,7 +1269,6 @@ protected: mFakeListener.clear(); mFakePolicy.clear(); - mFakeEventHub.clear(); } void addDevice(int32_t deviceId, const std::string& name, uint32_t classes, @@ -1578,10 +1578,14 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); mReader->loopOnce(); - // Check device. + // Device should only dispatch to the specified display. ASSERT_EQ(deviceId, device->getId()); ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, DISPLAY_ID)); ASSERT_TRUE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); + + // Can't dispatch event from a disabled device. + disableDevice(deviceId, device); + ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID)); } @@ -1590,12 +1594,13 @@ TEST_F(InputReaderTest, Device_CanDispatchToDisplay) { class InputDeviceTest : public testing::Test { protected: static const char* DEVICE_NAME; + static const char* DEVICE_LOCATION; static const int32_t DEVICE_ID; static const int32_t DEVICE_GENERATION; static const int32_t DEVICE_CONTROLLER_NUMBER; static const uint32_t DEVICE_CLASSES; - sp<FakeEventHub> mFakeEventHub; + std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; sp<TestInputListener> mFakeListener; FakeInputReaderContext* mFakeContext; @@ -1603,7 +1608,7 @@ protected: InputDevice* mDevice; virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); + mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener); @@ -1611,6 +1616,7 @@ protected: mFakeEventHub->addDevice(DEVICE_ID, DEVICE_NAME, 0); InputDeviceIdentifier identifier; identifier.name = DEVICE_NAME; + identifier.location = DEVICE_LOCATION; mDevice = new InputDevice(mFakeContext, DEVICE_ID, DEVICE_GENERATION, DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); } @@ -1621,11 +1627,11 @@ protected: delete mFakeContext; mFakeListener.clear(); mFakePolicy.clear(); - mFakeEventHub.clear(); } }; const char* InputDeviceTest::DEVICE_NAME = "device"; +const char* InputDeviceTest::DEVICE_LOCATION = "USB1"; const int32_t InputDeviceTest::DEVICE_ID = 1; const int32_t InputDeviceTest::DEVICE_GENERATION = 2; const int32_t InputDeviceTest::DEVICE_CONTROLLER_NUMBER = 0; @@ -1778,6 +1784,49 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe ASSERT_NO_FATAL_FAILURE(mapper2->assertProcessWasCalled()); } +// A single input device is associated with a specific display. Check that: +// 1. Device is disabled if the viewport corresponding to the associated display is not found +// 2. Device is disabled when setEnabled API is called +TEST_F(InputDeviceTest, Configure_AssignsDisplayPort) { + FakeInputMapper* mapper = new FakeInputMapper(mDevice, AINPUT_SOURCE_TOUCHSCREEN); + mDevice->addMapper(mapper); + + // First Configuration. + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); + + // Device should be enabled by default. + ASSERT_TRUE(mDevice->isEnabled()); + + // Prepare associated info. + constexpr uint8_t hdmi = 1; + const std::string UNIQUE_ID = "local:1"; + + mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi); + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + // Device should be disabled because it is associated with a specific display via + // input port <-> display port association, but the corresponding display is not found + ASSERT_FALSE(mDevice->isEnabled()); + + // Prepare displays. + mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, UNIQUE_ID, hdmi, + ViewportType::VIEWPORT_INTERNAL); + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + ASSERT_TRUE(mDevice->isEnabled()); + + // Device should be disabled after set disable. + mFakePolicy->addDisabledDevice(mDevice->getId()); + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_ENABLED_STATE); + ASSERT_FALSE(mDevice->isEnabled()); + + // Device should still be disabled even found the associated display. + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + ASSERT_FALSE(mDevice->isEnabled()); +} // --- InputMapperTest --- @@ -1790,14 +1839,14 @@ protected: static const int32_t DEVICE_CONTROLLER_NUMBER; static const uint32_t DEVICE_CLASSES; - sp<FakeEventHub> mFakeEventHub; + std::shared_ptr<FakeEventHub> mFakeEventHub; sp<FakeInputReaderPolicy> mFakePolicy; sp<TestInputListener> mFakeListener; FakeInputReaderContext* mFakeContext; InputDevice* mDevice; virtual void SetUp() { - mFakeEventHub = new FakeEventHub(); + mFakeEventHub = std::make_unique<FakeEventHub>(); mFakePolicy = new FakeInputReaderPolicy(); mFakeListener = new TestInputListener(); mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener); @@ -1815,7 +1864,6 @@ protected: delete mFakeContext; mFakeListener.clear(); mFakePolicy.clear(); - mFakeEventHub.clear(); } void addConfigurationProperty(const char* key, const char* value) { @@ -1950,8 +1998,9 @@ protected: void prepareDisplay(int32_t orientation); - void testDPadKeyRotation(KeyboardInputMapper* mapper, - int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode); + void testDPadKeyRotation(KeyboardInputMapper* mapper, int32_t originalScanCode, + int32_t originalKeyCode, int32_t rotatedKeyCode, + int32_t displayId = ADISPLAY_ID_NONE); }; /* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the @@ -1963,7 +2012,8 @@ void KeyboardInputMapperTest::prepareDisplay(int32_t orientation) { } void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper, - int32_t originalScanCode, int32_t originalKeyCode, int32_t rotatedKeyCode) { + int32_t originalScanCode, int32_t originalKeyCode, + int32_t rotatedKeyCode, int32_t displayId) { NotifyKeyArgs args; process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 1); @@ -1971,15 +2021,16 @@ void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper* mapper, ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, args.action); ASSERT_EQ(originalScanCode, args.scanCode); ASSERT_EQ(rotatedKeyCode, args.keyCode); + ASSERT_EQ(displayId, args.displayId); process(mapper, ARBITRARY_TIME, EV_KEY, originalScanCode, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); ASSERT_EQ(AKEY_EVENT_ACTION_UP, args.action); ASSERT_EQ(originalScanCode, args.scanCode); ASSERT_EQ(rotatedKeyCode, args.keyCode); + ASSERT_EQ(displayId, args.displayId); } - TEST_F(KeyboardInputMapperTest, GetSources) { KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, AINPUT_KEYBOARD_TYPE_ALPHABETIC); @@ -2160,47 +2211,47 @@ TEST_F(KeyboardInputMapperTest, Process_WhenOrientationAware_ShouldRotateDPad) { addMapperAndConfigure(mapper); prepareDisplay(DISPLAY_ORIENTATION_0); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_LEFT)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_DOWN, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_LEFT, DISPLAY_ID)); clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_90); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_DOWN)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_UP, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_DOWN, DISPLAY_ID)); clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_180); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_UP)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_RIGHT)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_UP, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_270); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_RIGHT, AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_DOWN)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_DOWN, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT)); - ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, - KEY_LEFT, AKEYCODE_DPAD_LEFT, AKEYCODE_DPAD_UP)); + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_DOWN, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_UP, DISPLAY_ID)); // Special case: if orientation changes while key is down, we still emit the same keycode // in the key up as we did in the key down. @@ -2384,6 +2435,84 @@ TEST_F(KeyboardInputMapperTest, Process_LockedKeysShouldToggleMetaStateAndLeds) ASSERT_EQ(AMETA_NONE, mapper->getMetaState()); } +TEST_F(KeyboardInputMapperTest, Configure_AssignsDisplayPort) { + // keyboard 1. + mFakeEventHub->addKey(DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + + // keyboard 2. + const std::string USB2 = "USB2"; + constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; + InputDeviceIdentifier identifier; + identifier.name = "KEYBOARD2"; + identifier.location = USB2; + std::unique_ptr<InputDevice> device2 = + std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, + DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); + mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/); + mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0); + mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_RIGHT, 0, AKEYCODE_DPAD_RIGHT, 0); + mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0); + mFakeEventHub->addKey(SECOND_DEVICE_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0); + + KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + addMapperAndConfigure(mapper); + + KeyboardInputMapper* mapper2 = new KeyboardInputMapper(device2.get(), AINPUT_SOURCE_KEYBOARD, + AINPUT_KEYBOARD_TYPE_ALPHABETIC); + device2->addMapper(mapper2); + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); + device2->reset(ARBITRARY_TIME); + + // Prepared displays and associated info. + constexpr uint8_t hdmi1 = 0; + constexpr uint8_t hdmi2 = 1; + const std::string SECONDARY_UNIQUE_ID = "local:1"; + + mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1); + mFakePolicy->addInputPortAssociation(USB2, hdmi2); + + // No associated display viewport found, should disable the device. + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + ASSERT_FALSE(device2->isEnabled()); + + // Prepare second display. + constexpr int32_t newDisplayId = 2; + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, + UNIQUE_ID, hdmi1, ViewportType::VIEWPORT_INTERNAL); + setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, + SECONDARY_UNIQUE_ID, hdmi2, ViewportType::VIEWPORT_EXTERNAL); + // Default device will reconfigure above, need additional reconfiguration for another device. + device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + + // Device should be enabled after the associated display is found. + ASSERT_TRUE(mDevice->isEnabled()); + ASSERT_TRUE(device2->isEnabled()); + + // Test pad key events + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_RIGHT, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_DOWN, DISPLAY_ID)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_LEFT, DISPLAY_ID)); + + ASSERT_NO_FATAL_FAILURE( + testDPadKeyRotation(mapper2, KEY_UP, AKEYCODE_DPAD_UP, AKEYCODE_DPAD_UP, newDisplayId)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_RIGHT, AKEYCODE_DPAD_RIGHT, + AKEYCODE_DPAD_RIGHT, newDisplayId)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_DOWN, AKEYCODE_DPAD_DOWN, + AKEYCODE_DPAD_DOWN, newDisplayId)); + ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper2, KEY_LEFT, AKEYCODE_DPAD_LEFT, + AKEYCODE_DPAD_LEFT, newDisplayId)); +} // --- CursorInputMapperTest --- @@ -4696,7 +4825,6 @@ protected: void processSlot(MultiTouchInputMapper* mapper, int32_t slot); void processToolType(MultiTouchInputMapper* mapper, int32_t toolType); void processKey(MultiTouchInputMapper* mapper, int32_t code, int32_t value); - void processTimestamp(MultiTouchInputMapper* mapper, uint32_t value); void processMTSync(MultiTouchInputMapper* mapper); void processSync(MultiTouchInputMapper* mapper); }; @@ -4812,10 +4940,6 @@ void MultiTouchInputMapperTest::processKey( process(mapper, ARBITRARY_TIME, EV_KEY, code, value); } -void MultiTouchInputMapperTest::processTimestamp(MultiTouchInputMapper* mapper, uint32_t value) { - process(mapper, ARBITRARY_TIME, EV_MSC, MSC_TIMESTAMP, value); -} - void MultiTouchInputMapperTest::processMTSync(MultiTouchInputMapper* mapper) { process(mapper, ARBITRARY_TIME, EV_SYN, SYN_MT_REPORT, 0); } @@ -6198,64 +6322,6 @@ TEST_F(MultiTouchInputMapperTest, Process_WhenAbsMTPressureIsPresent_HoversIfIts toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); } -TEST_F(MultiTouchInputMapperTest, Process_HandlesTimestamp) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); - - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - NotifyMotionArgs args; - - // By default, deviceTimestamp should be zero - processPosition(mapper, 100, 100); - processMTSync(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(0U, args.deviceTimestamp); - - // Now the timestamp of 1000 is reported by evdev and should appear in MotionArgs - processPosition(mapper, 0, 0); - processTimestamp(mapper, 1000); - processMTSync(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(1000U, args.deviceTimestamp); -} - -TEST_F(MultiTouchInputMapperTest, WhenMapperIsReset_TimestampIsCleared) { - MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); - - addConfigurationProperty("touch.deviceType", "touchScreen"); - prepareDisplay(DISPLAY_ORIENTATION_0); - prepareAxes(POSITION); - addMapperAndConfigure(mapper); - NotifyMotionArgs args; - - // Send a touch event with a timestamp - processPosition(mapper, 100, 100); - processTimestamp(mapper, 1); - processMTSync(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(1U, args.deviceTimestamp); - - // Since the data accumulates, and new timestamp has not arrived, deviceTimestamp won't change - processPosition(mapper, 100, 200); - processMTSync(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(1U, args.deviceTimestamp); - - mapper->reset(/* when */ 0); - // After the mapper is reset, deviceTimestamp should become zero again - processPosition(mapper, 100, 300); - processMTSync(mapper); - processSync(mapper); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(0U, args.deviceTimestamp); -} - /** * Set the input device port <--> display port associations, and check that the * events are routed to the display that matches the display port. @@ -6363,12 +6429,13 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { // Create the second touch screen device, and enable multi fingers. const std::string USB2 = "USB2"; - const int32_t SECOND_DEVICE_ID = 2; + constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1; InputDeviceIdentifier identifier; - identifier.name = DEVICE_NAME; + identifier.name = "TOUCHSCREEN2"; identifier.location = USB2; - InputDevice* device2 = new InputDevice(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, - DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); + std::unique_ptr<InputDevice> device2 = + std::make_unique<InputDevice>(mFakeContext, SECOND_DEVICE_ID, DEVICE_GENERATION, + DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES); mFakeEventHub->addDevice(SECOND_DEVICE_ID, DEVICE_NAME, 0 /*classes*/); mFakeEventHub->addAbsoluteAxis(SECOND_DEVICE_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX, 0 /*flat*/, 0 /*fuzz*/); @@ -6383,7 +6450,7 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { String8("touchScreen")); // Setup the second touch screen device. - MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2); + MultiTouchInputMapper* mapper2 = new MultiTouchInputMapper(device2.get()); device2->addMapper(mapper2); device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0 /*changes*/); device2->reset(ARBITRARY_TIME); @@ -6509,4 +6576,61 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) { ASSERT_EQ(frames, motionArgs.videoFrames); } +/** + * If we had defined port associations, but the viewport is not ready, the touch device would be + * expected to be disabled, and it should be enabled after the viewport has found. + */ +TEST_F(MultiTouchInputMapperTest, Configure_EnabledForAssociatedDisplay) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + constexpr uint8_t hdmi2 = 1; + const std::string secondaryUniqueId = "uniqueId2"; + constexpr ViewportType type = ViewportType::VIEWPORT_EXTERNAL; + + mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi2); + + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareAxes(POSITION); + addMapperAndConfigure(mapper); + + ASSERT_EQ(mDevice->isEnabled(), false); + + // Add display on hdmi2, the device should be enabled and can receive touch event. + prepareSecondaryDisplay(type, hdmi2); + ASSERT_EQ(mDevice->isEnabled(), true); + + // Send a touch event. + processPosition(mapper, 100, 100); + processSync(mapper); + + NotifyMotionArgs args; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(SECONDARY_DISPLAY_ID, args.displayId); +} + +/** + * Test touch should not work if outside of surface. + */ +TEST_F(MultiTouchInputMapperTest, Viewports_SurfaceRange) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + // Let surface be different from physical display. + std::optional<DisplayViewport> internalViewport = + mFakePolicy->getDisplayViewportByType(ViewportType::VIEWPORT_INTERNAL); + internalViewport->logicalLeft = internalViewport->physicalTop + 20; + internalViewport->logicalTop = internalViewport->physicalRight + 20; + internalViewport->logicalRight = internalViewport->physicalRight - 20; + internalViewport->logicalBottom = internalViewport->physicalBottom - 20; + mFakePolicy->updateViewport(internalViewport.value()); + + prepareAxes(POSITION); + addMapperAndConfigure(mapper); + + int32_t rawX = 10; + int32_t rawY = 10; + processPosition(mapper, rawX, rawY); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); +} + } // namespace android diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 717f31769b..c7a8f5bee3 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -134,7 +134,12 @@ void SensorDevice::initializeSensorList() { mActivationCount.add(list[i].sensorHandle, model); - checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */)); + // Only disable all sensors on HAL 1.0 since HAL 2.0 + // handles this in its initialize method + if (!mSensors->supportsMessageQueues()) { + checkReturn(mSensors->activate(list[i].sensorHandle, + 0 /* enabled */)); + } } })); } diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index cda982ac23..e5d23d0d1f 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -19,7 +19,7 @@ cc_defaults { "-DEGL_EGLEXT_PROTOTYPES", ], shared_libs: [ - "android.frameworks.vr.composer@1.0", + "android.frameworks.vr.composer@2.0", "android.hardware.configstore-utils", "android.hardware.configstore@1.0", "android.hardware.configstore@1.1", @@ -29,6 +29,7 @@ cc_defaults { "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", + "android.hardware.graphics.composer@2.4", "android.hardware.power@1.0", "android.hardware.power@1.3", "libbase", @@ -56,10 +57,20 @@ cc_defaults { "libutils", "libSurfaceFlingerProp", ], + // VrComposer is not used when building surfaceflinger for vendors + target: { + vendor: { + exclude_shared_libs: [ + "android.frameworks.vr.composer@2.0", + ], + }, + }, static_libs: [ "libcompositionengine", + "libperfetto_client_experimental", "librenderengine", "libserviceutils", + "libtimestats", "libtrace_proto", "libvr_manager", "libvrflinger", @@ -73,6 +84,7 @@ cc_defaults { "libcompositionengine", "librenderengine", "libserviceutils", + "libtimestats", ], export_shared_lib_headers: [ "android.hardware.graphics.allocator@2.0", @@ -81,6 +93,7 @@ cc_defaults { "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", + "android.hardware.graphics.composer@2.4", "android.hardware.power@1.3", "libhidlbase", ], @@ -130,11 +143,11 @@ filegroup { "DisplayHardware/VirtualDisplaySurface.cpp", "Effects/Daltonizer.cpp", "EventLog/EventLog.cpp", + "FrameTracer/FrameTracer.cpp", "FrameTracker.cpp", "Layer.cpp", "LayerProtoHelper.cpp", "LayerRejecter.cpp", - "LayerStats.cpp", "LayerVector.cpp", "MonitoredProducer.cpp", "NativeWindowSurface.cpp", @@ -145,7 +158,7 @@ filegroup { "Scheduler/DispSyncSource.cpp", "Scheduler/EventControlThread.cpp", "Scheduler/EventThread.cpp", - "Scheduler/IdleTimer.cpp", + "Scheduler/OneShotTimer.cpp", "Scheduler/LayerHistory.cpp", "Scheduler/LayerInfo.cpp", "Scheduler/MessageQueue.cpp", @@ -155,9 +168,9 @@ filegroup { "Scheduler/VSyncModulator.cpp", "StartPropertySetThread.cpp", "SurfaceFlinger.cpp", + "SurfaceFlingerDefaultFactory.cpp", "SurfaceInterceptor.cpp", "SurfaceTracing.cpp", - "TimeStats/TimeStats.cpp", "TransactionCompletedThread.cpp", ], } @@ -174,6 +187,17 @@ cc_library_shared { // can be easily replaced. "SurfaceFlingerFactory.cpp", ], + cflags: [ + "-DUSE_VR_COMPOSER=1", + ], + // VrComposer is not used when building surfaceflinger for vendors + target: { + vendor: { + cflags: [ + "-DUSE_VR_COMPOSER=0", + ], + }, + }, logtags: ["EventLog/EventLogTags.logtags"], } @@ -226,7 +250,6 @@ cc_binary { subdirs = [ "layerproto", - "TimeStats/timestatsproto", "tests", ] diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index f51fbb45f6..b500ad3eee 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -22,17 +22,17 @@ #include "BufferLayer.h" #include <compositionengine/CompositionEngine.h> -#include <compositionengine/Display.h> #include <compositionengine/Layer.h> #include <compositionengine/LayerCreationArgs.h> +#include <compositionengine/LayerFECompositionState.h> #include <compositionengine/OutputLayer.h> -#include <compositionengine/impl/LayerCompositionState.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <cutils/compiler.h> #include <cutils/native_handle.h> #include <cutils/properties.h> #include <gui/BufferItem.h> #include <gui/BufferQueue.h> +#include <gui/GLConsumer.h> #include <gui/LayerDebugInfo.h> #include <gui/Surface.h> #include <renderengine/RenderEngine.h> @@ -50,6 +50,7 @@ #include "Colorizer.h" #include "DisplayDevice.h" +#include "FrameTracer/FrameTracer.h" #include "LayerRejecter.h" #include "TimeStats/TimeStats.h" @@ -57,7 +58,7 @@ namespace android { BufferLayer::BufferLayer(const LayerCreationArgs& args) : Layer(args), - mTextureName(args.flinger->getNewTexture()), + mTextureName(args.textureName), mCompositionLayer{mFlinger->getCompositionEngine().createLayer( compositionengine::LayerCreationArgs{this})} { ALOGV("Creating Layer %s", args.name.string()); @@ -69,15 +70,23 @@ BufferLayer::BufferLayer(const LayerCreationArgs& args) } BufferLayer::~BufferLayer() { - mFlinger->deleteTextureAsync(mTextureName); - mFlinger->mTimeStats->onDestroy(getSequence()); + if (!isClone()) { + // The original layer and the clone layer share the same texture. Therefore, only one of + // the layers, in this case the original layer, needs to handle the deletion. The original + // layer and the clone should be removed at the same time so there shouldn't be any issue + // with the clone layer trying to use the deleted texture. + mFlinger->deleteTextureAsync(mTextureName); + } + const int32_t layerID = getSequence(); + mFlinger->mTimeStats->onDestroy(layerID); + mFlinger->mFrameTracer->onDestroy(layerID); } void BufferLayer::useSurfaceDamage() { if (mFlinger->mForceFullDamage) { surfaceDamageRegion = Region::INVALID_REGION; } else { - surfaceDamageRegion = getDrawingSurfaceDamage(); + surfaceDamageRegion = mBufferInfo.mSurfaceDamage; } } @@ -88,7 +97,7 @@ void BufferLayer::useEmptyDamage() { bool BufferLayer::isOpaque(const Layer::State& s) const { // if we don't have a buffer or sidebandStream yet, we're translucent regardless of the // layer's opaque flag. - if ((mSidebandStream == nullptr) && (mActiveBuffer == nullptr)) { + if ((mSidebandStream == nullptr) && (mBufferInfo.mBuffer == nullptr)) { return false; } @@ -99,7 +108,7 @@ bool BufferLayer::isOpaque(const Layer::State& s) const { bool BufferLayer::isVisible() const { bool visible = !(isHiddenByPolicy()) && getAlpha() > 0.0f && - (mActiveBuffer != nullptr || mSidebandStream != nullptr); + (mBufferInfo.mBuffer != nullptr || mSidebandStream != nullptr); mFlinger->mScheduler->setLayerVisibility(mSchedulerLayerHandle, visible); return visible; @@ -131,14 +140,16 @@ static constexpr mat4 inverseOrientation(uint32_t transform) { return inverse(tr); } -bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform, Region& clearRegion, - const bool supportProtectedContent, - renderengine::LayerSettings& layer) { +std::optional<renderengine::LayerSettings> BufferLayer::prepareClientComposition( + compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) { ATRACE_CALL(); - Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, - supportProtectedContent, layer); - if (CC_UNLIKELY(mActiveBuffer == 0)) { + + auto result = Layer::prepareClientComposition(targetSettings); + if (!result) { + return result; + } + + if (CC_UNLIKELY(mBufferInfo.mBuffer == 0)) { // the texture has not been created yet, this Layer has // in fact never been drawn into. This happens frequently with // SurfaceView because the WindowManager can't know when the client @@ -155,33 +166,33 @@ bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& finished = true; return; } - under.orSelf(layer->visibleRegion); + + under.orSelf(layer->getScreenBounds()); }); // if not everything below us is covered, we plug the holes! - Region holes(clip.subtract(under)); + Region holes(targetSettings.clip.subtract(under)); if (!holes.isEmpty()) { - clearRegion.orSelf(holes); + targetSettings.clearRegion.orSelf(holes); } - return false; + return std::nullopt; } - bool blackOutLayer = - (isProtected() && !supportProtectedContent) || (isSecure() && !renderArea.isSecure()); + bool blackOutLayer = (isProtected() && !targetSettings.supportsProtectedContent) || + (isSecure() && !targetSettings.isSecure); const State& s(getDrawingState()); + auto& layer = *result; if (!blackOutLayer) { - layer.source.buffer.buffer = mActiveBuffer; + layer.source.buffer.buffer = mBufferInfo.mBuffer; layer.source.buffer.isOpaque = isOpaque(s); - layer.source.buffer.fence = mActiveBufferFence; + layer.source.buffer.fence = mBufferInfo.mFence; layer.source.buffer.textureName = mTextureName; layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha(); layer.source.buffer.isY410BT2020 = isHdrY410(); // TODO: we could be more subtle with isFixedSize() - const bool useFiltering = needsFiltering(renderArea.getDisplayDevice()) || - renderArea.needsFiltering() || isFixedSize(); + const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize(); // Query the texture matrix given our current filtering mode. float textureMatrix[16]; - setFilteringEnabled(useFiltering); - getDrawingTransformMatrix(textureMatrix); + getDrawingTransformMatrix(useFiltering, textureMatrix); if (getTransformToDisplayInverse()) { /* @@ -243,104 +254,34 @@ bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& layer.alpha = 1.0; } - return true; + return result; } bool BufferLayer::isHdrY410() const { // pixel format is HDR Y410 masquerading as RGBA_1010102 - return (mCurrentDataSpace == ui::Dataspace::BT2020_ITU_PQ && - getDrawingApi() == NATIVE_WINDOW_API_MEDIA && - mActiveBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102); + return (mBufferInfo.mDataspace == ui::Dataspace::BT2020_ITU_PQ && + mBufferInfo.mApi == NATIVE_WINDOW_API_MEDIA && + mBufferInfo.mBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_RGBA_1010102); } -void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice, - const ui::Transform& transform, const Rect& viewport, - int32_t supportedPerFrameMetadata, - const ui::Dataspace targetDataspace) { - RETURN_IF_NO_HWC_LAYER(displayDevice); - - // Apply this display's projection's viewport to the visible region - // before giving it to the HWC HAL. - Region visible = transform.transform(visibleRegion.intersect(viewport)); - - const auto outputLayer = findOutputLayerForDisplay(displayDevice); - LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc); - - auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer; - auto error = hwcLayer->setVisibleRegion(visible); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(), - to_string(error).c_str(), static_cast<int32_t>(error)); - visible.dump(LOG_TAG); - } - outputLayer->editState().visibleRegion = visible; - - auto& layerCompositionState = getCompositionLayer()->editState().frontEnd; - - error = hwcLayer->setSurfaceDamage(surfaceDamageRegion); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(), - to_string(error).c_str(), static_cast<int32_t>(error)); - surfaceDamageRegion.dump(LOG_TAG); - } - layerCompositionState.surfaceDamage = surfaceDamageRegion; +void BufferLayer::latchPerFrameState( + compositionengine::LayerFECompositionState& compositionState) const { + Layer::latchPerFrameState(compositionState); // Sideband layers - if (layerCompositionState.sidebandStream.get()) { - setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::SIDEBAND); - ALOGV("[%s] Requesting Sideband composition", mName.string()); - error = hwcLayer->setSidebandStream(layerCompositionState.sidebandStream->handle()); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", mName.string(), - layerCompositionState.sidebandStream->handle(), to_string(error).c_str(), - static_cast<int32_t>(error)); - } - layerCompositionState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND; - return; - } - - // Device or Cursor layers - if (mPotentialCursor) { - ALOGV("[%s] Requesting Cursor composition", mName.string()); - setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CURSOR); + if (compositionState.sidebandStream.get()) { + compositionState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND; } else { - ALOGV("[%s] Requesting Device composition", mName.string()); - setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::DEVICE); - } - - ui::Dataspace dataspace = isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN - ? targetDataspace - : mCurrentDataSpace; - error = hwcLayer->setDataspace(dataspace); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace, - to_string(error).c_str(), static_cast<int32_t>(error)); - } - - const HdrMetadata& metadata = getDrawingHdrMetadata(); - error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata, metadata); - if (error != HWC2::Error::None && error != HWC2::Error::Unsupported) { - ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", mName.string(), - to_string(error).c_str(), static_cast<int32_t>(error)); + // Normal buffer layers + compositionState.hdrMetadata = mBufferInfo.mHdrMetadata; + compositionState.compositionType = mPotentialCursor + ? Hwc2::IComposerClient::Composition::CURSOR + : Hwc2::IComposerClient::Composition::DEVICE; } - - error = hwcLayer->setColorTransform(getColorTransform()); - if (error == HWC2::Error::Unsupported) { - // If per layer color transform is not supported, we use GPU composition. - setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::CLIENT); - } else if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(), - to_string(error).c_str(), static_cast<int32_t>(error)); - } - layerCompositionState.dataspace = mCurrentDataSpace; - layerCompositionState.colorTransform = getColorTransform(); - layerCompositionState.hdrMetadata = metadata; - - setHwcLayerBuffer(displayDevice); } bool BufferLayer::onPreComposition(nsecs_t refreshStartTime) { - if (mBufferLatched) { + if (mBufferInfo.mBuffer != nullptr) { Mutex::Autolock lock(mFrameEventHistoryMutex); mFrameEventHistory.addPreComposition(mCurrentFrameNumber, refreshStartTime); } @@ -354,7 +295,7 @@ bool BufferLayer::onPostComposition(const std::optional<DisplayId>& displayId, const CompositorTiming& compositorTiming) { // mFrameLatencyNeeded is true when a new frame was latched for the // composition. - if (!mFrameLatencyNeeded) return false; + if (!mBufferInfo.mFrameLatencyNeeded) return false; // Update mFrameEventHistory. { @@ -364,13 +305,13 @@ bool BufferLayer::onPostComposition(const std::optional<DisplayId>& displayId, } // Update mFrameTracker. - nsecs_t desiredPresentTime = getDesiredPresentTime(); + nsecs_t desiredPresentTime = mBufferInfo.mDesiredPresentTime; mFrameTracker.setDesiredPresentTime(desiredPresentTime); const int32_t layerID = getSequence(); mFlinger->mTimeStats->setDesiredTime(layerID, mCurrentFrameNumber, desiredPresentTime); - std::shared_ptr<FenceTime> frameReadyFence = getCurrentFenceTime(); + std::shared_ptr<FenceTime> frameReadyFence = mBufferInfo.mFenceTime; if (frameReadyFence->isValid()) { mFrameTracker.setFrameReadyFence(std::move(frameReadyFence)); } else { @@ -381,21 +322,27 @@ bool BufferLayer::onPostComposition(const std::optional<DisplayId>& displayId, if (presentFence->isValid()) { mFlinger->mTimeStats->setPresentFence(layerID, mCurrentFrameNumber, presentFence); + mFlinger->mFrameTracer->traceFence(layerID, getCurrentBufferId(), mCurrentFrameNumber, + presentFence, FrameTracer::FrameEvent::PRESENT_FENCE); mFrameTracker.setActualPresentFence(std::shared_ptr<FenceTime>(presentFence)); } else if (displayId && mFlinger->getHwComposer().isConnected(*displayId)) { // The HWC doesn't support present fences, so use the refresh // timestamp instead. const nsecs_t actualPresentTime = mFlinger->getHwComposer().getRefreshTimestamp(*displayId); mFlinger->mTimeStats->setPresentTime(layerID, mCurrentFrameNumber, actualPresentTime); + mFlinger->mFrameTracer->traceTimestamp(layerID, getCurrentBufferId(), mCurrentFrameNumber, + actualPresentTime, + FrameTracer::FrameEvent::PRESENT_FENCE); mFrameTracker.setActualPresentTime(actualPresentTime); } mFrameTracker.advanceFrame(); - mFrameLatencyNeeded = false; + mBufferInfo.mFrameLatencyNeeded = false; return true; } -bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) { +bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, + nsecs_t expectedPresentTime) { ATRACE_CALL(); bool refreshRequired = latchSidebandStream(recomputeVisibleRegions); @@ -428,14 +375,15 @@ bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) // Capture the old state of the layer for comparisons later const State& s(getDrawingState()); const bool oldOpacity = isOpaque(s); - sp<GraphicBuffer> oldBuffer = mActiveBuffer; - if (!allTransactionsSignaled()) { + BufferInfo oldBufferInfo = mBufferInfo; + + if (!allTransactionsSignaled(expectedPresentTime)) { mFlinger->setTransactionFlags(eTraversalNeeded); return false; } - status_t err = updateTexImage(recomputeVisibleRegions, latchTime); + status_t err = updateTexImage(recomputeVisibleRegions, latchTime, expectedPresentTime); if (err != NO_ERROR) { return false; } @@ -445,65 +393,33 @@ bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) return false; } - mBufferLatched = true; - err = updateFrameNumber(latchTime); if (err != NO_ERROR) { return false; } + gatherBufferInfo(); + mRefreshPending = true; - mFrameLatencyNeeded = true; - if (oldBuffer == nullptr) { + mBufferInfo.mFrameLatencyNeeded = true; + if (oldBufferInfo.mBuffer == nullptr) { // the first time we receive a buffer, we need to trigger a // geometry invalidation. recomputeVisibleRegions = true; } - ui::Dataspace dataSpace = getDrawingDataSpace(); - // translate legacy dataspaces to modern dataspaces - switch (dataSpace) { - case ui::Dataspace::SRGB: - dataSpace = ui::Dataspace::V0_SRGB; - break; - case ui::Dataspace::SRGB_LINEAR: - dataSpace = ui::Dataspace::V0_SRGB_LINEAR; - break; - case ui::Dataspace::JFIF: - dataSpace = ui::Dataspace::V0_JFIF; - break; - case ui::Dataspace::BT601_625: - dataSpace = ui::Dataspace::V0_BT601_625; - break; - case ui::Dataspace::BT601_525: - dataSpace = ui::Dataspace::V0_BT601_525; - break; - case ui::Dataspace::BT709: - dataSpace = ui::Dataspace::V0_BT709; - break; - default: - break; - } - mCurrentDataSpace = dataSpace; - - Rect crop(getDrawingCrop()); - const uint32_t transform(getDrawingTransform()); - const uint32_t scalingMode(getDrawingScalingMode()); - const bool transformToDisplayInverse(getTransformToDisplayInverse()); - if ((crop != mCurrentCrop) || (transform != mCurrentTransform) || - (scalingMode != mCurrentScalingMode) || - (transformToDisplayInverse != mTransformToDisplayInverse)) { - mCurrentCrop = crop; - mCurrentTransform = transform; - mCurrentScalingMode = scalingMode; - mTransformToDisplayInverse = transformToDisplayInverse; + if ((mBufferInfo.mCrop != oldBufferInfo.mCrop) || + (mBufferInfo.mTransform != oldBufferInfo.mTransform) || + (mBufferInfo.mScaleMode != oldBufferInfo.mScaleMode) || + (mBufferInfo.mTransformToDisplayInverse != oldBufferInfo.mTransformToDisplayInverse)) { recomputeVisibleRegions = true; } - if (oldBuffer != nullptr) { - uint32_t bufWidth = mActiveBuffer->getWidth(); - uint32_t bufHeight = mActiveBuffer->getHeight(); - if (bufWidth != uint32_t(oldBuffer->width) || bufHeight != uint32_t(oldBuffer->height)) { + if (oldBufferInfo.mBuffer != nullptr) { + uint32_t bufWidth = mBufferInfo.mBuffer->getWidth(); + uint32_t bufHeight = mBufferInfo.mBuffer->getHeight(); + if (bufWidth != uint32_t(oldBufferInfo.mBuffer->width) || + bufHeight != uint32_t(oldBufferInfo.mBuffer->height)) { recomputeVisibleRegions = true; } } @@ -540,10 +456,10 @@ bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) } // transaction -void BufferLayer::notifyAvailableFrames() { - const auto headFrameNumber = getHeadFrameNumber(); +void BufferLayer::notifyAvailableFrames(nsecs_t expectedPresentTime) { + const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime); const bool headFenceSignaled = fenceHasSignaled(); - const bool presentTimeIsCurrent = framePresentTimeIsCurrent(); + const bool presentTimeIsCurrent = framePresentTimeIsCurrent(expectedPresentTime); Mutex::Autolock lock(mLocalSyncPointMutex); for (auto& point : mLocalSyncPoints) { if (headFrameNumber >= point->getFrameNumber() && headFenceSignaled && @@ -568,11 +484,11 @@ uint32_t BufferLayer::getEffectiveScalingMode() const { return mOverrideScalingMode; } - return mCurrentScalingMode; + return mBufferInfo.mScaleMode; } bool BufferLayer::isProtected() const { - const sp<GraphicBuffer>& buffer(mActiveBuffer); + const sp<GraphicBuffer>& buffer(mBufferInfo.mBuffer); return (buffer != 0) && (buffer->getUsage() & GRALLOC_USAGE_PROTECTED); } @@ -591,8 +507,8 @@ bool BufferLayer::latchUnsignaledBuffers() { } // h/w composer set-up -bool BufferLayer::allTransactionsSignaled() { - auto headFrameNumber = getHeadFrameNumber(); +bool BufferLayer::allTransactionsSignaled(nsecs_t expectedPresentTime) { + const auto headFrameNumber = getHeadFrameNumber(expectedPresentTime); bool matchingFramesFound = false; bool allTransactionsApplied = true; Mutex::Autolock lock(mLocalSyncPointMutex); @@ -640,27 +556,29 @@ bool BufferLayer::getOpacityForFormat(uint32_t format) { } bool BufferLayer::needsFiltering(const sp<const DisplayDevice>& displayDevice) const { - // If we are not capturing based on the state of a known display device, we - // only return mNeedsFiltering + // If we are not capturing based on the state of a known display device, + // just return false. if (displayDevice == nullptr) { - return mNeedsFiltering; + return false; } const auto outputLayer = findOutputLayerForDisplay(displayDevice); if (outputLayer == nullptr) { - return mNeedsFiltering; + return false; } + // We need filtering if the sourceCrop rectangle size does not match the + // displayframe rectangle size (not a 1:1 render) const auto& compositionState = outputLayer->getState(); const auto displayFrame = compositionState.displayFrame; const auto sourceCrop = compositionState.sourceCrop; - return mNeedsFiltering || sourceCrop.getHeight() != displayFrame.getHeight() || + return sourceCrop.getHeight() != displayFrame.getHeight() || sourceCrop.getWidth() != displayFrame.getWidth(); } -uint64_t BufferLayer::getHeadFrameNumber() const { +uint64_t BufferLayer::getHeadFrameNumber(nsecs_t expectedPresentTime) const { if (hasFrameUpdate()) { - return getFrameNumber(); + return getFrameNumber(expectedPresentTime); } else { return mCurrentFrameNumber; } @@ -674,15 +592,15 @@ Rect BufferLayer::getBufferSize(const State& s) const { return Rect(getActiveWidth(s), getActiveHeight(s)); } - if (mActiveBuffer == nullptr) { + if (mBufferInfo.mBuffer == nullptr) { return Rect::INVALID_RECT; } - uint32_t bufWidth = mActiveBuffer->getWidth(); - uint32_t bufHeight = mActiveBuffer->getHeight(); + uint32_t bufWidth = mBufferInfo.mBuffer->getWidth(); + uint32_t bufHeight = mBufferInfo.mBuffer->getHeight(); // Undo any transformations on the buffer and return the result. - if (mCurrentTransform & ui::Transform::ROT_90) { + if (mBufferInfo.mTransform & ui::Transform::ROT_90) { std::swap(bufWidth, bufHeight); } @@ -710,15 +628,15 @@ FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const return FloatRect(0, 0, getActiveWidth(s), getActiveHeight(s)); } - if (mActiveBuffer == nullptr) { + if (mBufferInfo.mBuffer == nullptr) { return parentBounds; } - uint32_t bufWidth = mActiveBuffer->getWidth(); - uint32_t bufHeight = mActiveBuffer->getHeight(); + uint32_t bufWidth = mBufferInfo.mBuffer->getWidth(); + uint32_t bufHeight = mBufferInfo.mBuffer->getHeight(); // Undo any transformations on the buffer and return the result. - if (mCurrentTransform & ui::Transform::ROT_90) { + if (mBufferInfo.mTransform & ui::Transform::ROT_90) { std::swap(bufWidth, bufHeight); } @@ -732,6 +650,122 @@ FloatRect BufferLayer::computeSourceBounds(const FloatRect& parentBounds) const return FloatRect(0, 0, bufWidth, bufHeight); } +void BufferLayer::latchAndReleaseBuffer() { + mRefreshPending = false; + if (hasReadyFrame()) { + bool ignored = false; + latchBuffer(ignored, systemTime(), 0 /* expectedPresentTime */); + } + releasePendingBuffer(systemTime()); +} + +PixelFormat BufferLayer::getPixelFormat() const { + return mBufferInfo.mPixelFormat; +} + +bool BufferLayer::getTransformToDisplayInverse() const { + return mBufferInfo.mTransformToDisplayInverse; +} + +Rect BufferLayer::getBufferCrop() const { + // this is the crop rectangle that applies to the buffer + // itself (as opposed to the window) + if (!mBufferInfo.mCrop.isEmpty()) { + // if the buffer crop is defined, we use that + return mBufferInfo.mCrop; + } else if (mBufferInfo.mBuffer != nullptr) { + // otherwise we use the whole buffer + return mBufferInfo.mBuffer->getBounds(); + } else { + // if we don't have a buffer yet, we use an empty/invalid crop + return Rect(); + } +} + +uint32_t BufferLayer::getBufferTransform() const { + return mBufferInfo.mTransform; +} + +ui::Dataspace BufferLayer::getDataSpace() const { + return mBufferInfo.mDataspace; +} + +ui::Dataspace BufferLayer::translateDataspace(ui::Dataspace dataspace) { + ui::Dataspace updatedDataspace = dataspace; + // translate legacy dataspaces to modern dataspaces + switch (dataspace) { + case ui::Dataspace::SRGB: + updatedDataspace = ui::Dataspace::V0_SRGB; + break; + case ui::Dataspace::SRGB_LINEAR: + updatedDataspace = ui::Dataspace::V0_SRGB_LINEAR; + break; + case ui::Dataspace::JFIF: + updatedDataspace = ui::Dataspace::V0_JFIF; + break; + case ui::Dataspace::BT601_625: + updatedDataspace = ui::Dataspace::V0_BT601_625; + break; + case ui::Dataspace::BT601_525: + updatedDataspace = ui::Dataspace::V0_BT601_525; + break; + case ui::Dataspace::BT709: + updatedDataspace = ui::Dataspace::V0_BT709; + break; + default: + break; + } + + return updatedDataspace; +} + +sp<GraphicBuffer> BufferLayer::getBuffer() const { + return mBufferInfo.mBuffer; +} + +void BufferLayer::getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]) { + GLConsumer::computeTransformMatrix(outMatrix, mBufferInfo.mBuffer, mBufferInfo.mCrop, + mBufferInfo.mTransform, filteringEnabled); +} + +void BufferLayer::setInitialValuesForClone(const sp<Layer>& clonedFrom) { + Layer::setInitialValuesForClone(clonedFrom); + + sp<BufferLayer> bufferClonedFrom = static_cast<BufferLayer*>(clonedFrom.get()); + mPremultipliedAlpha = bufferClonedFrom->mPremultipliedAlpha; + mPotentialCursor = bufferClonedFrom->mPotentialCursor; + mProtectedByApp = bufferClonedFrom->mProtectedByApp; + + updateCloneBufferInfo(); +} + +void BufferLayer::updateCloneBufferInfo() { + if (!isClone() || !isClonedFromAlive()) { + return; + } + + sp<BufferLayer> clonedFrom = static_cast<BufferLayer*>(getClonedFrom().get()); + mBufferInfo = clonedFrom->mBufferInfo; + mSidebandStream = clonedFrom->mSidebandStream; + surfaceDamageRegion = clonedFrom->surfaceDamageRegion; + mCurrentFrameNumber = clonedFrom->mCurrentFrameNumber.load(); + mPreviousFrameNumber = clonedFrom->mPreviousFrameNumber; + + // After buffer info is updated, the drawingState from the real layer needs to be copied into + // the cloned. This is because some properties of drawingState can change when latchBuffer is + // called. However, copying the drawingState would also overwrite the cloned layer's relatives. + // Therefore, temporarily store the relatives so they can be set in the cloned drawingState + // again. + wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf; + SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives; + mDrawingState = clonedFrom->mDrawingState; + // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple + // InputWindows per client token yet. + mDrawingState.inputInfo.token = nullptr; + mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf; + mDrawingState.zOrderRelatives = tmpZOrderRelatives; +} + } // namespace android #if defined(__gl_h_) diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index 46a62eda91..656ba12a19 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -62,10 +62,6 @@ public: void useSurfaceDamage() override; void useEmptyDamage() override; - // getTypeId - Provide unique string for each class type in the Layer - // hierarchy - const char* getTypeId() const override { return "BufferLayer"; } - bool isOpaque(const Layer::State& s) const override; // isVisible - true if this layer is visible, false otherwise @@ -82,11 +78,6 @@ public: bool isHdrY410() const override; - void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, - const Rect& viewport, int32_t supportedPerFrameMetadata, - const ui::Dataspace targetDataspace) override; - - bool onPreComposition(nsecs_t refreshStartTime) override; bool onPostComposition(const std::optional<DisplayId>& displayId, const std::shared_ptr<FenceTime>& glDoneFence, const std::shared_ptr<FenceTime>& presentFence, @@ -96,17 +87,35 @@ public: // the visible regions need to be recomputed (this is a fairly heavy // operation, so this should be set only if needed). Typically this is used // to figure out if the content or size of a surface has changed. - bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override; + bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, + nsecs_t expectedPresentTime) override; bool isBufferLatched() const override { return mRefreshPending; } - void notifyAvailableFrames() override; + void notifyAvailableFrames(nsecs_t expectedPresentTime) override; bool hasReadyFrame() const override; // Returns the current scaling mode, unless mOverrideScalingMode // is set, in which case, it returns mOverrideScalingMode uint32_t getEffectiveScalingMode() const override; + + // Calls latchBuffer if the buffer has a frame queued and then releases the buffer. + // This is used if the buffer is just latched and releases to free up the buffer + // and will not be shown on screen. + // Should only be called on the main thread. + void latchAndReleaseBuffer() override; + + bool getTransformToDisplayInverse() const override; + + Rect getBufferCrop() const override; + + uint32_t getBufferTransform() const override; + + ui::Dataspace getDataSpace() const override; + + sp<GraphicBuffer> getBuffer() const override; + // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- @@ -114,22 +123,15 @@ public: // ----------------------------------------------------------------------- private: virtual bool fenceHasSignaled() const = 0; - virtual bool framePresentTimeIsCurrent() const = 0; + virtual bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const = 0; - virtual nsecs_t getDesiredPresentTime() = 0; - virtual std::shared_ptr<FenceTime> getCurrentFenceTime() const = 0; + PixelFormat getPixelFormat() const; - virtual void getDrawingTransformMatrix(float *matrix) = 0; - virtual uint32_t getDrawingTransform() const = 0; - virtual ui::Dataspace getDrawingDataSpace() const = 0; - virtual Rect getDrawingCrop() const = 0; - virtual uint32_t getDrawingScalingMode() const = 0; - virtual Region getDrawingSurfaceDamage() const = 0; - virtual const HdrMetadata& getDrawingHdrMetadata() const = 0; - virtual int getDrawingApi() const = 0; - virtual PixelFormat getPixelFormat() const = 0; + // Computes the transform matrix using the setFilteringEnabled to determine whether the + // transform matrix should be computed for use with bilinear filtering. + void getDrawingTransformMatrix(bool filteringEnabled, float outMatrix[16]); - virtual uint64_t getFrameNumber() const = 0; + virtual uint64_t getFrameNumber(nsecs_t expectedPresentTime) const = 0; virtual bool getAutoRefresh() const = 0; virtual bool getSidebandStreamChanged() const = 0; @@ -139,24 +141,52 @@ private: virtual bool hasFrameUpdate() const = 0; - virtual void setFilteringEnabled(bool enabled) = 0; - virtual status_t bindTextureImage() = 0; - virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) = 0; + virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + nsecs_t expectedPresentTime) = 0; virtual status_t updateActiveBuffer() = 0; virtual status_t updateFrameNumber(nsecs_t latchTime) = 0; - virtual void setHwcLayerBuffer(const sp<const DisplayDevice>& displayDevice) = 0; - protected: + struct BufferInfo { + nsecs_t mDesiredPresentTime; + std::shared_ptr<FenceTime> mFenceTime; + sp<Fence> mFence; + uint32_t mTransform{0}; + ui::Dataspace mDataspace{ui::Dataspace::UNKNOWN}; + Rect mCrop; + uint32_t mScaleMode{NATIVE_WINDOW_SCALING_MODE_FREEZE}; + Region mSurfaceDamage; + HdrMetadata mHdrMetadata; + int mApi; + PixelFormat mPixelFormat; + bool mTransformToDisplayInverse{false}; + + sp<GraphicBuffer> mBuffer; + int mBufferSlot{BufferQueue::INVALID_BUFFER_SLOT}; + + bool mFrameLatencyNeeded{false}; + }; + + BufferInfo mBufferInfo; + virtual void gatherBufferInfo() = 0; + + /* + * compositionengine::LayerFE overrides + */ + bool onPreComposition(nsecs_t) override; + void latchPerFrameState(compositionengine::LayerFECompositionState&) const override; + std::optional<renderengine::LayerSettings> prepareClientComposition( + compositionengine::LayerFE::ClientCompositionTargetSettings&) override; + // Loads the corresponding system property once per process static bool latchUnsignaledBuffers(); // Check all of the local sync points to ensure that all transactions // which need to have been applied prior to the frame which is about to // be latched have signaled - bool allTransactionsSignaled(); + bool allTransactionsSignaled(nsecs_t expectedPresentTime); static bool getOpacityForFormat(uint32_t format); @@ -165,24 +195,16 @@ protected: bool mRefreshPending{false}; - // prepareClientLayer - constructs a RenderEngine layer for GPU composition. - bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform, Region& clearRegion, - const bool supportProtectedContent, - renderengine::LayerSettings& layer) override; + ui::Dataspace translateDataspace(ui::Dataspace dataspace); + void setInitialValuesForClone(const sp<Layer>& clonedFrom); + void updateCloneBufferInfo() override; + uint64_t mPreviousFrameNumber = 0; private: // Returns true if this layer requires filtering - bool needsFiltering(const sp<const DisplayDevice>& displayDevice) const; - - uint64_t getHeadFrameNumber() const; - - uint32_t mCurrentScalingMode{NATIVE_WINDOW_SCALING_MODE_FREEZE}; - - bool mTransformToDisplayInverse{false}; + bool needsFiltering(const sp<const DisplayDevice>& displayDevice) const override; - // main thread. - bool mBufferLatched{false}; // TODO: Use mActiveBuffer? + uint64_t getHeadFrameNumber(nsecs_t expectedPresentTime) const; // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame // and its parent layer is not bounded diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp index 414814a6f4..ea55795119 100644 --- a/services/surfaceflinger/BufferLayerConsumer.cpp +++ b/services/surfaceflinger/BufferLayerConsumer.cpp @@ -428,30 +428,6 @@ std::shared_ptr<FenceTime> BufferLayerConsumer::getCurrentFenceTime() const { return mCurrentFenceTime; } -status_t BufferLayerConsumer::doFenceWaitLocked() const { - if (mCurrentFence->isValid()) { - if (mRE.useWaitSync()) { - base::unique_fd fenceFd(mCurrentFence->dup()); - if (fenceFd == -1) { - BLC_LOGE("doFenceWait: error dup'ing fence fd: %d", errno); - return -errno; - } - if (!mRE.waitFence(std::move(fenceFd))) { - BLC_LOGE("doFenceWait: failed to wait on fence fd"); - return UNKNOWN_ERROR; - } - } else { - status_t err = mCurrentFence->waitForever("BufferLayerConsumer::doFenceWaitLocked"); - if (err != NO_ERROR) { - BLC_LOGE("doFenceWait: error waiting for fence: %d", err); - return err; - } - } - } - - return NO_ERROR; -} - void BufferLayerConsumer::freeBufferLocked(int slotIndex) { BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); std::lock_guard<std::mutex> lock(mImagesMutex); diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h index 617b1c2323..39ed3707dd 100644 --- a/services/surfaceflinger/BufferLayerConsumer.h +++ b/services/surfaceflinger/BufferLayerConsumer.h @@ -254,11 +254,6 @@ private: // mCurrentTextureImage must not be nullptr. void computeCurrentTransformMatrixLocked(); - // doFenceWaitLocked inserts a wait command into the RenderEngine command - // stream to ensure that it is safe for future RenderEngine commands to - // access the current texture buffer. - status_t doFenceWaitLocked() const; - // getCurrentCropLocked returns the cropping rectangle of the current buffer. Rect getCurrentCropLocked() const; diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index cbb9d658e4..d80a70e4c6 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -17,18 +17,17 @@ #undef LOG_TAG #define LOG_TAG "BufferQueueLayer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include <compositionengine/Display.h> +#include "BufferQueueLayer.h" + #include <compositionengine/Layer.h> -#include <compositionengine/OutputLayer.h> -#include <compositionengine/impl/LayerCompositionState.h> -#include <compositionengine/impl/OutputLayerCompositionState.h> +#include <compositionengine/LayerFECompositionState.h> #include <gui/BufferQueueConsumer.h> #include <system/window.h> -#include "BufferQueueLayer.h" #include "LayerRejecter.h" #include "SurfaceInterceptor.h" +#include "FrameTracer/FrameTracer.h" #include "TimeStats/TimeStats.h" namespace android { @@ -45,6 +44,14 @@ BufferQueueLayer::~BufferQueueLayer() { void BufferQueueLayer::onLayerDisplayed(const sp<Fence>& releaseFence) { mConsumer->setReleaseFence(releaseFence); + + // Prevent tracing the same release multiple times. + if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) { + mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber, + std::make_shared<FenceTime>(releaseFence), + FrameTracer::FrameEvent::RELEASE_FENCE); + mPreviousReleasedFrameNumber = mPreviousFrameNumber; + } } void BufferQueueLayer::setTransformHint(uint32_t orientation) const { @@ -61,10 +68,6 @@ std::vector<OccupancyTracker::Segment> BufferQueueLayer::getOccupancyHistory(boo return history; } -bool BufferQueueLayer::getTransformToDisplayInverse() const { - return mConsumer->getTransformToDisplayInverse(); -} - void BufferQueueLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) { if (!mConsumer->releasePendingBuffer()) { return; @@ -137,72 +140,20 @@ bool BufferQueueLayer::fenceHasSignaled() const { return mQueueItems[0].mFenceTime->getSignalTime() != Fence::SIGNAL_TIME_PENDING; } -bool BufferQueueLayer::framePresentTimeIsCurrent() const { +bool BufferQueueLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const { if (!hasFrameUpdate() || isRemovedFromCurrentState()) { return true; } Mutex::Autolock lock(mQueueItemLock); - return mQueueItems[0].mTimestamp <= mFlinger->getExpectedPresentTime(); -} - -nsecs_t BufferQueueLayer::getDesiredPresentTime() { - return mConsumer->getTimestamp(); -} - -std::shared_ptr<FenceTime> BufferQueueLayer::getCurrentFenceTime() const { - return mConsumer->getCurrentFenceTime(); -} - -void BufferQueueLayer::getDrawingTransformMatrix(float *matrix) { - return mConsumer->getTransformMatrix(matrix); -} - -// NOTE: SurfaceFlinger's definitions of "Current" and "Drawing" do not neatly map to BufferQueue's -// These functions get the fields for the frame that is currently in SurfaceFlinger's Drawing state -// so the functions start with "getDrawing". The data is retrieved from the BufferQueueConsumer's -// current buffer so the consumer functions start with "getCurrent". -// -// This results in the rather confusing functions below. -uint32_t BufferQueueLayer::getDrawingTransform() const { - return mConsumer->getCurrentTransform(); -} - -ui::Dataspace BufferQueueLayer::getDrawingDataSpace() const { - return mConsumer->getCurrentDataSpace(); -} - -Rect BufferQueueLayer::getDrawingCrop() const { - return mConsumer->getCurrentCrop(); -} - -uint32_t BufferQueueLayer::getDrawingScalingMode() const { - return mConsumer->getCurrentScalingMode(); -} - -Region BufferQueueLayer::getDrawingSurfaceDamage() const { - return mConsumer->getSurfaceDamage(); + return mQueueItems[0].mTimestamp <= expectedPresentTime; } -const HdrMetadata& BufferQueueLayer::getDrawingHdrMetadata() const { - return mConsumer->getCurrentHdrMetadata(); -} - -int BufferQueueLayer::getDrawingApi() const { - return mConsumer->getCurrentApi(); -} - -PixelFormat BufferQueueLayer::getPixelFormat() const { - return mFormat; -} - -uint64_t BufferQueueLayer::getFrameNumber() const { +uint64_t BufferQueueLayer::getFrameNumber(nsecs_t expectedPresentTime) const { Mutex::Autolock lock(mQueueItemLock); uint64_t frameNumber = mQueueItems[0].mFrameNumber; // The head of the queue will be dropped if there are signaled and timely frames behind it - nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime(); - if (isRemovedFromCurrentState()) { expectedPresentTime = 0; } @@ -244,7 +195,7 @@ bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) { if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) { // mSidebandStreamChanged was changed to false mSidebandStream = mConsumer->getSidebandStream(); - auto& layerCompositionState = getCompositionLayer()->editState().frontEnd; + auto& layerCompositionState = getCompositionLayer()->editFEState(); layerCompositionState.sidebandStream = mSidebandStream; if (layerCompositionState.sidebandStream != nullptr) { setTransactionFlags(eTransactionNeeded); @@ -261,15 +212,12 @@ bool BufferQueueLayer::hasFrameUpdate() const { return mQueuedFrames > 0; } -void BufferQueueLayer::setFilteringEnabled(bool enabled) { - return mConsumer->setFilteringEnabled(enabled); -} - status_t BufferQueueLayer::bindTextureImage() { return mConsumer->bindTextureImage(); } -status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) { +status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + nsecs_t expectedPresentTime) { // This boolean is used to make sure that SurfaceFlinger's shadow copy // of the buffer queue isn't modified when the buffer queue is returning // BufferItem's that weren't actually queued. This can happen in shared @@ -278,9 +226,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t const int32_t layerID = getSequence(); LayerRejecter r(mDrawingState, getCurrentState(), recomputeVisibleRegions, getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode, - getTransformToDisplayInverse(), mFreezeGeometryUpdates); - - nsecs_t expectedPresentTime = mFlinger->getExpectedPresentTime(); + getTransformToDisplayInverse()); if (isRemovedFromCurrentState()) { expectedPresentTime = 0; @@ -333,6 +279,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t mQueueItems.clear(); mQueuedFrames = 0; mFlinger->mTimeStats->onDestroy(layerID); + mFlinger->mFrameTracer->onDestroy(layerID); } // Once we have hit this state, the shadow queue may no longer @@ -359,9 +306,15 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t mQueuedFrames--; } + uint64_t bufferID = mQueueItems[0].mGraphicBuffer->getId(); mFlinger->mTimeStats->setAcquireFence(layerID, currentFrameNumber, mQueueItems[0].mFenceTime); + mFlinger->mFrameTracer->traceFence(layerID, bufferID, currentFrameNumber, + mQueueItems[0].mFenceTime, + FrameTracer::FrameEvent::ACQUIRE_FENCE); mFlinger->mTimeStats->setLatchTime(layerID, currentFrameNumber, latchTime); + mFlinger->mFrameTracer->traceTimestamp(layerID, bufferID, currentFrameNumber, latchTime, + FrameTracer::FrameEvent::LATCH); mQueueItems.removeAt(0); } @@ -377,12 +330,13 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t status_t BufferQueueLayer::updateActiveBuffer() { // update the active buffer - mActiveBuffer = mConsumer->getCurrentBuffer(&mActiveBufferSlot, &mActiveBufferFence); - auto& layerCompositionState = getCompositionLayer()->editState().frontEnd; - layerCompositionState.buffer = mActiveBuffer; - layerCompositionState.bufferSlot = mActiveBufferSlot; + mPreviousBufferId = getCurrentBufferId(); + mBufferInfo.mBuffer = + mConsumer->getCurrentBuffer(&mBufferInfo.mBufferSlot, &mBufferInfo.mFence); + auto& layerCompositionState = getCompositionLayer()->editFEState(); + layerCompositionState.buffer = mBufferInfo.mBuffer; - if (mActiveBuffer == nullptr) { + if (mBufferInfo.mBuffer == nullptr) { // this can only happen if the very first buffer was rejected. return BAD_VALUE; } @@ -400,47 +354,30 @@ status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) { return NO_ERROR; } -void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) { - const auto outputLayer = findOutputLayerForDisplay(display); - LOG_FATAL_IF(!outputLayer); - LOG_FATAL_IF(!outputLayer->getState.hwc); - auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer; - - uint32_t hwcSlot = 0; - sp<GraphicBuffer> hwcBuffer; - - // INVALID_BUFFER_SLOT is used to identify BufferStateLayers. Default to 0 - // for BufferQueueLayers - int slot = (mActiveBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) ? 0 : mActiveBufferSlot; - (*outputLayer->editState().hwc) - .hwcBufferCache.getHwcBuffer(slot, mActiveBuffer, &hwcSlot, &hwcBuffer); - - auto acquireFence = mConsumer->getCurrentFence(); - auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), mActiveBuffer->handle, - to_string(error).c_str(), static_cast<int32_t>(error)); +void BufferQueueLayer::latchPerFrameState( + compositionengine::LayerFECompositionState& compositionState) const { + BufferLayer::latchPerFrameState(compositionState); + if (compositionState.compositionType == Hwc2::IComposerClient::Composition::SIDEBAND) { + return; } - auto& layerCompositionState = getCompositionLayer()->editState().frontEnd; - layerCompositionState.bufferSlot = mActiveBufferSlot; - layerCompositionState.buffer = mActiveBuffer; - layerCompositionState.acquireFence = acquireFence; + compositionState.buffer = mBufferInfo.mBuffer; + compositionState.bufferSlot = (mBufferInfo.mBufferSlot == BufferQueue::INVALID_BUFFER_SLOT) + ? 0 + : mBufferInfo.mBufferSlot; + compositionState.acquireFence = mBufferInfo.mFence; } // ----------------------------------------------------------------------- // Interface implementation for BufferLayerConsumer::ContentsChangedListener // ----------------------------------------------------------------------- -void BufferQueueLayer::fakeVsync() { - mRefreshPending = false; - bool ignored = false; - latchBuffer(ignored, systemTime()); - usleep(16000); - releasePendingBuffer(systemTime()); -} - void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { + const int32_t layerID = getSequence(); + mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str()); + mFlinger->mFrameTracer->traceTimestamp(layerID, item.mGraphicBuffer->getId(), item.mFrameNumber, + systemTime(), FrameTracer::FrameEvent::POST); + ATRACE_CALL(); // Add this buffer from our internal queue tracker { // Autolock scope @@ -476,13 +413,7 @@ void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(), item.mGraphicBuffer->getHeight(), item.mFrameNumber); - // If this layer is orphaned, then we run a fake vsync pulse so that - // dequeueBuffer doesn't block indefinitely. - if (isRemovedFromCurrentState()) { - fakeVsync(); - } else { - mFlinger->signalLayerUpdate(); - } + mFlinger->signalLayerUpdate(); mConsumer->onBufferAvailable(item); } @@ -530,12 +461,7 @@ void BufferQueueLayer::onFirstRef() { sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer, true); mProducer = new MonitoredProducer(producer, mFlinger, this); - { - // Grab the SF state lock during this since it's the only safe way to access RenderEngine - Mutex::Autolock lock(mFlinger->mStateLock); - mConsumer = - new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this); - } + mConsumer = new BufferLayerConsumer(consumer, mFlinger->getRenderEngine(), mTextureName, this); mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); mConsumer->setContentsChangedListener(this); mConsumer->setName(mName); @@ -545,7 +471,7 @@ void BufferQueueLayer::onFirstRef() { mProducer->setMaxDequeuedBufferCount(2); } - if (const auto display = mFlinger->getDefaultDisplayDevice()) { + if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) { updateTransformHint(display); } } @@ -585,4 +511,30 @@ uint32_t BufferQueueLayer::getProducerStickyTransform() const { return static_cast<uint32_t>(producerStickyTransform); } +void BufferQueueLayer::gatherBufferInfo() { + mBufferInfo.mDesiredPresentTime = mConsumer->getTimestamp(); + mBufferInfo.mFenceTime = mConsumer->getCurrentFenceTime(); + mBufferInfo.mFence = mConsumer->getCurrentFence(); + mBufferInfo.mTransform = mConsumer->getCurrentTransform(); + mBufferInfo.mDataspace = translateDataspace(mConsumer->getCurrentDataSpace()); + mBufferInfo.mCrop = mConsumer->getCurrentCrop(); + mBufferInfo.mScaleMode = mConsumer->getCurrentScalingMode(); + mBufferInfo.mSurfaceDamage = mConsumer->getSurfaceDamage(); + mBufferInfo.mHdrMetadata = mConsumer->getCurrentHdrMetadata(); + mBufferInfo.mApi = mConsumer->getCurrentApi(); + mBufferInfo.mPixelFormat = mFormat; + mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse(); +} + +sp<Layer> BufferQueueLayer::createClone() { + const String8 name = mName + " (Mirror)"; + LayerCreationArgs args = + LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, 0, LayerMetadata()); + args.textureName = mTextureName; + sp<BufferQueueLayer> layer = mFlinger->getFactory().createBufferQueueLayer(args); + layer->setInitialValuesForClone(this); + + return layer; +} + } // namespace android diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h index 7def33ad78..f3e8a19a7d 100644 --- a/services/surfaceflinger/BufferQueueLayer.h +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -31,6 +31,7 @@ namespace android { */ class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener { public: + // Only call while mStateLock is held explicit BufferQueueLayer(const LayerCreationArgs&); ~BufferQueueLayer() override; @@ -38,14 +39,14 @@ public: // Interface implementation for Layer // ----------------------------------------------------------------------- public: + const char* getType() const override { return "BufferQueueLayer"; } + void onLayerDisplayed(const sp<Fence>& releaseFence) override; void setTransformHint(uint32_t orientation) const override; std::vector<OccupancyTracker::Segment> getOccupancyHistory(bool forceFlush) override; - bool getTransformToDisplayInverse() const override; - // If a buffer was replaced this frame, release the former buffer void releasePendingBuffer(nsecs_t dequeueReadyTime) override; @@ -61,23 +62,10 @@ public: // ----------------------------------------------------------------------- public: bool fenceHasSignaled() const override; - bool framePresentTimeIsCurrent() const override; + bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override; private: - nsecs_t getDesiredPresentTime() override; - std::shared_ptr<FenceTime> getCurrentFenceTime() const override; - - void getDrawingTransformMatrix(float *matrix) override; - uint32_t getDrawingTransform() const override; - ui::Dataspace getDrawingDataSpace() const override; - Rect getDrawingCrop() const override; - uint32_t getDrawingScalingMode() const override; - Region getDrawingSurfaceDamage() const override; - const HdrMetadata& getDrawingHdrMetadata() const override; - int getDrawingApi() const override; - PixelFormat getPixelFormat() const override; - - uint64_t getFrameNumber() const override; + uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override; bool getAutoRefresh() const override; bool getSidebandStreamChanged() const override; @@ -86,20 +74,22 @@ private: bool hasFrameUpdate() const override; - void setFilteringEnabled(bool enabled) override; - status_t bindTextureImage() override; - status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override; + status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + nsecs_t expectedPresentTime) override; status_t updateActiveBuffer() override; status_t updateFrameNumber(nsecs_t latchTime) override; - void setHwcLayerBuffer(const sp<const DisplayDevice>& displayDevice) override; + void latchPerFrameState(compositionengine::LayerFECompositionState&) const override; + sp<Layer> createClone() override; // ----------------------------------------------------------------------- // Interface implementation for BufferLayerConsumer::ContentsChangedListener // ----------------------------------------------------------------------- protected: + void gatherBufferInfo() override; + void onFrameAvailable(const BufferItem& item) override; void onFrameReplaced(const BufferItem& item) override; void onSidebandStreamChanged() override; @@ -121,10 +111,11 @@ private: PixelFormat mFormat{PIXEL_FORMAT_NONE}; - // Only accessed on the main thread. - uint64_t mPreviousFrameNumber{0}; bool mUpdateTexImageFailed{false}; + uint64_t mPreviousBufferId = 0; + uint64_t mPreviousReleasedFrameNumber = 0; + // Local copy of the queued contents of the incoming BufferQueue mutable Mutex mQueueItemLock; Condition mQueueItemCondition; @@ -132,13 +123,10 @@ private: std::atomic<uint64_t> mLastFrameNumberReceived{0}; bool mAutoRefresh{false}; - int mActiveBufferSlot{BufferQueue::INVALID_BUFFER_SLOT}; // thread-safe std::atomic<int32_t> mQueuedFrames{0}; std::atomic<bool> mSidebandStreamChanged{false}; - - void fakeVsync(); }; } // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 63a07c35ca..75fc0e9c06 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -19,19 +19,18 @@ #define LOG_TAG "BufferStateLayer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include "BufferStateLayer.h" + #include <limits> -#include <compositionengine/Display.h> #include <compositionengine/Layer.h> -#include <compositionengine/OutputLayer.h> -#include <compositionengine/impl/LayerCompositionState.h> -#include <compositionengine/impl/OutputLayerCompositionState.h> +#include <compositionengine/LayerFECompositionState.h> #include <gui/BufferQueue.h> #include <private/gui/SyncFeatures.h> #include <renderengine/Image.h> -#include "BufferStateLayer.h" #include "ColorLayer.h" +#include "FrameTracer/FrameTracer.h" #include "TimeStats/TimeStats.h" namespace android { @@ -49,15 +48,22 @@ BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer(args), mHwcSlotGenerator(new HwcSlotGenerator()) { mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; mCurrentState.dataspace = ui::Dataspace::V0_SRGB; + if (const auto display = args.displayDevice) { + updateTransformHint(display); + } } BufferStateLayer::~BufferStateLayer() { - if (mActiveBuffer != nullptr) { - // Ensure that mActiveBuffer is uncached from RenderEngine here, as + // The original layer and the clone layer share the same texture and buffer. Therefore, only + // one of the layers, in this case the original layer, needs to handle the deletion. The + // original layer and the clone should be removed at the same time so there shouldn't be any + // issue with the clone layer trying to use the texture. + if (mBufferInfo.mBuffer != nullptr && !isClone()) { + // Ensure that mBuffer is uncached from RenderEngine here, as // RenderEngine may have been using the buffer as an external texture // after the client uncached the buffer. auto& engine(mFlinger->getRenderEngine()); - engine.unbindExternalTextureBuffer(mActiveBuffer->getId()); + engine.unbindExternalTextureBuffer(mBufferInfo.mBuffer->getId()); } } @@ -87,6 +93,14 @@ void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) { break; } } + + // Prevent tracing the same release multiple times. + if (mPreviousFrameNumber != mPreviousReleasedFrameNumber) { + mFlinger->mFrameTracer->traceFence(getSequence(), mPreviousBufferId, mPreviousFrameNumber, + std::make_shared<FenceTime>(releaseFence), + FrameTracer::FrameEvent::RELEASE_FENCE); + mPreviousReleasedFrameNumber = mPreviousFrameNumber; + } } void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const { @@ -95,7 +109,7 @@ void BufferStateLayer::setTransformHint(uint32_t /*orientation*/) const { } void BufferStateLayer::releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { - mFlinger->getTransactionCompletedThread().addPresentedCallbackHandles( + mFlinger->getTransactionCompletedThread().finalizePendingCallbackHandles( mDrawingState.callbackHandles); mDrawingState.callbackHandles = {}; @@ -111,13 +125,10 @@ bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { bool BufferStateLayer::willPresentCurrentTransaction() const { // Returns true if the most recent Transaction applied to CurrentState will be presented. - return getSidebandStreamChanged() || getAutoRefresh() || + return (getSidebandStreamChanged() || getAutoRefresh() || (mCurrentState.modified && - (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr)); -} - -bool BufferStateLayer::getTransformToDisplayInverse() const { - return mCurrentState.transformToDisplayInverse; + (mCurrentState.buffer != nullptr || mCurrentState.bgColorLayer != nullptr))) && + !mLayerDetached; } void BufferStateLayer::pushPendingState() { @@ -226,11 +237,15 @@ bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, nsecs_t postTi mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); - mFlinger->mTimeStats->setPostTime(getSequence(), getFrameNumber(), getName().c_str(), postTime); - mDesiredPresentTime = desiredPresentTime; + const int32_t layerID = getSequence(); + mFlinger->mTimeStats->setPostTime(layerID, mFrameNumber, getName().c_str(), postTime); + mFlinger->mFrameTracer->traceNewLayer(layerID, getName().c_str()); + mFlinger->mFrameTracer->traceTimestamp(layerID, buffer->getId(), mFrameNumber, postTime, + FrameTracer::FrameEvent::POST); + mCurrentState.desiredPresentTime = desiredPresentTime; if (mFlinger->mUseSmart90ForVideo) { - const nsecs_t presentTime = (mDesiredPresentTime == -1) ? 0 : mDesiredPresentTime; + const nsecs_t presentTime = (desiredPresentTime == -1) ? 0 : desiredPresentTime; mFlinger->mScheduler->addLayerPresentTimeAndHDR(mSchedulerLayerHandle, presentTime, mCurrentState.hdrMetadata.validTypes != 0); } @@ -320,7 +335,7 @@ bool BufferStateLayer::setTransactionCompletedListeners( } else { // If this layer will NOT need to be relatched and presented this frame // Notify the transaction completed thread this handle is done - mFlinger->getTransactionCompletedThread().addUnpresentedCallbackHandle(handle); + mFlinger->getTransactionCompletedThread().registerUnpresentedCallbackHandle(handle); } } @@ -379,84 +394,15 @@ bool BufferStateLayer::fenceHasSignaled() const { return getDrawingState().acquireFence->getStatus() == Fence::Status::Signaled; } -bool BufferStateLayer::framePresentTimeIsCurrent() const { +bool BufferStateLayer::framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const { if (!hasFrameUpdate() || isRemovedFromCurrentState()) { return true; } - return mDesiredPresentTime <= mFlinger->getExpectedPresentTime(); -} - -nsecs_t BufferStateLayer::getDesiredPresentTime() { - return mDesiredPresentTime; -} - -std::shared_ptr<FenceTime> BufferStateLayer::getCurrentFenceTime() const { - return std::make_shared<FenceTime>(getDrawingState().acquireFence); -} - -void BufferStateLayer::getDrawingTransformMatrix(float *matrix) { - std::copy(std::begin(mTransformMatrix), std::end(mTransformMatrix), matrix); -} - -uint32_t BufferStateLayer::getDrawingTransform() const { - return getDrawingState().transform; -} - -ui::Dataspace BufferStateLayer::getDrawingDataSpace() const { - return getDrawingState().dataspace; -} - -// Crop that applies to the buffer -Rect BufferStateLayer::getDrawingCrop() const { - const State& s(getDrawingState()); - - if (s.crop.isEmpty() && s.buffer) { - return s.buffer->getBounds(); - } else if (s.buffer) { - Rect crop = s.crop; - crop.left = std::max(crop.left, 0); - crop.top = std::max(crop.top, 0); - uint32_t bufferWidth = s.buffer->getWidth(); - uint32_t bufferHeight = s.buffer->getHeight(); - if (bufferHeight <= std::numeric_limits<int32_t>::max() && - bufferWidth <= std::numeric_limits<int32_t>::max()) { - crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth)); - crop.bottom = std::min(crop.bottom, static_cast<int32_t>(bufferHeight)); - } - if (!crop.isValid()) { - // Crop rect is out of bounds, return whole buffer - return s.buffer->getBounds(); - } - return crop; - } - return s.crop; -} - -uint32_t BufferStateLayer::getDrawingScalingMode() const { - return NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; -} - -Region BufferStateLayer::getDrawingSurfaceDamage() const { - return getDrawingState().surfaceDamageRegion; -} - -const HdrMetadata& BufferStateLayer::getDrawingHdrMetadata() const { - return getDrawingState().hdrMetadata; -} - -int BufferStateLayer::getDrawingApi() const { - return getDrawingState().api; -} - -PixelFormat BufferStateLayer::getPixelFormat() const { - if (!mActiveBuffer) { - return PIXEL_FORMAT_NONE; - } - return mActiveBuffer->format; + return mCurrentState.desiredPresentTime <= expectedPresentTime; } -uint64_t BufferStateLayer::getFrameNumber() const { +uint64_t BufferStateLayer::getFrameNumber(nsecs_t /*expectedPresentTime*/) const { return mFrameNumber; } @@ -475,7 +421,7 @@ bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) { // mSidebandStreamChanged was true LOG_ALWAYS_FATAL_IF(!getCompositionLayer()); mSidebandStream = s.sidebandStream; - getCompositionLayer()->editState().frontEnd.sidebandStream = mSidebandStream; + getCompositionLayer()->editFEState().sidebandStream = mSidebandStream; if (mSidebandStream != nullptr) { setTransactionFlags(eTransactionNeeded); mFlinger->setTransactionFlags(eTraversalNeeded); @@ -492,11 +438,6 @@ bool BufferStateLayer::hasFrameUpdate() const { return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr); } -void BufferStateLayer::setFilteringEnabled(bool enabled) { - GLConsumer::computeTransformMatrix(mTransformMatrix.data(), mActiveBuffer, mCurrentCrop, - mCurrentTransform, enabled); -} - status_t BufferStateLayer::bindTextureImage() { const State& s(getDrawingState()); auto& engine(mFlinger->getRenderEngine()); @@ -504,7 +445,8 @@ status_t BufferStateLayer::bindTextureImage() { return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence); } -status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime) { +status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime, + nsecs_t /*expectedPresentTime*/) { const State& s(getDrawingState()); if (!s.buffer) { @@ -538,7 +480,7 @@ status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nse ALOGE("[%s] rejecting buffer: " "bufferWidth=%d, bufferHeight=%d, front.active.{w=%d, h=%d}", mName.string(), bufferWidth, bufferHeight, s.active.w, s.active.h); - mFlinger->mTimeStats->removeTimeRecord(layerID, getFrameNumber()); + mFlinger->mTimeStats->removeTimeRecord(layerID, mFrameNumber); return BAD_VALUE; } @@ -556,12 +498,18 @@ status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nse status_t err = bindTextureImage(); if (err != NO_ERROR) { mFlinger->mTimeStats->onDestroy(layerID); + mFlinger->mFrameTracer->onDestroy(layerID); return BAD_VALUE; } } - mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime()); - mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime); + const uint64_t bufferID = getCurrentBufferId(); + mFlinger->mTimeStats->setAcquireFence(layerID, mFrameNumber, mBufferInfo.mFenceTime); + mFlinger->mFrameTracer->traceFence(layerID, bufferID, mFrameNumber, mBufferInfo.mFenceTime, + FrameTracer::FrameEvent::ACQUIRE_FENCE); + mFlinger->mTimeStats->setLatchTime(layerID, mFrameNumber, latchTime); + mFlinger->mFrameTracer->traceTimestamp(layerID, bufferID, mFrameNumber, latchTime, + FrameTracer::FrameEvent::LATCH); mCurrentStateModified = false; @@ -575,49 +523,34 @@ status_t BufferStateLayer::updateActiveBuffer() { return BAD_VALUE; } - mActiveBuffer = s.buffer; - mActiveBufferFence = s.acquireFence; - auto& layerCompositionState = getCompositionLayer()->editState().frontEnd; - layerCompositionState.buffer = mActiveBuffer; - layerCompositionState.bufferSlot = 0; + mPreviousBufferId = getCurrentBufferId(); + mBufferInfo.mBuffer = s.buffer; + mBufferInfo.mFence = s.acquireFence; + auto& layerCompositionState = getCompositionLayer()->editFEState(); + layerCompositionState.buffer = mBufferInfo.mBuffer; return NO_ERROR; } status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) { // TODO(marissaw): support frame history events + mPreviousFrameNumber = mCurrentFrameNumber; mCurrentFrameNumber = mFrameNumber; return NO_ERROR; } -void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) { - const auto outputLayer = findOutputLayerForDisplay(display); - LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc); - auto& hwcInfo = *outputLayer->editState().hwc; - auto& hwcLayer = hwcInfo.hwcLayer; - - const State& s(getDrawingState()); - - uint32_t hwcSlot; - sp<GraphicBuffer> buffer; - hwcInfo.hwcBufferCache.getHwcBuffer(mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId), - s.buffer, &hwcSlot, &buffer); - - auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), - s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error)); +void BufferStateLayer::latchPerFrameState( + compositionengine::LayerFECompositionState& compositionState) const { + BufferLayer::latchPerFrameState(compositionState); + if (compositionState.compositionType == Hwc2::IComposerClient::Composition::SIDEBAND) { + return; } - mFrameNumber++; -} - -void BufferStateLayer::onFirstRef() { - BufferLayer::onFirstRef(); + compositionState.buffer = mBufferInfo.mBuffer; + compositionState.bufferSlot = mBufferInfo.mBufferSlot; + compositionState.acquireFence = mBufferInfo.mFence; - if (const auto display = mFlinger->getDefaultDisplayDevice()) { - updateTransformHint(display); - } + mFrameNumber++; } void BufferStateLayer::HwcSlotGenerator::bufferErased(const client_cache_t& clientCacheId) { @@ -692,4 +625,57 @@ void BufferStateLayer::HwcSlotGenerator::eraseBufferLocked(const client_cache_t& mFreeHwcCacheSlots.push(hwcCacheSlot); mCachedBuffers.erase(clientCacheId); } + +void BufferStateLayer::gatherBufferInfo() { + const State& s(getDrawingState()); + + mBufferInfo.mDesiredPresentTime = s.desiredPresentTime; + mBufferInfo.mFenceTime = std::make_shared<FenceTime>(s.acquireFence); + mBufferInfo.mFence = s.acquireFence; + mBufferInfo.mTransform = s.transform; + mBufferInfo.mDataspace = translateDataspace(s.dataspace); + mBufferInfo.mCrop = computeCrop(s); + mBufferInfo.mScaleMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; + mBufferInfo.mSurfaceDamage = s.surfaceDamageRegion; + mBufferInfo.mHdrMetadata = s.hdrMetadata; + mBufferInfo.mApi = s.api; + mBufferInfo.mPixelFormat = + !mBufferInfo.mBuffer ? PIXEL_FORMAT_NONE : mBufferInfo.mBuffer->format; + mBufferInfo.mTransformToDisplayInverse = s.transformToDisplayInverse; + mBufferInfo.mBufferSlot = mHwcSlotGenerator->getHwcCacheSlot(s.clientCacheId); +} + +Rect BufferStateLayer::computeCrop(const State& s) { + if (s.crop.isEmpty() && s.buffer) { + return s.buffer->getBounds(); + } else if (s.buffer) { + Rect crop = s.crop; + crop.left = std::max(crop.left, 0); + crop.top = std::max(crop.top, 0); + uint32_t bufferWidth = s.buffer->getWidth(); + uint32_t bufferHeight = s.buffer->getHeight(); + if (bufferHeight <= std::numeric_limits<int32_t>::max() && + bufferWidth <= std::numeric_limits<int32_t>::max()) { + crop.right = std::min(crop.right, static_cast<int32_t>(bufferWidth)); + crop.bottom = std::min(crop.bottom, static_cast<int32_t>(bufferHeight)); + } + if (!crop.isValid()) { + // Crop rect is out of bounds, return whole buffer + return s.buffer->getBounds(); + } + return crop; + } + return s.crop; +} + +sp<Layer> BufferStateLayer::createClone() { + const String8 name = mName + " (Mirror)"; + LayerCreationArgs args = + LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, 0, LayerMetadata()); + args.textureName = mTextureName; + sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args); + layer->mHwcSlotGenerator = mHwcSlotGenerator; + layer->setInitialValuesForClone(this); + return layer; +} } // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 97e24cb277..3dfe76cf9f 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -19,7 +19,6 @@ #include "BufferLayer.h" #include "Layer.h" -#include <gui/GLConsumer.h> #include <renderengine/Image.h> #include <renderengine/RenderEngine.h> #include <system/window.h> @@ -40,14 +39,14 @@ public: // ----------------------------------------------------------------------- // Interface implementation for Layer // ----------------------------------------------------------------------- + const char* getType() const override { return "BufferStateLayer"; } + void onLayerDisplayed(const sp<Fence>& releaseFence) override; void setTransformHint(uint32_t orientation) const override; void releasePendingBuffer(nsecs_t dequeueReadyTime) override; bool shouldPresentNow(nsecs_t expectedPresentTime) const override; - bool getTransformToDisplayInverse() const override; - uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override { return flags; } @@ -80,13 +79,13 @@ public: // Override to ignore legacy layer state properties that are not used by BufferStateLayer bool setSize(uint32_t /*w*/, uint32_t /*h*/) override { return false; } - bool setPosition(float /*x*/, float /*y*/, bool /*immediate*/) override { return false; } + bool setPosition(float /*x*/, float /*y*/) override { return false; } bool setTransparentRegionHint(const Region& transparent) override; bool setMatrix(const layer_state_t::matrix22_t& /*matrix*/, bool /*allowNonRectPreservingTransforms*/) override { return false; } - bool setCrop_legacy(const Rect& /*crop*/, bool /*immediate*/) override { return false; } + bool setCrop_legacy(const Rect& /*crop*/) override { return false; } bool setOverrideScalingMode(int32_t /*overrideScalingMode*/) override { return false; } void deferTransactionUntil_legacy(const sp<IBinder>& /*barrierHandle*/, uint64_t /*frameNumber*/) override {} @@ -102,23 +101,13 @@ public: // Interface implementation for BufferLayer // ----------------------------------------------------------------------- bool fenceHasSignaled() const override; - bool framePresentTimeIsCurrent() const override; + bool framePresentTimeIsCurrent(nsecs_t expectedPresentTime) const override; -private: - nsecs_t getDesiredPresentTime() override; - std::shared_ptr<FenceTime> getCurrentFenceTime() const override; +protected: + void gatherBufferInfo() override; - void getDrawingTransformMatrix(float *matrix) override; - uint32_t getDrawingTransform() const override; - ui::Dataspace getDrawingDataSpace() const override; - Rect getDrawingCrop() const override; - uint32_t getDrawingScalingMode() const override; - Region getDrawingSurfaceDamage() const override; - const HdrMetadata& getDrawingHdrMetadata() const override; - int getDrawingApi() const override; - PixelFormat getPixelFormat() const override; - - uint64_t getFrameNumber() const override; +private: + uint64_t getFrameNumber(nsecs_t expectedPresentTime) const override; bool getAutoRefresh() const override; bool getSidebandStreamChanged() const override; @@ -127,39 +116,39 @@ private: bool hasFrameUpdate() const override; - void setFilteringEnabled(bool enabled) override; - status_t bindTextureImage() override; - status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override; + status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, + nsecs_t expectedPresentTime) override; status_t updateActiveBuffer() override; status_t updateFrameNumber(nsecs_t latchTime) override; - void setHwcLayerBuffer(const sp<const DisplayDevice>& display) override; + void latchPerFrameState(compositionengine::LayerFECompositionState&) const override; + sp<Layer> createClone() override; + + // Crop that applies to the buffer + Rect computeCrop(const State& s); private: friend class SlotGenerationTest; - void onFirstRef() override; bool willPresentCurrentTransaction() const; static const std::array<float, 16> IDENTITY_MATRIX; std::unique_ptr<renderengine::Image> mTextureImage; - std::array<float, 16> mTransformMatrix{IDENTITY_MATRIX}; - std::atomic<bool> mSidebandStreamChanged{false}; - uint32_t mFrameNumber{0}; + mutable uint32_t mFrameNumber{0}; sp<Fence> mPreviousReleaseFence; + uint64_t mPreviousBufferId = 0; + uint64_t mPreviousReleasedFrameNumber = 0; - bool mCurrentStateModified = false; + mutable bool mCurrentStateModified = false; bool mReleasePreviousBuffer = false; nsecs_t mCallbackHandleAcquireTime = -1; - nsecs_t mDesiredPresentTime = -1; - // TODO(marissaw): support sticky transform for LEGACY camera mode class HwcSlotGenerator : public ClientCache::ErasedRecipient { diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index 6bfd302b5c..c7ed9b0412 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -106,6 +106,10 @@ status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32 nullptr, layer); } +status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle) { + return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle); +} + status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const { sp<Layer> layer = getLayerUser(handle); if (layer == nullptr) { diff --git a/services/surfaceflinger/Client.h b/services/surfaceflinger/Client.h index 74e48188d6..7d7cef8c50 100644 --- a/services/surfaceflinger/Client.h +++ b/services/surfaceflinger/Client.h @@ -62,6 +62,8 @@ private: LayerMetadata metadata, sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp); + status_t mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* handle); + virtual status_t clearLayerFrameStats(const sp<IBinder>& handle) const; virtual status_t getLayerFrameStats(const sp<IBinder>& handle, FrameStats* outStats) const; diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp index fcc2d97ca3..172d44556a 100644 --- a/services/surfaceflinger/ColorLayer.cpp +++ b/services/surfaceflinger/ColorLayer.cpp @@ -18,23 +18,21 @@ #undef LOG_TAG #define LOG_TAG "ColorLayer" +#include "ColorLayer.h" + #include <stdint.h> #include <stdlib.h> #include <sys/types.h> #include <compositionengine/CompositionEngine.h> -#include <compositionengine/Display.h> #include <compositionengine/Layer.h> #include <compositionengine/LayerCreationArgs.h> -#include <compositionengine/OutputLayer.h> -#include <compositionengine/impl/LayerCompositionState.h> -#include <compositionengine/impl/OutputLayerCompositionState.h> +#include <compositionengine/LayerFECompositionState.h> #include <renderengine/RenderEngine.h> #include <ui/GraphicBuffer.h> #include <utils/Errors.h> #include <utils/Log.h> -#include "ColorLayer.h" #include "DisplayDevice.h" #include "SurfaceFlinger.h" @@ -48,16 +46,14 @@ ColorLayer::ColorLayer(const LayerCreationArgs& args) ColorLayer::~ColorLayer() = default; -bool ColorLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform, Region& clearRegion, - const bool supportProtectedContent, - renderengine::LayerSettings& layer) { - Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, - supportProtectedContent, layer); - half4 color(getColor()); - half3 solidColor(color.r, color.g, color.b); - layer.source.solidColor = solidColor; - return true; +std::optional<renderengine::LayerSettings> ColorLayer::prepareClientComposition( + compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) { + auto result = Layer::prepareClientComposition(targetSettings); + if (!result) { + return result; + } + result->source.solidColor = getColor().rgb; + return result; } bool ColorLayer::isVisible() const { @@ -91,84 +87,32 @@ bool ColorLayer::setDataspace(ui::Dataspace dataspace) { return true; } -void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display, - const ui::Transform& transform, const Rect& viewport, - int32_t /* supportedPerFrameMetadata */, - const ui::Dataspace targetDataspace) { - RETURN_IF_NO_HWC_LAYER(display); - - Region visible = transform.transform(visibleRegion.intersect(viewport)); - - const auto outputLayer = findOutputLayerForDisplay(display); - LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc); - - auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer; - - auto error = hwcLayer->setVisibleRegion(visible); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set visible region: %s (%d)", mName.string(), - to_string(error).c_str(), static_cast<int32_t>(error)); - visible.dump(LOG_TAG); - } - outputLayer->editState().visibleRegion = visible; - - setCompositionType(display, Hwc2::IComposerClient::Composition::SOLID_COLOR); - - const ui::Dataspace dataspace = - isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN ? targetDataspace - : mCurrentDataSpace; - error = hwcLayer->setDataspace(dataspace); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace, - to_string(error).c_str(), static_cast<int32_t>(error)); - } - - auto& layerCompositionState = getCompositionLayer()->editState().frontEnd; - layerCompositionState.dataspace = mCurrentDataSpace; +void ColorLayer::latchPerFrameState( + compositionengine::LayerFECompositionState& compositionState) const { + Layer::latchPerFrameState(compositionState); - half4 color = getColor(); - error = hwcLayer->setColor({static_cast<uint8_t>(std::round(255.0f * color.r)), - static_cast<uint8_t>(std::round(255.0f * color.g)), - static_cast<uint8_t>(std::round(255.0f * color.b)), 255}); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set color: %s (%d)", mName.string(), to_string(error).c_str(), - static_cast<int32_t>(error)); - } - layerCompositionState.color = {static_cast<uint8_t>(std::round(255.0f * color.r)), - static_cast<uint8_t>(std::round(255.0f * color.g)), - static_cast<uint8_t>(std::round(255.0f * color.b)), 255}; - - // Clear out the transform, because it doesn't make sense absent a source buffer - error = hwcLayer->setTransform(HWC2::Transform::None); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to clear transform: %s (%d)", mName.string(), to_string(error).c_str(), - static_cast<int32_t>(error)); - } - outputLayer->editState().bufferTransform = static_cast<Hwc2::Transform>(0); + compositionState.color = getColor(); + compositionState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR; +} - error = hwcLayer->setColorTransform(getColorTransform()); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to setColorTransform: %s (%d)", mName.string(), - to_string(error).c_str(), static_cast<int32_t>(error)); - } - layerCompositionState.colorTransform = getColorTransform(); +std::shared_ptr<compositionengine::Layer> ColorLayer::getCompositionLayer() const { + return mCompositionLayer; +} - error = hwcLayer->setSurfaceDamage(surfaceDamageRegion); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set surface damage: %s (%d)", mName.string(), - to_string(error).c_str(), static_cast<int32_t>(error)); - surfaceDamageRegion.dump(LOG_TAG); - } - layerCompositionState.surfaceDamage = surfaceDamageRegion; +bool ColorLayer::isOpaque(const Layer::State& s) const { + return (s.flags & layer_state_t::eLayerOpaque) != 0; } -void ColorLayer::commitTransaction(const State& stateToCommit) { - Layer::commitTransaction(stateToCommit); - mCurrentDataSpace = mDrawingState.dataspace; +ui::Dataspace ColorLayer::getDataSpace() const { + return mDrawingState.dataspace; } -std::shared_ptr<compositionengine::Layer> ColorLayer::getCompositionLayer() const { - return mCompositionLayer; +sp<Layer> ColorLayer::createClone() { + String8 name = mName + " (Mirror)"; + sp<ColorLayer> layer = mFlinger->getFactory().createColorLayer( + LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, 0, LayerMetadata())); + layer->setInitialValuesForClone(this); + return layer; } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h index 53d5b5b605..634a80003a 100644 --- a/services/surfaceflinger/ColorLayer.h +++ b/services/surfaceflinger/ColorLayer.h @@ -30,29 +30,28 @@ public: std::shared_ptr<compositionengine::Layer> getCompositionLayer() const override; - virtual const char* getTypeId() const { return "ColorLayer"; } + const char* getType() const override { return "ColorLayer"; } bool isVisible() const override; bool setColor(const half3& color) override; bool setDataspace(ui::Dataspace dataspace) override; - void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, - const Rect& viewport, int32_t supportedPerFrameMetadata, - const ui::Dataspace targetDataspace) override; + ui::Dataspace getDataSpace() const override; - void commitTransaction(const State& stateToCommit) override; - - bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; } + bool isOpaque(const Layer::State& s) const override; protected: - virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform, Region& clearRegion, - const bool supportProtectedContent, - renderengine::LayerSettings& layer); + /* + * compositionengine::LayerFE overrides + */ + void latchPerFrameState(compositionengine::LayerFECompositionState&) const override; + std::optional<renderengine::LayerSettings> prepareClientComposition( + compositionengine::LayerFE::ClientCompositionTargetSettings&) override; -private: std::shared_ptr<compositionengine::Layer> mCompositionLayer; + + sp<Layer> createClone() override; }; } // namespace android diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index 1c31ab9d7d..738a2a47ab 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -3,13 +3,15 @@ cc_defaults { defaults: ["surfaceflinger_defaults"], cflags: [ "-DLOG_TAG=\"CompositionEngine\"", + "-DATRACE_TAG=ATRACE_TAG_GRAPHICS", ], shared_libs: [ - "android.frameworks.vr.composer@1.0", + "android.frameworks.vr.composer@2.0", "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.composer@2.1", "android.hardware.graphics.composer@2.2", "android.hardware.graphics.composer@2.3", + "android.hardware.graphics.composer@2.4", "android.hardware.power@1.0", "android.hardware.power@1.3", "libbase", @@ -47,7 +49,7 @@ cc_library { "src/DumpHelpers.cpp", "src/HwcBufferCache.cpp", "src/Layer.cpp", - "src/LayerCompositionState.cpp", + "src/LayerFECompositionState.cpp", "src/Output.cpp", "src/OutputCompositionState.cpp", "src/OutputLayer.cpp", @@ -94,6 +96,7 @@ cc_test { "tests/LayerTest.cpp", "tests/MockHWC2.cpp", "tests/MockHWComposer.cpp", + "tests/MockPowerAdvisor.cpp", "tests/OutputTest.cpp", "tests/OutputLayerTest.cpp", "tests/RenderSurfaceTest.cpp", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h index 896f8aaa69..8687d0c9d2 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h @@ -18,6 +18,8 @@ #include <memory> +#include <utils/Timers.h> + namespace android { class HWComposer; @@ -31,6 +33,7 @@ namespace compositionengine { class Display; class Layer; +struct CompositionRefreshArgs; struct DisplayCreationArgs; struct LayerCreationArgs; @@ -43,14 +46,29 @@ public: virtual ~CompositionEngine(); // Create a composition Display - virtual std::shared_ptr<Display> createDisplay(DisplayCreationArgs&&) = 0; - virtual std::shared_ptr<Layer> createLayer(LayerCreationArgs&&) = 0; + virtual std::shared_ptr<Display> createDisplay(const DisplayCreationArgs&) = 0; + virtual std::shared_ptr<Layer> createLayer(const LayerCreationArgs&) = 0; virtual HWComposer& getHwComposer() const = 0; virtual void setHwComposer(std::unique_ptr<HWComposer>) = 0; virtual renderengine::RenderEngine& getRenderEngine() const = 0; virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0; + + virtual bool needsAnotherUpdate() const = 0; + virtual nsecs_t getLastFrameRefreshTimestamp() const = 0; + + // Presents the indicated outputs + virtual void present(CompositionRefreshArgs&) = 0; + + // Updates the cursor position for the indicated outputs. + virtual void updateCursorAsync(CompositionRefreshArgs&) = 0; + + // TODO(b/121291683): These will become private/internal + virtual void preComposition(CompositionRefreshArgs&) = 0; + + // Debugging + virtual void dump(std::string&) const = 0; }; } // namespace compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h new file mode 100644 index 0000000000..90158c721d --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h @@ -0,0 +1,78 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include <chrono> +#include <optional> +#include <vector> + +#include <compositionengine/Display.h> +#include <compositionengine/Layer.h> +#include <compositionengine/OutputColorSetting.h> +#include <math/mat4.h> + +namespace android::compositionengine { + +using Layers = std::vector<std::shared_ptr<compositionengine::Layer>>; +using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>; +using RawLayers = std::vector<compositionengine::Layer*>; + +/** + * A parameter object for refreshing a set of outputs + */ +struct CompositionRefreshArgs { + // All the outputs being refreshed + Outputs outputs; + + // All the layers that are potentially visible in the outputs. The order of + // the layers is important, and should be in traversal order from back to + // front. + Layers layers; + + // All the layers that have queued updates. + RawLayers layersWithQueuedFrames; + + // If true, forces the entire display to be considered dirty and repainted + bool repaintEverything{false}; + + // Controls how the color mode is chosen for an output + OutputColorSetting outputColorSetting{OutputColorSetting::kEnhanced}; + + // If not Dataspace::UNKNOWN, overrides the dataspace on each output + ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN}; + + // Forces a color mode on the outputs being refreshed + ui::ColorMode forceOutputColorMode{ui::ColorMode::NATIVE}; + + // If true, the complete output geometry needs to be recomputed this frame + bool updatingOutputGeometryThisFrame{false}; + + // If true, there was a geometry update this frame + bool updatingGeometryThisFrame{false}; + + // The color matrix to use for this + // frame. Only set if the color transform is changing this frame. + std::optional<mat4> colorTransformMatrix; + + // If true, client composition is always used. + bool devOptForceClientComposition{false}; + + // If set, causes the dirty regions to flash with the delay + std::optional<std::chrono::microseconds> devOptFlashDirtyRegionsDelay; +}; + +} // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h index dbcd3bd5f7..9193dc0e3f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h @@ -47,10 +47,10 @@ public: virtual void disconnect() = 0; // Creates a render color mode for the display - virtual void createDisplayColorProfile(DisplayColorProfileCreationArgs&&) = 0; + virtual void createDisplayColorProfile(const DisplayColorProfileCreationArgs&) = 0; // Creates a render surface for the display - virtual void createRenderSurface(RenderSurfaceCreationArgs&&) = 0; + virtual void createRenderSurface(const RenderSurfaceCreationArgs&) = 0; protected: ~Display() = default; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h index e2a0d42640..d93bfa30cc 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayColorProfile.h @@ -17,6 +17,7 @@ #pragma once #include <cstdint> +#include <string> #include <ui/GraphicTypes.h> @@ -75,6 +76,13 @@ public: // Gets the supported HDR capabilities for the profile virtual const HdrCapabilities& getHdrCapabilities() const = 0; + // Returns true if HWC for this profile supports the dataspace + virtual bool isDataspaceSupported(ui::Dataspace) const = 0; + + // Returns the target dataspace for picked color mode and dataspace + virtual ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace, + ui::Dataspace colorSpaceAgnosticDataspace) const = 0; + // Debugging virtual void dump(std::string&) const = 0; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h index 0b6b4e4f0f..ced4227b44 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h @@ -20,6 +20,7 @@ #include <optional> #include "DisplayHardware/DisplayIdentification.h" +#include "DisplayHardware/PowerAdvisor.h" namespace android::compositionengine { @@ -29,14 +30,15 @@ class CompositionEngine; * A parameter object for creating Display instances */ struct DisplayCreationArgs { - // True if this display is secure - bool isSecure = false; - // True if this display is a virtual display bool isVirtual = false; // Identifies the display to the HWC, if composition is supported by it std::optional<DisplayId> displayId; + + // Optional pointer to the power advisor interface, if one is needed for + // this display. + Hwc2::PowerAdvisor* powerAdvisor = nullptr; }; /** @@ -49,17 +51,13 @@ struct DisplayCreationArgs { * * Prefer: * - * DisplayCreationArgsBuilder().setIsSecure(false).setIsVirtual(false) + * DisplayCreationArgsBuilder().setIsVirtual(false) * .setDisplayId(displayId).build(); */ class DisplayCreationArgsBuilder { public: DisplayCreationArgs build() { return std::move(mArgs); } - DisplayCreationArgsBuilder& setIsSecure(bool isSecure) { - mArgs.isSecure = isSecure; - return *this; - } DisplayCreationArgsBuilder& setIsVirtual(bool isVirtual) { mArgs.isVirtual = isVirtual; return *this; @@ -68,6 +66,10 @@ public: mArgs.displayId = displayId; return *this; } + DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) { + mArgs.powerAdvisor = powerAdvisor; + return *this; + } private: DisplayCreationArgs mArgs; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h index 8cb92034f6..1259c524b2 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Layer.h @@ -21,18 +21,12 @@ #include <utils/StrongPointer.h> -namespace android { - -typedef int64_t nsecs_t; - -namespace compositionengine { +namespace android::compositionengine { class Display; class LayerFE; -namespace impl { -struct LayerCompositionState; -} // namespace impl +struct LayerFECompositionState; /** * A layer contains the output-independent composition state for a front-end @@ -46,21 +40,16 @@ public: // front-end layer no longer exists. virtual sp<LayerFE> getLayerFE() const = 0; - using CompositionState = impl::LayerCompositionState; - - // Gets the raw composition state data for the layer + // Gets the raw front-end composition state data for the layer // TODO(lpique): Make this protected once it is only internally called. - virtual const CompositionState& getState() const = 0; + virtual const LayerFECompositionState& getFEState() const = 0; - // Allows mutable access to the raw composition state data for the layer. - // This is meant to be used by the various functions that are part of the - // composition process. + // Allows mutable access to the raw front-end composition state // TODO(lpique): Make this protected once it is only internally called. - virtual CompositionState& editState() = 0; + virtual LayerFECompositionState& editFEState() = 0; // Debugging virtual void dump(std::string& result) const = 0; }; -} // namespace compositionengine -} // namespace android +} // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index 9f635b9a35..e5857694b9 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -16,7 +16,12 @@ #pragma once +#include <optional> +#include <unordered_set> + +#include <renderengine/LayerSettings.h> #include <utils/RefBase.h> +#include <utils/Timers.h> namespace android { @@ -30,9 +35,63 @@ struct LayerFECompositionState; // of the front-end layer class LayerFE : public virtual RefBase { public: - // Latches the output-independent state. If includeGeometry is false, the - // geometry state can be skipped. - virtual void latchCompositionState(LayerFECompositionState&, bool includeGeometry) const = 0; + // Called before composition starts. Should return true if this layer has + // pending updates which would require an extra display refresh cycle to + // process. + virtual bool onPreComposition(nsecs_t refreshStartTime) = 0; + + // Used with latchCompositionState() + enum class StateSubset { + // Gets the basic geometry (bounds, transparent region, visibility, + // transforms, alpha) for the layer, for computing visibility and + // coverage. + BasicGeometry, + + // Gets the full geometry (crops, buffer transforms, metadata) and + // content (buffer or color) state for the layer. + GeometryAndContent, + + // Gets the per frame content (buffer or color) state the layer. + Content, + }; + + // Latches the output-independent composition state for the layer. The + // StateSubset argument selects what portion of the state is actually needed + // by the CompositionEngine code, since computing everything may be + // expensive. + virtual void latchCompositionState(LayerFECompositionState&, StateSubset) const = 0; + + // Latches the minimal bit of state for the cursor for a fast asynchronous + // update. + virtual void latchCursorCompositionState(LayerFECompositionState&) const = 0; + + struct ClientCompositionTargetSettings { + // The clip region, or visible region that is being rendered to + const Region& clip; + + // If true, the layer should use an identity transform for its position + // transform. Used only by the captureScreen API call. + const bool useIdentityTransform; + + // If set to true, the layer should enable filtering when rendering. + const bool needsFiltering; + + // If set to true, the buffer is being sent to a destination that is + // expected to treat the buffer contents as secure. + const bool isSecure; + + // If set to true, the target buffer has protected content support. + const bool supportsProtectedContent; + + // Modified by each call to prepareClientComposition to indicate the + // region of the target buffer that should be cleared. + Region& clearRegion; + }; + + // Returns the LayerSettings to pass to RenderEngine::drawLayers, or + // nullopt_t if the layer does not render + virtual std::optional<renderengine::LayerSettings> prepareClientComposition( + ClientCompositionTargetSettings&) = 0; // Called after the layer is displayed to update the presentation fence virtual void onLayerDisplayed(const sp<Fence>&) = 0; @@ -41,5 +100,13 @@ public: virtual const char* getDebugName() const = 0; }; +// TODO(b/121291683): Specialize std::hash<> for sp<T> so these and others can +// be removed. +struct LayerFESpHash { + size_t operator()(const sp<LayerFE>& p) const { return std::hash<LayerFE*>()(p.get()); } +}; + +using LayerFESet = std::unordered_set<sp<LayerFE>, LayerFESpHash>; + } // namespace compositionengine } // namespace android diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index e6ee078624..2ba781d924 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -36,9 +36,48 @@ namespace android::compositionengine { * Used by LayerFE::getCompositionState */ struct LayerFECompositionState { - // TODO(lpique): b/121291683 Remove this one we are sure we don't need the - // value recomputed / set every frame. - Region geomVisibleRegion; + // If set to true, forces client composition on all output layers until + // the next geometry change. + bool forceClientComposition{false}; + + // TODO(b/121291683): Reorganize and rename the contents of this structure + + /* + * Visibility state + */ + // the layer stack this layer belongs to + std::optional<uint32_t> layerStackId; + + // If true, this layer should be only visible on the internal display + bool internalOnly{false}; + + // If false, this layer should not be considered visible + bool isVisible{true}; + + // True if the layer is completely opaque + bool isOpaque{true}; + + // If true, invalidates the entire visible region + bool contentDirty{false}; + + // The alpha value for this layer + float alpha{1.f}; + + // The transform from layer local coordinates to composition coordinates + ui::Transform geomLayerTransform; + + // The inverse of the layer transform + ui::Transform geomInverseLayerTransform; + + // The hint from the layer producer as to what portion of the layer is + // transparent. + Region transparentRegionHint; + + // The blend mode for this layer + Hwc2::IComposerClient::BlendMode blendMode{Hwc2::IComposerClient::BlendMode::INVALID}; + + // The bounds of the layer in layer local coordinates + FloatRect geomLayerBounds; /* * Geometry state @@ -48,23 +87,9 @@ struct LayerFECompositionState { bool geomUsesSourceCrop{false}; bool geomBufferUsesDisplayInverseTransform{false}; uint32_t geomBufferTransform{0}; - ui::Transform geomLayerTransform; - ui::Transform geomInverseLayerTransform; Rect geomBufferSize; Rect geomContentCrop; Rect geomCrop; - Region geomActiveTransparentRegion; - FloatRect geomLayerBounds; - - /* - * Presentation - */ - - // The blend mode for this layer - Hwc2::IComposerClient::BlendMode blendMode{Hwc2::IComposerClient::BlendMode::INVALID}; - - // The alpha value for this layer - float alpha{1.f}; /* * Extra metadata @@ -93,12 +118,16 @@ struct LayerFECompositionState { sp<NativeHandle> sidebandStream; // The color for this layer - Hwc2::IComposerClient::Color color; + half4 color; /* * Per-frame presentation state */ + // If true, this layer will use the dataspace chosen for the output and + // ignore the dataspace value just below + bool isColorspaceAgnostic{false}; + // The dataspace for this layer ui::Dataspace dataspace{ui::Dataspace::UNKNOWN}; @@ -107,6 +136,20 @@ struct LayerFECompositionState { // The color transform mat4 colorTransform; + bool colorTransformIsIdentity{true}; + + // True if the layer has protected content + bool hasProtectedContent{false}; + + /* + * Cursor state + */ + + // The output-independent frame for the cursor + Rect cursorFrame; + + // Debugging + void dump(std::string& out) const; }; } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index 54e6bd6af7..5f42ea16f6 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -17,10 +17,16 @@ #pragma once #include <cstdint> +#include <iterator> #include <optional> #include <string> +#include <type_traits> +#include <unordered_map> +#include <utility> -#include <math/mat4.h> +#include <compositionengine/LayerFE.h> +#include <renderengine/LayerSettings.h> +#include <ui/Fence.h> #include <ui/GraphicTypes.h> #include <ui/Region.h> #include <ui/Transform.h> @@ -28,6 +34,10 @@ #include "DisplayHardware/DisplayIdentification.h" +namespace HWC2 { +class Layer; +} // namespace HWC2 + namespace android::compositionengine { class DisplayColorProfile; @@ -36,6 +46,9 @@ class LayerFE; class RenderSurface; class OutputLayer; +struct CompositionRefreshArgs; +struct LayerFECompositionState; + namespace impl { struct OutputCompositionState; } // namespace impl @@ -45,7 +58,95 @@ struct OutputCompositionState; */ class Output { public: - using OutputLayers = std::vector<std::unique_ptr<compositionengine::OutputLayer>>; + using ReleasedLayers = std::vector<wp<LayerFE>>; + using UniqueFELayerStateMap = std::unordered_map<LayerFE*, LayerFECompositionState*>; + + // A helper class for enumerating the output layers using a C++11 ranged-based for loop + template <typename T> + class OutputLayersEnumerator { + public: + // TODO(lpique): Consider turning this into a C++20 view when possible. + template <bool IsConstIter> + class IteratorImpl { + public: + // Required definitions to be considered an iterator + using iterator_category = std::forward_iterator_tag; + using value_type = decltype(std::declval<T>().getOutputLayerOrderedByZByIndex(0)); + using difference_type = std::ptrdiff_t; + using pointer = std::conditional_t<IsConstIter, const value_type*, value_type*>; + using reference = std::conditional_t<IsConstIter, const value_type&, value_type&>; + + IteratorImpl() = default; + IteratorImpl(const T* output, size_t index) : mOutput(output), mIndex(index) {} + + value_type operator*() const { + return mOutput->getOutputLayerOrderedByZByIndex(mIndex); + } + value_type operator->() const { + return mOutput->getOutputLayerOrderedByZByIndex(mIndex); + } + + bool operator==(const IteratorImpl& other) const { + return mOutput == other.mOutput && mIndex == other.mIndex; + } + bool operator!=(const IteratorImpl& other) const { return !operator==(other); } + + IteratorImpl& operator++() { + ++mIndex; + return *this; + } + IteratorImpl operator++(int) { + auto prev = *this; + ++mIndex; + return prev; + } + + private: + const T* mOutput{nullptr}; + size_t mIndex{0}; + }; + + using iterator = IteratorImpl<false>; + using const_iterator = IteratorImpl<true>; + + explicit OutputLayersEnumerator(const T& output) : mOutput(output) {} + auto begin() const { return iterator(&mOutput, 0); } + auto end() const { return iterator(&mOutput, mOutput.getOutputLayerCount()); } + auto cbegin() const { return const_iterator(&mOutput, 0); } + auto cend() const { return const_iterator(&mOutput, mOutput.getOutputLayerCount()); } + + private: + const T& mOutput; + }; + + struct FrameFences { + sp<Fence> presentFence{Fence::NO_FENCE}; + sp<Fence> clientTargetAcquireFence{Fence::NO_FENCE}; + std::unordered_map<HWC2::Layer*, sp<Fence>> layerFences; + }; + + struct ColorProfile { + ui::ColorMode mode{ui::ColorMode::NATIVE}; + ui::Dataspace dataspace{ui::Dataspace::UNKNOWN}; + ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC}; + ui::Dataspace colorSpaceAgnosticDataspace{ui::Dataspace::UNKNOWN}; + }; + + // Use internally to incrementally compute visibility/coverage + struct CoverageState { + explicit CoverageState(LayerFESet& latchedLayers) : latchedLayers(latchedLayers) {} + + // The set of layers that had been latched for the coverage calls, to + // avoid duplicate requests to obtain the same front-end layer state. + LayerFESet& latchedLayers; + + // The region of the output which is covered by layers + Region aboveCoveredLayers; + // The region of the output which is opaquely covered by layers + Region aboveOpaqueLayers; + // The region of the output which should be considered dirty + Region dirtyRegion; + }; virtual ~Output(); @@ -54,6 +155,9 @@ public: // constructor. virtual bool isValid() const = 0; + // Returns the DisplayId the output represents, if it has one + virtual std::optional<DisplayId> getDisplayId() const = 0; + // Enables (or disables) composition on this output virtual void setCompositionEnabled(bool) = 0; @@ -67,11 +171,8 @@ public: // belongsInOutput for full details. virtual void setLayerStackFilter(uint32_t layerStackId, bool isInternal) = 0; - // Sets the color transform matrix to use - virtual void setColorTransform(const mat4&) = 0; - // Sets the output color mode - virtual void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) = 0; + virtual void setColorProfile(const ColorProfile&) = 0; // Outputs a string with a state dump virtual void dump(std::string&) const = 0; @@ -111,28 +212,71 @@ public: // A layer belongs to the output if its layerStackId matches. Additionally // if the layer should only show in the internal (primary) display only and // this output allows that. - virtual bool belongsInOutput(uint32_t layerStackId, bool internalOnly) const = 0; + virtual bool belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const = 0; + + // Determines if a layer belongs to the output. + virtual bool belongsInOutput(const Layer*) const = 0; // Returns a pointer to the output layer corresponding to the given layer on // this output, or nullptr if the layer does not have one virtual OutputLayer* getOutputLayerForLayer(Layer*) const = 0; - // Gets the OutputLayer corresponding to the input Layer instance from the - // current ordered set of output layers. If there is no such layer, a new - // one is created and returned. - virtual std::unique_ptr<OutputLayer> getOrCreateOutputLayer(std::optional<DisplayId>, - std::shared_ptr<Layer>, - sp<LayerFE>) = 0; + // Immediately clears all layers from the output. + virtual void clearOutputLayers() = 0; + + // For tests use only. Creates and appends an OutputLayer into the output. + virtual OutputLayer* injectOutputLayerForTest(const std::shared_ptr<Layer>&, + const sp<LayerFE>&) = 0; - // Sets the new ordered set of output layers for this output - virtual void setOutputLayersOrderedByZ(OutputLayers&&) = 0; + // Gets the count of output layers managed by this output + virtual size_t getOutputLayerCount() const = 0; - // Gets the ordered set of output layers for this output - virtual const OutputLayers& getOutputLayersOrderedByZ() const = 0; + // Gets an output layer in Z order given its index + virtual OutputLayer* getOutputLayerOrderedByZByIndex(size_t) const = 0; + + // A helper function for enumerating all the output layers in Z order using + // a C++11 range-based for loop. + auto getOutputLayersOrderedByZ() const { return OutputLayersEnumerator(*this); } + + // Sets the new set of layers being released this frame + virtual void setReleasedLayers(ReleasedLayers&&) = 0; + + // Prepare the output, updating the OutputLayers used in the output + virtual void prepare(const CompositionRefreshArgs&, LayerFESet&) = 0; + + // Presents the output, finalizing all composition details + virtual void present(const CompositionRefreshArgs&) = 0; + + // Latches the front-end layer state for each output layer + virtual void updateLayerStateFromFE(const CompositionRefreshArgs&) const = 0; protected: virtual void setDisplayColorProfile(std::unique_ptr<DisplayColorProfile>) = 0; virtual void setRenderSurface(std::unique_ptr<RenderSurface>) = 0; + + virtual void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) = 0; + virtual void collectVisibleLayers(const CompositionRefreshArgs&, CoverageState&) = 0; + virtual void ensureOutputLayerIfVisible(std::shared_ptr<Layer>, CoverageState&) = 0; + virtual void setReleasedLayers(const CompositionRefreshArgs&) = 0; + + virtual void updateAndWriteCompositionState(const CompositionRefreshArgs&) = 0; + virtual void setColorTransform(const CompositionRefreshArgs&) = 0; + virtual void updateColorProfile(const CompositionRefreshArgs&) = 0; + virtual void beginFrame() = 0; + virtual void prepareFrame() = 0; + virtual void devOptRepaintFlash(const CompositionRefreshArgs&) = 0; + virtual void finishFrame(const CompositionRefreshArgs&) = 0; + virtual std::optional<base::unique_fd> composeSurfaces(const Region&) = 0; + virtual void postFramebuffer() = 0; + virtual void chooseCompositionStrategy() = 0; + virtual bool getSkipColorTransform() const = 0; + virtual FrameFences presentAndGetFrameFences() = 0; + virtual std::vector<renderengine::LayerSettings> generateClientCompositionRequests( + bool supportsProtectedContent, Region& clearRegion) = 0; + virtual void appendRegionFlashRequests( + const Region& flashRegion, + std::vector<renderengine::LayerSettings>& clientCompositionLayers) = 0; + virtual void setExpensiveRenderingExpected(bool enabled) = 0; }; } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputColorSetting.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputColorSetting.h new file mode 100644 index 0000000000..6e798cea55 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputColorSetting.h @@ -0,0 +1,27 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +namespace android::compositionengine { + +enum class OutputColorSetting : int32_t { + kManaged = 0, + kUnmanaged = 1, + kEnhanced = 2, +}; + +} // namespace android::compositionengine
\ No newline at end of file diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h index cd63b57d86..a9a95cdb92 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h @@ -21,8 +21,13 @@ #include <utils/StrongPointer.h> +#include "DisplayHardware/ComposerHal.h" #include "DisplayHardware/DisplayIdentification.h" +namespace HWC2 { +class Layer; +} // namespace HWC2 + namespace android { namespace compositionengine { @@ -43,6 +48,9 @@ class OutputLayer { public: virtual ~OutputLayer(); + // Sets the HWC2::Layer associated with this layer + virtual void setHwcLayer(std::shared_ptr<HWC2::Layer>) = 0; + // Gets the output which owns this output layer virtual const Output& getOutput() const = 0; @@ -66,12 +74,36 @@ public: // Recalculates the state of the output layer from the output-independent // layer. If includeGeometry is false, the geometry state can be skipped. - virtual void updateCompositionState(bool includeGeometry) = 0; + virtual void updateCompositionState(bool includeGeometry, bool forceClientComposition) = 0; // Writes the geometry state to the HWC, or does nothing if this layer does // not use the HWC. If includeGeometry is false, the geometry state can be // skipped. - virtual void writeStateToHWC(bool includeGeometry) const = 0; + virtual void writeStateToHWC(bool includeGeometry) = 0; + + // Updates the cursor position with the HWC + virtual void writeCursorPositionToHWC() const = 0; + + // Returns the HWC2::Layer associated with this layer, if it exists + virtual HWC2::Layer* getHwcLayer() const = 0; + + // Returns true if the current layer state requires client composition + virtual bool requiresClientComposition() const = 0; + + // Returns true if the current layer should be treated as a cursor layer + virtual bool isHardwareCursor() const = 0; + + // Applies a HWC device requested composition type change + virtual void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) = 0; + + // Prepares to apply any HWC device layer requests + virtual void prepareForDeviceLayerRequests() = 0; + + // Applies a HWC device layer request + virtual void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) = 0; + + // Returns true if the composition settings scale pixels + virtual bool needsFiltering() const = 0; // Debugging virtual void dump(std::string& result) const = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h index e21128ca81..5ce2fdcf8c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h @@ -71,22 +71,18 @@ public: virtual status_t beginFrame(bool mustRecompose) = 0; // Prepares the frame for rendering - virtual status_t prepareFrame() = 0; + virtual void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) = 0; // Allocates a buffer as scratch space for GPU composition virtual sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) = 0; // Queues the drawn buffer for consumption by HWC. readyFence is the fence // which will fire when the buffer is ready for consumption. - virtual void queueBuffer(base::unique_fd&& readyFence) = 0; + virtual void queueBuffer(base::unique_fd readyFence) = 0; // Called after the HWC calls are made to present the display virtual void onPresentDisplayCompleted() = 0; - // Called to set the viewport and projection state for rendering into this - // surface - virtual void setViewportAndProjection() = 0; - // Called after the surface has been rendering to signal the surface should // be made ready for displaying virtual void flip() = 0; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h index b01eb649d8..f416c9ce84 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h @@ -26,9 +26,9 @@ public: ~CompositionEngine() override; std::shared_ptr<compositionengine::Display> createDisplay( - compositionengine::DisplayCreationArgs&&) override; + const compositionengine::DisplayCreationArgs&) override; std::shared_ptr<compositionengine::Layer> createLayer( - compositionengine::LayerCreationArgs&&) override; + const compositionengine::LayerCreationArgs&) override; HWComposer& getHwComposer() const override; void setHwComposer(std::unique_ptr<HWComposer>) override; @@ -36,9 +36,28 @@ public: renderengine::RenderEngine& getRenderEngine() const override; void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override; + bool needsAnotherUpdate() const override; + nsecs_t getLastFrameRefreshTimestamp() const override; + + void present(CompositionRefreshArgs&) override; + + void updateCursorAsync(CompositionRefreshArgs&) override; + + void preComposition(CompositionRefreshArgs&) override; + + // Debugging + void dump(std::string&) const override; + + void updateLayerStateFromFE(CompositionRefreshArgs& args); + + // Testing + void setNeedsAnotherUpdateForTest(bool); + private: std::unique_ptr<HWComposer> mHwComposer; std::unique_ptr<renderengine::RenderEngine> mRenderEngine; + bool mNeedsAnotherUpdate = false; + nsecs_t mRefreshStartTime = 0; }; std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine(); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index 0e20c43321..ace876c310 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -16,45 +16,83 @@ #pragma once -#include <memory> - #include <compositionengine/Display.h> +#include <compositionengine/DisplayCreationArgs.h> #include <compositionengine/impl/Output.h> +#include <memory> + #include "DisplayHardware/DisplayIdentification.h" +#include "DisplayHardware/HWComposer.h" +#include "DisplayHardware/PowerAdvisor.h" namespace android::compositionengine { class CompositionEngine; -struct DisplayCreationArgs; - namespace impl { -class Display : public compositionengine::impl::Output, public compositionengine::Display { +// The implementation class contains the common implementation, but does not +// actually contain the final display state. +class Display : public compositionengine::impl::Output, public virtual compositionengine::Display { public: - Display(const CompositionEngine&, compositionengine::DisplayCreationArgs&&); + explicit Display(const compositionengine::DisplayCreationArgs&); virtual ~Display(); // compositionengine::Output overrides + std::optional<DisplayId> getDisplayId() const override; void dump(std::string&) const override; - void setColorTransform(const mat4&) override; - void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override; + using compositionengine::impl::Output::setReleasedLayers; + void setReleasedLayers(const CompositionRefreshArgs&) override; + void setColorTransform(const CompositionRefreshArgs&) override; + void setColorProfile(const ColorProfile&) override; + void chooseCompositionStrategy() override; + bool getSkipColorTransform() const override; + compositionengine::Output::FrameFences presentAndGetFrameFences() override; + void setExpensiveRenderingExpected(bool) override; + void finishFrame(const CompositionRefreshArgs&) override; // compositionengine::Display overrides const std::optional<DisplayId>& getId() const override; bool isSecure() const override; bool isVirtual() const override; void disconnect() override; - void createDisplayColorProfile(compositionengine::DisplayColorProfileCreationArgs&&) override; - void createRenderSurface(compositionengine::RenderSurfaceCreationArgs&&) override; + void createDisplayColorProfile( + const compositionengine::DisplayColorProfileCreationArgs&) override; + void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override; + + // Internal helpers used by chooseCompositionStrategy() + using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes; + using DisplayRequests = android::HWComposer::DeviceRequestedChanges::DisplayRequests; + using LayerRequests = android::HWComposer::DeviceRequestedChanges::LayerRequests; + virtual bool anyLayersRequireClientComposition() const; + virtual bool allLayersRequireClientComposition() const; + virtual void applyChangedTypesToLayers(const ChangedTypes&); + virtual void applyDisplayRequests(const DisplayRequests&); + virtual void applyLayerRequestsToLayers(const LayerRequests&); + + // Internal + std::unique_ptr<compositionengine::OutputLayer> createOutputLayer( + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&) const; private: const bool mIsVirtual; std::optional<DisplayId> mId; + Hwc2::PowerAdvisor* const mPowerAdvisor{nullptr}; }; -std::shared_ptr<compositionengine::Display> createDisplay( - const compositionengine::CompositionEngine&, compositionengine::DisplayCreationArgs&&); +// This template factory function standardizes the implementation details of the +// final class using the types actually required by the implementation. This is +// not possible to do in the base class as those types may not even be visible +// to the base code. +template <typename BaseDisplay, typename CompositionEngine, typename DisplayCreationArgs> +std::shared_ptr<BaseDisplay> createDisplayTemplated(const CompositionEngine& compositionEngine, + const DisplayCreationArgs& args) { + return createOutputTemplated<BaseDisplay>(compositionEngine, args); +} + +std::shared_ptr<Display> createDisplay(const compositionengine::CompositionEngine&, + const compositionengine::DisplayCreationArgs&); + } // namespace impl } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h index 49c2d2c0ea..9bc0e681c7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/DisplayColorProfile.h @@ -33,7 +33,7 @@ namespace impl { class DisplayColorProfile : public compositionengine::DisplayColorProfile { public: - DisplayColorProfile(DisplayColorProfileCreationArgs&&); + DisplayColorProfile(const DisplayColorProfileCreationArgs&); ~DisplayColorProfile() override; bool isValid() const override; @@ -54,6 +54,8 @@ public: bool hasDolbyVisionSupport() const override; const HdrCapabilities& getHdrCapabilities() const override; + bool isDataspaceSupported(ui::Dataspace) const override; + ui::Dataspace getTargetDataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace) const override; void dump(std::string&) const override; @@ -89,7 +91,7 @@ private: }; std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile( - DisplayColorProfileCreationArgs&&); + const DisplayColorProfileCreationArgs&); } // namespace impl } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h index 3e56b21b18..46489fb9cd 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Layer.h @@ -19,42 +19,66 @@ #include <memory> #include <compositionengine/Layer.h> -#include <compositionengine/impl/LayerCompositionState.h> -#include <utils/RefBase.h> +#include <compositionengine/LayerCreationArgs.h> #include <utils/StrongPointer.h> namespace android::compositionengine { -class CompositionEngine; -class LayerFE; - struct LayerCreationArgs; namespace impl { -class Display; - -class Layer : public compositionengine::Layer { +// The implementation class contains the common implementation, but does not +// actually contain the final layer state. +class Layer : public virtual compositionengine::Layer { public: - Layer(const CompositionEngine&, compositionengine::LayerCreationArgs&&); ~Layer() override; - sp<LayerFE> getLayerFE() const override; - - const LayerCompositionState& getState() const override; - LayerCompositionState& editState() override; - - void dump(std::string& result) const override; - -private: - const compositionengine::CompositionEngine& mCompositionEngine; - const wp<LayerFE> mLayerFE; + // compositionengine::Layer overrides + void dump(std::string&) const override; - LayerCompositionState mState; +protected: + // Implemented by the final implementation for the final state it uses. + virtual void dumpFEState(std::string&) const = 0; }; -std::shared_ptr<compositionengine::Layer> createLayer(const compositionengine::CompositionEngine&, - compositionengine::LayerCreationArgs&&); +// This template factory function standardizes the implementation details of the +// final class using the types actually required by the implementation. This is +// not possible to do in the base class as those types may not even be visible +// to the base code. +template <typename BaseLayer, typename LayerCreationArgs> +std::shared_ptr<BaseLayer> createLayerTemplated(const LayerCreationArgs& args) { + class Layer final : public BaseLayer { + public: +// Clang incorrectly complains that these are unused. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-local-typedef" + using LayerFE = std::remove_pointer_t<decltype( + std::declval<decltype(std::declval<LayerCreationArgs>().layerFE)>().unsafe_get())>; + using LayerFECompositionState = std::remove_const_t< + std::remove_reference_t<decltype(std::declval<BaseLayer>().getFEState())>>; +#pragma clang diagnostic pop + + explicit Layer(const LayerCreationArgs& args) : mLayerFE(args.layerFE) {} + ~Layer() override = default; + + private: + // compositionengine::Layer overrides + sp<compositionengine::LayerFE> getLayerFE() const override { return mLayerFE.promote(); } + const LayerFECompositionState& getFEState() const override { return mFrontEndState; } + LayerFECompositionState& editFEState() override { return mFrontEndState; } + + // compositionengine::impl::Layer overrides + void dumpFEState(std::string& out) const override { mFrontEndState.dump(out); } + + const wp<LayerFE> mLayerFE; + LayerFECompositionState mFrontEndState; + }; + + return std::make_shared<Layer>(args); +} + +std::shared_ptr<Layer> createLayer(const LayerCreationArgs&); } // namespace impl } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index b1d1f42f46..a2342aea57 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -16,36 +16,33 @@ #pragma once -#include <memory> -#include <utility> -#include <vector> - +#include <compositionengine/CompositionEngine.h> #include <compositionengine/Output.h> #include <compositionengine/impl/OutputCompositionState.h> -namespace android::compositionengine { - -class CompositionEngine; -class Layer; -class OutputLayer; +#include <memory> +#include <utility> +#include <vector> -namespace impl { +namespace android::compositionengine::impl { +// The implementation class contains the common implementation, but does not +// actually contain the final output state. class Output : public virtual compositionengine::Output { public: - Output(const CompositionEngine&); ~Output() override; + // compositionengine::Output overrides bool isValid() const override; - + std::optional<DisplayId> getDisplayId() const override; void setCompositionEnabled(bool) override; void setProjection(const ui::Transform&, int32_t orientation, const Rect& frame, const Rect& viewport, const Rect& scissor, bool needsFiltering) override; void setBounds(const ui::Size&) override; void setLayerStackFilter(uint32_t layerStackId, bool isInternal) override; - void setColorTransform(const mat4&) override; - void setColorMode(ui::ColorMode, ui::Dataspace, ui::RenderIntent) override; + void setColorTransform(const compositionengine::CompositionRefreshArgs&) override; + void setColorProfile(const ColorProfile&) override; void dump(std::string&) const override; @@ -58,42 +55,176 @@ public: compositionengine::RenderSurface* getRenderSurface() const override; void setRenderSurface(std::unique_ptr<compositionengine::RenderSurface>) override; - const OutputCompositionState& getState() const override; - OutputCompositionState& editState() override; - Region getDirtyRegion(bool repaintEverything) const override; - bool belongsInOutput(uint32_t, bool) const override; + bool belongsInOutput(std::optional<uint32_t>, bool) const override; + bool belongsInOutput(const compositionengine::Layer*) const override; compositionengine::OutputLayer* getOutputLayerForLayer( compositionengine::Layer*) const override; - std::unique_ptr<compositionengine::OutputLayer> getOrCreateOutputLayer( - std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>, - sp<LayerFE>) override; - void setOutputLayersOrderedByZ(OutputLayers&&) override; - const OutputLayers& getOutputLayersOrderedByZ() const override; + + void setReleasedLayers(ReleasedLayers&&) override; + + void prepare(const CompositionRefreshArgs&, LayerFESet&) override; + void present(const CompositionRefreshArgs&) override; + + void rebuildLayerStacks(const CompositionRefreshArgs&, LayerFESet&) override; + void collectVisibleLayers(const CompositionRefreshArgs&, + compositionengine::Output::CoverageState&) override; + void ensureOutputLayerIfVisible(std::shared_ptr<compositionengine::Layer>, + compositionengine::Output::CoverageState&) override; + void setReleasedLayers(const compositionengine::CompositionRefreshArgs&) override; + + void updateLayerStateFromFE(const CompositionRefreshArgs&) const override; + void updateAndWriteCompositionState(const compositionengine::CompositionRefreshArgs&) override; + void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override; + void beginFrame() override; + void prepareFrame() override; + void devOptRepaintFlash(const CompositionRefreshArgs&) override; + void finishFrame(const CompositionRefreshArgs&) override; + std::optional<base::unique_fd> composeSurfaces(const Region&) override; + void postFramebuffer() override; // Testing + const ReleasedLayers& getReleasedLayersForTest() const; void setDisplayColorProfileForTest(std::unique_ptr<compositionengine::DisplayColorProfile>); void setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSurface>); protected: - const CompositionEngine& getCompositionEngine() const; + std::unique_ptr<compositionengine::OutputLayer> createOutputLayer( + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&) const; + std::optional<size_t> findCurrentOutputLayerForLayer(compositionengine::Layer*) const; + void chooseCompositionStrategy() override; + bool getSkipColorTransform() const override; + compositionengine::Output::FrameFences presentAndGetFrameFences() override; + std::vector<renderengine::LayerSettings> generateClientCompositionRequests( + bool supportsProtectedContent, Region& clearRegion) override; + void appendRegionFlashRequests(const Region&, + std::vector<renderengine::LayerSettings>&) override; + void setExpensiveRenderingExpected(bool enabled) override; void dumpBase(std::string&) const; + // Implemented by the final implementation for the final state it uses. + virtual compositionengine::OutputLayer* ensureOutputLayer( + std::optional<size_t>, const std::shared_ptr<compositionengine::Layer>&, + const sp<LayerFE>&) = 0; + virtual compositionengine::OutputLayer* injectOutputLayerForTest( + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&) = 0; + virtual void finalizePendingOutputLayers() = 0; + virtual const compositionengine::CompositionEngine& getCompositionEngine() const = 0; + virtual void dumpState(std::string& out) const = 0; + private: void dirtyEntireOutput(); - - const CompositionEngine& mCompositionEngine; + ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const; + compositionengine::Output::ColorProfile pickColorProfile( + const compositionengine::CompositionRefreshArgs&) const; std::string mName; - OutputCompositionState mState; - std::unique_ptr<compositionengine::DisplayColorProfile> mDisplayColorProfile; std::unique_ptr<compositionengine::RenderSurface> mRenderSurface; - OutputLayers mOutputLayersOrderedByZ; + ReleasedLayers mReleasedLayers; }; -} // namespace impl -} // namespace android::compositionengine +// This template factory function standardizes the implementation details of the +// final class using the types actually required by the implementation. This is +// not possible to do in the base class as those types may not even be visible +// to the base code. +template <typename BaseOutput, typename CompositionEngine, typename... Args> +std::shared_ptr<BaseOutput> createOutputTemplated(const CompositionEngine& compositionEngine, + Args... args) { + class Output final : public BaseOutput { + public: +// Clang incorrectly complains that these are unused. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-local-typedef" + + using OutputCompositionState = std::remove_const_t< + std::remove_reference_t<decltype(std::declval<BaseOutput>().getState())>>; + using OutputLayer = std::remove_pointer_t<decltype( + std::declval<BaseOutput>().getOutputLayerOrderedByZByIndex(0))>; + +#pragma clang diagnostic pop + + explicit Output(const CompositionEngine& compositionEngine, Args... args) + : BaseOutput(std::forward<Args>(args)...), mCompositionEngine(compositionEngine) {} + ~Output() override = default; + + private: + // compositionengine::Output overrides + const OutputCompositionState& getState() const override { return mState; } + + OutputCompositionState& editState() override { return mState; } + + size_t getOutputLayerCount() const override { + return mCurrentOutputLayersOrderedByZ.size(); + } + + OutputLayer* getOutputLayerOrderedByZByIndex(size_t index) const override { + if (index >= mCurrentOutputLayersOrderedByZ.size()) { + return nullptr; + } + return mCurrentOutputLayersOrderedByZ[index].get(); + } + + // compositionengine::impl::Output overrides + const CompositionEngine& getCompositionEngine() const override { + return mCompositionEngine; + }; + + OutputLayer* ensureOutputLayer(std::optional<size_t> prevIndex, + const std::shared_ptr<compositionengine::Layer>& layer, + const sp<LayerFE>& layerFE) { + auto outputLayer = (prevIndex && *prevIndex <= mCurrentOutputLayersOrderedByZ.size()) + ? std::move(mCurrentOutputLayersOrderedByZ[*prevIndex]) + : BaseOutput::createOutputLayer(layer, layerFE); + auto result = outputLayer.get(); + mPendingOutputLayersOrderedByZ.emplace_back(std::move(outputLayer)); + return result; + } + + void finalizePendingOutputLayers() override { + // The pending layers are added in reverse order. Reverse them to + // get the back-to-front ordered list of layers. + std::reverse(mPendingOutputLayersOrderedByZ.begin(), + mPendingOutputLayersOrderedByZ.end()); + + mCurrentOutputLayersOrderedByZ = std::move(mPendingOutputLayersOrderedByZ); + } + + void dumpState(std::string& out) const override { mState.dump(out); } + + OutputLayer* injectOutputLayerForTest( + const std::shared_ptr<compositionengine::Layer>& layer, + const sp<LayerFE>& layerFE) override { + auto outputLayer = BaseOutput::createOutputLayer(layer, layerFE); + auto result = outputLayer.get(); + mCurrentOutputLayersOrderedByZ.emplace_back(std::move(outputLayer)); + return result; + } + + // Note: This is declared as a private virtual non-override so it can be + // an override implementation in the unit tests, but otherwise is not an + // accessible override for the normal implementation. + virtual void injectOutputLayerForTest(std::unique_ptr<OutputLayer> outputLayer) { + mCurrentOutputLayersOrderedByZ.emplace_back(std::move(outputLayer)); + } + + void clearOutputLayers() override { + mCurrentOutputLayersOrderedByZ.clear(); + mPendingOutputLayersOrderedByZ.clear(); + } + + const CompositionEngine& mCompositionEngine; + OutputCompositionState mState; + std::vector<std::unique_ptr<OutputLayer>> mCurrentOutputLayersOrderedByZ; + std::vector<std::unique_ptr<OutputLayer>> mPendingOutputLayersOrderedByZ; + }; + + return std::make_shared<Output>(compositionEngine, std::forward<Args>(args)...); +} + +std::shared_ptr<Output> createOutput(const compositionengine::CompositionEngine&); + +} // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h index 0c47eb5a01..17d3d3fb7d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h @@ -35,6 +35,16 @@ struct OutputCompositionState { // If false, this output is not considered secure bool isSecure{false}; + // If true, the current frame on this output uses client composition + bool usesClientComposition{false}; + + // If true, the current frame on this output uses device composition + bool usesDeviceComposition{false}; + + // If true, the client target should be flipped when performing client + // composition + bool flipClientTarget{false}; + // If true, this output displays layers that are internal-only bool layerStackInternal{false}; @@ -76,11 +86,8 @@ struct OutputCompositionState { // True if the last composition frame had visible layers bool lastCompositionHadVisibleLayers{false}; - // The color transform to apply - android_color_transform_t colorTransform{HAL_COLOR_TRANSFORM_IDENTITY}; - - // The color transform matrix to apply, corresponding with colorTransform. - mat4 colorTransformMat; + // The color transform matrix to apply + mat4 colorTransformMatrix; // Current active color mode ui::ColorMode colorMode{ui::ColorMode::NATIVE}; @@ -88,9 +95,12 @@ struct OutputCompositionState { // Current active render intent ui::RenderIntent renderIntent{ui::RenderIntent::COLORIMETRIC}; - // Current active dstaspace + // Current active dataspace ui::Dataspace dataspace{ui::Dataspace::UNKNOWN}; + // Current target dataspace + ui::Dataspace targetDataspace{ui::Dataspace::UNKNOWN}; + // Debugging void dump(std::string& result) const; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index 6a4818f10f..95c8afbede 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -20,50 +20,113 @@ #include <string> #include <compositionengine/OutputLayer.h> -#include <compositionengine/impl/OutputLayerCompositionState.h> #include <ui/FloatRect.h> #include <ui/Rect.h> #include "DisplayHardware/DisplayIdentification.h" -namespace android::compositionengine::impl { +namespace android::compositionengine { -class OutputLayer : public compositionengine::OutputLayer { +struct LayerFECompositionState; + +namespace impl { + +// The implementation class contains the common implementation, but does not +// actually contain the final layer state. +class OutputLayer : public virtual compositionengine::OutputLayer { public: - OutputLayer(const compositionengine::Output&, std::shared_ptr<compositionengine::Layer>, - sp<compositionengine::LayerFE>); ~OutputLayer() override; - void initialize(const CompositionEngine&, std::optional<DisplayId>); + void setHwcLayer(std::shared_ptr<HWC2::Layer>) override; - const compositionengine::Output& getOutput() const override; - compositionengine::Layer& getLayer() const override; - compositionengine::LayerFE& getLayerFE() const override; + void updateCompositionState(bool includeGeometry, bool forceClientComposition) override; + void writeStateToHWC(bool) override; + void writeCursorPositionToHWC() const override; - const OutputLayerCompositionState& getState() const override; - OutputLayerCompositionState& editState() override; + HWC2::Layer* getHwcLayer() const override; + bool requiresClientComposition() const override; + bool isHardwareCursor() const override; + void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) override; + void prepareForDeviceLayerRequests() override; + void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override; + bool needsFiltering() const override; - void updateCompositionState(bool) override; - void writeStateToHWC(bool) const override; - - void dump(std::string& result) const override; + void dump(std::string&) const override; virtual FloatRect calculateOutputSourceCrop() const; virtual Rect calculateOutputDisplayFrame() const; virtual uint32_t calculateOutputRelativeBufferTransform() const; +protected: + // Implemented by the final implementation for the final state it uses. + virtual void dumpState(std::string&) const = 0; + private: Rect calculateInitialCrop() const; - - const compositionengine::Output& mOutput; - std::shared_ptr<compositionengine::Layer> mLayer; - sp<compositionengine::LayerFE> mLayerFE; - - OutputLayerCompositionState mState; + void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition); + void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&); + void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*); + void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&); + void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&); + void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&); + void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&); + void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition); + void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from, + Hwc2::IComposerClient::Composition to) const; }; -std::unique_ptr<compositionengine::OutputLayer> createOutputLayer( - const CompositionEngine&, std::optional<DisplayId>, const compositionengine::Output&, - std::shared_ptr<compositionengine::Layer>, sp<compositionengine::LayerFE>); - -} // namespace android::compositionengine::impl +// This template factory function standardizes the implementation details of the +// final class using the types actually required by the implementation. This is +// not possible to do in the base class as those types may not even be visible +// to the base code. +template <typename BaseOutputLayer> +std::unique_ptr<BaseOutputLayer> createOutputLayerTemplated(const Output& output, + std::shared_ptr<Layer> layer, + sp<LayerFE> layerFE) { + class OutputLayer final : public BaseOutputLayer { + public: +// Clang incorrectly complains that these are unused. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-local-typedef" + + using OutputLayerCompositionState = std::remove_const_t< + std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getState())>>; + using Output = std::remove_const_t< + std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getOutput())>>; + using Layer = std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getLayer())>; + using LayerFE = + std::remove_reference_t<decltype(std::declval<BaseOutputLayer>().getLayerFE())>; + +#pragma clang diagnostic pop + + OutputLayer(const Output& output, const std::shared_ptr<Layer>& layer, + const sp<LayerFE>& layerFE) + : mOutput(output), mLayer(layer), mLayerFE(layerFE) {} + ~OutputLayer() override = default; + + private: + // compositionengine::OutputLayer overrides + const Output& getOutput() const override { return mOutput; } + Layer& getLayer() const override { return *mLayer; } + LayerFE& getLayerFE() const override { return *mLayerFE; } + const OutputLayerCompositionState& getState() const override { return mState; } + OutputLayerCompositionState& editState() override { return mState; } + + // compositionengine::impl::OutputLayer overrides + void dumpState(std::string& out) const override { mState.dump(out); } + + const Output& mOutput; + const std::shared_ptr<Layer> mLayer; + const sp<LayerFE> mLayerFE; + OutputLayerCompositionState mState; + }; + + return std::make_unique<OutputLayer>(output, layer, layerFE); +} + +std::unique_ptr<OutputLayer> createOutputLayer(const compositionengine::Output&, + const std::shared_ptr<compositionengine::Layer>&, + const sp<LayerFE>&); + +} // namespace impl +} // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h index b78e9e0076..13474492af 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h @@ -23,6 +23,7 @@ #include <compositionengine/impl/HwcBufferCache.h> #include <renderengine/Mesh.h> #include <ui/FloatRect.h> +#include <ui/GraphicTypes.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -39,9 +40,18 @@ class HWComposer; namespace compositionengine::impl { struct OutputLayerCompositionState { - // The region of this layer which is visible on this output + // The portion of the layer that is not obscured by opaque layers on top Region visibleRegion; + // The portion of the layer that is not obscured and is also opaque + Region visibleNonTransparentRegion; + + // The portion of the layer that is obscured by opaque layers on top + Region coveredRegion; + + // The visibleRegion transformed to output space + Region outputSpaceVisibleRegion; + // If true, client composition will be used on this output bool forceClientComposition{false}; @@ -57,8 +67,11 @@ struct OutputLayerCompositionState { // The buffer transform to use for this layer o on this output. Hwc2::Transform bufferTransform{static_cast<Hwc2::Transform>(0)}; + // The dataspace for this layer + ui::Dataspace dataspace{ui::Dataspace::UNKNOWN}; + // The Z order index of this layer on this output - uint32_t z; + uint32_t z{0}; /* * HWC state @@ -70,7 +83,7 @@ struct OutputLayerCompositionState { // The HWC Layer backing this layer std::shared_ptr<HWC2::Layer> hwcLayer; - // The HWC composition type for this layer + // The most recently set HWC composition type for this layer Hwc2::IComposerClient::Composition hwcCompositionType{ Hwc2::IComposerClient::Composition::INVALID}; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h index 0f57315eb6..692d78d825 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h @@ -39,7 +39,7 @@ namespace impl { class RenderSurface : public compositionengine::RenderSurface { public: RenderSurface(const CompositionEngine&, compositionengine::Display&, - compositionengine::RenderSurfaceCreationArgs&&); + const compositionengine::RenderSurfaceCreationArgs&); ~RenderSurface() override; bool isValid() const override; @@ -52,11 +52,10 @@ public: void setDisplaySize(const ui::Size&) override; void setProtected(bool useProtected) override; status_t beginFrame(bool mustRecompose) override; - status_t prepareFrame() override; + void prepareFrame(bool usesClientComposition, bool usesDeviceComposition) override; sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) override; - void queueBuffer(base::unique_fd&& readyFence) override; + void queueBuffer(base::unique_fd readyFence) override; void onPresentDisplayCompleted() override; - void setViewportAndProjection() override; void flip() override; // Debugging @@ -85,7 +84,7 @@ private: std::unique_ptr<compositionengine::RenderSurface> createRenderSurface( const compositionengine::CompositionEngine&, compositionengine::Display&, - compositionengine::RenderSurfaceCreationArgs&&); + const compositionengine::RenderSurfaceCreationArgs&); } // namespace impl } // namespace compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h index 0f57685861..8e6f2e2359 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h @@ -17,6 +17,7 @@ #pragma once #include <compositionengine/CompositionEngine.h> +#include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/DisplayCreationArgs.h> #include <compositionengine/LayerCreationArgs.h> #include <gmock/gmock.h> @@ -31,14 +32,24 @@ public: CompositionEngine(); ~CompositionEngine() override; - MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(DisplayCreationArgs&&)); - MOCK_METHOD1(createLayer, std::shared_ptr<Layer>(LayerCreationArgs&&)); + MOCK_METHOD1(createDisplay, std::shared_ptr<Display>(const DisplayCreationArgs&)); + MOCK_METHOD1(createLayer, std::shared_ptr<Layer>(const LayerCreationArgs&)); MOCK_CONST_METHOD0(getHwComposer, HWComposer&()); MOCK_METHOD1(setHwComposer, void(std::unique_ptr<HWComposer>)); MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&()); MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>)); + + MOCK_CONST_METHOD0(needsAnotherUpdate, bool()); + MOCK_CONST_METHOD0(getLastFrameRefreshTimestamp, nsecs_t()); + + MOCK_METHOD1(present, void(CompositionRefreshArgs&)); + MOCK_METHOD1(updateCursorAsync, void(CompositionRefreshArgs&)); + + MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&)); + + MOCK_CONST_METHOD1(dump, void(std::string&)); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h index d763aa6a23..57f33ae818 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h @@ -38,8 +38,8 @@ public: MOCK_METHOD0(disconnect, void()); - MOCK_METHOD1(createDisplayColorProfile, void(DisplayColorProfileCreationArgs&&)); - MOCK_METHOD1(createRenderSurface, void(RenderSurfaceCreationArgs&&)); + MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&)); + MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&)); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h index 8056c9d290..1aaebea295 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/DisplayColorProfile.h @@ -42,6 +42,9 @@ public: MOCK_CONST_METHOD0(hasDolbyVisionSupport, bool()); MOCK_CONST_METHOD0(getHdrCapabilities, const HdrCapabilities&()); + MOCK_CONST_METHOD1(isDataspaceSupported, bool(ui::Dataspace)); + MOCK_CONST_METHOD3(getTargetDataspace, + ui::Dataspace(ui::ColorMode, ui::Dataspace, ui::Dataspace)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h index cce3b97bbd..4f03cb46ae 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Layer.h @@ -18,7 +18,7 @@ #include <compositionengine/Layer.h> #include <compositionengine/LayerFE.h> -#include <compositionengine/impl/LayerCompositionState.h> +#include <compositionengine/LayerFECompositionState.h> #include <gmock/gmock.h> namespace android::compositionengine::mock { @@ -30,8 +30,8 @@ public: MOCK_CONST_METHOD0(getLayerFE, sp<LayerFE>()); - MOCK_CONST_METHOD0(getState, const CompositionState&()); - MOCK_METHOD0(editState, CompositionState&()); + MOCK_CONST_METHOD0(getFEState, const LayerFECompositionState&()); + MOCK_METHOD0(editFEState, LayerFECompositionState&()); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index aab18db3c9..3eada3c6aa 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -30,7 +30,14 @@ public: LayerFE(); virtual ~LayerFE(); - MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool)); + MOCK_METHOD1(onPreComposition, bool(nsecs_t)); + + MOCK_CONST_METHOD2(latchCompositionState, + void(LayerFECompositionState&, compositionengine::LayerFE::StateSubset)); + MOCK_CONST_METHOD1(latchCursorCompositionState, void(LayerFECompositionState&)); + MOCK_METHOD1(prepareClientComposition, + std::optional<renderengine::LayerSettings>( + compositionengine::LayerFE::ClientCompositionTargetSettings&)); MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&)); MOCK_CONST_METHOD0(getDebugName, const char*()); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index d0e7b195a8..02e68fcae2 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -16,6 +16,7 @@ #pragma once +#include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/DisplayColorProfile.h> #include <compositionengine/Layer.h> #include <compositionengine/LayerFE.h> @@ -33,6 +34,7 @@ public: virtual ~Output(); MOCK_CONST_METHOD0(isValid, bool()); + MOCK_CONST_METHOD0(getDisplayId, std::optional<DisplayId>()); MOCK_METHOD1(setCompositionEnabled, void(bool)); MOCK_METHOD6(setProjection, @@ -40,33 +42,75 @@ public: MOCK_METHOD1(setBounds, void(const ui::Size&)); MOCK_METHOD2(setLayerStackFilter, void(uint32_t, bool)); - MOCK_METHOD1(setColorTransform, void(const mat4&)); - MOCK_METHOD3(setColorMode, void(ui::ColorMode, ui::Dataspace, ui::RenderIntent)); + MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&)); + MOCK_METHOD1(setColorProfile, void(const ColorProfile&)); MOCK_CONST_METHOD1(dump, void(std::string&)); MOCK_CONST_METHOD0(getName, const std::string&()); MOCK_METHOD1(setName, void(const std::string&)); - MOCK_CONST_METHOD0(getDisplayColorProfile, DisplayColorProfile*()); - MOCK_METHOD1(setDisplayColorProfile, void(std::unique_ptr<DisplayColorProfile>)); + MOCK_CONST_METHOD0(getDisplayColorProfile, compositionengine::DisplayColorProfile*()); + MOCK_METHOD1(setDisplayColorProfile, + void(std::unique_ptr<compositionengine::DisplayColorProfile>)); - MOCK_CONST_METHOD0(getRenderSurface, RenderSurface*()); - MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<RenderSurface>)); + MOCK_CONST_METHOD0(getRenderSurface, compositionengine::RenderSurface*()); + MOCK_METHOD1(setRenderSurface, void(std::unique_ptr<compositionengine::RenderSurface>)); MOCK_CONST_METHOD0(getState, const OutputCompositionState&()); MOCK_METHOD0(editState, OutputCompositionState&()); MOCK_CONST_METHOD1(getDirtyRegion, Region(bool)); - MOCK_CONST_METHOD2(belongsInOutput, bool(uint32_t, bool)); + MOCK_CONST_METHOD2(belongsInOutput, bool(std::optional<uint32_t>, bool)); + MOCK_CONST_METHOD1(belongsInOutput, bool(const compositionengine::Layer*)); MOCK_CONST_METHOD1(getOutputLayerForLayer, compositionengine::OutputLayer*(compositionengine::Layer*)); - MOCK_METHOD3(getOrCreateOutputLayer, - std::unique_ptr<compositionengine::OutputLayer>( - std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>, - sp<compositionengine::LayerFE>)); - MOCK_METHOD1(setOutputLayersOrderedByZ, void(OutputLayers&&)); - MOCK_CONST_METHOD0(getOutputLayersOrderedByZ, OutputLayers&()); + MOCK_METHOD0(clearOutputLayers, void()); + MOCK_METHOD2(injectOutputLayerForTest, + compositionengine::OutputLayer*(const std::shared_ptr<compositionengine::Layer>&, + const sp<compositionengine::LayerFE>&)); + MOCK_CONST_METHOD0(getOutputLayerCount, size_t()); + MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t)); + + MOCK_METHOD1(setReleasedLayers, void(ReleasedLayers&&)); + + MOCK_METHOD2(prepare, void(const compositionengine::CompositionRefreshArgs&, LayerFESet&)); + MOCK_METHOD1(present, void(const compositionengine::CompositionRefreshArgs&)); + + MOCK_METHOD2(rebuildLayerStacks, + void(const compositionengine::CompositionRefreshArgs&, LayerFESet&)); + MOCK_METHOD2(collectVisibleLayers, + void(const compositionengine::CompositionRefreshArgs&, + compositionengine::Output::CoverageState&)); + MOCK_METHOD2(ensureOutputLayerIfVisible, + void(std::shared_ptr<compositionengine::Layer>, + compositionengine::Output::CoverageState&)); + MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&)); + + MOCK_CONST_METHOD1(updateLayerStateFromFE, void(const CompositionRefreshArgs&)); + MOCK_METHOD1(updateAndWriteCompositionState, void(const CompositionRefreshArgs&)); + MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&)); + + MOCK_METHOD0(beginFrame, void()); + + MOCK_METHOD0(prepareFrame, void()); + MOCK_METHOD0(chooseCompositionStrategy, void()); + + MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&)); + + MOCK_METHOD1(finishFrame, void(const compositionengine::CompositionRefreshArgs&)); + + MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&)); + MOCK_CONST_METHOD0(getSkipColorTransform, bool()); + + MOCK_METHOD0(postFramebuffer, void()); + MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences()); + + MOCK_METHOD2(generateClientCompositionRequests, + std::vector<renderengine::LayerSettings>(bool, Region&)); + MOCK_METHOD2(appendRegionFlashRequests, + void(const Region&, std::vector<renderengine::LayerSettings>&)); + MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h index 29cd08a681..631760afe7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h @@ -31,6 +31,8 @@ public: OutputLayer(); virtual ~OutputLayer(); + MOCK_METHOD1(setHwcLayer, void(std::shared_ptr<HWC2::Layer>)); + MOCK_CONST_METHOD0(getOutput, const compositionengine::Output&()); MOCK_CONST_METHOD0(getLayer, compositionengine::Layer&()); MOCK_CONST_METHOD0(getLayerFE, compositionengine::LayerFE&()); @@ -38,8 +40,17 @@ public: MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&()); MOCK_METHOD0(editState, impl::OutputLayerCompositionState&()); - MOCK_METHOD1(updateCompositionState, void(bool)); - MOCK_CONST_METHOD1(writeStateToHWC, void(bool)); + MOCK_METHOD2(updateCompositionState, void(bool, bool)); + MOCK_METHOD1(writeStateToHWC, void(bool)); + MOCK_CONST_METHOD0(writeCursorPositionToHWC, void()); + + MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*()); + MOCK_CONST_METHOD0(requiresClientComposition, bool()); + MOCK_CONST_METHOD0(isHardwareCursor, bool()); + MOCK_METHOD1(applyDeviceCompositionTypeChange, void(Hwc2::IComposerClient::Composition)); + MOCK_METHOD0(prepareForDeviceLayerRequests, void()); + MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request)); + MOCK_CONST_METHOD0(needsFiltering, bool()); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h index ca2299aa26..ed4d492654 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h @@ -37,11 +37,10 @@ public: MOCK_METHOD1(setProtected, void(bool)); MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace)); MOCK_METHOD1(beginFrame, status_t(bool mustRecompose)); - MOCK_METHOD0(prepareFrame, status_t()); + MOCK_METHOD2(prepareFrame, void(bool, bool)); MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*)); - MOCK_METHOD1(queueBuffer, void(base::unique_fd&&)); + MOCK_METHOD1(queueBuffer, void(base::unique_fd)); MOCK_METHOD0(onPresentDisplayCompleted, void()); - MOCK_METHOD0(setViewportAndProjection, void()); MOCK_METHOD0(flip, void()); MOCK_CONST_METHOD1(dump, void(std::string& result)); MOCK_CONST_METHOD0(getPageFlipCount, std::uint32_t()); diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp index cb08b81627..be8646c3b2 100644 --- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp +++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp @@ -14,10 +14,14 @@ * limitations under the License. */ +#include <compositionengine/CompositionRefreshArgs.h> +#include <compositionengine/LayerFE.h> +#include <compositionengine/OutputLayer.h> #include <compositionengine/impl/CompositionEngine.h> #include <compositionengine/impl/Display.h> #include <compositionengine/impl/Layer.h> #include <renderengine/RenderEngine.h> +#include <utils/Trace.h> #include "DisplayHardware/HWComposer.h" @@ -35,12 +39,13 @@ CompositionEngine::CompositionEngine() = default; CompositionEngine::~CompositionEngine() = default; std::shared_ptr<compositionengine::Display> CompositionEngine::createDisplay( - DisplayCreationArgs&& args) { - return compositionengine::impl::createDisplay(*this, std::move(args)); + const DisplayCreationArgs& args) { + return compositionengine::impl::createDisplay(*this, args); } -std::shared_ptr<compositionengine::Layer> CompositionEngine::createLayer(LayerCreationArgs&& args) { - return compositionengine::impl::createLayer(*this, std::move(args)); +std::shared_ptr<compositionengine::Layer> CompositionEngine::createLayer( + const LayerCreationArgs& args) { + return compositionengine::impl::createLayer(args); } HWComposer& CompositionEngine::getHwComposer() const { @@ -59,5 +64,86 @@ void CompositionEngine::setRenderEngine(std::unique_ptr<renderengine::RenderEngi mRenderEngine = std::move(renderEngine); } +bool CompositionEngine::needsAnotherUpdate() const { + return mNeedsAnotherUpdate; +} + +nsecs_t CompositionEngine::getLastFrameRefreshTimestamp() const { + return mRefreshStartTime; +} + +void CompositionEngine::present(CompositionRefreshArgs& args) { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + preComposition(args); + + { + // latchedLayers is used to track the set of front-end layer state that + // has been latched across all outputs for the prepare step, and is not + // needed for anything else. + LayerFESet latchedLayers; + + for (const auto& output : args.outputs) { + output->prepare(args, latchedLayers); + } + } + + updateLayerStateFromFE(args); + + for (const auto& output : args.outputs) { + output->present(args); + } +} + +void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) { + std::unordered_map<compositionengine::LayerFE*, compositionengine::LayerFECompositionState*> + uniqueVisibleLayers; + + for (const auto& output : args.outputs) { + for (auto* layer : output->getOutputLayersOrderedByZ()) { + if (layer->isHardwareCursor()) { + // Latch the cursor composition state from each front-end layer. + layer->getLayerFE().latchCursorCompositionState(layer->getLayer().editFEState()); + + layer->writeCursorPositionToHWC(); + } + } + } +} + +void CompositionEngine::preComposition(CompositionRefreshArgs& args) { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + bool needsAnotherUpdate = false; + + mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + + for (auto& layer : args.layers) { + sp<compositionengine::LayerFE> layerFE = layer->getLayerFE(); + if (layerFE && layerFE->onPreComposition(mRefreshStartTime)) { + needsAnotherUpdate = true; + } + } + + mNeedsAnotherUpdate = needsAnotherUpdate; +} + +void CompositionEngine::dump(std::string&) const { + // The base class has no state to dump, but derived classes might. +} + +void CompositionEngine::setNeedsAnotherUpdateForTest(bool value) { + mNeedsAnotherUpdate = value; +} + +void CompositionEngine::updateLayerStateFromFE(CompositionRefreshArgs& args) { + // Update the composition state from each front-end layer + for (const auto& output : args.outputs) { + output->updateLayerStateFromFE(args); + } +} + } // namespace impl } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index f9d70e3c91..e885629c4b 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -16,29 +16,30 @@ #include <android-base/stringprintf.h> #include <compositionengine/CompositionEngine.h> +#include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/DisplayCreationArgs.h> #include <compositionengine/DisplaySurface.h> +#include <compositionengine/LayerFE.h> #include <compositionengine/impl/Display.h> #include <compositionengine/impl/DisplayColorProfile.h> #include <compositionengine/impl/DumpHelpers.h> +#include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/RenderSurface.h> +#include <utils/Trace.h> #include "DisplayHardware/HWComposer.h" +#include "DisplayHardware/PowerAdvisor.h" namespace android::compositionengine::impl { -std::shared_ptr<compositionengine::Display> createDisplay( +std::shared_ptr<Display> createDisplay( const compositionengine::CompositionEngine& compositionEngine, - compositionengine::DisplayCreationArgs&& args) { - return std::make_shared<Display>(compositionEngine, std::move(args)); + const compositionengine::DisplayCreationArgs& args) { + return createDisplayTemplated<Display>(compositionEngine, args); } -Display::Display(const CompositionEngine& compositionEngine, DisplayCreationArgs&& args) - : compositionengine::impl::Output(compositionEngine), - mIsVirtual(args.isVirtual), - mId(args.displayId) { - editState().isSecure = args.isSecure; -} +Display::Display(const DisplayCreationArgs& args) + : mIsVirtual(args.isVirtual), mId(args.displayId), mPowerAdvisor(args.powerAdvisor) {} Display::~Display() = default; @@ -54,6 +55,10 @@ bool Display::isVirtual() const { return mIsVirtual; } +std::optional<DisplayId> Display::getDisplayId() const { + return mId; +} + void Display::disconnect() { if (!mId) { return; @@ -64,19 +69,28 @@ void Display::disconnect() { mId.reset(); } -void Display::setColorTransform(const mat4& transform) { - Output::setColorTransform(transform); +void Display::setColorTransform(const compositionengine::CompositionRefreshArgs& args) { + Output::setColorTransform(args); + + if (!mId || CC_LIKELY(!args.colorTransformMatrix)) { + return; + } auto& hwc = getCompositionEngine().getHwComposer(); - status_t result = hwc.setColorTransform(*mId, transform); + status_t result = hwc.setColorTransform(*mId, *args.colorTransformMatrix); ALOGE_IF(result != NO_ERROR, "Failed to set color transform on display \"%s\": %d", mId ? to_string(*mId).c_str() : "", result); } -void Display::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace, - ui::RenderIntent renderIntent) { - if (mode == getState().colorMode && dataspace == getState().dataspace && - renderIntent == getState().renderIntent) { +void Display::setColorProfile(const ColorProfile& colorProfile) { + const ui::Dataspace targetDataspace = + getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace, + colorProfile.colorSpaceAgnosticDataspace); + + if (colorProfile.mode == getState().colorMode && + colorProfile.dataspace == getState().dataspace && + colorProfile.renderIntent == getState().renderIntent && + targetDataspace == getState().targetDataspace) { return; } @@ -85,10 +99,10 @@ void Display::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace, return; } - Output::setColorMode(mode, dataspace, renderIntent); + Output::setColorProfile(colorProfile); auto& hwc = getCompositionEngine().getHwComposer(); - hwc.setActiveColorMode(*mId, mode, renderIntent); + hwc.setActiveColorMode(*mId, colorProfile.mode, colorProfile.renderIntent); } void Display::dump(std::string& out) const { @@ -110,13 +124,216 @@ void Display::dump(std::string& out) const { Output::dumpBase(out); } -void Display::createDisplayColorProfile(DisplayColorProfileCreationArgs&& args) { - setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(std::move(args))); +void Display::createDisplayColorProfile(const DisplayColorProfileCreationArgs& args) { + setDisplayColorProfile(compositionengine::impl::createDisplayColorProfile(args)); +} + +void Display::createRenderSurface(const RenderSurfaceCreationArgs& args) { + setRenderSurface( + compositionengine::impl::createRenderSurface(getCompositionEngine(), *this, args)); +} + +std::unique_ptr<compositionengine::OutputLayer> Display::createOutputLayer( + const std::shared_ptr<compositionengine::Layer>& layer, + const sp<compositionengine::LayerFE>& layerFE) const { + auto result = impl::createOutputLayer(*this, layer, layerFE); + + if (result && mId) { + auto& hwc = getCompositionEngine().getHwComposer(); + auto displayId = *mId; + // Note: For the moment we ensure it is safe to take a reference to the + // HWComposer implementation by destroying all the OutputLayers (and + // hence the HWC2::Layers they own) before setting a new HWComposer. See + // for example SurfaceFlinger::updateVrFlinger(). + // TODO(b/121291683): Make this safer. + auto hwcLayer = std::shared_ptr<HWC2::Layer>(hwc.createLayer(displayId), + [&hwc, displayId](HWC2::Layer* layer) { + hwc.destroyLayer(displayId, layer); + }); + ALOGE_IF(!hwcLayer, "Failed to create a HWC layer for a HWC supported display %s", + getName().c_str()); + result->setHwcLayer(std::move(hwcLayer)); + } + return result; +} + +void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& refreshArgs) { + Output::setReleasedLayers(refreshArgs); + + if (!mId || refreshArgs.layersWithQueuedFrames.empty()) { + return; + } + + // For layers that are being removed from a HWC display, and that have + // queued frames, add them to a a list of released layers so we can properly + // set a fence. + compositionengine::Output::ReleasedLayers releasedLayers; + + // Any non-null entries in the current list of layers are layers that are no + // longer going to be visible + for (auto* layer : getOutputLayersOrderedByZ()) { + if (!layer) { + continue; + } + + sp<compositionengine::LayerFE> layerFE(&layer->getLayerFE()); + const bool hasQueuedFrames = + std::find(refreshArgs.layersWithQueuedFrames.cbegin(), + refreshArgs.layersWithQueuedFrames.cend(), + &layer->getLayer()) != refreshArgs.layersWithQueuedFrames.cend(); + + if (hasQueuedFrames) { + releasedLayers.emplace_back(layerFE); + } + } + + setReleasedLayers(std::move(releasedLayers)); +} + +void Display::chooseCompositionStrategy() { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + // Default to the base settings -- client composition only. + Output::chooseCompositionStrategy(); + + // If we don't have a HWC display, then we are done + if (!mId) { + return; + } + + // Get any composition changes requested by the HWC device, and apply them. + std::optional<android::HWComposer::DeviceRequestedChanges> changes; + auto& hwc = getCompositionEngine().getHwComposer(); + if (status_t result = hwc.getDeviceCompositionChanges(*mId, anyLayersRequireClientComposition(), + &changes); + result != NO_ERROR) { + ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result, + strerror(-result)); + return; + } + if (changes) { + applyChangedTypesToLayers(changes->changedTypes); + applyDisplayRequests(changes->displayRequests); + applyLayerRequestsToLayers(changes->layerRequests); + } + + // Determine what type of composition we are doing from the final state + auto& state = editState(); + state.usesClientComposition = anyLayersRequireClientComposition(); + state.usesDeviceComposition = !allLayersRequireClientComposition(); +} + +bool Display::getSkipColorTransform() const { + if (!mId) { + return false; + } + + auto& hwc = getCompositionEngine().getHwComposer(); + return hwc.hasDisplayCapability(*mId, HWC2::DisplayCapability::SkipClientColorTransform); +} + +bool Display::anyLayersRequireClientComposition() const { + const auto layers = getOutputLayersOrderedByZ(); + return std::any_of(layers.begin(), layers.end(), + [](const auto& layer) { return layer->requiresClientComposition(); }); +} + +bool Display::allLayersRequireClientComposition() const { + const auto layers = getOutputLayersOrderedByZ(); + return std::all_of(layers.begin(), layers.end(), + [](const auto& layer) { return layer->requiresClientComposition(); }); +} + +void Display::applyChangedTypesToLayers(const ChangedTypes& changedTypes) { + if (changedTypes.empty()) { + return; + } + + for (auto* layer : getOutputLayersOrderedByZ()) { + auto hwcLayer = layer->getHwcLayer(); + if (!hwcLayer) { + continue; + } + + if (auto it = changedTypes.find(hwcLayer); it != changedTypes.end()) { + layer->applyDeviceCompositionTypeChange( + static_cast<Hwc2::IComposerClient::Composition>(it->second)); + } + } +} + +void Display::applyDisplayRequests(const DisplayRequests& displayRequests) { + auto& state = editState(); + state.flipClientTarget = (static_cast<uint32_t>(displayRequests) & + static_cast<uint32_t>(HWC2::DisplayRequest::FlipClientTarget)) != 0; + // Note: HWC2::DisplayRequest::WriteClientTargetToOutput is currently ignored. +} + +void Display::applyLayerRequestsToLayers(const LayerRequests& layerRequests) { + for (auto* layer : getOutputLayersOrderedByZ()) { + layer->prepareForDeviceLayerRequests(); + + auto hwcLayer = layer->getHwcLayer(); + if (!hwcLayer) { + continue; + } + + if (auto it = layerRequests.find(hwcLayer); it != layerRequests.end()) { + layer->applyDeviceLayerRequest( + static_cast<Hwc2::IComposerClient::LayerRequest>(it->second)); + } + } } -void Display::createRenderSurface(RenderSurfaceCreationArgs&& args) { - setRenderSurface(compositionengine::impl::createRenderSurface(getCompositionEngine(), *this, - std::move(args))); +compositionengine::Output::FrameFences Display::presentAndGetFrameFences() { + auto result = impl::Output::presentAndGetFrameFences(); + + if (!mId) { + return result; + } + + auto& hwc = getCompositionEngine().getHwComposer(); + hwc.presentAndGetReleaseFences(*mId); + + result.presentFence = hwc.getPresentFence(*mId); + + // TODO(b/121291683): Change HWComposer call to return entire map + for (const auto* layer : getOutputLayersOrderedByZ()) { + auto hwcLayer = layer->getHwcLayer(); + if (!hwcLayer) { + continue; + } + + result.layerFences.emplace(hwcLayer, hwc.getLayerReleaseFence(*mId, hwcLayer)); + } + + hwc.clearReleaseFences(*mId); + + return result; +} + +void Display::setExpensiveRenderingExpected(bool enabled) { + Output::setExpensiveRenderingExpected(enabled); + + if (mPowerAdvisor && mId) { + mPowerAdvisor->setExpensiveRenderingExpected(*mId, enabled); + } +} + +void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs) { + // We only need to actually compose the display if: + // 1) It is being handled by hardware composer, which may need this to + // keep its virtual display state machine in sync, or + // 2) There is work to be done (the dirty region isn't empty) + if (!mId) { + if (getDirtyRegion(refreshArgs.repaintEverything).isEmpty()) { + ALOGV("Skipping display composition"); + return; + } + } + + impl::Output::finishFrame(refreshArgs); } } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp index 130ab1dd54..a7c45129b1 100644 --- a/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DisplayColorProfile.cpp @@ -64,6 +64,12 @@ const std::array<RenderIntent, 2> sHdrRenderIntents = { RenderIntent::TONE_MAP_COLORIMETRIC, }; +// Returns true if the given colorMode is considered an HDR color mode +bool isHdrColorMode(const ColorMode colorMode) { + return std::any_of(std::begin(sHdrColorModes), std::end(sHdrColorModes), + [colorMode](ColorMode hdrColorMode) { return hdrColorMode == colorMode; }); +} + // map known color mode to dataspace Dataspace colorModeToDataspace(ColorMode mode) { switch (mode) { @@ -90,13 +96,7 @@ std::vector<ColorMode> getColorModeCandidates(ColorMode mode) { candidates.push_back(mode); // check if mode is HDR - bool isHdr = false; - for (auto hdrMode : sHdrColorModes) { - if (hdrMode == mode) { - isHdr = true; - break; - } - } + bool isHdr = isHdrColorMode(mode); // add other HDR candidates when mode is HDR if (isHdr) { @@ -184,11 +184,11 @@ RenderIntent getHwcRenderIntent(const std::vector<RenderIntent>& hwcIntents, Ren } // anonymous namespace std::unique_ptr<compositionengine::DisplayColorProfile> createDisplayColorProfile( - DisplayColorProfileCreationArgs&& args) { - return std::make_unique<DisplayColorProfile>(std::move(args)); + const DisplayColorProfileCreationArgs& args) { + return std::make_unique<DisplayColorProfile>(args); } -DisplayColorProfile::DisplayColorProfile(DisplayColorProfileCreationArgs&& args) +DisplayColorProfile::DisplayColorProfile(const DisplayColorProfileCreationArgs& args) : mHasWideColorGamut(args.hasWideColorGamut), mSupportedPerFrameMetadata(args.supportedPerFrameMetadata) { populateColorModes(args.hwcColorModes); @@ -376,6 +376,32 @@ void DisplayColorProfile::getBestColorMode(Dataspace dataspace, RenderIntent int } } +bool DisplayColorProfile::isDataspaceSupported(Dataspace dataspace) const { + switch (dataspace) { + case Dataspace::BT2020_PQ: + case Dataspace::BT2020_ITU_PQ: + return hasHDR10Support(); + + case Dataspace::BT2020_HLG: + case Dataspace::BT2020_ITU_HLG: + return hasHLGSupport(); + + default: + return true; + } +} + +ui::Dataspace DisplayColorProfile::getTargetDataspace(ColorMode mode, Dataspace dataspace, + Dataspace colorSpaceAgnosticDataspace) const { + if (isHdrColorMode(mode)) { + return Dataspace::UNKNOWN; + } + if (colorSpaceAgnosticDataspace != ui::Dataspace::UNKNOWN) { + return colorSpaceAgnosticDataspace; + } + return dataspace; +} + void DisplayColorProfile::dump(std::string& out) const { out.append(" Composition Display Color State:"); diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp index 96e9731768..ecacaeee9c 100644 --- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp @@ -15,9 +15,8 @@ */ #include <android-base/stringprintf.h> -#include <compositionengine/CompositionEngine.h> -#include <compositionengine/LayerCreationArgs.h> #include <compositionengine/LayerFE.h> +#include <compositionengine/LayerFECompositionState.h> #include <compositionengine/impl/Layer.h> namespace android::compositionengine { @@ -26,36 +25,18 @@ Layer::~Layer() = default; namespace impl { -std::shared_ptr<compositionengine::Layer> createLayer( - const compositionengine::CompositionEngine& compositionEngine, - compositionengine::LayerCreationArgs&& args) { - return std::make_shared<Layer>(compositionEngine, std::move(args)); -} - -Layer::Layer(const CompositionEngine& compositionEngine, LayerCreationArgs&& args) - : mCompositionEngine(compositionEngine), mLayerFE(args.layerFE) { - static_cast<void>(mCompositionEngine); // Temporary use to prevent an unused warning +std::shared_ptr<Layer> createLayer(const LayerCreationArgs& args) { + return compositionengine::impl::createLayerTemplated<Layer>(args); } Layer::~Layer() = default; -sp<LayerFE> Layer::getLayerFE() const { - return mLayerFE.promote(); -} - -const LayerCompositionState& Layer::getState() const { - return mState; -} - -LayerCompositionState& Layer::editState() { - return mState; -} - void Layer::dump(std::string& out) const { auto layerFE = getLayerFE(); android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this, layerFE ? layerFE->getDebugName() : "<unknown>"); - mState.dump(out); + out.append(" frontend:\n"); + dumpFEState(out); } } // namespace impl diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp deleted file mode 100644 index 40c4da97a8..0000000000 --- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2019 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 <android-base/stringprintf.h> -#include <compositionengine/impl/DumpHelpers.h> -#include <compositionengine/impl/LayerCompositionState.h> - -namespace android::compositionengine::impl { - -namespace { - -using android::compositionengine::impl::dumpVal; - -void dumpVal(std::string& out, const char* name, Hwc2::IComposerClient::Color value) { - using android::base::StringAppendF; - StringAppendF(&out, "%s=[%d %d %d] ", name, value.r, value.g, value.b); -} - -void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) { - out.append(" "); - dumpVal(out, "isSecure", state.isSecure); - dumpVal(out, "geomUsesSourceCrop", state.geomUsesSourceCrop); - dumpVal(out, "geomBufferUsesDisplayInverseTransform", - state.geomBufferUsesDisplayInverseTransform); - dumpVal(out, "geomLayerTransform", state.geomLayerTransform); - - out.append("\n "); - dumpVal(out, "geomBufferSize", state.geomBufferSize); - dumpVal(out, "geomContentCrop", state.geomContentCrop); - dumpVal(out, "geomCrop", state.geomCrop); - dumpVal(out, "geomBufferTransform", state.geomBufferTransform); - - out.append("\n "); - dumpVal(out, "geomActiveTransparentRegion", state.geomActiveTransparentRegion); - - out.append(" "); - dumpVal(out, "geomLayerBounds", state.geomLayerBounds); - - out.append("\n "); - dumpVal(out, "blend", toString(state.blendMode), state.blendMode); - dumpVal(out, "alpha", state.alpha); - - out.append("\n "); - dumpVal(out, "type", state.type); - dumpVal(out, "appId", state.appId); - - dumpVal(out, "composition type", toString(state.compositionType), state.compositionType); - - out.append("\n buffer: "); - dumpVal(out, "buffer", state.buffer.get()); - dumpVal(out, "slot", state.bufferSlot); - - out.append("\n "); - dumpVal(out, "sideband stream", state.sidebandStream.get()); - - out.append("\n "); - dumpVal(out, "color", state.color); - - out.append("\n "); - dumpVal(out, "dataspace", toString(state.dataspace), state.dataspace); - dumpVal(out, "hdr metadata types", state.hdrMetadata.validTypes); - dumpVal(out, "colorTransform", state.colorTransform); - - out.append("\n"); -} - -} // namespace - -void LayerCompositionState::dump(std::string& out) const { - out.append(" frontend:\n"); - dumpFrontEnd(out, frontEnd); -} - -} // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp new file mode 100644 index 0000000000..1ca03dca5f --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2019 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 <android-base/stringprintf.h> +#include <compositionengine/LayerFECompositionState.h> +#include <compositionengine/impl/DumpHelpers.h> + +namespace android::compositionengine { + +namespace { + +using android::compositionengine::impl::dumpVal; + +void dumpVal(std::string& out, const char* name, half4 value) { + using android::base::StringAppendF; + StringAppendF(&out, "%s=[%f %f %f] ", name, static_cast<float>(value.r), + static_cast<float>(value.g), static_cast<float>(value.b)); +} + +} // namespace + +void LayerFECompositionState::dump(std::string& out) const { + out.append(" "); + dumpVal(out, "isSecure", isSecure); + dumpVal(out, "geomUsesSourceCrop", geomUsesSourceCrop); + dumpVal(out, "geomBufferUsesDisplayInverseTransform", geomBufferUsesDisplayInverseTransform); + dumpVal(out, "geomLayerTransform", geomLayerTransform); + + out.append("\n "); + dumpVal(out, "geomBufferSize", geomBufferSize); + dumpVal(out, "geomContentCrop", geomContentCrop); + dumpVal(out, "geomCrop", geomCrop); + dumpVal(out, "geomBufferTransform", geomBufferTransform); + + out.append("\n "); + dumpVal(out, "transparentRegionHint", transparentRegionHint); + + out.append(" "); + dumpVal(out, "geomLayerBounds", geomLayerBounds); + + out.append("\n "); + dumpVal(out, "blend", toString(blendMode), blendMode); + dumpVal(out, "alpha", alpha); + + out.append("\n "); + dumpVal(out, "type", type); + dumpVal(out, "appId", appId); + + dumpVal(out, "composition type", toString(compositionType), compositionType); + + out.append("\n buffer: "); + dumpVal(out, "slot", bufferSlot); + dumpVal(out, "buffer", buffer.get()); + + out.append("\n "); + dumpVal(out, "sideband stream", sidebandStream.get()); + + out.append("\n "); + dumpVal(out, "color", color); + + out.append("\n "); + dumpVal(out, "isOpaque", isOpaque); + dumpVal(out, "hasProtectedContent", hasProtectedContent); + dumpVal(out, "isColorspaceAgnostic", isColorspaceAgnostic); + dumpVal(out, "dataspace", toString(dataspace), dataspace); + dumpVal(out, "hdr metadata types", hdrMetadata.validTypes); + dumpVal(out, "colorTransform", colorTransform); + + out.append("\n"); +} + +} // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 01b5781987..aa638b7d91 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -14,14 +14,27 @@ * limitations under the License. */ +#include <thread> + #include <android-base/stringprintf.h> #include <compositionengine/CompositionEngine.h> +#include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/DisplayColorProfile.h> +#include <compositionengine/Layer.h> #include <compositionengine/LayerFE.h> +#include <compositionengine/LayerFECompositionState.h> #include <compositionengine/RenderSurface.h> #include <compositionengine/impl/Output.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/OutputLayer.h> +#include <compositionengine/impl/OutputLayerCompositionState.h> +#include <renderengine/DisplaySettings.h> +#include <renderengine/RenderEngine.h> #include <ui/DebugUtils.h> +#include <ui/HdrCapabilities.h> +#include <utils/Trace.h> + +#include "TracedOrdinal.h" namespace android::compositionengine { @@ -29,20 +42,43 @@ Output::~Output() = default; namespace impl { -Output::Output(const CompositionEngine& compositionEngine) - : mCompositionEngine(compositionEngine) {} +namespace { -Output::~Output() = default; +template <typename T> +class Reversed { +public: + explicit Reversed(const T& container) : mContainer(container) {} + auto begin() { return mContainer.rbegin(); } + auto end() { return mContainer.rend(); } + +private: + const T& mContainer; +}; + +// Helper for enumerating over a container in reverse order +template <typename T> +Reversed<T> reversed(const T& c) { + return Reversed<T>(c); +} + +} // namespace -const CompositionEngine& Output::getCompositionEngine() const { - return mCompositionEngine; +std::shared_ptr<Output> createOutput( + const compositionengine::CompositionEngine& compositionEngine) { + return createOutputTemplated<Output>(compositionEngine); } +Output::~Output() = default; + bool Output::isValid() const { return mDisplayColorProfile && mDisplayColorProfile->isValid() && mRenderSurface && mRenderSurface->isValid(); } +std::optional<DisplayId> Output::getDisplayId() const { + return {}; +} + const std::string& Output::getName() const { return mName; } @@ -52,73 +88,79 @@ void Output::setName(const std::string& name) { } void Output::setCompositionEnabled(bool enabled) { - if (mState.isEnabled == enabled) { + auto& outputState = editState(); + if (outputState.isEnabled == enabled) { return; } - mState.isEnabled = enabled; + outputState.isEnabled = enabled; dirtyEntireOutput(); } void Output::setProjection(const ui::Transform& transform, int32_t orientation, const Rect& frame, const Rect& viewport, const Rect& scissor, bool needsFiltering) { - mState.transform = transform; - mState.orientation = orientation; - mState.scissor = scissor; - mState.frame = frame; - mState.viewport = viewport; - mState.needsFiltering = needsFiltering; + auto& outputState = editState(); + outputState.transform = transform; + outputState.orientation = orientation; + outputState.scissor = scissor; + outputState.frame = frame; + outputState.viewport = viewport; + outputState.needsFiltering = needsFiltering; dirtyEntireOutput(); } -// TODO(lpique): Rename setSize() once more is moved. +// TODO(b/121291683): Rename setSize() once more is moved. void Output::setBounds(const ui::Size& size) { mRenderSurface->setDisplaySize(size); - // TODO(lpique): Rename mState.size once more is moved. - mState.bounds = Rect(mRenderSurface->getSize()); + // TODO(b/121291683): Rename outputState.size once more is moved. + editState().bounds = Rect(mRenderSurface->getSize()); dirtyEntireOutput(); } void Output::setLayerStackFilter(uint32_t layerStackId, bool isInternal) { - mState.layerStackId = layerStackId; - mState.layerStackInternal = isInternal; + auto& outputState = editState(); + outputState.layerStackId = layerStackId; + outputState.layerStackInternal = isInternal; dirtyEntireOutput(); } -void Output::setColorTransform(const mat4& transform) { - if (mState.colorTransformMat == transform) { +void Output::setColorTransform(const compositionengine::CompositionRefreshArgs& args) { + auto& colorTransformMatrix = editState().colorTransformMatrix; + if (!args.colorTransformMatrix || colorTransformMatrix == args.colorTransformMatrix) { return; } - const bool isIdentity = (transform == mat4()); - const auto newColorTransform = - isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX; - - mState.colorTransform = newColorTransform; - mState.colorTransformMat = transform; + colorTransformMatrix = *args.colorTransformMatrix; dirtyEntireOutput(); } -void Output::setColorMode(ui::ColorMode mode, ui::Dataspace dataspace, - ui::RenderIntent renderIntent) { - if (mState.colorMode == mode && mState.dataspace == dataspace && - mState.renderIntent == renderIntent) { +void Output::setColorProfile(const ColorProfile& colorProfile) { + ui::Dataspace targetDataspace = + getDisplayColorProfile()->getTargetDataspace(colorProfile.mode, colorProfile.dataspace, + colorProfile.colorSpaceAgnosticDataspace); + + auto& outputState = editState(); + if (outputState.colorMode == colorProfile.mode && + outputState.dataspace == colorProfile.dataspace && + outputState.renderIntent == colorProfile.renderIntent && + outputState.targetDataspace == targetDataspace) { return; } - mState.colorMode = mode; - mState.dataspace = dataspace; - mState.renderIntent = renderIntent; + outputState.colorMode = colorProfile.mode; + outputState.dataspace = colorProfile.dataspace; + outputState.renderIntent = colorProfile.renderIntent; + outputState.targetDataspace = targetDataspace; - mRenderSurface->setBufferDataspace(dataspace); + mRenderSurface->setBufferDataspace(colorProfile.dataspace); ALOGV("Set active color mode: %s (%d), active render intent: %s (%d)", - decodeColorMode(mode).c_str(), mode, decodeRenderIntent(renderIntent).c_str(), - renderIntent); + decodeColorMode(colorProfile.mode).c_str(), colorProfile.mode, + decodeRenderIntent(colorProfile.renderIntent).c_str(), colorProfile.renderIntent); dirtyEntireOutput(); } @@ -134,7 +176,7 @@ void Output::dump(std::string& out) const { } void Output::dumpBase(std::string& out) const { - mState.dump(out); + dumpState(out); if (mDisplayColorProfile) { mDisplayColorProfile->dump(out); @@ -148,8 +190,8 @@ void Output::dumpBase(std::string& out) const { out.append(" No render surface!\n"); } - android::base::StringAppendF(&out, "\n %zu Layers\b", mOutputLayersOrderedByZ.size()); - for (const auto& outputLayer : mOutputLayersOrderedByZ) { + android::base::StringAppendF(&out, "\n %zu Layers\n", getOutputLayerCount()); + for (const auto* outputLayer : getOutputLayersOrderedByZ()) { if (!outputLayer) { continue; } @@ -165,6 +207,10 @@ void Output::setDisplayColorProfile(std::unique_ptr<compositionengine::DisplayCo mDisplayColorProfile = std::move(mode); } +const Output::ReleasedLayers& Output::getReleasedLayersForTest() const { + return mReleasedLayers; +} + void Output::setDisplayColorProfileForTest( std::unique_ptr<compositionengine::DisplayColorProfile> mode) { mDisplayColorProfile = std::move(mode); @@ -176,7 +222,7 @@ compositionengine::RenderSurface* Output::getRenderSurface() const { void Output::setRenderSurface(std::unique_ptr<compositionengine::RenderSurface> surface) { mRenderSurface = std::move(surface); - mState.bounds = Rect(mRenderSurface->getSize()); + editState().bounds = Rect(mRenderSurface->getSize()); dirtyEntireOutput(); } @@ -185,59 +231,784 @@ void Output::setRenderSurfaceForTest(std::unique_ptr<compositionengine::RenderSu mRenderSurface = std::move(surface); } -const OutputCompositionState& Output::getState() const { - return mState; -} - -OutputCompositionState& Output::editState() { - return mState; -} - Region Output::getDirtyRegion(bool repaintEverything) const { - Region dirty(mState.viewport); + const auto& outputState = getState(); + Region dirty(outputState.viewport); if (!repaintEverything) { - dirty.andSelf(mState.dirtyRegion); + dirty.andSelf(outputState.dirtyRegion); } return dirty; } -bool Output::belongsInOutput(uint32_t layerStackId, bool internalOnly) const { +bool Output::belongsInOutput(std::optional<uint32_t> layerStackId, bool internalOnly) const { // The layerStackId's must match, and also the layer must not be internal // only when not on an internal output. - return (layerStackId == mState.layerStackId) && (!internalOnly || mState.layerStackInternal); + const auto& outputState = getState(); + return layerStackId && (*layerStackId == outputState.layerStackId) && + (!internalOnly || outputState.layerStackInternal); +} + +bool Output::belongsInOutput(const compositionengine::Layer* layer) const { + if (!layer) { + return false; + } + + const auto& layerFEState = layer->getFEState(); + return belongsInOutput(layerFEState.layerStackId, layerFEState.internalOnly); +} + +std::unique_ptr<compositionengine::OutputLayer> Output::createOutputLayer( + const std::shared_ptr<compositionengine::Layer>& layer, const sp<LayerFE>& layerFE) const { + return impl::createOutputLayer(*this, layer, layerFE); } compositionengine::OutputLayer* Output::getOutputLayerForLayer( compositionengine::Layer* layer) const { - for (const auto& outputLayer : mOutputLayersOrderedByZ) { + auto index = findCurrentOutputLayerForLayer(layer); + return index ? getOutputLayerOrderedByZByIndex(*index) : nullptr; +} + +std::optional<size_t> Output::findCurrentOutputLayerForLayer( + compositionengine::Layer* layer) const { + for (size_t i = 0; i < getOutputLayerCount(); i++) { + auto outputLayer = getOutputLayerOrderedByZByIndex(i); if (outputLayer && &outputLayer->getLayer() == layer) { - return outputLayer.get(); + return i; + } + } + return std::nullopt; +} + +void Output::setReleasedLayers(Output::ReleasedLayers&& layers) { + mReleasedLayers = std::move(layers); +} + +void Output::prepare(const compositionengine::CompositionRefreshArgs& refreshArgs, + LayerFESet& geomSnapshots) { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + rebuildLayerStacks(refreshArgs, geomSnapshots); +} + +void Output::present(const compositionengine::CompositionRefreshArgs& refreshArgs) { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + updateColorProfile(refreshArgs); + updateAndWriteCompositionState(refreshArgs); + setColorTransform(refreshArgs); + beginFrame(); + prepareFrame(); + devOptRepaintFlash(refreshArgs); + finishFrame(refreshArgs); + postFramebuffer(); +} + +void Output::rebuildLayerStacks(const compositionengine::CompositionRefreshArgs& refreshArgs, + LayerFESet& layerFESet) { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + auto& outputState = editState(); + + // Do nothing if this output is not enabled or there is no need to perform this update + if (!outputState.isEnabled || CC_LIKELY(!refreshArgs.updatingOutputGeometryThisFrame)) { + return; + } + + // Process the layers to determine visibility and coverage + compositionengine::Output::CoverageState coverage{layerFESet}; + collectVisibleLayers(refreshArgs, coverage); + + // Compute the resulting coverage for this output, and store it for later + const ui::Transform& tr = outputState.transform; + Region undefinedRegion{outputState.bounds}; + undefinedRegion.subtractSelf(tr.transform(coverage.aboveOpaqueLayers)); + + outputState.undefinedRegion = undefinedRegion; + outputState.dirtyRegion.orSelf(coverage.dirtyRegion); +} + +void Output::collectVisibleLayers(const compositionengine::CompositionRefreshArgs& refreshArgs, + compositionengine::Output::CoverageState& coverage) { + // Evaluate the layers from front to back to determine what is visible. This + // also incrementally calculates the coverage information for each layer as + // well as the entire output. + for (auto& layer : reversed(refreshArgs.layers)) { + // Incrementally process the coverage for each layer + ensureOutputLayerIfVisible(layer, coverage); + + // TODO(b/121291683): Stop early if the output is completely covered and + // no more layers could even be visible underneath the ones on top. + } + + setReleasedLayers(refreshArgs); + + finalizePendingOutputLayers(); + + // Generate a simple Z-order values to each visible output layer + uint32_t zOrder = 0; + for (auto* outputLayer : getOutputLayersOrderedByZ()) { + outputLayer->editState().z = zOrder++; + } +} + +void Output::ensureOutputLayerIfVisible(std::shared_ptr<compositionengine::Layer> layer, + compositionengine::Output::CoverageState& coverage) { + // Note: Converts a wp<LayerFE> to a sp<LayerFE> + auto layerFE = layer->getLayerFE(); + if (layerFE == nullptr) { + return; + } + + // Ensure we have a snapshot of the basic geometry layer state. Limit the + // snapshots to once per frame for each candidate layer, as layers may + // appear on multiple outputs. + if (!coverage.latchedLayers.count(layerFE)) { + coverage.latchedLayers.insert(layerFE); + layerFE->latchCompositionState(layer->editFEState(), + compositionengine::LayerFE::StateSubset::BasicGeometry); + } + + // Obtain a read-only reference to the front-end layer state + const auto& layerFEState = layer->getFEState(); + + // Only consider the layers on the given layer stack + if (!belongsInOutput(layer.get())) { + return; + } + + /* + * opaqueRegion: area of a surface that is fully opaque. + */ + Region opaqueRegion; + + /* + * visibleRegion: area of a surface that is visible on screen and not fully + * transparent. This is essentially the layer's footprint minus the opaque + * regions above it. Areas covered by a translucent surface are considered + * visible. + */ + Region visibleRegion; + + /* + * coveredRegion: area of a surface that is covered by all visible regions + * above it (which includes the translucent areas). + */ + Region coveredRegion; + + /* + * transparentRegion: area of a surface that is hinted to be completely + * transparent. This is only used to tell when the layer has no visible non- + * transparent regions and can be removed from the layer list. It does not + * affect the visibleRegion of this layer or any layers beneath it. The hint + * may not be correct if apps don't respect the SurfaceView restrictions + * (which, sadly, some don't). + */ + Region transparentRegion; + + // handle hidden surfaces by setting the visible region to empty + if (CC_UNLIKELY(!layerFEState.isVisible)) { + return; + } + + const ui::Transform& tr = layerFEState.geomLayerTransform; + + // Get the visible region + // TODO(b/121291683): Is it worth creating helper methods on LayerFEState + // for computations like this? + visibleRegion.set(Rect(tr.transform(layerFEState.geomLayerBounds))); + + if (visibleRegion.isEmpty()) { + return; + } + + // Remove the transparent area from the visible region + if (!layerFEState.isOpaque) { + if (tr.preserveRects()) { + // transform the transparent region + transparentRegion = tr.transform(layerFEState.transparentRegionHint); + } else { + // transformation too complex, can't do the + // transparent region optimization. + transparentRegion.clear(); + } + } + + // compute the opaque region + const int32_t layerOrientation = tr.getOrientation(); + if (layerFEState.isOpaque && ((layerOrientation & ui::Transform::ROT_INVALID) == 0)) { + // If we one of the simple category of transforms (0/90/180/270 rotation + // + any flip), then the opaque region is the layer's footprint. + // Otherwise we don't try and compute the opaque region since there may + // be errors at the edges, and we treat the entire layer as + // translucent. + opaqueRegion = visibleRegion; + } + + // Clip the covered region to the visible region + coveredRegion = coverage.aboveCoveredLayers.intersect(visibleRegion); + + // Update accumAboveCoveredLayers for next (lower) layer + coverage.aboveCoveredLayers.orSelf(visibleRegion); + + // subtract the opaque region covered by the layers above us + visibleRegion.subtractSelf(coverage.aboveOpaqueLayers); + + if (visibleRegion.isEmpty()) { + return; + } + + // Get coverage information for the layer as previously displayed, + // also taking over ownership from mOutputLayersorderedByZ. + auto prevOutputLayerIndex = findCurrentOutputLayerForLayer(layer.get()); + auto prevOutputLayer = + prevOutputLayerIndex ? getOutputLayerOrderedByZByIndex(*prevOutputLayerIndex) : nullptr; + + // Get coverage information for the layer as previously displayed + // TODO(b/121291683): Define kEmptyRegion as a constant in Region.h + const Region kEmptyRegion; + const Region& oldVisibleRegion = + prevOutputLayer ? prevOutputLayer->getState().visibleRegion : kEmptyRegion; + const Region& oldCoveredRegion = + prevOutputLayer ? prevOutputLayer->getState().coveredRegion : kEmptyRegion; + + // compute this layer's dirty region + Region dirty; + if (layerFEState.contentDirty) { + // we need to invalidate the whole region + dirty = visibleRegion; + // as well, as the old visible region + dirty.orSelf(oldVisibleRegion); + } else { + /* compute the exposed region: + * the exposed region consists of two components: + * 1) what's VISIBLE now and was COVERED before + * 2) what's EXPOSED now less what was EXPOSED before + * + * note that (1) is conservative, we start with the whole visible region + * but only keep what used to be covered by something -- which mean it + * may have been exposed. + * + * (2) handles areas that were not covered by anything but got exposed + * because of a resize. + * + */ + const Region newExposed = visibleRegion - coveredRegion; + const Region oldExposed = oldVisibleRegion - oldCoveredRegion; + dirty = (visibleRegion & oldCoveredRegion) | (newExposed - oldExposed); + } + dirty.subtractSelf(coverage.aboveOpaqueLayers); + + // accumulate to the screen dirty region + coverage.dirtyRegion.orSelf(dirty); + + // Update accumAboveOpaqueLayers for next (lower) layer + coverage.aboveOpaqueLayers.orSelf(opaqueRegion); + + // Compute the visible non-transparent region + Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion); + + // Peform the final check to see if this layer is visible on this output + // TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below) + const auto& outputState = getState(); + Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion)); + drawRegion.andSelf(outputState.bounds); + if (drawRegion.isEmpty()) { + return; + } + + // The layer is visible. Either reuse the existing outputLayer if we have + // one, or create a new one if we do not. + auto result = ensureOutputLayer(prevOutputLayerIndex, layer, layerFE); + + // Store the layer coverage information into the layer state as some of it + // is useful later. + auto& outputLayerState = result->editState(); + outputLayerState.visibleRegion = visibleRegion; + outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion; + outputLayerState.coveredRegion = coveredRegion; + outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform( + outputLayerState.visibleRegion.intersect(outputState.viewport)); +} + +void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) { + // The base class does nothing with this call. +} + +void Output::updateLayerStateFromFE(const CompositionRefreshArgs& args) const { + for (auto* layer : getOutputLayersOrderedByZ()) { + layer->getLayerFE().latchCompositionState(layer->getLayer().editFEState(), + args.updatingGeometryThisFrame + ? LayerFE::StateSubset::GeometryAndContent + : LayerFE::StateSubset::Content); + } +} + +void Output::updateAndWriteCompositionState( + const compositionengine::CompositionRefreshArgs& refreshArgs) { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + for (auto* layer : getOutputLayersOrderedByZ()) { + layer->updateCompositionState(refreshArgs.updatingGeometryThisFrame, + refreshArgs.devOptForceClientComposition); + + // Send the updated state to the HWC, if appropriate. + layer->writeStateToHWC(refreshArgs.updatingGeometryThisFrame); + } +} + +void Output::updateColorProfile(const compositionengine::CompositionRefreshArgs& refreshArgs) { + setColorProfile(pickColorProfile(refreshArgs)); +} + +// Returns a data space that fits all visible layers. The returned data space +// can only be one of +// - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced) +// - Dataspace::DISPLAY_P3 +// - Dataspace::DISPLAY_BT2020 +// The returned HDR data space is one of +// - Dataspace::UNKNOWN +// - Dataspace::BT2020_HLG +// - Dataspace::BT2020_PQ +ui::Dataspace Output::getBestDataspace(ui::Dataspace* outHdrDataSpace, + bool* outIsHdrClientComposition) const { + ui::Dataspace bestDataSpace = ui::Dataspace::V0_SRGB; + *outHdrDataSpace = ui::Dataspace::UNKNOWN; + + for (const auto* layer : getOutputLayersOrderedByZ()) { + switch (layer->getLayer().getFEState().dataspace) { + case ui::Dataspace::V0_SCRGB: + case ui::Dataspace::V0_SCRGB_LINEAR: + case ui::Dataspace::BT2020: + case ui::Dataspace::BT2020_ITU: + case ui::Dataspace::BT2020_LINEAR: + case ui::Dataspace::DISPLAY_BT2020: + bestDataSpace = ui::Dataspace::DISPLAY_BT2020; + break; + case ui::Dataspace::DISPLAY_P3: + bestDataSpace = ui::Dataspace::DISPLAY_P3; + break; + case ui::Dataspace::BT2020_PQ: + case ui::Dataspace::BT2020_ITU_PQ: + bestDataSpace = ui::Dataspace::DISPLAY_P3; + *outHdrDataSpace = ui::Dataspace::BT2020_PQ; + *outIsHdrClientComposition = layer->getLayer().getFEState().forceClientComposition; + break; + case ui::Dataspace::BT2020_HLG: + case ui::Dataspace::BT2020_ITU_HLG: + bestDataSpace = ui::Dataspace::DISPLAY_P3; + // When there's mixed PQ content and HLG content, we set the HDR + // data space to be BT2020_PQ and convert HLG to PQ. + if (*outHdrDataSpace == ui::Dataspace::UNKNOWN) { + *outHdrDataSpace = ui::Dataspace::BT2020_HLG; + } + break; + default: + break; } } - return nullptr; + + return bestDataSpace; +} + +compositionengine::Output::ColorProfile Output::pickColorProfile( + const compositionengine::CompositionRefreshArgs& refreshArgs) const { + if (refreshArgs.outputColorSetting == OutputColorSetting::kUnmanaged) { + return ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN, + ui::RenderIntent::COLORIMETRIC, + refreshArgs.colorSpaceAgnosticDataspace}; + } + + ui::Dataspace hdrDataSpace; + bool isHdrClientComposition = false; + ui::Dataspace bestDataSpace = getBestDataspace(&hdrDataSpace, &isHdrClientComposition); + + switch (refreshArgs.forceOutputColorMode) { + case ui::ColorMode::SRGB: + bestDataSpace = ui::Dataspace::V0_SRGB; + break; + case ui::ColorMode::DISPLAY_P3: + bestDataSpace = ui::Dataspace::DISPLAY_P3; + break; + default: + break; + } + + // respect hdrDataSpace only when there is no legacy HDR support + const bool isHdr = hdrDataSpace != ui::Dataspace::UNKNOWN && + !mDisplayColorProfile->hasLegacyHdrSupport(hdrDataSpace) && !isHdrClientComposition; + if (isHdr) { + bestDataSpace = hdrDataSpace; + } + + ui::RenderIntent intent; + switch (refreshArgs.outputColorSetting) { + case OutputColorSetting::kManaged: + case OutputColorSetting::kUnmanaged: + intent = isHdr ? ui::RenderIntent::TONE_MAP_COLORIMETRIC + : ui::RenderIntent::COLORIMETRIC; + break; + case OutputColorSetting::kEnhanced: + intent = isHdr ? ui::RenderIntent::TONE_MAP_ENHANCE : ui::RenderIntent::ENHANCE; + break; + default: // vendor display color setting + intent = static_cast<ui::RenderIntent>(refreshArgs.outputColorSetting); + break; + } + + ui::ColorMode outMode; + ui::Dataspace outDataSpace; + ui::RenderIntent outRenderIntent; + mDisplayColorProfile->getBestColorMode(bestDataSpace, intent, &outDataSpace, &outMode, + &outRenderIntent); + + return ColorProfile{outMode, outDataSpace, outRenderIntent, + refreshArgs.colorSpaceAgnosticDataspace}; +} + +void Output::beginFrame() { + auto& outputState = editState(); + const bool dirty = !getDirtyRegion(false).isEmpty(); + const bool empty = getOutputLayerCount() == 0; + const bool wasEmpty = !outputState.lastCompositionHadVisibleLayers; + + // If nothing has changed (!dirty), don't recompose. + // If something changed, but we don't currently have any visible layers, + // and didn't when we last did a composition, then skip it this time. + // The second rule does two things: + // - When all layers are removed from a display, we'll emit one black + // frame, then nothing more until we get new layers. + // - When a display is created with a private layer stack, we won't + // emit any black frames until a layer is added to the layer stack. + const bool mustRecompose = dirty && !(empty && wasEmpty); + + const char flagPrefix[] = {'-', '+'}; + static_cast<void>(flagPrefix); + ALOGV_IF("%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", __FUNCTION__, + mustRecompose ? "doing" : "skipping", getName().c_str(), flagPrefix[dirty], + flagPrefix[empty], flagPrefix[wasEmpty]); + + mRenderSurface->beginFrame(mustRecompose); + + if (mustRecompose) { + outputState.lastCompositionHadVisibleLayers = !empty; + } } -std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer( - std::optional<DisplayId> displayId, std::shared_ptr<compositionengine::Layer> layer, - sp<compositionengine::LayerFE> layerFE) { - for (auto& outputLayer : mOutputLayersOrderedByZ) { - if (outputLayer && &outputLayer->getLayer() == layer.get()) { - return std::move(outputLayer); +void Output::prepareFrame() { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + const auto& outputState = getState(); + if (!outputState.isEnabled) { + return; + } + + chooseCompositionStrategy(); + + mRenderSurface->prepareFrame(outputState.usesClientComposition, + outputState.usesDeviceComposition); +} + +void Output::devOptRepaintFlash(const compositionengine::CompositionRefreshArgs& refreshArgs) { + if (CC_LIKELY(!refreshArgs.devOptFlashDirtyRegionsDelay)) { + return; + } + + if (getState().isEnabled) { + // transform the dirty region into this screen's coordinate space + const Region dirtyRegion = getDirtyRegion(refreshArgs.repaintEverything); + if (!dirtyRegion.isEmpty()) { + base::unique_fd readyFence; + // redraw the whole screen + static_cast<void>(composeSurfaces(dirtyRegion)); + + mRenderSurface->queueBuffer(std::move(readyFence)); } } - return createOutputLayer(mCompositionEngine, displayId, *this, layer, layerFE); + + postFramebuffer(); + + std::this_thread::sleep_for(*refreshArgs.devOptFlashDirtyRegionsDelay); + + prepareFrame(); } -void Output::setOutputLayersOrderedByZ(OutputLayers&& layers) { - mOutputLayersOrderedByZ = std::move(layers); +void Output::finishFrame(const compositionengine::CompositionRefreshArgs&) { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + if (!getState().isEnabled) { + return; + } + + // Repaint the framebuffer (if needed), getting the optional fence for when + // the composition completes. + auto optReadyFence = composeSurfaces(Region::INVALID_REGION); + if (!optReadyFence) { + return; + } + + // swap buffers (presentation) + mRenderSurface->queueBuffer(std::move(*optReadyFence)); } -const Output::OutputLayers& Output::getOutputLayersOrderedByZ() const { - return mOutputLayersOrderedByZ; +std::optional<base::unique_fd> Output::composeSurfaces(const Region& debugRegion) { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + const auto& outputState = getState(); + const TracedOrdinal<bool> hasClientComposition = {"hasClientComposition", + outputState.usesClientComposition}; + base::unique_fd readyFence; + if (!hasClientComposition) { + return readyFence; + } + + ALOGV("hasClientComposition"); + + auto& renderEngine = getCompositionEngine().getRenderEngine(); + const bool supportsProtectedContent = renderEngine.supportsProtectedContent(); + + renderengine::DisplaySettings clientCompositionDisplay; + clientCompositionDisplay.physicalDisplay = outputState.scissor; + clientCompositionDisplay.clip = outputState.scissor; + clientCompositionDisplay.globalTransform = outputState.transform.asMatrix4(); + clientCompositionDisplay.orientation = outputState.orientation; + clientCompositionDisplay.outputDataspace = mDisplayColorProfile->hasWideColorGamut() + ? outputState.dataspace + : ui::Dataspace::UNKNOWN; + clientCompositionDisplay.maxLuminance = + mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance(); + + // Compute the global color transform matrix. + if (!outputState.usesDeviceComposition && !getSkipColorTransform()) { + clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix; + } + + // Note: Updated by generateClientCompositionRequests + clientCompositionDisplay.clearRegion = Region::INVALID_REGION; + + // Generate the client composition requests for the layers on this output. + std::vector<renderengine::LayerSettings> clientCompositionLayers = + generateClientCompositionRequests(supportsProtectedContent, + clientCompositionDisplay.clearRegion); + appendRegionFlashRequests(debugRegion, clientCompositionLayers); + + // If we the display is secure, protected content support is enabled, and at + // least one layer has protected content, we need to use a secure back + // buffer. + if (outputState.isSecure && supportsProtectedContent) { + auto layers = getOutputLayersOrderedByZ(); + bool needsProtected = std::any_of(layers.begin(), layers.end(), [](auto* layer) { + return layer->getLayer().getFEState().hasProtectedContent; + }); + if (needsProtected != renderEngine.isProtected()) { + renderEngine.useProtectedContext(needsProtected); + } + if (needsProtected != mRenderSurface->isProtected() && + needsProtected == renderEngine.isProtected()) { + mRenderSurface->setProtected(needsProtected); + } + } + + base::unique_fd fd; + sp<GraphicBuffer> buf = mRenderSurface->dequeueBuffer(&fd); + if (buf == nullptr) { + ALOGW("Dequeuing buffer for display [%s] failed, bailing out of " + "client composition for this frame", + mName.c_str()); + return std::nullopt; + } + + // We boost GPU frequency here because there will be color spaces conversion + // and it's expensive. We boost the GPU frequency so that GPU composition can + // finish in time. We must reset GPU frequency afterwards, because high frequency + // consumes extra battery. + const bool expensiveRenderingExpected = + clientCompositionDisplay.outputDataspace == ui::Dataspace::DISPLAY_P3; + if (expensiveRenderingExpected) { + setExpensiveRenderingExpected(true); + } + + renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers, + buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd), + &readyFence); + + if (expensiveRenderingExpected) { + setExpensiveRenderingExpected(false); + } + + return readyFence; +} + +std::vector<renderengine::LayerSettings> Output::generateClientCompositionRequests( + bool supportsProtectedContent, Region& clearRegion) { + std::vector<renderengine::LayerSettings> clientCompositionLayers; + ALOGV("Rendering client layers"); + + const auto& outputState = getState(); + const Region viewportRegion(outputState.viewport); + const bool useIdentityTransform = false; + bool firstLayer = true; + // Used when a layer clears part of the buffer. + Region dummyRegion; + + for (auto* layer : getOutputLayersOrderedByZ()) { + const auto& layerState = layer->getState(); + const auto& layerFEState = layer->getLayer().getFEState(); + auto& layerFE = layer->getLayerFE(); + + const Region clip(viewportRegion.intersect(layerState.visibleRegion)); + ALOGV("Layer: %s", layerFE.getDebugName()); + if (clip.isEmpty()) { + ALOGV(" Skipping for empty clip"); + firstLayer = false; + continue; + } + + bool clientComposition = layer->requiresClientComposition(); + + // We clear the client target for non-client composed layers if + // requested by the HWC. We skip this if the layer is not an opaque + // rectangle, as by definition the layer must blend with whatever is + // underneath. We also skip the first layer as the buffer target is + // guaranteed to start out cleared. + bool clearClientComposition = + layerState.clearClientTarget && layerFEState.isOpaque && !firstLayer; + + ALOGV(" Composition type: client %d clear %d", clientComposition, clearClientComposition); + + if (clientComposition || clearClientComposition) { + compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{ + clip, + useIdentityTransform, + layer->needsFiltering() || outputState.needsFiltering, + outputState.isSecure, + supportsProtectedContent, + clientComposition ? clearRegion : dummyRegion, + }; + if (auto result = layerFE.prepareClientComposition(targetSettings)) { + if (!clientComposition) { + auto& layerSettings = *result; + layerSettings.source.buffer.buffer = nullptr; + layerSettings.source.solidColor = half3(0.0, 0.0, 0.0); + layerSettings.alpha = half(0.0); + layerSettings.disableBlending = true; + } + + clientCompositionLayers.push_back(*result); + } + } + + firstLayer = false; + } + + return clientCompositionLayers; +} + +void Output::appendRegionFlashRequests( + const Region& flashRegion, + std::vector<renderengine::LayerSettings>& clientCompositionLayers) { + if (flashRegion.isEmpty()) { + return; + } + + renderengine::LayerSettings layerSettings; + layerSettings.source.buffer.buffer = nullptr; + layerSettings.source.solidColor = half3(1.0, 0.0, 1.0); + layerSettings.alpha = half(1.0); + + for (const auto& rect : flashRegion) { + layerSettings.geometry.boundaries = rect.toFloatRect(); + clientCompositionLayers.push_back(layerSettings); + } +} + +void Output::setExpensiveRenderingExpected(bool) { + // The base class does nothing with this call. +} + +void Output::postFramebuffer() { + ATRACE_CALL(); + ALOGV(__FUNCTION__); + + if (!getState().isEnabled) { + return; + } + + auto& outputState = editState(); + outputState.dirtyRegion.clear(); + mRenderSurface->flip(); + + auto frame = presentAndGetFrameFences(); + + mRenderSurface->onPresentDisplayCompleted(); + + for (auto* layer : getOutputLayersOrderedByZ()) { + // The layer buffer from the previous frame (if any) is released + // by HWC only when the release fence from this frame (if any) is + // signaled. Always get the release fence from HWC first. + sp<Fence> releaseFence = Fence::NO_FENCE; + + if (auto hwcLayer = layer->getHwcLayer()) { + if (auto f = frame.layerFences.find(hwcLayer); f != frame.layerFences.end()) { + releaseFence = f->second; + } + } + + // If the layer was client composited in the previous frame, we + // need to merge with the previous client target acquire fence. + // Since we do not track that, always merge with the current + // client target acquire fence when it is available, even though + // this is suboptimal. + // TODO(b/121291683): Track previous frame client target acquire fence. + if (outputState.usesClientComposition) { + releaseFence = + Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence); + } + + layer->getLayerFE().onLayerDisplayed(releaseFence); + } + + // We've got a list of layers needing fences, that are disjoint with + // OutputLayersOrderedByZ. The best we can do is to + // supply them with the present fence. + for (auto& weakLayer : mReleasedLayers) { + if (auto layer = weakLayer.promote(); layer != nullptr) { + layer->onLayerDisplayed(frame.presentFence); + } + } + + // Clear out the released layers now that we're done with them. + mReleasedLayers.clear(); } void Output::dirtyEntireOutput() { - mState.dirtyRegion.set(mState.bounds); + auto& outputState = editState(); + outputState.dirtyRegion.set(outputState.bounds); +} + +void Output::chooseCompositionStrategy() { + // The base output implementation can only do client composition + auto& outputState = editState(); + outputState.usesClientComposition = true; + outputState.usesDeviceComposition = false; +} + +bool Output::getSkipColorTransform() const { + return true; +} + +compositionengine::Output::FrameFences Output::presentAndGetFrameFences() { + compositionengine::Output::FrameFences result; + if (getState().usesClientComposition) { + result.clientTargetAcquireFence = mRenderSurface->getClientTargetAcquireFence(); + } + return result; } } // namespace impl diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp index 9549054bd6..0fcc308ca0 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp @@ -24,6 +24,10 @@ void OutputCompositionState::dump(std::string& out) const { dumpVal(out, "isEnabled", isEnabled); dumpVal(out, "isSecure", isSecure); + dumpVal(out, "usesClientComposition", usesClientComposition); + dumpVal(out, "usesDeviceComposition", usesDeviceComposition); + dumpVal(out, "flipClientTarget", flipClientTarget); + dumpVal(out, "layerStack", layerStackId); dumpVal(out, "layerStackInternal", layerStackInternal); @@ -43,7 +47,8 @@ void OutputCompositionState::dump(std::string& out) const { dumpVal(out, "colorMode", toString(colorMode), colorMode); dumpVal(out, "renderIntent", toString(renderIntent), renderIntent); dumpVal(out, "dataspace", toString(dataspace), dataspace); - dumpVal(out, "colorTransform", colorTransform); + dumpVal(out, "colorTransformMatrix", colorTransformMatrix); + dumpVal(out, "target dataspace", toString(targetDataspace), targetDataspace); out.append("\n"); } diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 5ce72b0879..721e953d25 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -15,11 +15,11 @@ */ #include <android-base/stringprintf.h> -#include <compositionengine/CompositionEngine.h> +#include <compositionengine/DisplayColorProfile.h> #include <compositionengine/Layer.h> #include <compositionengine/LayerFE.h> +#include <compositionengine/LayerFECompositionState.h> #include <compositionengine/Output.h> -#include <compositionengine/impl/LayerCompositionState.h> #include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> @@ -44,56 +44,26 @@ FloatRect reduce(const FloatRect& win, const Region& exclude) { } // namespace -std::unique_ptr<compositionengine::OutputLayer> createOutputLayer( - const CompositionEngine& compositionEngine, std::optional<DisplayId> displayId, - const compositionengine::Output& output, std::shared_ptr<compositionengine::Layer> layer, - sp<compositionengine::LayerFE> layerFE) { - auto result = std::make_unique<OutputLayer>(output, layer, layerFE); - result->initialize(compositionEngine, displayId); - return result; +std::unique_ptr<OutputLayer> createOutputLayer( + const compositionengine::Output& output, + const std::shared_ptr<compositionengine::Layer>& layer, + const sp<compositionengine::LayerFE>& layerFE) { + return createOutputLayerTemplated<OutputLayer>(output, layer, layerFE); } -OutputLayer::OutputLayer(const Output& output, std::shared_ptr<Layer> layer, sp<LayerFE> layerFE) - : mOutput(output), mLayer(layer), mLayerFE(layerFE) {} - OutputLayer::~OutputLayer() = default; -void OutputLayer::initialize(const CompositionEngine& compositionEngine, - std::optional<DisplayId> displayId) { - if (!displayId) { - return; +void OutputLayer::setHwcLayer(std::shared_ptr<HWC2::Layer> hwcLayer) { + auto& state = editState(); + if (hwcLayer) { + state.hwc.emplace(std::move(hwcLayer)); + } else { + state.hwc.reset(); } - - auto& hwc = compositionEngine.getHwComposer(); - - mState.hwc.emplace(std::shared_ptr<HWC2::Layer>(hwc.createLayer(*displayId), - [&hwc, displayId](HWC2::Layer* layer) { - hwc.destroyLayer(*displayId, layer); - })); -} - -const compositionengine::Output& OutputLayer::getOutput() const { - return mOutput; -} - -compositionengine::Layer& OutputLayer::getLayer() const { - return *mLayer; -} - -compositionengine::LayerFE& OutputLayer::getLayerFE() const { - return *mLayerFE; -} - -const OutputLayerCompositionState& OutputLayer::getState() const { - return mState; -} - -OutputLayerCompositionState& OutputLayer::editState() { - return mState; } Rect OutputLayer::calculateInitialCrop() const { - const auto& layerState = mLayer->getState().frontEnd; + const auto& layerState = getLayer().getFEState(); // apply the projection's clipping to the window crop in // layerstack space, and convert-back to layer space. @@ -101,9 +71,9 @@ Rect OutputLayer::calculateInitialCrop() const { // pixels in the buffer. FloatRect activeCropFloat = - reduce(layerState.geomLayerBounds, layerState.geomActiveTransparentRegion); + reduce(layerState.geomLayerBounds, layerState.transparentRegionHint); - const Rect& viewport = mOutput.getState().viewport; + const Rect& viewport = getOutput().getState().viewport; const ui::Transform& layerTransform = layerState.geomLayerTransform; const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; // Transform to screen space. @@ -126,8 +96,8 @@ Rect OutputLayer::calculateInitialCrop() const { } FloatRect OutputLayer::calculateOutputSourceCrop() const { - const auto& layerState = mLayer->getState().frontEnd; - const auto& outputState = mOutput.getState(); + const auto& layerState = getLayer().getFEState(); + const auto& outputState = getOutput().getState(); if (!layerState.geomUsesSourceCrop) { return {}; @@ -203,12 +173,12 @@ FloatRect OutputLayer::calculateOutputSourceCrop() const { } Rect OutputLayer::calculateOutputDisplayFrame() const { - const auto& layerState = mLayer->getState().frontEnd; - const auto& outputState = mOutput.getState(); + const auto& layerState = getLayer().getFEState(); + const auto& outputState = getOutput().getState(); // apply the layer's transform, followed by the display's global transform // here we're guaranteed that the layer's transform preserves rects - Region activeTransparentRegion = layerState.geomActiveTransparentRegion; + Region activeTransparentRegion = layerState.transparentRegionHint; const ui::Transform& layerTransform = layerState.geomLayerTransform; const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; const Rect& bufferSize = layerState.geomBufferSize; @@ -250,8 +220,8 @@ Rect OutputLayer::calculateOutputDisplayFrame() const { } uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const { - const auto& layerState = mLayer->getState().frontEnd; - const auto& outputState = mOutput.getState(); + const auto& layerState = getLayer().getFEState(); + const auto& outputState = getOutput().getState(); /* * Transformations are applied in this order: @@ -289,99 +259,381 @@ uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const { return transform.getOrientation(); } // namespace impl -void OutputLayer::updateCompositionState(bool includeGeometry) { +void OutputLayer::updateCompositionState(bool includeGeometry, bool forceClientComposition) { + const auto& layerFEState = getLayer().getFEState(); + const auto& outputState = getOutput().getState(); + const auto& profile = *getOutput().getDisplayColorProfile(); + auto& state = editState(); + if (includeGeometry) { - mState.displayFrame = calculateOutputDisplayFrame(); - mState.sourceCrop = calculateOutputSourceCrop(); - mState.bufferTransform = + // Clear the forceClientComposition flag before it is set for any + // reason. Note that since it can be set by some checks below when + // updating the geometry state, we only clear it when updating the + // geometry since those conditions for forcing client composition won't + // go away otherwise. + state.forceClientComposition = false; + + state.displayFrame = calculateOutputDisplayFrame(); + state.sourceCrop = calculateOutputSourceCrop(); + state.bufferTransform = static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform()); - if ((mLayer->getState().frontEnd.isSecure && !mOutput.getState().isSecure) || - (mState.bufferTransform & ui::Transform::ROT_INVALID)) { - mState.forceClientComposition = true; + if ((layerFEState.isSecure && !outputState.isSecure) || + (state.bufferTransform & ui::Transform::ROT_INVALID)) { + state.forceClientComposition = true; } } + + // Determine the output dependent dataspace for this layer. If it is + // colorspace agnostic, it just uses the dataspace chosen for the output to + // avoid the need for color conversion. + state.dataspace = layerFEState.isColorspaceAgnostic && + outputState.targetDataspace != ui::Dataspace::UNKNOWN + ? outputState.targetDataspace + : layerFEState.dataspace; + + // These are evaluated every frame as they can potentially change at any + // time. + if (layerFEState.forceClientComposition || !profile.isDataspaceSupported(state.dataspace) || + forceClientComposition) { + state.forceClientComposition = true; + } } -void OutputLayer::writeStateToHWC(bool includeGeometry) const { +void OutputLayer::writeStateToHWC(bool includeGeometry) { + const auto& state = getState(); // Skip doing this if there is no HWC interface - if (!mState.hwc) { + if (!state.hwc) { return; } - auto& hwcLayer = (*mState.hwc).hwcLayer; + auto& hwcLayer = (*state.hwc).hwcLayer; if (!hwcLayer) { ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s", - mLayerFE->getDebugName(), mOutput.getName().c_str()); + getLayerFE().getDebugName(), getOutput().getName().c_str()); return; } + const auto& outputIndependentState = getLayer().getFEState(); + auto requestedCompositionType = outputIndependentState.compositionType; + if (includeGeometry) { - // Output dependent state + writeOutputDependentGeometryStateToHWC(hwcLayer.get(), requestedCompositionType); + writeOutputIndependentGeometryStateToHWC(hwcLayer.get(), outputIndependentState); + } - if (auto error = hwcLayer->setDisplayFrame(mState.displayFrame); - error != HWC2::Error::None) { - ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", - mLayerFE->getDebugName(), mState.displayFrame.left, mState.displayFrame.top, - mState.displayFrame.right, mState.displayFrame.bottom, to_string(error).c_str(), - static_cast<int32_t>(error)); - } + writeOutputDependentPerFrameStateToHWC(hwcLayer.get()); + writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), outputIndependentState); - if (auto error = hwcLayer->setSourceCrop(mState.sourceCrop); error != HWC2::Error::None) { - ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " - "%s (%d)", - mLayerFE->getDebugName(), mState.sourceCrop.left, mState.sourceCrop.top, - mState.sourceCrop.right, mState.sourceCrop.bottom, to_string(error).c_str(), - static_cast<int32_t>(error)); - } + writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType); +} + +void OutputLayer::writeOutputDependentGeometryStateToHWC( + HWC2::Layer* hwcLayer, Hwc2::IComposerClient::Composition requestedCompositionType) { + const auto& outputDependentState = getState(); + + if (auto error = hwcLayer->setDisplayFrame(outputDependentState.displayFrame); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", + getLayerFE().getDebugName(), outputDependentState.displayFrame.left, + outputDependentState.displayFrame.top, outputDependentState.displayFrame.right, + outputDependentState.displayFrame.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + } - if (auto error = hwcLayer->setZOrder(mState.z); error != HWC2::Error::None) { - ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), mState.z, + if (auto error = hwcLayer->setSourceCrop(outputDependentState.sourceCrop); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " + "%s (%d)", + getLayerFE().getDebugName(), outputDependentState.sourceCrop.left, + outputDependentState.sourceCrop.top, outputDependentState.sourceCrop.right, + outputDependentState.sourceCrop.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != HWC2::Error::None) { + ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(), + outputDependentState.z, to_string(error).c_str(), static_cast<int32_t>(error)); + } + + // Solid-color layers should always use an identity transform. + const auto bufferTransform = + requestedCompositionType != Hwc2::IComposerClient::Composition::SOLID_COLOR + ? outputDependentState.bufferTransform + : static_cast<Hwc2::Transform>(0); + if (auto error = hwcLayer->setTransform(static_cast<HWC2::Transform>(bufferTransform)); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set transform %s: %s (%d)", getLayerFE().getDebugName(), + toString(outputDependentState.bufferTransform).c_str(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } +} + +void OutputLayer::writeOutputIndependentGeometryStateToHWC( + HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) { + if (auto error = hwcLayer->setBlendMode( + static_cast<HWC2::BlendMode>(outputIndependentState.blendMode)); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set blend mode %s: %s (%d)", getLayerFE().getDebugName(), + toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", getLayerFE().getDebugName(), + outputIndependentState.alpha, to_string(error).c_str(), static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set info %s (%d)", getLayerFE().getDebugName(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } +} + +void OutputLayer::writeOutputDependentPerFrameStateToHWC(HWC2::Layer* hwcLayer) { + const auto& outputDependentState = getState(); + + // TODO(lpique): b/121291683 outputSpaceVisibleRegion is output-dependent geometry + // state and should not change every frame. + if (auto error = hwcLayer->setVisibleRegion(outputDependentState.outputSpaceVisibleRegion); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set visible region: %s (%d)", getLayerFE().getDebugName(), + to_string(error).c_str(), static_cast<int32_t>(error)); + outputDependentState.outputSpaceVisibleRegion.dump(LOG_TAG); + } + + if (auto error = hwcLayer->setDataspace(outputDependentState.dataspace); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(), + outputDependentState.dataspace, to_string(error).c_str(), + static_cast<int32_t>(error)); + } +} + +void OutputLayer::writeOutputIndependentPerFrameStateToHWC( + HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState) { + switch (auto error = hwcLayer->setColorTransform(outputIndependentState.colorTransform)) { + case HWC2::Error::None: + break; + case HWC2::Error::Unsupported: + editState().forceClientComposition = true; + break; + default: + ALOGE("[%s] Failed to set color transform: %s (%d)", getLayerFE().getDebugName(), to_string(error).c_str(), static_cast<int32_t>(error)); - } + } - if (auto error = - hwcLayer->setTransform(static_cast<HWC2::Transform>(mState.bufferTransform)); - error != HWC2::Error::None) { - ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(), - toString(mState.bufferTransform).c_str(), to_string(error).c_str(), - static_cast<int32_t>(error)); - } + if (auto error = hwcLayer->setSurfaceDamage(outputIndependentState.surfaceDamage); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set surface damage: %s (%d)", getLayerFE().getDebugName(), + to_string(error).c_str(), static_cast<int32_t>(error)); + outputIndependentState.surfaceDamage.dump(LOG_TAG); + } - // Output independent state + // Content-specific per-frame state + switch (outputIndependentState.compositionType) { + case Hwc2::IComposerClient::Composition::SOLID_COLOR: + writeSolidColorStateToHWC(hwcLayer, outputIndependentState); + break; + case Hwc2::IComposerClient::Composition::SIDEBAND: + writeSidebandStateToHWC(hwcLayer, outputIndependentState); + break; + case Hwc2::IComposerClient::Composition::CURSOR: + case Hwc2::IComposerClient::Composition::DEVICE: + writeBufferStateToHWC(hwcLayer, outputIndependentState); + break; + case Hwc2::IComposerClient::Composition::INVALID: + case Hwc2::IComposerClient::Composition::CLIENT: + // Ignored + break; + } +} - const auto& outputIndependentState = mLayer->getState().frontEnd; +void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer, + const LayerFECompositionState& outputIndependentState) { + hwc_color_t color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)), + static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)), + static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)), + 255}; - if (auto error = hwcLayer->setBlendMode( - static_cast<HWC2::BlendMode>(outputIndependentState.blendMode)); - error != HWC2::Error::None) { - ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(), - toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(), - static_cast<int32_t>(error)); - } + if (auto error = hwcLayer->setColor(color); error != HWC2::Error::None) { + ALOGE("[%s] Failed to set color: %s (%d)", getLayerFE().getDebugName(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } +} - if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha); +void OutputLayer::writeSidebandStateToHWC(HWC2::Layer* hwcLayer, + const LayerFECompositionState& outputIndependentState) { + if (auto error = hwcLayer->setSidebandStream(outputIndependentState.sidebandStream->handle()); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set sideband stream %p: %s (%d)", getLayerFE().getDebugName(), + outputIndependentState.sidebandStream->handle(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } +} + +void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, + const LayerFECompositionState& outputIndependentState) { + auto supportedPerFrameMetadata = + getOutput().getDisplayColorProfile()->getSupportedPerFrameMetadata(); + if (auto error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata, + outputIndependentState.hdrMetadata); + error != HWC2::Error::None && error != HWC2::Error::Unsupported) { + ALOGE("[%s] Failed to set hdrMetadata: %s (%d)", getLayerFE().getDebugName(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } + + uint32_t hwcSlot = 0; + sp<GraphicBuffer> hwcBuffer; + // We need access to the output-dependent state for the buffer cache there, + // though otherwise the buffer is not output-dependent. + editState().hwc->hwcBufferCache.getHwcBuffer(outputIndependentState.bufferSlot, + outputIndependentState.buffer, &hwcSlot, + &hwcBuffer); + + if (auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, outputIndependentState.acquireFence); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set buffer %p: %s (%d)", getLayerFE().getDebugName(), + outputIndependentState.buffer->handle, to_string(error).c_str(), + static_cast<int32_t>(error)); + } +} + +void OutputLayer::writeCompositionTypeToHWC( + HWC2::Layer* hwcLayer, Hwc2::IComposerClient::Composition requestedCompositionType) { + auto& outputDependentState = editState(); + + // If we are forcing client composition, we need to tell the HWC + if (outputDependentState.forceClientComposition) { + requestedCompositionType = Hwc2::IComposerClient::Composition::CLIENT; + } + + // Set the requested composition type with the HWC whenever it changes + if (outputDependentState.hwc->hwcCompositionType != requestedCompositionType) { + outputDependentState.hwc->hwcCompositionType = requestedCompositionType; + + if (auto error = hwcLayer->setCompositionType( + static_cast<HWC2::Composition>(requestedCompositionType)); error != HWC2::Error::None) { - ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(), - outputIndependentState.alpha, to_string(error).c_str(), + ALOGE("[%s] Failed to set composition type %s: %s (%d)", getLayerFE().getDebugName(), + toString(requestedCompositionType).c_str(), to_string(error).c_str(), static_cast<int32_t>(error)); } + } +} - if (auto error = - hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId); - error != HWC2::Error::None) { - ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(), - to_string(error).c_str(), static_cast<int32_t>(error)); - } +void OutputLayer::writeCursorPositionToHWC() const { + // Skip doing this if there is no HWC interface + auto hwcLayer = getHwcLayer(); + if (!hwcLayer) { + return; } + + const auto& layerFEState = getLayer().getFEState(); + const auto& outputState = getOutput().getState(); + + Rect frame = layerFEState.cursorFrame; + frame.intersect(outputState.viewport, &frame); + Rect position = outputState.transform.transform(frame); + + if (auto error = hwcLayer->setCursorPosition(position.left, position.top); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set cursor position to (%d, %d): %s (%d)", + getLayerFE().getDebugName(), position.left, position.top, to_string(error).c_str(), + static_cast<int32_t>(error)); + } +} + +HWC2::Layer* OutputLayer::getHwcLayer() const { + const auto& state = getState(); + return state.hwc ? state.hwc->hwcLayer.get() : nullptr; +} + +bool OutputLayer::requiresClientComposition() const { + const auto& state = getState(); + return !state.hwc || + state.hwc->hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT; +} + +bool OutputLayer::isHardwareCursor() const { + const auto& state = getState(); + return state.hwc && state.hwc->hwcCompositionType == Hwc2::IComposerClient::Composition::CURSOR; +} + +void OutputLayer::detectDisallowedCompositionTypeChange( + Hwc2::IComposerClient::Composition from, Hwc2::IComposerClient::Composition to) const { + bool result = false; + switch (from) { + case Hwc2::IComposerClient::Composition::INVALID: + case Hwc2::IComposerClient::Composition::CLIENT: + result = false; + break; + + case Hwc2::IComposerClient::Composition::DEVICE: + case Hwc2::IComposerClient::Composition::SOLID_COLOR: + result = (to == Hwc2::IComposerClient::Composition::CLIENT); + break; + + case Hwc2::IComposerClient::Composition::CURSOR: + case Hwc2::IComposerClient::Composition::SIDEBAND: + result = (to == Hwc2::IComposerClient::Composition::CLIENT || + to == Hwc2::IComposerClient::Composition::DEVICE); + break; + } + + if (!result) { + ALOGE("[%s] Invalid device requested composition type change: %s (%d) --> %s (%d)", + getLayerFE().getDebugName(), toString(from).c_str(), static_cast<int>(from), + toString(to).c_str(), static_cast<int>(to)); + } +} + +void OutputLayer::applyDeviceCompositionTypeChange( + Hwc2::IComposerClient::Composition compositionType) { + auto& state = editState(); + LOG_FATAL_IF(!state.hwc); + auto& hwcState = *state.hwc; + + detectDisallowedCompositionTypeChange(hwcState.hwcCompositionType, compositionType); + + hwcState.hwcCompositionType = compositionType; +} + +void OutputLayer::prepareForDeviceLayerRequests() { + auto& state = editState(); + state.clearClientTarget = false; +} + +void OutputLayer::applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) { + auto& state = editState(); + switch (request) { + case Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET: + state.clearClientTarget = true; + break; + + default: + ALOGE("[%s] Unknown device layer request %s (%d)", getLayerFE().getDebugName(), + toString(request).c_str(), static_cast<int>(request)); + break; + } +} + +bool OutputLayer::needsFiltering() const { + const auto& state = getState(); + const auto& displayFrame = state.displayFrame; + const auto& sourceCrop = state.sourceCrop; + return sourceCrop.getHeight() != displayFrame.getHeight() || + sourceCrop.getWidth() != displayFrame.getWidth(); } void OutputLayer::dump(std::string& out) const { using android::base::StringAppendF; - StringAppendF(&out, " - Output Layer %p (Composition layer %p) (%s)\n", this, mLayer.get(), - mLayerFE->getDebugName()); - mState.dump(out); + StringAppendF(&out, " - Output Layer %p (Composition layer %p) (%s)\n", this, &getLayer(), + getLayerFE().getDebugName()); + dumpState(out); } } // namespace impl diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp index 861ea5757b..ad668b6b89 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp @@ -42,11 +42,21 @@ void OutputLayerCompositionState::dump(std::string& out) const { dumpVal(out, "visibleRegion", visibleRegion); out.append(" "); + dumpVal(out, "visibleNonTransparentRegion", visibleNonTransparentRegion); + + out.append(" "); + dumpVal(out, "coveredRegion", coveredRegion); + + out.append(" "); + dumpVal(out, "output visibleRegion", outputSpaceVisibleRegion); + + out.append(" "); dumpVal(out, "forceClientComposition", forceClientComposition); dumpVal(out, "clearClientTarget", clearClientTarget); dumpVal(out, "displayFrame", displayFrame); dumpVal(out, "sourceCrop", sourceCrop); dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform); + dumpVal(out, "dataspace", toString(dataspace), dataspace); dumpVal(out, "z-index", z); if (hwc) { diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp index 3fcd9d155d..5ed21fc4b0 100644 --- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp +++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp @@ -23,6 +23,7 @@ #include <compositionengine/DisplaySurface.h> #include <compositionengine/RenderSurfaceCreationArgs.h> #include <compositionengine/impl/DumpHelpers.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/RenderSurface.h> #include <log/log.h> #include <renderengine/RenderEngine.h> @@ -42,12 +43,13 @@ namespace impl { std::unique_ptr<compositionengine::RenderSurface> createRenderSurface( const compositionengine::CompositionEngine& compositionEngine, - compositionengine::Display& display, compositionengine::RenderSurfaceCreationArgs&& args) { - return std::make_unique<RenderSurface>(compositionEngine, display, std::move(args)); + compositionengine::Display& display, + const compositionengine::RenderSurfaceCreationArgs& args) { + return std::make_unique<RenderSurface>(compositionEngine, display, args); } RenderSurface::RenderSurface(const CompositionEngine& compositionEngine, Display& display, - RenderSurfaceCreationArgs&& args) + const RenderSurfaceCreationArgs& args) : mCompositionEngine(compositionEngine), mDisplay(display), mNativeWindow(args.nativeWindow), @@ -110,24 +112,13 @@ status_t RenderSurface::beginFrame(bool mustRecompose) { return mDisplaySurface->beginFrame(mustRecompose); } -status_t RenderSurface::prepareFrame() { - auto& hwc = mCompositionEngine.getHwComposer(); - const auto id = mDisplay.getId(); - if (id) { - status_t error = hwc.prepare(*id, mDisplay); - if (error != NO_ERROR) { - return error; - } - } - +void RenderSurface::prepareFrame(bool usesClientComposition, bool usesDeviceComposition) { DisplaySurface::CompositionType compositionType; - const bool hasClient = hwc.hasClientComposition(id); - const bool hasDevice = hwc.hasDeviceComposition(id); - if (hasClient && hasDevice) { + if (usesClientComposition && usesDeviceComposition) { compositionType = DisplaySurface::COMPOSITION_MIXED; - } else if (hasClient) { + } else if (usesClientComposition) { compositionType = DisplaySurface::COMPOSITION_GLES; - } else if (hasDevice) { + } else if (usesDeviceComposition) { compositionType = DisplaySurface::COMPOSITION_HWC; } else { // Nothing to do -- when turning the screen off we get a frame like @@ -135,7 +126,11 @@ status_t RenderSurface::prepareFrame() { // will do a prepare/set cycle. compositionType = DisplaySurface::COMPOSITION_HWC; } - return mDisplaySurface->prepareFrame(compositionType); + + if (status_t result = mDisplaySurface->prepareFrame(compositionType); result != NO_ERROR) { + ALOGE("updateCompositionType failed for %s: %d (%s)", mDisplay.getName().c_str(), result, + strerror(-result)); + } } sp<GraphicBuffer> RenderSurface::dequeueBuffer(base::unique_fd* bufferFence) { @@ -162,11 +157,10 @@ sp<GraphicBuffer> RenderSurface::dequeueBuffer(base::unique_fd* bufferFence) { return mGraphicBuffer; } -void RenderSurface::queueBuffer(base::unique_fd&& readyFence) { - auto& hwc = mCompositionEngine.getHwComposer(); - const auto id = mDisplay.getId(); +void RenderSurface::queueBuffer(base::unique_fd readyFence) { + auto& state = mDisplay.getState(); - if (hwc.hasClientComposition(id) || hwc.hasFlipClientTargetRequest(id)) { + if (state.usesClientComposition || state.flipClientTarget) { // hasFlipClientTargetRequest could return true even if we haven't // dequeued a buffer before. Try dequeueing one if we don't have a // buffer ready. @@ -215,13 +209,6 @@ void RenderSurface::onPresentDisplayCompleted() { mDisplaySurface->onFrameCommitted(); } -void RenderSurface::setViewportAndProjection() { - auto& renderEngine = mCompositionEngine.getRenderEngine(); - Rect sourceCrop = Rect(mSize); - renderEngine.setViewportAndProjection(mSize.width, mSize.height, sourceCrop, - ui::Transform::ROT_0); -} - void RenderSurface::flip() { mPageFlipCount++; } diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp index 3766f27ce6..0dbf8f0781 100644 --- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp @@ -14,7 +14,11 @@ * limitations under the License. */ +#include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/impl/CompositionEngine.h> +#include <compositionengine/mock/Layer.h> +#include <compositionengine/mock/LayerFE.h> +#include <compositionengine/mock/Output.h> #include <gtest/gtest.h> #include <renderengine/mock/RenderEngine.h> @@ -23,19 +27,19 @@ namespace android::compositionengine { namespace { +using ::testing::_; +using ::testing::Return; +using ::testing::SaveArg; using ::testing::StrictMock; class CompositionEngineTest : public testing::Test { public: - ~CompositionEngineTest() override; - mock::HWComposer* mHwc = new StrictMock<mock::HWComposer>(); + android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>(); renderengine::mock::RenderEngine* mRenderEngine = new StrictMock<renderengine::mock::RenderEngine>(); impl::CompositionEngine mEngine; }; -CompositionEngineTest::~CompositionEngineTest() = default; - TEST_F(CompositionEngineTest, canInstantiateCompositionEngine) { auto engine = impl::createCompositionEngine(); EXPECT_TRUE(engine.get() != nullptr); @@ -53,5 +57,84 @@ TEST_F(CompositionEngineTest, canSetRenderEngine) { EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine()); } +/* + * CompositionEngine::preComposition + */ + +class PreCompositionTest : public CompositionEngineTest { +public: + PreCompositionTest() { + EXPECT_CALL(*mLayer1, getLayerFE()).WillRepeatedly(Return(mLayer1FE)); + EXPECT_CALL(*mLayer2, getLayerFE()).WillRepeatedly(Return(mLayer2FE)); + EXPECT_CALL(*mLayer3, getLayerFE()).WillRepeatedly(Return(mLayer3FE)); + // getLayerFE() can return nullptr. Ensure that this is handled. + EXPECT_CALL(*mLayer4, getLayerFE()).WillRepeatedly(Return(nullptr)); + + mRefreshArgs.outputs = {mOutput}; + mRefreshArgs.layers = {mLayer1, mLayer2, mLayer3, mLayer4}; + } + + std::shared_ptr<mock::Output> mOutput{std::make_shared<StrictMock<mock::Output>>()}; + std::shared_ptr<mock::Layer> mLayer1{std::make_shared<StrictMock<mock::Layer>>()}; + std::shared_ptr<mock::Layer> mLayer2{std::make_shared<StrictMock<mock::Layer>>()}; + std::shared_ptr<mock::Layer> mLayer3{std::make_shared<StrictMock<mock::Layer>>()}; + std::shared_ptr<mock::Layer> mLayer4{std::make_shared<StrictMock<mock::Layer>>()}; + sp<StrictMock<mock::LayerFE>> mLayer1FE{new StrictMock<mock::LayerFE>()}; + sp<StrictMock<mock::LayerFE>> mLayer2FE{new StrictMock<mock::LayerFE>()}; + sp<StrictMock<mock::LayerFE>> mLayer3FE{new StrictMock<mock::LayerFE>()}; + + CompositionRefreshArgs mRefreshArgs; +}; + +TEST_F(PreCompositionTest, preCompositionSetsFrameTimestamp) { + const nsecs_t before = systemTime(SYSTEM_TIME_MONOTONIC); + CompositionRefreshArgs emptyArgs; + mEngine.preComposition(emptyArgs); + const nsecs_t after = systemTime(SYSTEM_TIME_MONOTONIC); + + // The frame timestamp should be between the before and after timestamps + EXPECT_GE(mEngine.getLastFrameRefreshTimestamp(), before); + EXPECT_LE(mEngine.getLastFrameRefreshTimestamp(), after); +} + +TEST_F(PreCompositionTest, preCompositionInvokesLayerPreCompositionWithFrameTimestamp) { + nsecs_t ts1 = 0; + nsecs_t ts2 = 0; + nsecs_t ts3 = 0; + EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false))); + EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false))); + EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false))); + + mEngine.preComposition(mRefreshArgs); + + // Each of the onPreComposition calls should used the same refresh timestamp + EXPECT_EQ(ts1, mEngine.getLastFrameRefreshTimestamp()); + EXPECT_EQ(ts2, mEngine.getLastFrameRefreshTimestamp()); + EXPECT_EQ(ts3, mEngine.getLastFrameRefreshTimestamp()); +} + +TEST_F(PreCompositionTest, preCompositionDefaultsToNoUpdateNeeded) { + EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false)); + + mEngine.setNeedsAnotherUpdateForTest(true); + + mEngine.preComposition(mRefreshArgs); + + // The call should have cleared the needsAnotherUpdate flag + EXPECT_FALSE(mEngine.needsAnotherUpdate()); +} + +TEST_F(PreCompositionTest, preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) { + EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true)); + EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false)); + EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false)); + + mEngine.preComposition(mRefreshArgs); + + EXPECT_TRUE(mEngine.needsAnotherUpdate()); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp index 9215884f58..c07dfbb38d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp @@ -638,5 +638,66 @@ TEST_F(DisplayColorProfileTest, getBestColorModeReturnsExpectedModesWhenOutputHa checkGetBestColorMode(profile, expectedResults); } +/* + * RenderSurface::isDataspaceSupported() + */ + +TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithNoHdrSupport) { + auto profile = ProfileFactory::createProfileWithNoColorModeSupport(); + + EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN)); + EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB)); + EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_PQ)); + EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ)); + EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_HLG)); + EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG)); +} + +TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithHdr10Support) { + auto profile = ProfileFactory::createProfileWithSRGBColorModeSupport(); + + EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN)); + EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB)); + EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_PQ)); + EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ)); + EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_HLG)); + EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG)); +} + +TEST_F(DisplayColorProfileTest, isDataspaceSupportedWorksForProfileWithHlgSupport) { + auto profile = ProfileFactory::createProfileWithBT2100PQSupport(); + + EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::UNKNOWN)); + EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::V0_SRGB)); + EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_PQ)); + EXPECT_FALSE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_PQ)); + EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_HLG)); + EXPECT_TRUE(profile.isDataspaceSupported(Dataspace::BT2020_ITU_HLG)); +} + +/* + * RenderSurface::getTargetDataspace() + */ + +TEST_F(DisplayColorProfileTest, getTargetDataspaceWorks) { + auto profile = ProfileFactory::createProfileWithNoColorModeSupport(); + + // For a non-HDR colorspace with no colorSpaceAgnosticDataspace override, + // the input dataspace should be returned. + EXPECT_EQ(Dataspace::DISPLAY_P3, + profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3, + Dataspace::UNKNOWN)); + + // If colorSpaceAgnosticDataspace is set, its value should be returned + EXPECT_EQ(Dataspace::V0_SRGB, + profile.getTargetDataspace(ColorMode::DISPLAY_P3, Dataspace::DISPLAY_P3, + Dataspace::V0_SRGB)); + + // For an HDR colorspace, Dataspace::UNKNOWN should be returned. + EXPECT_EQ(Dataspace::UNKNOWN, + profile.getTargetDataspace(ColorMode::BT2100_PQ, Dataspace::BT2020_PQ, + Dataspace::UNKNOWN)); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 33444a5df1..4d71d4370a 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -22,33 +22,79 @@ #include <compositionengine/RenderSurfaceCreationArgs.h> #include <compositionengine/impl/Display.h> #include <compositionengine/mock/CompositionEngine.h> +#include <compositionengine/mock/DisplayColorProfile.h> +#include <compositionengine/mock/Layer.h> +#include <compositionengine/mock/LayerFE.h> #include <compositionengine/mock/NativeWindow.h> +#include <compositionengine/mock/OutputLayer.h> #include <compositionengine/mock/RenderSurface.h> #include <gtest/gtest.h> +#include "MockHWC2.h" #include "MockHWComposer.h" +#include "MockPowerAdvisor.h" namespace android::compositionengine { namespace { +using testing::_; +using testing::DoAll; using testing::Return; using testing::ReturnRef; +using testing::Sequence; +using testing::SetArgPointee; using testing::StrictMock; constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42}; -class DisplayTest : public testing::Test { -public: - ~DisplayTest() override = default; +struct DisplayTest : public testing::Test { + class Display : public impl::Display { + public: + explicit Display(const compositionengine::DisplayCreationArgs& args) + : impl::Display(args) {} + + using impl::Display::injectOutputLayerForTest; + virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0; + }; + + static std::shared_ptr<Display> createDisplay( + const compositionengine::CompositionEngine& compositionEngine, + compositionengine::DisplayCreationArgs&& args) { + return impl::createDisplayTemplated<Display>(compositionEngine, args); + } + + DisplayTest() { + EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer)); + EXPECT_CALL(*mLayer1, getHwcLayer()).WillRepeatedly(Return(&mHWC2Layer1)); + EXPECT_CALL(*mLayer2, getHwcLayer()).WillRepeatedly(Return(&mHWC2Layer2)); + EXPECT_CALL(*mLayer3, getHwcLayer()).WillRepeatedly(Return(nullptr)); + + mDisplay->injectOutputLayerForTest( + std::unique_ptr<compositionengine::OutputLayer>(mLayer1)); + mDisplay->injectOutputLayerForTest( + std::unique_ptr<compositionengine::OutputLayer>(mLayer2)); + mDisplay->injectOutputLayerForTest( + std::unique_ptr<compositionengine::OutputLayer>(mLayer3)); + } StrictMock<android::mock::HWComposer> mHwComposer; + StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor; StrictMock<mock::CompositionEngine> mCompositionEngine; sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>(); - impl::Display mDisplay{mCompositionEngine, - DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()}; + StrictMock<HWC2::mock::Layer> mHWC2Layer1; + StrictMock<HWC2::mock::Layer> mHWC2Layer2; + StrictMock<HWC2::mock::Layer> mHWC2LayerUnknown; + mock::OutputLayer* mLayer1 = new StrictMock<mock::OutputLayer>(); + mock::OutputLayer* mLayer2 = new StrictMock<mock::OutputLayer>(); + mock::OutputLayer* mLayer3 = new StrictMock<mock::OutputLayer>(); + std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine, + DisplayCreationArgsBuilder() + .setDisplayId(DEFAULT_DISPLAY_ID) + .setPowerAdvisor(&mPowerAdvisor) + .build()); }; -/* ------------------------------------------------------------------------ +/* * Basic construction */ @@ -65,12 +111,10 @@ TEST_F(DisplayTest, canInstantiateDisplay) { { constexpr DisplayId display2 = DisplayId{546u}; - auto display = impl::createDisplay(mCompositionEngine, - DisplayCreationArgsBuilder() - .setIsSecure(true) - .setDisplayId(display2) - .build()); - EXPECT_TRUE(display->isSecure()); + auto display = + impl::createDisplay(mCompositionEngine, + DisplayCreationArgsBuilder().setDisplayId(display2).build()); + EXPECT_FALSE(display->isSecure()); EXPECT_FALSE(display->isVirtual()); EXPECT_EQ(display2, display->getId()); } @@ -88,73 +132,79 @@ TEST_F(DisplayTest, canInstantiateDisplay) { } } -/* ------------------------------------------------------------------------ +/* * Display::disconnect() */ TEST_F(DisplayTest, disconnectDisconnectsDisplay) { - EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer)); - // The first call to disconnect will disconnect the display with the HWC and // set mHwcId to -1. EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1); - mDisplay.disconnect(); - EXPECT_FALSE(mDisplay.getId()); + mDisplay->disconnect(); + EXPECT_FALSE(mDisplay->getId()); // Subsequent calls will do nothing, EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(0); - mDisplay.disconnect(); - EXPECT_FALSE(mDisplay.getId()); + mDisplay->disconnect(); + EXPECT_FALSE(mDisplay->getId()); } -/* ------------------------------------------------------------------------ +/* * Display::setColorTransform() */ TEST_F(DisplayTest, setColorTransformSetsTransform) { - // Identity matrix sets an identity state value - const mat4 identity; + // No change does nothing + CompositionRefreshArgs refreshArgs; + refreshArgs.colorTransformMatrix = std::nullopt; + mDisplay->setColorTransform(refreshArgs); - EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer)); - - EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, identity)).Times(1); + // Identity matrix sets an identity state value + const mat4 kIdentity; - mDisplay.setColorTransform(identity); + EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kIdentity)).Times(1); - EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mDisplay.getState().colorTransform); + refreshArgs.colorTransformMatrix = kIdentity; + mDisplay->setColorTransform(refreshArgs); // Non-identity matrix sets a non-identity state value - const mat4 nonIdentity = mat4() * 2; - - EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, nonIdentity)).Times(1); + const mat4 kNonIdentity = mat4() * 2; - mDisplay.setColorTransform(nonIdentity); + EXPECT_CALL(mHwComposer, setColorTransform(DEFAULT_DISPLAY_ID, kNonIdentity)).Times(1); - EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mDisplay.getState().colorTransform); + refreshArgs.colorTransformMatrix = kNonIdentity; + mDisplay->setColorTransform(refreshArgs); } -/* ------------------------------------------------------------------------ +/* * Display::setColorMode() */ TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) { + using ColorProfile = Output::ColorProfile; + mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>(); - mDisplay.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface)); + mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface)); + mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>(); + mDisplay->setDisplayColorProfileForTest(std::unique_ptr<DisplayColorProfile>(colorProfile)); - EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer)); + EXPECT_CALL(*colorProfile, getTargetDataspace(_, _, _)) + .WillRepeatedly(Return(ui::Dataspace::UNKNOWN)); // These values are expected to be the initial state. - ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode); - ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace); - ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent); + ASSERT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode); + ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace); + ASSERT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent); + ASSERT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace); // Otherwise if the values are unchanged, nothing happens - mDisplay.setColorMode(ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN, - ui::RenderIntent::COLORIMETRIC); + mDisplay->setColorProfile(ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN, + ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN}); - EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay.getState().colorMode); - EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay.getState().dataspace); - EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay.getState().renderIntent); + EXPECT_EQ(ui::ColorMode::NATIVE, mDisplay->getState().colorMode); + EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().dataspace); + EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, mDisplay->getState().renderIntent); + EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace); // Otherwise if the values are different, updates happen EXPECT_CALL(*renderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1); @@ -163,47 +213,559 @@ TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) { ui::RenderIntent::TONE_MAP_COLORIMETRIC)) .Times(1); - mDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, - ui::RenderIntent::TONE_MAP_COLORIMETRIC); + mDisplay->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, + ui::RenderIntent::TONE_MAP_COLORIMETRIC, + ui::Dataspace::UNKNOWN}); - EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay.getState().colorMode); - EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay.getState().dataspace); - EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay.getState().renderIntent); + EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mDisplay->getState().colorMode); + EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mDisplay->getState().dataspace); + EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mDisplay->getState().renderIntent); + EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace); } TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) { - impl::Display virtualDisplay{mCompositionEngine, - DisplayCreationArgs{false, true, DEFAULT_DISPLAY_ID}}; + using ColorProfile = Output::ColorProfile; + + std::shared_ptr<impl::Display> virtualDisplay{ + impl::createDisplay(mCompositionEngine, DisplayCreationArgs{true, DEFAULT_DISPLAY_ID})}; - virtualDisplay.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, - ui::RenderIntent::TONE_MAP_COLORIMETRIC); + mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>(); + virtualDisplay->setDisplayColorProfileForTest( + std::unique_ptr<DisplayColorProfile>(colorProfile)); - EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay.getState().colorMode); - EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay.getState().dataspace); - EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay.getState().renderIntent); + EXPECT_CALL(*colorProfile, + getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, + ui::Dataspace::UNKNOWN)) + .WillOnce(Return(ui::Dataspace::UNKNOWN)); + + virtualDisplay->setColorProfile( + ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, + ui::RenderIntent::TONE_MAP_COLORIMETRIC, ui::Dataspace::UNKNOWN}); + + EXPECT_EQ(ui::ColorMode::NATIVE, virtualDisplay->getState().colorMode); + EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().dataspace); + EXPECT_EQ(ui::RenderIntent::COLORIMETRIC, virtualDisplay->getState().renderIntent); + EXPECT_EQ(ui::Dataspace::UNKNOWN, virtualDisplay->getState().targetDataspace); } -/* ------------------------------------------------------------------------ +/* * Display::createDisplayColorProfile() */ TEST_F(DisplayTest, createDisplayColorProfileSetsDisplayColorProfile) { - EXPECT_TRUE(mDisplay.getDisplayColorProfile() == nullptr); - mDisplay.createDisplayColorProfile( + EXPECT_TRUE(mDisplay->getDisplayColorProfile() == nullptr); + mDisplay->createDisplayColorProfile( DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0, DisplayColorProfileCreationArgs::HwcColorModes()}); - EXPECT_TRUE(mDisplay.getDisplayColorProfile() != nullptr); + EXPECT_TRUE(mDisplay->getDisplayColorProfile() != nullptr); } -/* ------------------------------------------------------------------------ +/* * Display::createRenderSurface() */ TEST_F(DisplayTest, createRenderSurfaceSetsRenderSurface) { EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)).WillRepeatedly(Return(NO_ERROR)); - EXPECT_TRUE(mDisplay.getRenderSurface() == nullptr); - mDisplay.createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr}); - EXPECT_TRUE(mDisplay.getRenderSurface() != nullptr); + EXPECT_TRUE(mDisplay->getRenderSurface() == nullptr); + mDisplay->createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr}); + EXPECT_TRUE(mDisplay->getRenderSurface() != nullptr); +} + +/* + * Display::createOutputLayer() + */ + +TEST_F(DisplayTest, createOutputLayerSetsHwcLayer) { + sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>(); + auto layer = std::make_shared<StrictMock<mock::Layer>>(); + StrictMock<HWC2::mock::Layer> hwcLayer; + + EXPECT_CALL(mHwComposer, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer)); + + auto outputLayer = mDisplay->createOutputLayer(layer, layerFE); + + EXPECT_EQ(&hwcLayer, outputLayer->getHwcLayer()); + + EXPECT_CALL(mHwComposer, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer)); + outputLayer.reset(); +} + +/* + * Display::setReleasedLayers() + */ + +TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNotHwcDisplay) { + std::shared_ptr<impl::Display> nonHwcDisplay{ + impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())}; + + sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>(); + mock::Layer layerXLayer; + + { + Output::ReleasedLayers releasedLayers; + releasedLayers.emplace_back(layerXLayerFE); + nonHwcDisplay->setReleasedLayers(std::move(releasedLayers)); + } + + CompositionRefreshArgs refreshArgs; + refreshArgs.layersWithQueuedFrames.push_back(&layerXLayer); + + nonHwcDisplay->setReleasedLayers(refreshArgs); + + const auto& releasedLayers = nonHwcDisplay->getReleasedLayersForTest(); + ASSERT_EQ(1, releasedLayers.size()); +} + +TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNoLayersWithQueuedFrames) { + sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>(); + + { + Output::ReleasedLayers releasedLayers; + releasedLayers.emplace_back(layerXLayerFE); + mDisplay->setReleasedLayers(std::move(releasedLayers)); + } + + CompositionRefreshArgs refreshArgs; + mDisplay->setReleasedLayers(refreshArgs); + + const auto& releasedLayers = mDisplay->getReleasedLayersForTest(); + ASSERT_EQ(1, releasedLayers.size()); +} + +TEST_F(DisplayTest, setReleasedLayers) { + sp<mock::LayerFE> layer1LayerFE = new StrictMock<mock::LayerFE>(); + sp<mock::LayerFE> layer2LayerFE = new StrictMock<mock::LayerFE>(); + sp<mock::LayerFE> layer3LayerFE = new StrictMock<mock::LayerFE>(); + sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>(); + mock::Layer layer1Layer; + mock::Layer layer2Layer; + mock::Layer layer3Layer; + mock::Layer layerXLayer; + + EXPECT_CALL(*mLayer1, getLayer()).WillRepeatedly(ReturnRef(layer1Layer)); + EXPECT_CALL(*mLayer1, getLayerFE()).WillRepeatedly(ReturnRef(*layer1LayerFE.get())); + EXPECT_CALL(*mLayer2, getLayer()).WillRepeatedly(ReturnRef(layer2Layer)); + EXPECT_CALL(*mLayer2, getLayerFE()).WillRepeatedly(ReturnRef(*layer2LayerFE.get())); + EXPECT_CALL(*mLayer3, getLayer()).WillRepeatedly(ReturnRef(layer3Layer)); + EXPECT_CALL(*mLayer3, getLayerFE()).WillRepeatedly(ReturnRef(*layer3LayerFE.get())); + + CompositionRefreshArgs refreshArgs; + refreshArgs.layersWithQueuedFrames.push_back(&layer1Layer); + refreshArgs.layersWithQueuedFrames.push_back(&layer2Layer); + refreshArgs.layersWithQueuedFrames.push_back(&layerXLayer); + refreshArgs.layersWithQueuedFrames.push_back(nullptr); + + mDisplay->setReleasedLayers(refreshArgs); + + const auto& releasedLayers = mDisplay->getReleasedLayersForTest(); + ASSERT_EQ(2, releasedLayers.size()); + ASSERT_EQ(layer1LayerFE.get(), releasedLayers[0].promote().get()); + ASSERT_EQ(layer2LayerFE.get(), releasedLayers[1].promote().get()); +} + +/* + * Display::chooseCompositionStrategy() + */ + +struct DisplayChooseCompositionStrategyTest : public testing::Test { + struct DisplayPartialMock : public impl::Display { + DisplayPartialMock(const compositionengine::CompositionEngine& compositionEngine, + const compositionengine::DisplayCreationArgs& args) + : impl::Display(args), mCompositionEngine(compositionEngine) {} + + // Sets up the helper functions called by chooseCompositionStrategy to + // use a mock implementations. + MOCK_CONST_METHOD0(anyLayersRequireClientComposition, bool()); + MOCK_CONST_METHOD0(allLayersRequireClientComposition, bool()); + MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&)); + MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&)); + MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&)); + + // compositionengine::Output overrides + const OutputCompositionState& getState() const override { return mState; } + OutputCompositionState& editState() override { return mState; } + + // compositionengine::impl::Output overrides + const CompositionEngine& getCompositionEngine() const override { + return mCompositionEngine; + }; + + // These need implementations though are not expected to be called. + MOCK_CONST_METHOD0(getOutputLayerCount, size_t()); + MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, + compositionengine::OutputLayer*(size_t)); + MOCK_METHOD3(ensureOutputLayer, + compositionengine::OutputLayer*( + std::optional<size_t>, + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&)); + MOCK_METHOD0(finalizePendingOutputLayers, void()); + MOCK_METHOD0(clearOutputLayers, void()); + MOCK_CONST_METHOD1(dumpState, void(std::string&)); + MOCK_METHOD2(injectOutputLayerForTest, + compositionengine::OutputLayer*( + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&)); + MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>)); + + const compositionengine::CompositionEngine& mCompositionEngine; + impl::OutputCompositionState mState; + }; + + DisplayChooseCompositionStrategyTest() { + EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer)); + } + + StrictMock<android::mock::HWComposer> mHwComposer; + StrictMock<mock::CompositionEngine> mCompositionEngine; + StrictMock<DisplayPartialMock> + mDisplay{mCompositionEngine, + DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()}; +}; + +TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfNotAHwcDisplay) { + std::shared_ptr<impl::Display> nonHwcDisplay{ + impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())}; + EXPECT_FALSE(nonHwcDisplay->getId()); + + nonHwcDisplay->chooseCompositionStrategy(); + + auto& state = nonHwcDisplay->getState(); + EXPECT_TRUE(state.usesClientComposition); + EXPECT_FALSE(state.usesDeviceComposition); +} + +TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) { + EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false)); + EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, false, _)) + .WillOnce(Return(INVALID_OPERATION)); + + mDisplay.chooseCompositionStrategy(); + + auto& state = mDisplay.getState(); + EXPECT_TRUE(state.usesClientComposition); + EXPECT_FALSE(state.usesDeviceComposition); +} + +TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) { + // Since two calls are made to anyLayersRequireClientComposition with different return values, + // use a Sequence to control the matching so the values are returned in a known order. + Sequence s; + EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()) + .InSequence(s) + .WillOnce(Return(false)); + + EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false)); + + mDisplay.chooseCompositionStrategy(); + + auto& state = mDisplay.getState(); + EXPECT_FALSE(state.usesClientComposition); + EXPECT_TRUE(state.usesDeviceComposition); +} + +TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) { + android::HWComposer::DeviceRequestedChanges changes{ + {{nullptr, HWC2::Composition::Client}}, + HWC2::DisplayRequest::FlipClientTarget, + {{nullptr, HWC2::LayerRequest::ClearClientTarget}}, + }; + + // Since two calls are made to anyLayersRequireClientComposition with different return values, + // use a Sequence to control the matching so the values are returned in a known order. + Sequence s; + EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()) + .InSequence(s) + .WillOnce(Return(false)); + + EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _)) + .WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR))); + EXPECT_CALL(mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1); + EXPECT_CALL(mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1); + EXPECT_CALL(mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1); + EXPECT_CALL(mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false)); + + mDisplay.chooseCompositionStrategy(); + + auto& state = mDisplay.getState(); + EXPECT_FALSE(state.usesClientComposition); + EXPECT_TRUE(state.usesDeviceComposition); +} + +/* + * Display::getSkipColorTransform() + */ + +TEST_F(DisplayTest, getSkipColorTransformDoesNothingIfNonHwcDisplay) { + auto nonHwcDisplay{ + impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())}; + EXPECT_FALSE(nonHwcDisplay->getSkipColorTransform()); +} + +TEST_F(DisplayTest, getSkipColorTransformChecksHwcCapability) { + EXPECT_CALL(mHwComposer, + hasDisplayCapability(std::make_optional(DEFAULT_DISPLAY_ID), + HWC2::DisplayCapability::SkipClientColorTransform)) + .WillOnce(Return(true)); + EXPECT_TRUE(mDisplay->getSkipColorTransform()); +} + +/* + * Display::anyLayersRequireClientComposition() + */ + +TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsFalse) { + EXPECT_CALL(*mLayer1, requiresClientComposition()).WillOnce(Return(false)); + EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(false)); + EXPECT_CALL(*mLayer3, requiresClientComposition()).WillOnce(Return(false)); + + EXPECT_FALSE(mDisplay->anyLayersRequireClientComposition()); +} + +TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsTrue) { + EXPECT_CALL(*mLayer1, requiresClientComposition()).WillOnce(Return(false)); + EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(true)); + + EXPECT_TRUE(mDisplay->anyLayersRequireClientComposition()); +} + +/* + * Display::allLayersRequireClientComposition() + */ + +TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsTrue) { + EXPECT_CALL(*mLayer1, requiresClientComposition()).WillOnce(Return(true)); + EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(true)); + EXPECT_CALL(*mLayer3, requiresClientComposition()).WillOnce(Return(true)); + + EXPECT_TRUE(mDisplay->allLayersRequireClientComposition()); +} + +TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsFalse) { + EXPECT_CALL(*mLayer1, requiresClientComposition()).WillOnce(Return(true)); + EXPECT_CALL(*mLayer2, requiresClientComposition()).WillOnce(Return(false)); + + EXPECT_FALSE(mDisplay->allLayersRequireClientComposition()); +} + +/* + * Display::applyChangedTypesToLayers() + */ + +TEST_F(DisplayTest, applyChangedTypesToLayersTakesEarlyOutIfNoChangedLayers) { + mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes()); +} + +TEST_F(DisplayTest, applyChangedTypesToLayersAppliesChanges) { + EXPECT_CALL(*mLayer1, + applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT)) + .Times(1); + EXPECT_CALL(*mLayer2, + applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::DEVICE)) + .Times(1); + + mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes{ + {&mHWC2Layer1, HWC2::Composition::Client}, + {&mHWC2Layer2, HWC2::Composition::Device}, + {&mHWC2LayerUnknown, HWC2::Composition::SolidColor}, + }); +} + +/* + * Display::applyDisplayRequests() + */ + +TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesNoRequests) { + mDisplay->applyDisplayRequests(static_cast<HWC2::DisplayRequest>(0)); + + auto& state = mDisplay->getState(); + EXPECT_FALSE(state.flipClientTarget); +} + +TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesFlipClientTarget) { + mDisplay->applyDisplayRequests(HWC2::DisplayRequest::FlipClientTarget); + + auto& state = mDisplay->getState(); + EXPECT_TRUE(state.flipClientTarget); +} + +TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesWriteClientTargetToOutput) { + mDisplay->applyDisplayRequests(HWC2::DisplayRequest::WriteClientTargetToOutput); + + auto& state = mDisplay->getState(); + EXPECT_FALSE(state.flipClientTarget); +} + +TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesAllRequestFlagsSet) { + mDisplay->applyDisplayRequests(static_cast<HWC2::DisplayRequest>(~0)); + + auto& state = mDisplay->getState(); + EXPECT_TRUE(state.flipClientTarget); +} + +/* + * Display::applyLayerRequestsToLayers() + */ + +TEST_F(DisplayTest, applyLayerRequestsToLayersPreparesAllLayers) { + EXPECT_CALL(*mLayer1, prepareForDeviceLayerRequests()).Times(1); + EXPECT_CALL(*mLayer2, prepareForDeviceLayerRequests()).Times(1); + EXPECT_CALL(*mLayer3, prepareForDeviceLayerRequests()).Times(1); + + mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests()); +} + +TEST_F(DisplayTest, applyLayerRequestsToLayers2) { + EXPECT_CALL(*mLayer1, prepareForDeviceLayerRequests()).Times(1); + EXPECT_CALL(*mLayer2, prepareForDeviceLayerRequests()).Times(1); + EXPECT_CALL(*mLayer3, prepareForDeviceLayerRequests()).Times(1); + + EXPECT_CALL(*mLayer1, + applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET)) + .Times(1); + + mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests{ + {&mHWC2Layer1, HWC2::LayerRequest::ClearClientTarget}, + {&mHWC2LayerUnknown, HWC2::LayerRequest::ClearClientTarget}, + }); +} + +/* + * Display::presentAndGetFrameFences() + */ + +TEST_F(DisplayTest, presentAndGetFrameFencesReturnsNoFencesOnNonHwcDisplay) { + auto nonHwcDisplay{ + impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())}; + + auto result = nonHwcDisplay->presentAndGetFrameFences(); + + ASSERT_TRUE(result.presentFence.get()); + EXPECT_FALSE(result.presentFence->isValid()); + EXPECT_EQ(0u, result.layerFences.size()); +} + +TEST_F(DisplayTest, presentAndGetFrameFencesReturnsPresentAndLayerFences) { + sp<Fence> presentFence = new Fence(); + sp<Fence> layer1Fence = new Fence(); + sp<Fence> layer2Fence = new Fence(); + + EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(DEFAULT_DISPLAY_ID)).Times(1); + EXPECT_CALL(mHwComposer, getPresentFence(DEFAULT_DISPLAY_ID)).WillOnce(Return(presentFence)); + EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mHWC2Layer1)) + .WillOnce(Return(layer1Fence)); + EXPECT_CALL(mHwComposer, getLayerReleaseFence(DEFAULT_DISPLAY_ID, &mHWC2Layer2)) + .WillOnce(Return(layer2Fence)); + EXPECT_CALL(mHwComposer, clearReleaseFences(DEFAULT_DISPLAY_ID)).Times(1); + + auto result = mDisplay->presentAndGetFrameFences(); + + EXPECT_EQ(presentFence, result.presentFence); + + EXPECT_EQ(2u, result.layerFences.size()); + ASSERT_EQ(1, result.layerFences.count(&mHWC2Layer1)); + EXPECT_EQ(layer1Fence, result.layerFences[&mHWC2Layer1]); + ASSERT_EQ(1, result.layerFences.count(&mHWC2Layer2)); + EXPECT_EQ(layer2Fence, result.layerFences[&mHWC2Layer2]); +} + +/* + * Display::setExpensiveRenderingExpected() + */ + +TEST_F(DisplayTest, setExpensiveRenderingExpectedForwardsToPowerAdvisor) { + EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, true)).Times(1); + mDisplay->setExpensiveRenderingExpected(true); + + EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, false)).Times(1); + mDisplay->setExpensiveRenderingExpected(false); +} + +/* + * Display::finishFrame() + */ + +TEST_F(DisplayTest, finishFrameDoesNotSkipCompositionIfNotDirtyOnHwcDisplay) { + mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>(); + mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface)); + + // We expect no calls to queueBuffer if composition was skipped. + EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1); + + mDisplay->editState().isEnabled = true; + mDisplay->editState().usesClientComposition = false; + mDisplay->editState().viewport = Rect(0, 0, 1, 1); + mDisplay->editState().dirtyRegion = Region::INVALID_REGION; + + CompositionRefreshArgs refreshArgs; + refreshArgs.repaintEverything = false; + + mDisplay->finishFrame(refreshArgs); +} + +TEST_F(DisplayTest, finishFrameSkipsCompositionIfNotDirty) { + std::shared_ptr<impl::Display> nonHwcDisplay{ + impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())}; + + mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>(); + nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface)); + + // We expect no calls to queueBuffer if composition was skipped. + EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(0); + + nonHwcDisplay->editState().isEnabled = true; + nonHwcDisplay->editState().usesClientComposition = false; + nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1); + nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION; + + CompositionRefreshArgs refreshArgs; + refreshArgs.repaintEverything = false; + + nonHwcDisplay->finishFrame(refreshArgs); +} + +TEST_F(DisplayTest, finishFramePerformsCompositionIfDirty) { + std::shared_ptr<impl::Display> nonHwcDisplay{ + impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())}; + + mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>(); + nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface)); + + // We expect a single call to queueBuffer when composition is not skipped. + EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1); + + nonHwcDisplay->editState().isEnabled = true; + nonHwcDisplay->editState().usesClientComposition = false; + nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1); + nonHwcDisplay->editState().dirtyRegion = Region(Rect(0, 0, 1, 1)); + + CompositionRefreshArgs refreshArgs; + refreshArgs.repaintEverything = false; + + nonHwcDisplay->finishFrame(refreshArgs); +} + +TEST_F(DisplayTest, finishFramePerformsCompositionIfRepaintEverything) { + std::shared_ptr<impl::Display> nonHwcDisplay{ + impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())}; + + mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>(); + nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface)); + + // We expect a single call to queueBuffer when composition is not skipped. + EXPECT_CALL(*renderSurface, queueBuffer(_)).Times(1); + + nonHwcDisplay->editState().isEnabled = true; + nonHwcDisplay->editState().usesClientComposition = false; + nonHwcDisplay->editState().viewport = Rect(0, 0, 1, 1); + nonHwcDisplay->editState().dirtyRegion = Region::INVALID_REGION; + + CompositionRefreshArgs refreshArgs; + refreshArgs.repaintEverything = true; + + nonHwcDisplay->finishFrame(refreshArgs); } } // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h new file mode 100644 index 0000000000..6741cc9b7a --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include <string> + +#include <android-base/stringprintf.h> +#include <gmock/gmock.h> + +namespace { + +using android::base::StringAppendF; +using FloatRect = android::FloatRect; + +void dumpFloatRect(const FloatRect& rect, std::string& result, const char* name) { + StringAppendF(&result, "%s (%f %f %f %f) ", name, rect.left, rect.top, rect.right, rect.bottom); +} + +// Checks for a region match +MATCHER_P(FloatRectEq, expected, "") { + std::string buf; + buf.append("FloatRects are not equal\n"); + dumpFloatRect(expected, buf, "expected rect"); + dumpFloatRect(arg, buf, "actual rect"); + *result_listener << buf; + + const float TOLERANCE = 1e-3f; + return (std::fabs(expected.left - arg.left) < TOLERANCE) && + (std::fabs(expected.top - arg.top) < TOLERANCE) && + (std::fabs(expected.right - arg.right) < TOLERANCE) && + (std::fabs(expected.bottom - arg.bottom) < TOLERANCE); +} + +} // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp index 26115a344e..787f973215 100644 --- a/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/LayerTest.cpp @@ -17,6 +17,7 @@ #include <gtest/gtest.h> #include <compositionengine/LayerCreationArgs.h> +#include <compositionengine/LayerFECompositionState.h> #include <compositionengine/impl/Layer.h> #include <compositionengine/mock/CompositionEngine.h> #include <compositionengine/mock/LayerFE.h> @@ -26,13 +27,28 @@ namespace { using testing::StrictMock; -class LayerTest : public testing::Test { -public: +struct LayerTest : public testing::Test { + struct Layer final : public impl::Layer { + explicit Layer(const LayerCreationArgs& args) : mLayerFE(args.layerFE) {} + ~Layer() override = default; + + // compositionengine::Layer overrides + sp<LayerFE> getLayerFE() const { return mLayerFE.promote(); } + const LayerFECompositionState& getFEState() const override { return mFrontEndState; } + LayerFECompositionState& editFEState() override { return mFrontEndState; } + + // compositionengine::impl::Layer overrides + void dumpFEState(std::string& out) const override { mFrontEndState.dump(out); } + + const wp<LayerFE> mLayerFE; + LayerFECompositionState mFrontEndState; + }; + ~LayerTest() override = default; StrictMock<mock::CompositionEngine> mCompositionEngine; sp<LayerFE> mLayerFE = new StrictMock<mock::LayerFE>(); - impl::Layer mLayer{mCompositionEngine, LayerCreationArgs{mLayerFE}}; + Layer mLayer{LayerCreationArgs{mLayerFE}}; }; /* ------------------------------------------------------------------------ diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 94349dea14..5cfec778a4 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -40,7 +40,9 @@ public: std::optional<DisplayId>(uint32_t, uint32_t, ui::PixelFormat*)); MOCK_METHOD1(createLayer, HWC2::Layer*(DisplayId)); MOCK_METHOD2(destroyLayer, void(DisplayId, HWC2::Layer*)); - MOCK_METHOD2(prepare, status_t(DisplayId, const compositionengine::Output&)); + MOCK_METHOD3(getDeviceCompositionChanges, + status_t(DisplayId, bool, + std::optional<android::HWComposer::DeviceRequestedChanges>*)); MOCK_METHOD5(setClientTarget, status_t(DisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&, ui::Dataspace)); @@ -50,8 +52,6 @@ public: MOCK_METHOD2(setColorTransform, status_t(DisplayId, const mat4&)); MOCK_METHOD1(disconnectDisplay, void(DisplayId)); MOCK_CONST_METHOD1(hasDeviceComposition, bool(const std::optional<DisplayId>&)); - MOCK_CONST_METHOD1(hasFlipClientTargetRequest, bool(const std::optional<DisplayId>&)); - MOCK_CONST_METHOD1(hasClientComposition, bool(const std::optional<DisplayId>&)); MOCK_CONST_METHOD1(getPresentFence, sp<Fence>(DisplayId)); MOCK_CONST_METHOD2(getLayerReleaseFence, sp<Fence>(DisplayId, HWC2::Layer*)); MOCK_METHOD3(setOutputBuffer, status_t(DisplayId, const sp<Fence>&, const sp<GraphicBuffer>&)); diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp new file mode 100644 index 0000000000..85b94031c3 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.cpp @@ -0,0 +1,34 @@ +/* + * Copyright 2019 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 "MockPowerAdvisor.h" + +namespace android { +namespace Hwc2 { + +// This will go away once PowerAdvisor is moved into the "backend" library +PowerAdvisor::~PowerAdvisor() = default; + +namespace mock { + +// The Google Mock documentation recommends explicit non-header instantiations +// for better compile time performance. +PowerAdvisor::PowerAdvisor() = default; +PowerAdvisor::~PowerAdvisor() = default; + +} // namespace mock +} // namespace Hwc2 +} // namespace android diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h index ab01c209d1..c5a73f22f9 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/LayerCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h @@ -1,6 +1,6 @@ /* * Copyright 2019 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 @@ -16,26 +16,22 @@ #pragma once -#include <cstdint> -#include <string> +#include <gmock/gmock.h> -#include <compositionengine/LayerFECompositionState.h> -#include <renderengine/Mesh.h> +#include "DisplayHardware/PowerAdvisor.h" namespace android { +namespace Hwc2 { +namespace mock { -namespace compositionengine::impl { - -struct LayerCompositionState { - /* - * State intended to be set by LayerFE::getCompositionState - */ - - LayerFECompositionState frontEnd; +class PowerAdvisor : public android::Hwc2::PowerAdvisor { +public: + PowerAdvisor(); + ~PowerAdvisor() override; - // Debugging - void dump(std::string& result) const; + MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected)); }; -} // namespace compositionengine::impl +} // namespace mock +} // namespace Hwc2 } // namespace android diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index 2060c5aaff..a33878448f 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -15,15 +15,19 @@ */ #include <compositionengine/impl/OutputLayer.h> +#include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/mock/CompositionEngine.h> +#include <compositionengine/mock/DisplayColorProfile.h> #include <compositionengine/mock/Layer.h> #include <compositionengine/mock/LayerFE.h> #include <compositionengine/mock/Output.h> #include <gtest/gtest.h> +#include "FloatRectMatcher.h" #include "MockHWC2.h" #include "MockHWComposer.h" #include "RectMatcher.h" +#include "RegionMatcher.h" namespace android::compositionengine { namespace { @@ -33,8 +37,6 @@ using testing::Return; using testing::ReturnRef; using testing::StrictMock; -constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42}; - constexpr auto TR_IDENT = 0u; constexpr auto TR_FLP_H = HAL_TRANSFORM_FLIP_H; constexpr auto TR_FLP_V = HAL_TRANSFORM_FLIP_V; @@ -44,26 +46,55 @@ constexpr auto TR_ROT_270 = TR_ROT_90 | TR_ROT_180; const std::string kOutputName{"Test Output"}; -class OutputLayerTest : public testing::Test { -public: +MATCHER_P(ColorEq, expected, "") { + *result_listener << "Colors are not equal\n"; + *result_listener << "expected " << expected.r << " " << expected.g << " " << expected.b << " " + << expected.a << "\n"; + *result_listener << "actual " << arg.r << " " << arg.g << " " << arg.b << " " << arg.a << "\n"; + + return expected.r == arg.r && expected.g == arg.g && expected.b == arg.b && expected.a == arg.a; +} + +struct OutputLayerTest : public testing::Test { + struct OutputLayer final : public impl::OutputLayer { + OutputLayer(const compositionengine::Output& output, + std::shared_ptr<compositionengine::Layer> layer, + sp<compositionengine::LayerFE> layerFE) + : mOutput(output), mLayer(layer), mLayerFE(layerFE) {} + ~OutputLayer() override = default; + + // compositionengine::OutputLayer overrides + const compositionengine::Output& getOutput() const override { return mOutput; } + compositionengine::Layer& getLayer() const override { return *mLayer; } + compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; } + const impl::OutputLayerCompositionState& getState() const override { return mState; } + impl::OutputLayerCompositionState& editState() override { return mState; } + + // compositionengine::impl::OutputLayer overrides + void dumpState(std::string& out) const override { mState.dump(out); } + + const compositionengine::Output& mOutput; + std::shared_ptr<compositionengine::Layer> mLayer; + sp<compositionengine::LayerFE> mLayerFE; + impl::OutputLayerCompositionState mState; + }; + OutputLayerTest() { EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE")); EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName)); - EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState)); + EXPECT_CALL(*mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState)); EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState)); } - ~OutputLayerTest() override = default; - compositionengine::mock::Output mOutput; std::shared_ptr<compositionengine::mock::Layer> mLayer{ new StrictMock<compositionengine::mock::Layer>()}; sp<compositionengine::mock::LayerFE> mLayerFE{ new StrictMock<compositionengine::mock::LayerFE>()}; - impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE}; + OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE}; - impl::LayerCompositionState mLayerState; + LayerFECompositionState mLayerFEState; impl::OutputCompositionState mOutputState; }; @@ -74,35 +105,134 @@ public: TEST_F(OutputLayerTest, canInstantiateOutputLayer) {} /* - * OutputLayer::initialize() + * OutputLayer::setHwcLayer() */ -TEST_F(OutputLayerTest, initializingOutputLayerWithoutHwcDoesNothingInteresting) { +TEST_F(OutputLayerTest, settingNullHwcLayerSetsEmptyHwcState) { StrictMock<compositionengine::mock::CompositionEngine> compositionEngine; - mOutputLayer.initialize(compositionEngine, std::nullopt); + mOutputLayer.setHwcLayer(nullptr); EXPECT_FALSE(mOutputLayer.getState().hwc); } -TEST_F(OutputLayerTest, initializingOutputLayerWithHwcDisplayCreatesHwcLayer) { - StrictMock<compositionengine::mock::CompositionEngine> compositionEngine; - StrictMock<android::mock::HWComposer> hwc; - StrictMock<HWC2::mock::Layer> hwcLayer; - - EXPECT_CALL(compositionEngine, getHwComposer()).WillOnce(ReturnRef(hwc)); - EXPECT_CALL(hwc, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer)); +TEST_F(OutputLayerTest, settingHwcLayerSetsHwcState) { + auto hwcLayer = std::make_shared<StrictMock<HWC2::mock::Layer>>(); - mOutputLayer.initialize(compositionEngine, DEFAULT_DISPLAY_ID); + mOutputLayer.setHwcLayer(hwcLayer); const auto& outputLayerState = mOutputLayer.getState(); ASSERT_TRUE(outputLayerState.hwc); const auto& hwcState = *outputLayerState.hwc; - EXPECT_EQ(&hwcLayer, hwcState.hwcLayer.get()); + EXPECT_EQ(hwcLayer, hwcState.hwcLayer); +} - EXPECT_CALL(hwc, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer)); - mOutputLayer.editState().hwc.reset(); +/* + * OutputLayer::calculateOutputSourceCrop() + */ + +struct OutputLayerSourceCropTest : public OutputLayerTest { + OutputLayerSourceCropTest() { + // Set reasonable default values for a simple case. Each test will + // set one specific value to something different. + mLayerFEState.geomUsesSourceCrop = true; + mLayerFEState.geomContentCrop = Rect{0, 0, 1920, 1080}; + mLayerFEState.transparentRegionHint = Region{}; + mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f}; + mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT}; + mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080}; + mLayerFEState.geomBufferTransform = TR_IDENT; + + mOutputState.viewport = Rect{0, 0, 1920, 1080}; + } + + FloatRect calculateOutputSourceCrop() { + mLayerFEState.geomInverseLayerTransform = mLayerFEState.geomLayerTransform.inverse(); + + return mOutputLayer.calculateOutputSourceCrop(); + } +}; + +TEST_F(OutputLayerSourceCropTest, computesEmptyIfSourceCropNotUsed) { + mLayerFEState.geomUsesSourceCrop = false; + + const FloatRect expected{}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, correctForSimpleDefaultCase) { + const FloatRect expected{0.f, 0.f, 1920.f, 1080.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewport) { + mLayerFEState.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f}; + + const FloatRect expected{0.f, 0.f, 1920.f, 1080.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewportRotated) { + mLayerFEState.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f}; + mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080); + + const FloatRect expected{0.f, 0.f, 1080.f, 1080.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, calculateOutputSourceCropWorksWithATransformedBuffer) { + struct Entry { + uint32_t bufferInvDisplay; + uint32_t buffer; + uint32_t display; + FloatRect expected; + }; + // Not an exhaustive list of cases, but hopefully enough. + const std::array<Entry, 12> testData = { + // clang-format off + // inv buffer display expected + /* 0 */ Entry{false, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 1 */ Entry{false, TR_IDENT, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 2 */ Entry{false, TR_IDENT, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 3 */ Entry{false, TR_IDENT, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + + /* 4 */ Entry{true, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 5 */ Entry{true, TR_IDENT, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 6 */ Entry{true, TR_IDENT, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 7 */ Entry{true, TR_IDENT, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + + /* 8 */ Entry{false, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 9 */ Entry{false, TR_ROT_90, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 10 */ Entry{false, TR_ROT_180, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 11 */ Entry{false, TR_ROT_270, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + + // clang-format on + }; + + for (size_t i = 0; i < testData.size(); i++) { + const auto& entry = testData[i]; + + mLayerFEState.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay; + mLayerFEState.geomBufferTransform = entry.buffer; + mOutputState.orientation = entry.display; + + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(entry.expected)) << "entry " << i; + } +} + +TEST_F(OutputLayerSourceCropTest, geomContentCropAffectsCrop) { + mLayerFEState.geomContentCrop = Rect{0, 0, 960, 540}; + + const FloatRect expected{0.f, 0.f, 960.f, 540.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) { + mOutputState.viewport = Rect{0, 0, 960, 540}; + + const FloatRect expected{0.f, 0.f, 960.f, 540.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); } /* @@ -114,20 +244,19 @@ struct OutputLayerDisplayFrameTest : public OutputLayerTest { // Set reasonable default values for a simple case. Each test will // set one specific value to something different. - mLayerState.frontEnd.geomActiveTransparentRegion = Region{}; - mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT}; - mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080}; - mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false; - mLayerState.frontEnd.geomCrop = Rect{0, 0, 1920, 1080}; - mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f}; + mLayerFEState.transparentRegionHint = Region{}; + mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT}; + mLayerFEState.geomBufferSize = Rect{0, 0, 1920, 1080}; + mLayerFEState.geomBufferUsesDisplayInverseTransform = false; + mLayerFEState.geomCrop = Rect{0, 0, 1920, 1080}; + mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f}; mOutputState.viewport = Rect{0, 0, 1920, 1080}; mOutputState.transform = ui::Transform{TR_IDENT}; } Rect calculateOutputDisplayFrame() { - mLayerState.frontEnd.geomInverseLayerTransform = - mLayerState.frontEnd.geomLayerTransform.inverse(); + mLayerFEState.geomInverseLayerTransform = mLayerFEState.geomLayerTransform.inverse(); return mOutputLayer.calculateOutputDisplayFrame(); } @@ -139,32 +268,32 @@ TEST_F(OutputLayerDisplayFrameTest, correctForSimpleDefaultCase) { } TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) { - mLayerState.frontEnd.geomActiveTransparentRegion = Region{Rect{0, 0, 1920, 1080}}; + mLayerFEState.transparentRegionHint = Region{Rect{0, 0, 1920, 1080}}; const Rect expected{0, 0, 0, 0}; EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); } TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) { - mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500}; + mLayerFEState.geomCrop = Rect{100, 200, 300, 500}; const Rect expected{100, 200, 300, 500}; EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); } TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) { - mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500}; - mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080); + mLayerFEState.geomCrop = Rect{100, 200, 300, 500}; + mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080); const Rect expected{1420, 100, 1720, 300}; EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); } TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) { - mLayerState.frontEnd.geomCrop = Rect{}; + mLayerFEState.geomCrop = Rect{}; const Rect expected{0, 0, 1920, 1080}; EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); } -TEST_F(OutputLayerDisplayFrameTest, geomLayerSnapToBoundsAffectsFrame) { - mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f}; +TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) { + mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f}; const Rect expected{0, 0, 960, 540}; EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); } @@ -186,7 +315,7 @@ TEST_F(OutputLayerDisplayFrameTest, outputTransformAffectsDisplayFrame) { */ TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) { - mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false; + mLayerFEState.geomBufferUsesDisplayInverseTransform = false; struct Entry { uint32_t layer; @@ -233,8 +362,66 @@ TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) { for (size_t i = 0; i < testData.size(); i++) { const auto& entry = testData[i]; - mLayerState.frontEnd.geomLayerTransform.set(entry.layer, 1920, 1080); - mLayerState.frontEnd.geomBufferTransform = entry.buffer; + mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080); + mLayerFEState.geomBufferTransform = entry.buffer; + mOutputState.orientation = entry.display; + + auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(); + EXPECT_EQ(entry.expected, actual) << "entry " << i; + } +} + +TEST_F(OutputLayerTest, + calculateOutputRelativeBufferTransformTestWithOfBufferUsesDisplayInverseTransform) { + mLayerFEState.geomBufferUsesDisplayInverseTransform = true; + + struct Entry { + uint32_t layer; + uint32_t buffer; + uint32_t display; + uint32_t expected; + }; + // Not an exhaustive list of cases, but hopefully enough. + const std::array<Entry, 24> testData = { + // clang-format off + // layer buffer display expected + /* 0 */ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_IDENT}, + /* 1 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_90, TR_IDENT}, + /* 2 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_180, TR_IDENT}, + /* 3 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_270, TR_IDENT}, + + /* 4 */ Entry{TR_IDENT, TR_FLP_H, TR_IDENT, TR_FLP_H}, + /* 5 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_90, TR_FLP_H}, + /* 6 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_180, TR_FLP_H}, + /* 7 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_270, TR_FLP_H}, + + /* 8 */ Entry{TR_IDENT, TR_FLP_V, TR_IDENT, TR_FLP_V}, + /* 9 */ Entry{TR_IDENT, TR_ROT_90, TR_ROT_90, TR_ROT_90}, + /* 10 */ Entry{TR_IDENT, TR_ROT_180, TR_ROT_180, TR_ROT_180}, + /* 11 */ Entry{TR_IDENT, TR_ROT_270, TR_ROT_270, TR_ROT_270}, + + /* 12 */ Entry{TR_ROT_90, TR_IDENT, TR_IDENT, TR_IDENT}, + /* 13 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_90, TR_FLP_H}, + /* 14 */ Entry{TR_ROT_90, TR_IDENT, TR_ROT_180, TR_IDENT}, + /* 15 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_270, TR_FLP_H}, + + /* 16 */ Entry{TR_ROT_180, TR_FLP_H, TR_IDENT, TR_FLP_H}, + /* 17 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_90, TR_IDENT}, + /* 18 */ Entry{TR_ROT_180, TR_FLP_H, TR_ROT_180, TR_FLP_H}, + /* 19 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_270, TR_IDENT}, + + /* 20 */ Entry{TR_ROT_270, TR_IDENT, TR_IDENT, TR_IDENT}, + /* 21 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_90, TR_FLP_H}, + /* 22 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_180, TR_FLP_H}, + /* 23 */ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT}, + // clang-format on + }; + + for (size_t i = 0; i < testData.size(); i++) { + const auto& entry = testData[i]; + + mLayerFEState.geomLayerTransform = ui::Transform{entry.layer}; + mLayerFEState.geomBufferTransform = entry.buffer; mOutputState.orientation = entry.display; auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(); @@ -243,6 +430,189 @@ TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) { } /* + * OutputLayer::updateCompositionState() + */ + +struct OutputLayerPartialMockForUpdateCompositionState : public impl::OutputLayer { + OutputLayerPartialMockForUpdateCompositionState(const compositionengine::Output& output, + std::shared_ptr<compositionengine::Layer> layer, + sp<compositionengine::LayerFE> layerFE) + : mOutput(output), mLayer(layer), mLayerFE(layerFE) {} + // Mock everything called by updateCompositionState to simplify testing it. + MOCK_CONST_METHOD0(calculateOutputSourceCrop, FloatRect()); + MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect()); + MOCK_CONST_METHOD0(calculateOutputRelativeBufferTransform, uint32_t()); + + // compositionengine::OutputLayer overrides + const compositionengine::Output& getOutput() const override { return mOutput; } + compositionengine::Layer& getLayer() const override { return *mLayer; } + compositionengine::LayerFE& getLayerFE() const override { return *mLayerFE; } + const impl::OutputLayerCompositionState& getState() const override { return mState; } + impl::OutputLayerCompositionState& editState() override { return mState; } + + // These need implementations though are not expected to be called. + MOCK_CONST_METHOD1(dumpState, void(std::string&)); + + const compositionengine::Output& mOutput; + std::shared_ptr<compositionengine::Layer> mLayer; + sp<compositionengine::LayerFE> mLayerFE; + impl::OutputLayerCompositionState mState; +}; + +struct OutputLayerUpdateCompositionStateTest : public OutputLayerTest { +public: + OutputLayerUpdateCompositionStateTest() { + EXPECT_CALL(*mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState)); + EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState)); + EXPECT_CALL(mOutput, getDisplayColorProfile()) + .WillRepeatedly(Return(&mDisplayColorProfile)); + EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(true)); + } + + ~OutputLayerUpdateCompositionStateTest() = default; + + void setupGeometryChildCallValues() { + EXPECT_CALL(mOutputLayer, calculateOutputSourceCrop()).WillOnce(Return(kSourceCrop)); + EXPECT_CALL(mOutputLayer, calculateOutputDisplayFrame()).WillOnce(Return(kDisplayFrame)); + EXPECT_CALL(mOutputLayer, calculateOutputRelativeBufferTransform()) + .WillOnce(Return(mBufferTransform)); + } + + void validateComputedGeometryState() { + const auto& state = mOutputLayer.getState(); + EXPECT_EQ(kSourceCrop, state.sourceCrop); + EXPECT_EQ(kDisplayFrame, state.displayFrame); + EXPECT_EQ(static_cast<Hwc2::Transform>(mBufferTransform), state.bufferTransform); + } + + const FloatRect kSourceCrop{1.f, 2.f, 3.f, 4.f}; + const Rect kDisplayFrame{11, 12, 13, 14}; + uint32_t mBufferTransform{21}; + + using OutputLayer = OutputLayerPartialMockForUpdateCompositionState; + StrictMock<OutputLayer> mOutputLayer{mOutput, mLayer, mLayerFE}; + StrictMock<mock::DisplayColorProfile> mDisplayColorProfile; +}; + +TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) { + mLayerFEState.isSecure = true; + mOutputState.isSecure = true; + mOutputLayer.editState().forceClientComposition = true; + + setupGeometryChildCallValues(); + + mOutputLayer.updateCompositionState(true, false); + + validateComputedGeometryState(); + + EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, + alsoSetsForceCompositionIfSecureLayerOnNonsecureOutput) { + mLayerFEState.isSecure = true; + mOutputState.isSecure = false; + + setupGeometryChildCallValues(); + + mOutputLayer.updateCompositionState(true, false); + + validateComputedGeometryState(); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, + alsoSetsForceCompositionIfUnsupportedBufferTransform) { + mLayerFEState.isSecure = true; + mOutputState.isSecure = true; + + mBufferTransform = ui::Transform::ROT_INVALID; + + setupGeometryChildCallValues(); + + mOutputLayer.updateCompositionState(true, false); + + validateComputedGeometryState(); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, setsOutputLayerColorspaceCorrectly) { + mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3; + mOutputState.targetDataspace = ui::Dataspace::V0_SCRGB; + + // If the layer is not colorspace agnostic, the output layer dataspace + // should use the layers requested colorspace. + mLayerFEState.isColorspaceAgnostic = false; + + mOutputLayer.updateCompositionState(false, false); + + EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutputLayer.getState().dataspace); + + // If the layer is colorspace agnostic, the output layer dataspace + // should use the colorspace chosen for the whole output. + mLayerFEState.isColorspaceAgnostic = true; + + mOutputLayer.updateCompositionState(false, false); + + EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) { + mOutputLayer.editState().forceClientComposition = false; + + mOutputLayer.updateCompositionState(false, false); + + EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, + doesNotClearForceClientCompositionIfNotDoingGeometry) { + mOutputLayer.editState().forceClientComposition = true; + + mOutputLayer.updateCompositionState(false, false); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, clientCompositionForcedFromFrontEndFlagAtAnyTime) { + mLayerFEState.forceClientComposition = true; + mOutputLayer.editState().forceClientComposition = false; + + mOutputLayer.updateCompositionState(false, false); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, + clientCompositionForcedFromUnsupportedDataspaceAtAnyTime) { + mOutputLayer.editState().forceClientComposition = false; + EXPECT_CALL(mDisplayColorProfile, isDataspaceSupported(_)).WillRepeatedly(Return(false)); + + mOutputLayer.updateCompositionState(false, false); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, clientCompositionForcedFromArgumentFlag) { + mLayerFEState.forceClientComposition = false; + mOutputLayer.editState().forceClientComposition = false; + + mOutputLayer.updateCompositionState(false, true); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); + + mOutputLayer.editState().forceClientComposition = false; + + setupGeometryChildCallValues(); + + mOutputLayer.updateCompositionState(true, true); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); +} + +/* * OutputLayer::writeStateToHWC() */ @@ -256,8 +626,19 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { static constexpr float kAlpha = 51.f; static constexpr uint32_t kType = 61u; static constexpr uint32_t kAppId = 62u; + static constexpr ui::Dataspace kDataspace = static_cast<ui::Dataspace>(71); + static constexpr int kSupportedPerFrameMetadata = 101; + static constexpr int kExpectedHwcSlot = 0; + static const half4 kColor; static const Rect kDisplayFrame; + static const Region kOutputSpaceVisibleRegion; + static const mat4 kColorTransform; + static const Region kSurfaceDamage; + static const HdrMetadata kHdrMetadata; + static native_handle_t* kSidebandStreamHandle; + static const sp<GraphicBuffer> kBuffer; + static const sp<Fence> kFence; OutputLayerWriteStateToHWCTest() { auto& outputLayerState = mOutputLayer.editState(); @@ -267,13 +648,31 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { outputLayerState.sourceCrop = kSourceCrop; outputLayerState.z = kZOrder; outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform); - - mLayerState.frontEnd.blendMode = kBlendMode; - mLayerState.frontEnd.alpha = kAlpha; - mLayerState.frontEnd.type = kType; - mLayerState.frontEnd.appId = kAppId; + outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion; + outputLayerState.dataspace = kDataspace; + + mLayerFEState.blendMode = kBlendMode; + mLayerFEState.alpha = kAlpha; + mLayerFEState.type = kType; + mLayerFEState.appId = kAppId; + mLayerFEState.colorTransform = kColorTransform; + mLayerFEState.color = kColor; + mLayerFEState.surfaceDamage = kSurfaceDamage; + mLayerFEState.hdrMetadata = kHdrMetadata; + mLayerFEState.sidebandStream = NativeHandle::create(kSidebandStreamHandle, false); + mLayerFEState.buffer = kBuffer; + mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT; + mLayerFEState.acquireFence = kFence; + + EXPECT_CALL(mOutput, getDisplayColorProfile()) + .WillRepeatedly(Return(&mDisplayColorProfile)); + EXPECT_CALL(mDisplayColorProfile, getSupportedPerFrameMetadata()) + .WillRepeatedly(Return(kSupportedPerFrameMetadata)); } + // Some tests may need to simulate unsupported HWC calls + enum class SimulateUnsupported { None, ColorTransform }; + void expectGeometryCommonCalls() { EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError)); EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError)); @@ -287,10 +686,63 @@ struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError)); } + void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None) { + EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(kOutputSpaceVisibleRegion))) + .WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setDataspace(kDataspace)).WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform)) + .WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform + ? HWC2::Error::Unsupported + : HWC2::Error::None)); + EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(kSurfaceDamage))) + .WillOnce(Return(kError)); + } + + void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) { + EXPECT_CALL(*mHwcLayer, setCompositionType(static_cast<HWC2::Composition>(compositionType))) + .WillOnce(Return(kError)); + } + + void expectNoSetCompositionTypeCall() { + EXPECT_CALL(*mHwcLayer, setCompositionType(_)).Times(0); + } + + void expectSetColorCall() { + hwc_color_t color = {static_cast<uint8_t>(std::round(kColor.r * 255)), + static_cast<uint8_t>(std::round(kColor.g * 255)), + static_cast<uint8_t>(std::round(kColor.b * 255)), 255}; + + EXPECT_CALL(*mHwcLayer, setColor(ColorEq(color))).WillOnce(Return(kError)); + } + + void expectSetSidebandHandleCall() { + EXPECT_CALL(*mHwcLayer, setSidebandStream(kSidebandStreamHandle)); + } + + void expectSetHdrMetadataAndBufferCalls() { + EXPECT_CALL(*mHwcLayer, setPerFrameMetadata(kSupportedPerFrameMetadata, kHdrMetadata)); + EXPECT_CALL(*mHwcLayer, setBuffer(kExpectedHwcSlot, kBuffer, kFence)); + } + std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()}; + StrictMock<mock::DisplayColorProfile> mDisplayColorProfile; }; +const half4 OutputLayerWriteStateToHWCTest::kColor{81.f / 255.f, 82.f / 255.f, 83.f / 255.f, + 84.f / 255.f}; const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044}; +const Region OutputLayerWriteStateToHWCTest::kOutputSpaceVisibleRegion{ + Rect{1005, 1006, 1007, 1008}}; +const mat4 OutputLayerWriteStateToHWCTest::kColorTransform{ + 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, + 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, +}; +const Region OutputLayerWriteStateToHWCTest::kSurfaceDamage{Rect{1025, 1026, 1027, 1028}}; +const HdrMetadata OutputLayerWriteStateToHWCTest::kHdrMetadata{{/* LightFlattenable */}, 1029}; +native_handle_t* OutputLayerWriteStateToHWCTest::kSidebandStreamHandle = + reinterpret_cast<native_handle_t*>(1031); +const sp<GraphicBuffer> OutputLayerWriteStateToHWCTest::kBuffer; +const sp<Fence> OutputLayerWriteStateToHWCTest::kFence; TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) { mOutputLayer.editState().hwc.reset(); @@ -304,11 +756,280 @@ TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) { mOutputLayer.writeStateToHWC(true); } -TEST_F(OutputLayerWriteStateToHWCTest, canSetsAllState) { +TEST_F(OutputLayerWriteStateToHWCTest, canSetAllState) { expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + + expectNoSetCompositionTypeCall(); mOutputLayer.writeStateToHWC(true); } +TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) { + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR; + + expectPerFrameCommonCalls(); + expectSetColorCall(); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR); + + mOutputLayer.writeStateToHWC(false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) { + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND; + + expectPerFrameCommonCalls(); + expectSetSidebandHandleCall(); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND); + + mOutputLayer.writeStateToHWC(false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) { + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::CURSOR; + + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR); + + mOutputLayer.writeStateToHWC(false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) { + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE; + + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE); + + mOutputLayer.writeStateToHWC(false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) { + (*mOutputLayer.editState().hwc).hwcCompositionType = + Hwc2::IComposerClient::Composition::SOLID_COLOR; + + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR; + + expectPerFrameCommonCalls(); + expectSetColorCall(); + expectNoSetCompositionTypeCall(); + + mOutputLayer.writeStateToHWC(false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) { + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR; + + expectPerFrameCommonCalls(SimulateUnsupported::ColorTransform); + expectSetColorCall(); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT); + + mOutputLayer.writeStateToHWC(false); +} + +TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) { + mOutputLayer.editState().forceClientComposition = true; + + mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR; + + expectPerFrameCommonCalls(); + expectSetColorCall(); + expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT); + + mOutputLayer.writeStateToHWC(false); +} + +/* + * OutputLayer::writeCursorPositionToHWC() + */ + +struct OutputLayerWriteCursorPositionToHWCTest : public OutputLayerTest { + static constexpr int kDefaultTransform = TR_IDENT; + static constexpr HWC2::Error kDefaultError = HWC2::Error::Unsupported; + + static const Rect kDefaultDisplayViewport; + static const Rect kDefaultCursorFrame; + + OutputLayerWriteCursorPositionToHWCTest() { + auto& outputLayerState = mOutputLayer.editState(); + outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer); + + mLayerFEState.cursorFrame = kDefaultCursorFrame; + + mOutputState.viewport = kDefaultDisplayViewport; + mOutputState.transform = ui::Transform{kDefaultTransform}; + } + + std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()}; +}; + +const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultDisplayViewport{0, 0, 1920, 1080}; +const Rect OutputLayerWriteCursorPositionToHWCTest::kDefaultCursorFrame{1, 2, 3, 4}; + +TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCHandlesNoHwcState) { + mOutputLayer.editState().hwc.reset(); + + mOutputLayer.writeCursorPositionToHWC(); +} + +TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCWritesStateToHWC) { + EXPECT_CALL(*mHwcLayer, setCursorPosition(1, 2)).WillOnce(Return(kDefaultError)); + + mOutputLayer.writeCursorPositionToHWC(); +} + +TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCIntersectedWithViewport) { + mLayerFEState.cursorFrame = Rect{3000, 3000, 3016, 3016}; + + EXPECT_CALL(*mHwcLayer, setCursorPosition(1920, 1080)).WillOnce(Return(kDefaultError)); + + mOutputLayer.writeCursorPositionToHWC(); +} + +TEST_F(OutputLayerWriteCursorPositionToHWCTest, writeCursorPositionToHWCRotatedByTransform) { + mOutputState.transform = ui::Transform{TR_ROT_90}; + + EXPECT_CALL(*mHwcLayer, setCursorPosition(-4, 1)).WillOnce(Return(kDefaultError)); + + mOutputLayer.writeCursorPositionToHWC(); +} + +/* + * OutputLayer::getHwcLayer() + */ + +TEST_F(OutputLayerTest, getHwcLayerHandlesNoHwcState) { + mOutputLayer.editState().hwc.reset(); + + EXPECT_TRUE(mOutputLayer.getHwcLayer() == nullptr); +} + +TEST_F(OutputLayerTest, getHwcLayerHandlesNoHwcLayer) { + mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr}; + + EXPECT_TRUE(mOutputLayer.getHwcLayer() == nullptr); +} + +TEST_F(OutputLayerTest, getHwcLayerReturnsHwcLayer) { + auto hwcLayer = std::make_shared<StrictMock<HWC2::mock::Layer>>(); + mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{hwcLayer}; + + EXPECT_EQ(hwcLayer.get(), mOutputLayer.getHwcLayer()); +} + +/* + * OutputLayer::requiresClientComposition() + */ + +TEST_F(OutputLayerTest, requiresClientCompositionReturnsTrueIfNoHWC2State) { + mOutputLayer.editState().hwc.reset(); + + EXPECT_TRUE(mOutputLayer.requiresClientComposition()); +} + +TEST_F(OutputLayerTest, requiresClientCompositionReturnsTrueIfSetToClientComposition) { + mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr}; + mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT; + + EXPECT_TRUE(mOutputLayer.requiresClientComposition()); +} + +TEST_F(OutputLayerTest, requiresClientCompositionReturnsFalseIfSetToDeviceComposition) { + mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr}; + mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE; + + EXPECT_FALSE(mOutputLayer.requiresClientComposition()); +} + +/* + * OutputLayer::isHardwareCursor() + */ + +TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfNoHWC2State) { + mOutputLayer.editState().hwc.reset(); + + EXPECT_FALSE(mOutputLayer.isHardwareCursor()); +} + +TEST_F(OutputLayerTest, isHardwareCursorReturnsTrueIfSetToCursorComposition) { + mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr}; + mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CURSOR; + + EXPECT_TRUE(mOutputLayer.isHardwareCursor()); +} + +TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfSetToDeviceComposition) { + mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr}; + mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE; + + EXPECT_FALSE(mOutputLayer.isHardwareCursor()); +} + +/* + * OutputLayer::applyDeviceCompositionTypeChange() + */ + +TEST_F(OutputLayerTest, applyDeviceCompositionTypeChangeSetsNewType) { + mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr}; + mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE; + + mOutputLayer.applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT); + + ASSERT_TRUE(mOutputLayer.getState().hwc); + EXPECT_EQ(Hwc2::IComposerClient::Composition::CLIENT, + mOutputLayer.getState().hwc->hwcCompositionType); +} + +/* + * OutputLayer::prepareForDeviceLayerRequests() + */ + +TEST_F(OutputLayerTest, prepareForDeviceLayerRequestsResetsRequestState) { + mOutputLayer.editState().clearClientTarget = true; + + mOutputLayer.prepareForDeviceLayerRequests(); + + EXPECT_FALSE(mOutputLayer.getState().clearClientTarget); +} + +/* + * OutputLayer::applyDeviceLayerRequest() + */ + +TEST_F(OutputLayerTest, applyDeviceLayerRequestHandlesClearClientTarget) { + mOutputLayer.editState().clearClientTarget = false; + + mOutputLayer.applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest::CLEAR_CLIENT_TARGET); + + EXPECT_TRUE(mOutputLayer.getState().clearClientTarget); +} + +TEST_F(OutputLayerTest, applyDeviceLayerRequestHandlesUnknownRequest) { + mOutputLayer.editState().clearClientTarget = false; + + mOutputLayer.applyDeviceLayerRequest(static_cast<Hwc2::IComposerClient::LayerRequest>(0)); + + EXPECT_FALSE(mOutputLayer.getState().clearClientTarget); +} + +/* + * OutputLayer::needsFiltering() + */ + +TEST_F(OutputLayerTest, needsFilteringReturnsFalseIfDisplaySizeSameAsSourceSize) { + mOutputLayer.editState().displayFrame = Rect(100, 100, 200, 200); + mOutputLayer.editState().sourceCrop = FloatRect{0.f, 0.f, 100.f, 100.f}; + + EXPECT_FALSE(mOutputLayer.needsFiltering()); +} + +TEST_F(OutputLayerTest, needsFilteringReturnsTrueIfDisplaySizeDifferentFromSourceSize) { + mOutputLayer.editState().displayFrame = Rect(100, 100, 200, 200); + mOutputLayer.editState().sourceCrop = FloatRect{0.f, 0.f, 100.1f, 100.1f}; + + EXPECT_TRUE(mOutputLayer.needsFiltering()); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index fee0c11e25..95ae888767 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -16,7 +16,10 @@ #include <cmath> +#include <compositionengine/LayerFECompositionState.h> #include <compositionengine/impl/Output.h> +#include <compositionengine/impl/OutputCompositionState.h> +#include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/mock/CompositionEngine.h> #include <compositionengine/mock/DisplayColorProfile.h> #include <compositionengine/mock/Layer.h> @@ -24,6 +27,7 @@ #include <compositionengine/mock/OutputLayer.h> #include <compositionengine/mock/RenderSurface.h> #include <gtest/gtest.h> +#include <renderengine/mock/RenderEngine.h> #include <ui/Rect.h> #include <ui/Region.h> @@ -33,32 +37,49 @@ namespace android::compositionengine { namespace { +using testing::_; using testing::Return; using testing::ReturnRef; using testing::StrictMock; -class OutputTest : public testing::Test { -public: +constexpr auto TR_IDENT = 0u; +constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90; + +const mat4 kIdentity; +const mat4 kNonIdentityHalf = mat4() * 0.5; +const mat4 kNonIdentityQuarter = mat4() * 0.25; + +struct OutputTest : public testing::Test { + class Output : public impl::Output { + public: + using impl::Output::injectOutputLayerForTest; + virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0; + }; + + static std::shared_ptr<Output> createOutput( + const compositionengine::CompositionEngine& compositionEngine) { + return impl::createOutputTemplated<Output>(compositionEngine); + } + OutputTest() { - mOutput.setDisplayColorProfileForTest( + mOutput->setDisplayColorProfileForTest( std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile)); - mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); + mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); - mOutput.editState().bounds = kDefaultDisplaySize; + mOutput->editState().bounds = kDefaultDisplaySize; } - ~OutputTest() override = default; static const Rect kDefaultDisplaySize; StrictMock<mock::CompositionEngine> mCompositionEngine; mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>(); mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>(); - impl::Output mOutput{mCompositionEngine}; + std::shared_ptr<Output> mOutput = createOutput(mCompositionEngine); }; const Rect OutputTest::kDefaultDisplaySize{100, 200}; -/* ------------------------------------------------------------------------ +/* * Basic construction */ @@ -67,48 +88,48 @@ TEST_F(OutputTest, canInstantiateOutput) { EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true)); EXPECT_CALL(*mRenderSurface, isValid()).WillOnce(Return(true)); - EXPECT_TRUE(mOutput.isValid()); + EXPECT_TRUE(mOutput->isValid()); // If we take away the required components, it is no longer valid. - mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>()); + mOutput->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>()); EXPECT_CALL(*mDisplayColorProfile, isValid()).WillOnce(Return(true)); - EXPECT_FALSE(mOutput.isValid()); + EXPECT_FALSE(mOutput->isValid()); } -/* ------------------------------------------------------------------------ +/* * Output::setCompositionEnabled() */ TEST_F(OutputTest, setCompositionEnabledDoesNothingIfAlreadyEnabled) { - mOutput.editState().isEnabled = true; + mOutput->editState().isEnabled = true; - mOutput.setCompositionEnabled(true); + mOutput->setCompositionEnabled(true); - EXPECT_TRUE(mOutput.getState().isEnabled); - EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region())); + EXPECT_TRUE(mOutput->getState().isEnabled); + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region())); } TEST_F(OutputTest, setCompositionEnabledSetsEnabledAndDirtiesEntireOutput) { - mOutput.editState().isEnabled = false; + mOutput->editState().isEnabled = false; - mOutput.setCompositionEnabled(true); + mOutput->setCompositionEnabled(true); - EXPECT_TRUE(mOutput.getState().isEnabled); - EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); + EXPECT_TRUE(mOutput->getState().isEnabled); + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } TEST_F(OutputTest, setCompositionEnabledSetsDisabledAndDirtiesEntireOutput) { - mOutput.editState().isEnabled = true; + mOutput->editState().isEnabled = true; - mOutput.setCompositionEnabled(false); + mOutput->setCompositionEnabled(false); - EXPECT_FALSE(mOutput.getState().isEnabled); - EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); + EXPECT_FALSE(mOutput->getState().isEnabled); + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } -/* ------------------------------------------------------------------------ +/* * Output::setProjection() */ @@ -120,17 +141,17 @@ TEST_F(OutputTest, setProjectionTriviallyWorks) { const Rect scissor{9, 10, 11, 12}; const bool needsFiltering = true; - mOutput.setProjection(transform, orientation, frame, viewport, scissor, needsFiltering); + mOutput->setProjection(transform, orientation, frame, viewport, scissor, needsFiltering); - EXPECT_THAT(mOutput.getState().transform, TransformEq(transform)); - EXPECT_EQ(orientation, mOutput.getState().orientation); - EXPECT_EQ(frame, mOutput.getState().frame); - EXPECT_EQ(viewport, mOutput.getState().viewport); - EXPECT_EQ(scissor, mOutput.getState().scissor); - EXPECT_EQ(needsFiltering, mOutput.getState().needsFiltering); + EXPECT_THAT(mOutput->getState().transform, TransformEq(transform)); + EXPECT_EQ(orientation, mOutput->getState().orientation); + EXPECT_EQ(frame, mOutput->getState().frame); + EXPECT_EQ(viewport, mOutput->getState().viewport); + EXPECT_EQ(scissor, mOutput->getState().scissor); + EXPECT_EQ(needsFiltering, mOutput->getState().needsFiltering); } -/* ------------------------------------------------------------------------ +/* * Output::setBounds() */ @@ -140,94 +161,158 @@ TEST_F(OutputTest, setBoundsSetsSizeAndDirtiesEntireOutput) { EXPECT_CALL(*mRenderSurface, setDisplaySize(displaySize)).Times(1); EXPECT_CALL(*mRenderSurface, getSize()).WillOnce(ReturnRef(displaySize)); - mOutput.setBounds(displaySize); + mOutput->setBounds(displaySize); - EXPECT_EQ(Rect(displaySize), mOutput.getState().bounds); + EXPECT_EQ(Rect(displaySize), mOutput->getState().bounds); - EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(Rect(displaySize)))); + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(Rect(displaySize)))); } -/* ------------------------------------------------------------------------ +/* * Output::setLayerStackFilter() */ TEST_F(OutputTest, setLayerStackFilterSetsFilterAndDirtiesEntireOutput) { const uint32_t layerStack = 123u; - mOutput.setLayerStackFilter(layerStack, true); + mOutput->setLayerStackFilter(layerStack, true); - EXPECT_TRUE(mOutput.getState().layerStackInternal); - EXPECT_EQ(layerStack, mOutput.getState().layerStackId); + EXPECT_TRUE(mOutput->getState().layerStackInternal); + EXPECT_EQ(layerStack, mOutput->getState().layerStackId); - EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } -/* ------------------------------------------------------------------------ +/* * Output::setColorTransform */ -TEST_F(OutputTest, setColorTransformSetsTransform) { - // Identity matrix sets an identity state value - const mat4 identity; +TEST_F(OutputTest, setColorTransformWithNoChangeFlaggedSkipsUpdates) { + mOutput->editState().colorTransformMatrix = kIdentity; + + // If no colorTransformMatrix is set the update should be skipped. + CompositionRefreshArgs refreshArgs; + refreshArgs.colorTransformMatrix = std::nullopt; - mOutput.setColorTransform(identity); + mOutput->setColorTransform(refreshArgs); - EXPECT_EQ(HAL_COLOR_TRANSFORM_IDENTITY, mOutput.getState().colorTransform); - EXPECT_EQ(identity, mOutput.getState().colorTransformMat); + // The internal state should be unchanged + EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix); - // Since identity is the default, the dirty region should be unchanged (empty) - EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region())); + // No dirty region should be set + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region())); +} + +TEST_F(OutputTest, setColorTransformWithNoActualChangeSkipsUpdates) { + mOutput->editState().colorTransformMatrix = kIdentity; - // Non-identity matrix sets a non-identity state value - const mat4 nonIdentityHalf = mat4() * 0.5; + // Attempting to set the same colorTransformMatrix that is already set should + // also skip the update. + CompositionRefreshArgs refreshArgs; + refreshArgs.colorTransformMatrix = kIdentity; - mOutput.setColorTransform(nonIdentityHalf); + mOutput->setColorTransform(refreshArgs); - EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform); - EXPECT_EQ(nonIdentityHalf, mOutput.getState().colorTransformMat); + // The internal state should be unchanged + EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix); - // Since this is a state change, the entire output should now be dirty. - EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); + // No dirty region should be set + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region())); +} - // Non-identity matrix sets a non-identity state value - const mat4 nonIdentityQuarter = mat4() * 0.25; +TEST_F(OutputTest, setColorTransformPerformsUpdateToIdentity) { + mOutput->editState().colorTransformMatrix = kNonIdentityHalf; - mOutput.setColorTransform(nonIdentityQuarter); + // Setting a different colorTransformMatrix should perform the update. + CompositionRefreshArgs refreshArgs; + refreshArgs.colorTransformMatrix = kIdentity; - EXPECT_EQ(HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX, mOutput.getState().colorTransform); - EXPECT_EQ(nonIdentityQuarter, mOutput.getState().colorTransformMat); + mOutput->setColorTransform(refreshArgs); - // Since this is a state change, the entire output should now be dirty. - EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); + // The internal state should have been updated + EXPECT_EQ(kIdentity, mOutput->getState().colorTransformMatrix); + + // The dirtyRegion should be set to the full display size + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } -/* ------------------------------------------------------------------------ +TEST_F(OutputTest, setColorTransformPerformsUpdateForIdentityToHalf) { + mOutput->editState().colorTransformMatrix = kIdentity; + + // Setting a different colorTransformMatrix should perform the update. + CompositionRefreshArgs refreshArgs; + refreshArgs.colorTransformMatrix = kNonIdentityHalf; + + mOutput->setColorTransform(refreshArgs); + + // The internal state should have been updated + EXPECT_EQ(kNonIdentityHalf, mOutput->getState().colorTransformMatrix); + + // The dirtyRegion should be set to the full display size + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); +} + +TEST_F(OutputTest, setColorTransformPerformsUpdateForHalfToQuarter) { + mOutput->editState().colorTransformMatrix = kNonIdentityHalf; + + // Setting a different colorTransformMatrix should perform the update. + CompositionRefreshArgs refreshArgs; + refreshArgs.colorTransformMatrix = kNonIdentityQuarter; + + mOutput->setColorTransform(refreshArgs); + + // The internal state should have been updated + EXPECT_EQ(kNonIdentityQuarter, mOutput->getState().colorTransformMatrix); + + // The dirtyRegion should be set to the full display size + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); +} + +/* * Output::setColorMode */ TEST_F(OutputTest, setColorModeSetsStateAndDirtiesOutputIfChanged) { + using ColorProfile = Output::ColorProfile; + + EXPECT_CALL(*mDisplayColorProfile, + getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, + ui::Dataspace::UNKNOWN)) + .WillOnce(Return(ui::Dataspace::UNKNOWN)); EXPECT_CALL(*mRenderSurface, setBufferDataspace(ui::Dataspace::DISPLAY_P3)).Times(1); - mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, - ui::RenderIntent::TONE_MAP_COLORIMETRIC); + mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, + ui::RenderIntent::TONE_MAP_COLORIMETRIC, + ui::Dataspace::UNKNOWN}); + + EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput->getState().colorMode); + EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput->getState().dataspace); + EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput->getState().renderIntent); + EXPECT_EQ(ui::Dataspace::UNKNOWN, mOutput->getState().targetDataspace); - EXPECT_EQ(ui::ColorMode::DISPLAY_P3, mOutput.getState().colorMode); - EXPECT_EQ(ui::Dataspace::DISPLAY_P3, mOutput.getState().dataspace); - EXPECT_EQ(ui::RenderIntent::TONE_MAP_COLORIMETRIC, mOutput.getState().renderIntent); - EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize))); } TEST_F(OutputTest, setColorModeDoesNothingIfNoChange) { - mOutput.editState().colorMode = ui::ColorMode::DISPLAY_P3; - mOutput.editState().dataspace = ui::Dataspace::DISPLAY_P3; - mOutput.editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC; + using ColorProfile = Output::ColorProfile; + + EXPECT_CALL(*mDisplayColorProfile, + getTargetDataspace(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, + ui::Dataspace::UNKNOWN)) + .WillOnce(Return(ui::Dataspace::UNKNOWN)); - mOutput.setColorMode(ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, - ui::RenderIntent::TONE_MAP_COLORIMETRIC); + mOutput->editState().colorMode = ui::ColorMode::DISPLAY_P3; + mOutput->editState().dataspace = ui::Dataspace::DISPLAY_P3; + mOutput->editState().renderIntent = ui::RenderIntent::TONE_MAP_COLORIMETRIC; + mOutput->editState().targetDataspace = ui::Dataspace::UNKNOWN; - EXPECT_THAT(mOutput.getState().dirtyRegion, RegionEq(Region())); + mOutput->setColorProfile(ColorProfile{ui::ColorMode::DISPLAY_P3, ui::Dataspace::DISPLAY_P3, + ui::RenderIntent::TONE_MAP_COLORIMETRIC, + ui::Dataspace::UNKNOWN}); + + EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region())); } -/* ------------------------------------------------------------------------ +/* * Output::setRenderSurface() */ @@ -237,22 +322,22 @@ TEST_F(OutputTest, setRenderSurfaceResetsBounds) { mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>(); EXPECT_CALL(*renderSurface, getSize()).WillOnce(ReturnRef(newDisplaySize)); - mOutput.setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface)); + mOutput->setRenderSurface(std::unique_ptr<RenderSurface>(renderSurface)); - EXPECT_EQ(Rect(newDisplaySize), mOutput.getState().bounds); + EXPECT_EQ(Rect(newDisplaySize), mOutput->getState().bounds); } -/* ------------------------------------------------------------------------ +/* * Output::getDirtyRegion() */ TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) { const Rect viewport{100, 200}; - mOutput.editState().viewport = viewport; - mOutput.editState().dirtyRegion.set(50, 300); + mOutput->editState().viewport = viewport; + mOutput->editState().dirtyRegion.set(50, 300); { - Region result = mOutput.getDirtyRegion(true); + Region result = mOutput->getDirtyRegion(true); EXPECT_THAT(result, RegionEq(Region(viewport))); } @@ -260,18 +345,18 @@ TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingTrue) { TEST_F(OutputTest, getDirtyRegionWithRepaintEverythingFalse) { const Rect viewport{100, 200}; - mOutput.editState().viewport = viewport; - mOutput.editState().dirtyRegion.set(50, 300); + mOutput->editState().viewport = viewport; + mOutput->editState().dirtyRegion.set(50, 300); { - Region result = mOutput.getDirtyRegion(false); + Region result = mOutput->getDirtyRegion(false); // The dirtyRegion should be clipped to the display bounds. EXPECT_THAT(result, RegionEq(Region(Rect(50, 200)))); } } -/* ------------------------------------------------------------------------ +/* * Output::belongsInOutput() */ @@ -280,25 +365,94 @@ TEST_F(OutputTest, belongsInOutputFiltersAsExpected) { const uint32_t layerStack2 = 456u; // If the output accepts layerStack1 and internal-only layers.... - mOutput.setLayerStackFilter(layerStack1, true); + mOutput->setLayerStackFilter(layerStack1, true); + + // A layer with no layerStack does not belong to it, internal-only or not. + EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, false)); + EXPECT_FALSE(mOutput->belongsInOutput(std::nullopt, true)); // Any layer with layerStack1 belongs to it, internal-only or not. - EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false)); - EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, true)); - EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true)); - EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false)); + EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false)); + EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, true)); + EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true)); + EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false)); // If the output accepts layerStack21 but not internal-only layers... - mOutput.setLayerStackFilter(layerStack1, false); + mOutput->setLayerStackFilter(layerStack1, false); // Only non-internal layers with layerStack1 belong to it. - EXPECT_TRUE(mOutput.belongsInOutput(layerStack1, false)); - EXPECT_FALSE(mOutput.belongsInOutput(layerStack1, true)); - EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, true)); - EXPECT_FALSE(mOutput.belongsInOutput(layerStack2, false)); + EXPECT_TRUE(mOutput->belongsInOutput(layerStack1, false)); + EXPECT_FALSE(mOutput->belongsInOutput(layerStack1, true)); + EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, true)); + EXPECT_FALSE(mOutput->belongsInOutput(layerStack2, false)); } -/* ------------------------------------------------------------------------ +TEST_F(OutputTest, belongsInOutputFiltersLayersAsExpected) { + StrictMock<mock::Layer> layer; + LayerFECompositionState layerFEState; + + EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState)); + + const uint32_t layerStack1 = 123u; + const uint32_t layerStack2 = 456u; + + // If the output accepts layerStack1 and internal-only layers.... + mOutput->setLayerStackFilter(layerStack1, true); + + // A null layer pointer does not belong to the output + EXPECT_FALSE(mOutput->belongsInOutput(nullptr)); + + // A layer with no layerStack does not belong to it, internal-only or not. + layerFEState.layerStackId = std::nullopt; + layerFEState.internalOnly = false; + EXPECT_FALSE(mOutput->belongsInOutput(&layer)); + + layerFEState.layerStackId = std::nullopt; + layerFEState.internalOnly = true; + EXPECT_FALSE(mOutput->belongsInOutput(&layer)); + + // Any layer with layerStack1 belongs to it, internal-only or not. + layerFEState.layerStackId = layerStack1; + layerFEState.internalOnly = false; + EXPECT_TRUE(mOutput->belongsInOutput(&layer)); + + layerFEState.layerStackId = layerStack1; + layerFEState.internalOnly = true; + EXPECT_TRUE(mOutput->belongsInOutput(&layer)); + + layerFEState.layerStackId = layerStack2; + layerFEState.internalOnly = true; + EXPECT_FALSE(mOutput->belongsInOutput(&layer)); + + layerFEState.layerStackId = layerStack2; + layerFEState.internalOnly = false; + EXPECT_FALSE(mOutput->belongsInOutput(&layer)); + + // If the output accepts layerStack1 but not internal-only layers... + mOutput->setLayerStackFilter(layerStack1, false); + + // A null layer pointer does not belong to the output + EXPECT_FALSE(mOutput->belongsInOutput(nullptr)); + + // Only non-internal layers with layerStack1 belong to it. + layerFEState.layerStackId = layerStack1; + layerFEState.internalOnly = false; + EXPECT_TRUE(mOutput->belongsInOutput(&layer)); + + layerFEState.layerStackId = layerStack1; + layerFEState.internalOnly = true; + EXPECT_FALSE(mOutput->belongsInOutput(&layer)); + + layerFEState.layerStackId = layerStack2; + layerFEState.internalOnly = true; + EXPECT_FALSE(mOutput->belongsInOutput(&layer)); + + layerFEState.layerStackId = layerStack2; + layerFEState.internalOnly = false; + EXPECT_FALSE(mOutput->belongsInOutput(&layer)); +} + +/* * Output::getOutputLayerForLayer() */ @@ -306,75 +460,489 @@ TEST_F(OutputTest, getOutputLayerForLayerWorks) { mock::OutputLayer* outputLayer1 = new StrictMock<mock::OutputLayer>(); mock::OutputLayer* outputLayer2 = new StrictMock<mock::OutputLayer>(); - Output::OutputLayers outputLayers; - outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer1)); - outputLayers.emplace_back(nullptr); - outputLayers.emplace_back(std::unique_ptr<OutputLayer>(outputLayer2)); - mOutput.setOutputLayersOrderedByZ(std::move(outputLayers)); + mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(outputLayer1)); + mOutput->injectOutputLayerForTest(nullptr); + mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(outputLayer2)); StrictMock<mock::Layer> layer; StrictMock<mock::Layer> otherLayer; // If the input layer matches the first OutputLayer, it will be returned. EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(layer)); - EXPECT_EQ(outputLayer1, mOutput.getOutputLayerForLayer(&layer)); + EXPECT_EQ(outputLayer1, mOutput->getOutputLayerForLayer(&layer)); // If the input layer matches the second OutputLayer, it will be returned. EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer)); EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(layer)); - EXPECT_EQ(outputLayer2, mOutput.getOutputLayerForLayer(&layer)); + EXPECT_EQ(outputLayer2, mOutput->getOutputLayerForLayer(&layer)); // If the input layer does not match an output layer, null will be returned. EXPECT_CALL(*outputLayer1, getLayer()).WillOnce(ReturnRef(otherLayer)); EXPECT_CALL(*outputLayer2, getLayer()).WillOnce(ReturnRef(otherLayer)); - EXPECT_EQ(nullptr, mOutput.getOutputLayerForLayer(&layer)); + EXPECT_EQ(nullptr, mOutput->getOutputLayerForLayer(&layer)); } -/* ------------------------------------------------------------------------ - * Output::getOrCreateOutputLayer() +/* + * Output::prepareFrame() */ -TEST_F(OutputTest, getOrCreateOutputLayerWorks) { - mock::OutputLayer* existingOutputLayer = new StrictMock<mock::OutputLayer>(); +struct OutputPrepareFrameTest : public testing::Test { + struct OutputPartialMock : public impl::Output { + // Sets up the helper functions called by prepareFrame to use a mock + // implementations. + MOCK_METHOD0(chooseCompositionStrategy, void()); + + // compositionengine::Output overrides + const OutputCompositionState& getState() const override { return mState; } + OutputCompositionState& editState() override { return mState; } + + // These need implementations though are not expected to be called. + MOCK_CONST_METHOD0(getOutputLayerCount, size_t()); + MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, + compositionengine::OutputLayer*(size_t)); + MOCK_METHOD3(ensureOutputLayer, + compositionengine::OutputLayer*( + std::optional<size_t>, + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&)); + MOCK_METHOD0(finalizePendingOutputLayers, void()); + MOCK_METHOD0(clearOutputLayers, void()); + MOCK_CONST_METHOD1(dumpState, void(std::string&)); + MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&()); + MOCK_METHOD2(injectOutputLayerForTest, + compositionengine::OutputLayer*( + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&)); + MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>)); + + impl::OutputCompositionState mState; + }; + + OutputPrepareFrameTest() { + mOutput.setDisplayColorProfileForTest( + std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile)); + mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); + } - Output::OutputLayers outputLayers; - outputLayers.emplace_back(nullptr); - outputLayers.emplace_back(std::unique_ptr<OutputLayer>(existingOutputLayer)); - mOutput.setOutputLayersOrderedByZ(std::move(outputLayers)); + StrictMock<mock::CompositionEngine> mCompositionEngine; + mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>(); + mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>(); + StrictMock<OutputPartialMock> mOutput; +}; - std::shared_ptr<mock::Layer> layer{new StrictMock<mock::Layer>()}; - sp<LayerFE> layerFE{new StrictMock<mock::LayerFE>()}; +TEST_F(OutputPrepareFrameTest, takesEarlyOutIfNotEnabled) { + mOutput.editState().isEnabled = false; - StrictMock<mock::Layer> otherLayer; + mOutput.prepareFrame(); +} - { - // If there is no OutputLayer corresponding to the input layer, a - // new OutputLayer is constructed and returned. - EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(otherLayer)); - auto result = mOutput.getOrCreateOutputLayer(std::nullopt, layer, layerFE); - EXPECT_NE(existingOutputLayer, result.get()); - EXPECT_TRUE(result.get() != nullptr); - EXPECT_EQ(layer.get(), &result->getLayer()); - EXPECT_EQ(layerFE.get(), &result->getLayerFE()); - - // The entries in the ordered array should be unchanged. - auto& outputLayers = mOutput.getOutputLayersOrderedByZ(); - EXPECT_EQ(nullptr, outputLayers[0].get()); - EXPECT_EQ(existingOutputLayer, outputLayers[1].get()); +TEST_F(OutputPrepareFrameTest, delegatesToChooseCompositionStrategyAndRenderSurface) { + mOutput.editState().isEnabled = true; + mOutput.editState().usesClientComposition = false; + mOutput.editState().usesDeviceComposition = true; + + EXPECT_CALL(mOutput, chooseCompositionStrategy()).Times(1); + EXPECT_CALL(*mRenderSurface, prepareFrame(false, true)); + + mOutput.prepareFrame(); +} + +// Note: Use OutputTest and not OutputPrepareFrameTest, so the real +// base chooseCompositionStrategy() is invoked. +TEST_F(OutputTest, prepareFrameSetsClientCompositionOnlyByDefault) { + mOutput->editState().isEnabled = true; + mOutput->editState().usesClientComposition = false; + mOutput->editState().usesDeviceComposition = true; + + EXPECT_CALL(*mRenderSurface, prepareFrame(true, false)); + + mOutput->prepareFrame(); + + EXPECT_TRUE(mOutput->getState().usesClientComposition); + EXPECT_FALSE(mOutput->getState().usesDeviceComposition); +} + +/* + * Output::composeSurfaces() + */ + +struct OutputComposeSurfacesTest : public testing::Test { + static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT; + static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::DISPLAY_P3; + + static const Rect kDefaultOutputFrame; + static const Rect kDefaultOutputViewport; + static const Rect kDefaultOutputScissor; + static const mat4 kDefaultColorTransformMat; + + struct OutputPartialMock : public impl::Output { + // Sets up the helper functions called by composeSurfaces to use a mock + // implementations. + MOCK_CONST_METHOD0(getSkipColorTransform, bool()); + MOCK_METHOD2(generateClientCompositionRequests, + std::vector<renderengine::LayerSettings>(bool, Region&)); + MOCK_METHOD2(appendRegionFlashRequests, + void(const Region&, std::vector<renderengine::LayerSettings>&)); + MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); + + // compositionengine::Output overrides + const OutputCompositionState& getState() const override { return mState; } + OutputCompositionState& editState() override { return mState; } + + // These need implementations though are not expected to be called. + MOCK_CONST_METHOD0(getOutputLayerCount, size_t()); + MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, + compositionengine::OutputLayer*(size_t)); + MOCK_METHOD3(ensureOutputLayer, + compositionengine::OutputLayer*( + std::optional<size_t>, + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&)); + MOCK_METHOD0(finalizePendingOutputLayers, void()); + MOCK_METHOD0(clearOutputLayers, void()); + MOCK_CONST_METHOD1(dumpState, void(std::string&)); + MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&()); + MOCK_METHOD2(injectOutputLayerForTest, + compositionengine::OutputLayer*( + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&)); + MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>)); + + impl::OutputCompositionState mState; + }; + + OutputComposeSurfacesTest() { + mOutput.setDisplayColorProfileForTest( + std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile)); + mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); + + mOutput.editState().frame = kDefaultOutputFrame; + mOutput.editState().viewport = kDefaultOutputViewport; + mOutput.editState().scissor = kDefaultOutputScissor; + mOutput.editState().transform = ui::Transform{kDefaultOutputOrientation}; + mOutput.editState().orientation = kDefaultOutputOrientation; + mOutput.editState().dataspace = kDefaultOutputDataspace; + mOutput.editState().colorTransformMatrix = kDefaultColorTransformMat; + mOutput.editState().isSecure = true; + mOutput.editState().needsFiltering = false; + mOutput.editState().usesClientComposition = true; + mOutput.editState().usesDeviceComposition = false; + + EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u)); + EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) + .WillRepeatedly(Return(&mOutputLayer1)); + EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u)) + .WillRepeatedly(Return(&mOutputLayer2)); + EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine)); + EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); } - { - // If there is an existing OutputLayer for the requested layer, an owned - // pointer is returned - EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(*layer)); - auto result = mOutput.getOrCreateOutputLayer(std::nullopt, layer, layerFE); - EXPECT_EQ(existingOutputLayer, result.get()); - - // The corresponding entry in the ordered array should be cleared. - auto& outputLayers = mOutput.getOutputLayersOrderedByZ(); - EXPECT_EQ(nullptr, outputLayers[0].get()); - EXPECT_EQ(nullptr, outputLayers[1].get()); + StrictMock<mock::CompositionEngine> mCompositionEngine; + StrictMock<renderengine::mock::RenderEngine> mRenderEngine; + mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>(); + mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>(); + StrictMock<mock::OutputLayer> mOutputLayer1; + StrictMock<mock::OutputLayer> mOutputLayer2; + StrictMock<OutputPartialMock> mOutput; + sp<GraphicBuffer> mOutputBuffer = new GraphicBuffer(); +}; + +const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004}; +const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008}; +const Rect OutputComposeSurfacesTest::kDefaultOutputScissor{1009, 1010, 1011, 1012}; +const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5}; + +// TODO(b/121291683): Expand unit test coverage for composeSurfaces beyond these +// basic tests. + +TEST_F(OutputComposeSurfacesTest, doesNothingIfNoClientComposition) { + mOutput.editState().usesClientComposition = false; + + Region debugRegion; + std::optional<base::unique_fd> readyFence = mOutput.composeSurfaces(debugRegion); + EXPECT_TRUE(readyFence); +} + +TEST_F(OutputComposeSurfacesTest, worksIfNoClientLayersQueued) { + const Region kDebugRegion{Rect{100, 101, 102, 103}}; + + constexpr float kDefaultMaxLuminance = 1.0f; + constexpr float kDefaultAvgLuminance = 0.7f; + constexpr float kDefaultMinLuminance = 0.1f; + HdrCapabilities HdrCapabilities{{}, + kDefaultMaxLuminance, + kDefaultAvgLuminance, + kDefaultMinLuminance}; + + EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillOnce(Return(false)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).Times(1); + + EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillOnce(Return(true)); + EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities()).WillOnce(ReturnRef(HdrCapabilities)); + + EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer)); + + EXPECT_CALL(mOutput, getSkipColorTransform()).WillOnce(Return(false)); + EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _)).Times(1); + EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)).Times(1); + EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(1); + EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)).Times(1); + + std::optional<base::unique_fd> readyFence = mOutput.composeSurfaces(kDebugRegion); + EXPECT_TRUE(readyFence); +} + +/* + * Output::generateClientCompositionRequests() + */ + +struct GenerateClientCompositionRequestsTest : public testing::Test { + struct OutputPartialMock : public impl::Output { + // compositionengine::Output overrides + + std::vector<renderengine::LayerSettings> generateClientCompositionRequests( + bool supportsProtectedContent, Region& clearRegion) override { + return impl::Output::generateClientCompositionRequests(supportsProtectedContent, + clearRegion); + } + + const OutputCompositionState& getState() const override { return mState; } + OutputCompositionState& editState() override { return mState; } + + // These need implementations though are not expected to be called. + MOCK_CONST_METHOD0(getOutputLayerCount, size_t()); + MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, + compositionengine::OutputLayer*(size_t)); + MOCK_METHOD3(ensureOutputLayer, + compositionengine::OutputLayer*( + std::optional<size_t>, + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&)); + MOCK_METHOD0(finalizePendingOutputLayers, void()); + MOCK_METHOD0(clearOutputLayers, void()); + MOCK_CONST_METHOD1(dumpState, void(std::string&)); + MOCK_CONST_METHOD0(getCompositionEngine, const CompositionEngine&()); + MOCK_METHOD2(injectOutputLayerForTest, + compositionengine::OutputLayer*( + const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&)); + MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>)); + + impl::OutputCompositionState mState; + }; + + GenerateClientCompositionRequestsTest() { + mOutput.setDisplayColorProfileForTest( + std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile)); + mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface)); } + + mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>(); + mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>(); + StrictMock<OutputPartialMock> mOutput; +}; + +// TODO(b/121291683): Add more unit test coverage for generateClientCompositionRequests + +TEST_F(GenerateClientCompositionRequestsTest, worksForLandscapeModeSplitScreen) { + // In split-screen landscape mode, the screen is rotated 90 degrees, with + // one layer on the left covering the left side of the output, and one layer + // on the right covering that side of the output. + + StrictMock<mock::OutputLayer> leftOutputLayer; + StrictMock<mock::OutputLayer> rightOutputLayer; + + StrictMock<mock::Layer> leftLayer; + StrictMock<mock::LayerFE> leftLayerFE; + StrictMock<mock::Layer> rightLayer; + StrictMock<mock::LayerFE> rightLayerFE; + + impl::OutputLayerCompositionState leftOutputLayerState; + leftOutputLayerState.clearClientTarget = false; + leftOutputLayerState.visibleRegion = Region{Rect{0, 0, 1000, 1000}}; + + LayerFECompositionState leftLayerFEState; + leftLayerFEState.isOpaque = true; + + const half3 leftLayerColor{1.f, 0.f, 0.f}; + renderengine::LayerSettings leftLayerRESettings; + leftLayerRESettings.source.solidColor = leftLayerColor; + + impl::OutputLayerCompositionState rightOutputLayerState; + rightOutputLayerState.clearClientTarget = false; + rightOutputLayerState.visibleRegion = Region{Rect{1000, 0, 2000, 1000}}; + + LayerFECompositionState rightLayerFEState; + rightLayerFEState.isOpaque = true; + + const half3 rightLayerColor{0.f, 1.f, 0.f}; + renderengine::LayerSettings rightLayerRESettings; + rightLayerRESettings.source.solidColor = rightLayerColor; + + EXPECT_CALL(leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState)); + EXPECT_CALL(leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer)); + EXPECT_CALL(leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE)); + EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true)); + EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false)); + EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState)); + EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings)); + + EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState)); + EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer)); + EXPECT_CALL(rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE)); + EXPECT_CALL(rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true)); + EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false)); + EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState)); + EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings)); + + EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u)); + EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) + .WillRepeatedly(Return(&leftOutputLayer)); + EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u)) + .WillRepeatedly(Return(&rightOutputLayer)); + + const Rect kPortraitFrame(0, 0, 1000, 2000); + const Rect kPortraitViewport(0, 0, 2000, 1000); + const Rect kPortraitScissor(0, 0, 1000, 2000); + const uint32_t kPortraitOrientation = TR_ROT_90; + + mOutput.editState().frame = kPortraitFrame; + mOutput.editState().viewport = kPortraitViewport; + mOutput.editState().scissor = kPortraitScissor; + mOutput.editState().transform = ui::Transform{kPortraitOrientation}; + mOutput.editState().orientation = kPortraitOrientation; + mOutput.editState().needsFiltering = true; + mOutput.editState().isSecure = false; + + constexpr bool supportsProtectedContent = false; + Region clearRegion; + auto requests = + mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion); + + ASSERT_EQ(2u, requests.size()); + EXPECT_EQ(leftLayerColor, requests[0].source.solidColor); + EXPECT_EQ(rightLayerColor, requests[1].source.solidColor); +} + +TEST_F(GenerateClientCompositionRequestsTest, ignoresLayersThatDoNotIntersectWithViewport) { + // Layers whose visible region does not intersect with the viewport will be + // skipped when generating client composition request state. + + StrictMock<mock::OutputLayer> outputLayer; + StrictMock<mock::Layer> layer; + StrictMock<mock::LayerFE> layerFE; + + impl::OutputLayerCompositionState outputLayerState; + outputLayerState.clearClientTarget = false; + outputLayerState.visibleRegion = Region{Rect{3000, 0, 4000, 1000}}; + + LayerFECompositionState layerFEState; + layerFEState.isOpaque = true; + + EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState)); + EXPECT_CALL(outputLayer, getLayer()).WillRepeatedly(ReturnRef(layer)); + EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE)); + EXPECT_CALL(outputLayer, requiresClientComposition()).WillRepeatedly(Return(true)); + EXPECT_CALL(outputLayer, needsFiltering()).WillRepeatedly(Return(false)); + EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState)); + EXPECT_CALL(layerFE, prepareClientComposition(_)).Times(0); + + EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u)); + EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)).WillRepeatedly(Return(&outputLayer)); + + const Rect kPortraitFrame(0, 0, 1000, 2000); + const Rect kPortraitViewport(0, 0, 2000, 1000); + const Rect kPortraitScissor(0, 0, 1000, 2000); + const uint32_t kPortraitOrientation = TR_ROT_90; + + mOutput.editState().frame = kPortraitFrame; + mOutput.editState().viewport = kPortraitViewport; + mOutput.editState().scissor = kPortraitScissor; + mOutput.editState().transform = ui::Transform{kPortraitOrientation}; + mOutput.editState().orientation = kPortraitOrientation; + mOutput.editState().needsFiltering = true; + mOutput.editState().isSecure = false; + + constexpr bool supportsProtectedContent = false; + Region clearRegion; + auto requests = + mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion); + + EXPECT_EQ(0u, requests.size()); +} + +TEST_F(GenerateClientCompositionRequestsTest, clearsDeviceLayesAfterFirst) { + // If client composition is performed with some layers set to use device + // composition, device layers after the first layer (device or client) will + // clear the frame buffer if they are opaque and if that layer has a flag + // set to do so. The first layer is skipped as the frame buffer is already + // expected to be clear. + + StrictMock<mock::OutputLayer> leftOutputLayer; + StrictMock<mock::OutputLayer> rightOutputLayer; + + StrictMock<mock::Layer> leftLayer; + StrictMock<mock::LayerFE> leftLayerFE; + StrictMock<mock::Layer> rightLayer; + StrictMock<mock::LayerFE> rightLayerFE; + + impl::OutputLayerCompositionState leftOutputLayerState; + leftOutputLayerState.clearClientTarget = true; + leftOutputLayerState.visibleRegion = Region{Rect{0, 0, 1000, 1000}}; + + LayerFECompositionState leftLayerFEState; + leftLayerFEState.isOpaque = true; + + impl::OutputLayerCompositionState rightOutputLayerState; + rightOutputLayerState.clearClientTarget = true; + rightOutputLayerState.visibleRegion = Region{Rect{1000, 0, 2000, 1000}}; + + LayerFECompositionState rightLayerFEState; + rightLayerFEState.isOpaque = true; + + const half3 rightLayerColor{0.f, 1.f, 0.f}; + renderengine::LayerSettings rightLayerRESettings; + rightLayerRESettings.geometry.boundaries = FloatRect{456, 0, 0, 0}; + rightLayerRESettings.source.solidColor = rightLayerColor; + + EXPECT_CALL(leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState)); + EXPECT_CALL(leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer)); + EXPECT_CALL(leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE)); + EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false)); + EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState)); + + EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState)); + EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer)); + EXPECT_CALL(rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE)); + EXPECT_CALL(rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false)); + EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false)); + EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState)); + EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings)); + + EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u)); + EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)) + .WillRepeatedly(Return(&leftOutputLayer)); + EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u)) + .WillRepeatedly(Return(&rightOutputLayer)); + + const Rect kPortraitFrame(0, 0, 1000, 2000); + const Rect kPortraitViewport(0, 0, 2000, 1000); + const Rect kPortraitScissor(0, 0, 1000, 2000); + const uint32_t kPortraitOrientation = TR_ROT_90; + + mOutput.editState().frame = kPortraitFrame; + mOutput.editState().viewport = kPortraitViewport; + mOutput.editState().scissor = kPortraitScissor; + mOutput.editState().transform = ui::Transform{kPortraitOrientation}; + mOutput.editState().orientation = kPortraitOrientation; + mOutput.editState().needsFiltering = true; + mOutput.editState().isSecure = false; + + constexpr bool supportsProtectedContent = false; + Region clearRegion; + auto requests = + mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion); + + const half3 clearColor{0.f, 0.f, 0.f}; + + ASSERT_EQ(1u, requests.size()); + EXPECT_EQ(456.f, requests[0].geometry.boundaries.left); + EXPECT_EQ(clearColor, requests[0].source.solidColor); } } // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp index f75a4dcb85..da3f4fb4cd 100644 --- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp @@ -18,6 +18,7 @@ #include <cstdint> #include <compositionengine/RenderSurfaceCreationArgs.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/RenderSurface.h> #include <compositionengine/mock/CompositionEngine.h> #include <compositionengine/mock/Display.h> @@ -27,15 +28,9 @@ #include <gtest/gtest.h> #include <renderengine/mock/RenderEngine.h> -#include "MockHWComposer.h" - namespace android::compositionengine { namespace { -/* ------------------------------------------------------------------------ - * RenderSurfaceTest - */ - constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920; constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080; constexpr std::optional<DisplayId> DEFAULT_DISPLAY_ID = std::make_optional(DisplayId{123u}); @@ -55,14 +50,11 @@ public: RenderSurfaceTest() { EXPECT_CALL(mDisplay, getId()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_ID)); EXPECT_CALL(mDisplay, getName()).WillRepeatedly(ReturnRef(DEFAULT_DISPLAY_NAME)); - EXPECT_CALL(mCompositionEngine, getHwComposer).WillRepeatedly(ReturnRef(mHwComposer)); EXPECT_CALL(mCompositionEngine, getRenderEngine).WillRepeatedly(ReturnRef(mRenderEngine)); EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)) .WillRepeatedly(Return(NO_ERROR)); } - ~RenderSurfaceTest() override = default; - StrictMock<android::mock::HWComposer> mHwComposer; StrictMock<renderengine::mock::RenderEngine> mRenderEngine; StrictMock<mock::CompositionEngine> mCompositionEngine; StrictMock<mock::Display> mDisplay; @@ -74,7 +66,7 @@ public: mDisplaySurface}}; }; -/* ------------------------------------------------------------------------ +/* * Basic construction */ @@ -82,7 +74,7 @@ TEST_F(RenderSurfaceTest, canInstantiate) { EXPECT_TRUE(mSurface.isValid()); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::initialize() */ @@ -95,7 +87,7 @@ TEST_F(RenderSurfaceTest, initializeConfiguresNativeWindow) { mSurface.initialize(); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::getSize() */ @@ -105,7 +97,7 @@ TEST_F(RenderSurfaceTest, sizeReturnsConstructedSize) { EXPECT_EQ(expected, mSurface.getSize()); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::getClientTargetAcquireFence() */ @@ -117,7 +109,7 @@ TEST_F(RenderSurfaceTest, getClientTargetAcquireFenceForwardsCall) { EXPECT_EQ(fence.get(), mSurface.getClientTargetAcquireFence().get()); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::setDisplaySize() */ @@ -127,7 +119,7 @@ TEST_F(RenderSurfaceTest, setDisplaySizeAppliesChange) { mSurface.setDisplaySize(ui::Size(640, 480)); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::setBufferDataspace() */ @@ -138,7 +130,7 @@ TEST_F(RenderSurfaceTest, setBufferDataspaceAppliesChange) { mSurface.setBufferDataspace(ui::Dataspace::DISPLAY_P3); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::setProtected() */ @@ -179,7 +171,7 @@ TEST_F(RenderSurfaceTest, setProtectedEnableWithError) { EXPECT_FALSE(mSurface.isProtected()); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::beginFrame() */ @@ -189,73 +181,39 @@ TEST_F(RenderSurfaceTest, beginFrameAppliesChange) { EXPECT_EQ(NO_ERROR, mSurface.beginFrame(true)); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::prepareFrame() */ -TEST_F(RenderSurfaceTest, prepareFramePassesOutputLayersToHwc) { - EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay))) - .WillOnce(Return(INVALID_OPERATION)); - - EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame()); -} - -TEST_F(RenderSurfaceTest, prepareFrameTakesEarlyOutOnHwcError) { - EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay))) - .WillOnce(Return(INVALID_OPERATION)); - - EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame()); -} - TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) { - EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay))) - .WillOnce(Return(NO_ERROR)); - EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true)); - EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true)); - EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_MIXED)) - .WillOnce(Return(INVALID_OPERATION)); + .WillOnce(Return(NO_ERROR)); - EXPECT_EQ(INVALID_OPERATION, mSurface.prepareFrame()); + mSurface.prepareFrame(true, true); } TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGlesComposition) { - EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay))) - .WillOnce(Return(NO_ERROR)); - EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true)); - EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false)); - EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES)) .WillOnce(Return(NO_ERROR)); - EXPECT_EQ(NO_ERROR, mSurface.prepareFrame()); + mSurface.prepareFrame(true, false); } TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) { - EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay))) - .WillOnce(Return(NO_ERROR)); - EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false)); - EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true)); - EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC)) .WillOnce(Return(NO_ERROR)); - EXPECT_EQ(NO_ERROR, mSurface.prepareFrame()); + mSurface.prepareFrame(false, true); } TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) { - EXPECT_CALL(mHwComposer, prepare(*DEFAULT_DISPLAY_ID, Ref(mDisplay))) - .WillOnce(Return(NO_ERROR)); - EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false)); - EXPECT_CALL(mHwComposer, hasDeviceComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false)); - EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC)) .WillOnce(Return(NO_ERROR)); - EXPECT_EQ(NO_ERROR, mSurface.prepareFrame()); + mSurface.prepareFrame(false, false); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::dequeueBuffer() */ @@ -272,7 +230,7 @@ TEST_F(RenderSurfaceTest, dequeueBufferObtainsABuffer) { EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get()); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::queueBuffer() */ @@ -280,9 +238,11 @@ TEST_F(RenderSurfaceTest, queueBufferHandlesNoClientComposition) { sp<GraphicBuffer> buffer = new GraphicBuffer(); mSurface.mutableGraphicBufferForTest() = buffer; - EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false)); - EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)) - .WillOnce(Return(false)); + impl::OutputCompositionState state; + state.usesClientComposition = false; + state.flipClientTarget = false; + + EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state)); EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1); mSurface.queueBuffer(base::unique_fd()); @@ -294,7 +254,11 @@ TEST_F(RenderSurfaceTest, queueBufferHandlesClientComposition) { sp<GraphicBuffer> buffer = new GraphicBuffer(); mSurface.mutableGraphicBufferForTest() = buffer; - EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true)); + impl::OutputCompositionState state; + state.usesClientComposition = true; + state.flipClientTarget = false; + + EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state)); EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1)) .WillOnce(Return(NO_ERROR)); EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1); @@ -308,8 +272,11 @@ TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) { sp<GraphicBuffer> buffer = new GraphicBuffer(); mSurface.mutableGraphicBufferForTest() = buffer; - EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false)); - EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true)); + impl::OutputCompositionState state; + state.usesClientComposition = false; + state.flipClientTarget = true; + + EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state)); EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1)) .WillOnce(Return(NO_ERROR)); EXPECT_CALL(*mDisplaySurface, advanceFrame()).Times(1); @@ -322,8 +289,11 @@ TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequest) { TEST_F(RenderSurfaceTest, queueBufferHandlesFlipClientTargetRequestWithNoBufferYetDequeued) { sp<GraphicBuffer> buffer = new GraphicBuffer(); - EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(false)); - EXPECT_CALL(mHwComposer, hasFlipClientTargetRequest(DEFAULT_DISPLAY_ID)).WillOnce(Return(true)); + impl::OutputCompositionState state; + state.usesClientComposition = false; + state.flipClientTarget = true; + + EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state)); EXPECT_CALL(*mNativeWindow, dequeueBuffer(_, _)) .WillOnce( DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR))); @@ -340,7 +310,10 @@ TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirt sp<GraphicBuffer> buffer = new GraphicBuffer(); mSurface.mutableGraphicBufferForTest() = buffer; - EXPECT_CALL(mHwComposer, hasClientComposition(DEFAULT_DISPLAY_ID)).WillOnce(Return(true)); + impl::OutputCompositionState state; + state.usesClientComposition = true; + + EXPECT_CALL(mDisplay, getState()).WillOnce(ReturnRef(state)); EXPECT_CALL(*mNativeWindow, queueBuffer(buffer->getNativeBuffer(), -1)) .WillOnce(Return(INVALID_OPERATION)); EXPECT_CALL(mDisplay, isVirtual()).WillOnce(Return(true)); @@ -353,7 +326,7 @@ TEST_F(RenderSurfaceTest, queueBufferHandlesNativeWindowQueueBufferFailureOnVirt EXPECT_EQ(nullptr, mSurface.mutableGraphicBufferForTest().get()); } -/* ------------------------------------------------------------------------ +/* * RenderSurface::onPresentDisplayCompleted() */ @@ -363,21 +336,7 @@ TEST_F(RenderSurfaceTest, onPresentDisplayCompletedForwardsSignal) { mSurface.onPresentDisplayCompleted(); } -/* ------------------------------------------------------------------------ - * RenderSurface::setViewportAndProjection() - */ - -TEST_F(RenderSurfaceTest, setViewportAndProjectionAppliesChang) { - mSurface.setSizeForTest(ui::Size(100, 200)); - - EXPECT_CALL(mRenderEngine, - setViewportAndProjection(100, 200, Rect(100, 200), ui::Transform::ROT_0)) - .Times(1); - - mSurface.setViewportAndProjection(); -} - -/* ------------------------------------------------------------------------ +/* * RenderSurface::flip() */ diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp index 7927fa95b6..e58e6f425e 100644 --- a/services/surfaceflinger/ContainerLayer.cpp +++ b/services/surfaceflinger/ContainerLayer.cpp @@ -26,20 +26,15 @@ ContainerLayer::ContainerLayer(const LayerCreationArgs& args) : Layer(args) {} ContainerLayer::~ContainerLayer() = default; -bool ContainerLayer::prepareClientLayer(const RenderArea&, const Region&, bool, Region&, const bool, - renderengine::LayerSettings&) { - return false; -} - bool ContainerLayer::isVisible() const { return false; } -bool ContainerLayer::canReceiveInput() const { - return !isHiddenByPolicy(); +sp<Layer> ContainerLayer::createClone() { + String8 name = mName + " (Mirror)"; + sp<ContainerLayer> layer = mFlinger->getFactory().createContainerLayer( + LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, 0, LayerMetadata())); + layer->setInitialValuesForClone(this); + return layer; } - -void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&, - const Rect&, int32_t, const ui::Dataspace) {} - } // namespace android diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h index 7222a3e15a..b48d471bde 100644 --- a/services/surfaceflinger/ContainerLayer.h +++ b/services/surfaceflinger/ContainerLayer.h @@ -28,24 +28,13 @@ public: explicit ContainerLayer(const LayerCreationArgs&); ~ContainerLayer() override; - const char* getTypeId() const override { return "ContainerLayer"; } + const char* getType() const override { return "ContainerLayer"; } bool isVisible() const override; - bool canReceiveInput() const override; - - void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, - const Rect& viewport, int32_t supportedPerFrameMetadata, - const ui::Dataspace targetDataspace) override; - bool isCreatedFromMainThread() const override { return true; } - bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; } - protected: - bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform, Region& clearRegion, - const bool supportProtectedContent, - renderengine::LayerSettings& layer) override; + sp<Layer> createClone() override; }; } // namespace android diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 5700d72cae..89123df46c 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -59,12 +59,13 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args) mSequenceId(args.sequenceId), mDisplayInstallOrientation(args.displayInstallOrientation), mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay( - compositionengine::DisplayCreationArgs{args.isSecure, args.isVirtual, - args.displayId})}, + compositionengine::DisplayCreationArgs{args.isVirtual, args.displayId, + args.powerAdvisor})}, mIsVirtual(args.isVirtual), mOrientation(), mActiveConfig(0), mIsPrimary(args.isPrimary) { + mCompositionDisplay->editState().isSecure = args.isSecure; mCompositionDisplay->createRenderSurface( compositionengine::RenderSurfaceCreationArgs{ANativeWindow_getWidth( args.nativeWindow.get()), @@ -117,24 +118,6 @@ uint32_t DisplayDevice::getPageFlipCount() const { } // ---------------------------------------------------------------------------- - -void DisplayDevice::setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers) { - mVisibleLayersSortedByZ = layers; -} - -const Vector< sp<Layer> >& DisplayDevice::getVisibleLayersSortedByZ() const { - return mVisibleLayersSortedByZ; -} - -void DisplayDevice::setLayersNeedingFences(const Vector< sp<Layer> >& layers) { - mLayersNeedingFences = layers; -} - -const Vector< sp<Layer> >& DisplayDevice::getLayersNeedingFences() const { - return mLayersNeedingFences; -} - -// ---------------------------------------------------------------------------- void DisplayDevice::setPowerMode(int mode) { mPowerMode = mode; getCompositionDisplay()->setCompositionEnabled(mPowerMode != HWC_POWER_MODE_OFF); @@ -303,7 +286,6 @@ void DisplayDevice::dump(std::string& result) const { result.append(" "); StringAppendF(&result, "powerMode=%d, ", mPowerMode); StringAppendF(&result, "activeConfig=%d, ", mActiveConfig); - StringAppendF(&result, "numLayers=%zu\n", mVisibleLayersSortedByZ.size()); getCompositionDisplay()->dump(result); } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 0067b505e3..ce4e1e6b09 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -31,6 +31,7 @@ #include <math/mat4.h> #include <renderengine/RenderEngine.h> #include <system/window.h> +#include <ui/DisplayInfo.h> #include <ui/GraphicTypes.h> #include <ui/HdrCapabilities.h> #include <ui/Region.h> @@ -40,6 +41,7 @@ #include <utils/Timers.h> #include "DisplayHardware/DisplayIdentification.h" +#include "DisplayHardware/PowerAdvisor.h" #include "RenderArea.h" namespace android { @@ -64,10 +66,6 @@ public: constexpr static float sDefaultMinLumiance = 0.0; constexpr static float sDefaultMaxLumiance = 500.0; - enum { - NO_LAYER_STACK = 0xFFFFFFFF, - }; - explicit DisplayDevice(DisplayDeviceCreationArgs&& args); virtual ~DisplayDevice(); @@ -86,11 +84,6 @@ public: int getHeight() const; int getInstallOrientation() const { return mDisplayInstallOrientation; } - void setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers); - const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const; - void setLayersNeedingFences(const Vector< sp<Layer> >& layers); - const Vector< sp<Layer> >& getLayersNeedingFences() const; - void setLayerStack(uint32_t stack); void setDisplaySize(const int newWidth, const int newHeight); void setProjection(int orientation, const Rect& viewport, const Rect& frame); @@ -180,11 +173,6 @@ private: * don't need synchronization. */ - // list of visible layers on that display - Vector< sp<Layer> > mVisibleLayersSortedByZ; - // list of layers needing fences - Vector< sp<Layer> > mLayersNeedingFences; - /* * Transaction state */ @@ -210,7 +198,7 @@ struct DisplayDeviceState { int32_t sequenceId = sNextSequenceId++; std::optional<DisplayId> displayId; sp<IGraphicBufferProducer> surface; - uint32_t layerStack = DisplayDevice::NO_LAYER_STACK; + uint32_t layerStack = NO_LAYER_STACK; Rect viewport; Rect frame; uint8_t orientation = 0; @@ -245,6 +233,7 @@ struct DisplayDeviceCreationArgs { std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes; int initialPowerMode{HWC_POWER_MODE_NORMAL}; bool isPrimary{false}; + Hwc2::PowerAdvisor* powerAdvisor{nullptr}; }; class DisplayRenderArea : public RenderArea { diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index 7f47a2ecd4..acddc42601 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -17,9 +17,11 @@ #undef LOG_TAG #define LOG_TAG "HwcComposer" -#include <inttypes.h> #include <log/log.h> +#include <algorithm> +#include <cinttypes> + #include "ComposerHal.h" #include <composer-command-buffer/2.2/ComposerCommandBuffer.h> @@ -110,6 +112,7 @@ Error unwrapRet(Return<Error>& ret) namespace impl { +#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER Composer::CommandWriter::CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {} @@ -160,6 +163,7 @@ void Composer::CommandWriter::writeBufferMetadata( writeSigned(static_cast<int32_t>(metadata.format)); write64(metadata.usage); } +#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER Composer::Composer(const std::string& serviceName) : mWriter(kWriterInitialSize), @@ -171,7 +175,16 @@ Composer::Composer(const std::string& serviceName) LOG_ALWAYS_FATAL("failed to get hwcomposer service"); } - if (sp<IComposer> composer_2_3 = IComposer::castFrom(mComposer)) { + if (sp<IComposer> composer_2_4 = IComposer::castFrom(mComposer)) { + composer_2_4->createClient_2_4([&](const auto& tmpError, const auto& tmpClient) { + if (tmpError == Error::NONE) { + mClient = tmpClient; + mClient_2_2 = tmpClient; + mClient_2_3 = tmpClient; + mClient_2_4 = tmpClient; + } + }); + } else if (sp<V2_3::IComposer> composer_2_3 = V2_3::IComposer::castFrom(mComposer)) { composer_2_3->createClient_2_3([&](const auto& tmpError, const auto& tmpClient) { if (tmpError == Error::NONE) { mClient = tmpClient; @@ -198,12 +211,14 @@ Composer::Composer(const std::string& serviceName) LOG_ALWAYS_FATAL("failed to create composer client"); } +#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER if (mIsUsingVrComposer) { sp<IVrComposerClient> vrClient = IVrComposerClient::castFrom(mClient); if (vrClient == nullptr) { LOG_ALWAYS_FATAL("failed to create vr composer client"); } } +#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER } Composer::~Composer() = default; @@ -452,23 +467,6 @@ Error Composer::getDisplayRequests(Display display, return Error::NONE; } -Error Composer::getDisplayType(Display display, - IComposerClient::DisplayType* outType) -{ - Error error = kDefaultError; - mClient->getDisplayType(display, - [&](const auto& tmpError, const auto& tmpType) { - error = tmpError; - if (error != Error::NONE) { - return; - } - - *outType = tmpType; - }); - - return error; -} - Error Composer::getDozeSupport(Display display, bool* outSupport) { Error error = kDefaultError; @@ -565,17 +563,20 @@ Error Composer::setClientTarget(Display display, uint32_t slot, const std::vector<IComposerClient::Rect>& damage) { mWriter.selectDisplay(display); + +#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER if (mIsUsingVrComposer && target.get()) { IVrComposerClient::BufferMetadata metadata = { - .width = target->getWidth(), - .height = target->getHeight(), - .stride = target->getStride(), - .layerCount = target->getLayerCount(), - .format = static_cast<types::V1_0::PixelFormat>(target->getPixelFormat()), - .usage = target->getUsage(), + .width = target->getWidth(), + .height = target->getHeight(), + .stride = target->getStride(), + .layerCount = target->getLayerCount(), + .format = static_cast<types::V1_2::PixelFormat>(target->getPixelFormat()), + .usage = target->getUsage(), }; mWriter.setClientTargetMetadata(metadata); } +#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER const native_handle_t* handle = nullptr; if (target.get()) { @@ -695,17 +696,20 @@ Error Composer::setLayerBuffer(Display display, Layer layer, { mWriter.selectDisplay(display); mWriter.selectLayer(layer); + +#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER if (mIsUsingVrComposer && buffer.get()) { IVrComposerClient::BufferMetadata metadata = { - .width = buffer->getWidth(), - .height = buffer->getHeight(), - .stride = buffer->getStride(), - .layerCount = buffer->getLayerCount(), - .format = static_cast<types::V1_0::PixelFormat>(buffer->getPixelFormat()), - .usage = buffer->getUsage(), + .width = buffer->getWidth(), + .height = buffer->getHeight(), + .stride = buffer->getStride(), + .layerCount = buffer->getLayerCount(), + .format = static_cast<types::V1_2::PixelFormat>(buffer->getPixelFormat()), + .usage = buffer->getUsage(), }; mWriter.setLayerBufferMetadata(metadata); } +#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER const native_handle_t* handle = nullptr; if (buffer.get()) { @@ -823,6 +827,7 @@ Error Composer::setLayerZOrder(Display display, Layer layer, uint32_t z) return Error::NONE; } +#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER Error Composer::setLayerInfo(Display display, Layer layer, uint32_t type, uint32_t appId) { @@ -833,6 +838,15 @@ Error Composer::setLayerInfo(Display display, Layer layer, uint32_t type, } return Error::NONE; } +#else +Error Composer::setLayerInfo(Display display, Layer layer, uint32_t, uint32_t) { + if (mIsUsingVrComposer) { + mWriter.selectDisplay(display); + mWriter.selectLayer(layer); + } + return Error::NONE; +} +#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER Error Composer::execute() { @@ -1093,23 +1107,6 @@ Error Composer::getDisplayedContentSamplingAttributes(Display display, PixelForm return error; } -Error Composer::getDisplayCapabilities(Display display, - std::vector<DisplayCapability>* outCapabilities) { - if (!mClient_2_3) { - return Error::UNSUPPORTED; - } - Error error = kDefaultError; - mClient_2_3->getDisplayCapabilities(display, - [&](const auto& tmpError, const auto& tmpCapabilities) { - error = tmpError; - if (error != Error::NONE) { - return; - } - *outCapabilities = tmpCapabilities; - }); - return error; -} - Error Composer::setDisplayContentSamplingEnabled(Display display, bool enabled, uint8_t componentMask, uint64_t maxFrames) { if (!mClient_2_3) { @@ -1167,6 +1164,60 @@ Error Composer::setDisplayBrightness(Display display, float brightness) { return mClient_2_3->setDisplayBrightness(display, brightness); } +// Composer HAL 2.4 + +Error Composer::getDisplayCapabilities(Display display, + std::vector<DisplayCapability>* outCapabilities) { + if (!mClient_2_3) { + return Error::UNSUPPORTED; + } + + Error error = kDefaultError; + if (mClient_2_4) { + mClient_2_4->getDisplayCapabilities_2_4(display, + [&](const auto& tmpError, const auto& tmpCaps) { + error = tmpError; + if (error != Error::NONE) { + return; + } + *outCapabilities = tmpCaps; + }); + } else { + mClient_2_3 + ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) { + error = tmpError; + if (error != Error::NONE) { + return; + } + + outCapabilities->resize(tmpCaps.size()); + std::transform(tmpCaps.begin(), tmpCaps.end(), outCapabilities->begin(), + [](auto cap) { return static_cast<DisplayCapability>(cap); }); + }); + } + + return error; +} + +Error Composer::getDisplayConnectionType(Display display, + IComposerClient::DisplayConnectionType* outType) { + if (!mClient_2_4) { + return Error::UNSUPPORTED; + } + + Error error = kDefaultError; + mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) { + error = tmpError; + if (error != Error::NONE) { + return; + } + + *outType = tmpType; + }); + + return error; +} + CommandReader::~CommandReader() { resetData(); diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index c4e952b8d9..e743e59bb6 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -23,10 +23,12 @@ #include <utility> #include <vector> -#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h> +#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER +#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h> +#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER #include <android/hardware/graphics/common/1.1/types.h> -#include <android/hardware/graphics/composer/2.3/IComposer.h> -#include <android/hardware/graphics/composer/2.3/IComposerClient.h> +#include <android/hardware/graphics/composer/2.4/IComposer.h> +#include <android/hardware/graphics/composer/2.4/IComposerClient.h> #include <composer-command-buffer/2.3/ComposerCommandBuffer.h> #include <gui/HdrMetadata.h> #include <math/mat4.h> @@ -38,13 +40,16 @@ namespace android { namespace Hwc2 { -using frameworks::vr::composer::V1_0::IVrComposerClient; +#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER +using frameworks::vr::composer::V2_0::IVrComposerClient; +#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER namespace types = hardware::graphics::common; namespace V2_1 = hardware::graphics::composer::V2_1; namespace V2_2 = hardware::graphics::composer::V2_2; namespace V2_3 = hardware::graphics::composer::V2_3; +namespace V2_4 = hardware::graphics::composer::V2_4; using types::V1_0::ColorTransform; using types::V1_0::Transform; @@ -61,8 +66,8 @@ using V2_1::IComposerCallback; using V2_1::Layer; using V2_3::CommandReaderBase; using V2_3::CommandWriterBase; -using V2_3::IComposer; -using V2_3::IComposerClient; +using V2_4::IComposer; +using V2_4::IComposerClient; using DisplayCapability = IComposerClient::DisplayCapability; using PerFrameMetadata = IComposerClient::PerFrameMetadata; using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey; @@ -114,7 +119,6 @@ public: std::vector<Layer>* outLayers, std::vector<uint32_t>* outLayerRequestMasks) = 0; - virtual Error getDisplayType(Display display, IComposerClient::DisplayType* outType) = 0; virtual Error getDozeSupport(Display display, bool* outSupport) = 0; virtual Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance, float* outMaxAverageLuminance, @@ -199,11 +203,15 @@ public: uint8_t componentMask, uint64_t maxFrames) = 0; virtual Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp, DisplayedFrameStats* outStats) = 0; - virtual Error getDisplayCapabilities(Display display, - std::vector<DisplayCapability>* outCapabilities) = 0; virtual Error setLayerPerFrameMetadataBlobs( Display display, Layer layer, const std::vector<PerFrameMetadataBlob>& metadata) = 0; virtual Error setDisplayBrightness(Display display, float brightness) = 0; + + // Composer HAL 2.4 + virtual Error getDisplayCapabilities(Display display, + std::vector<DisplayCapability>* outCapabilities) = 0; + virtual Error getDisplayConnectionType(Display display, + IComposerClient::DisplayConnectionType* outType) = 0; }; namespace impl { @@ -330,7 +338,6 @@ public: std::vector<Layer>* outLayers, std::vector<uint32_t>* outLayerRequestMasks) override; - Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override; Error getDozeSupport(Display display, bool* outSupport) override; Error getHdrCapabilities(Display display, std::vector<Hdr>* outTypes, float* outMaxLuminance, float* outMaxAverageLuminance, float* outMinLuminance) override; @@ -410,14 +417,19 @@ public: uint64_t maxFrames) override; Error getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp, DisplayedFrameStats* outStats) override; - Error getDisplayCapabilities(Display display, - std::vector<DisplayCapability>* outCapabilities) override; Error setLayerPerFrameMetadataBlobs( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override; Error setDisplayBrightness(Display display, float brightness) override; + // Composer HAL 2.4 + Error getDisplayCapabilities(Display display, + std::vector<DisplayCapability>* outCapabilities) override; + Error getDisplayConnectionType(Display display, + IComposerClient::DisplayConnectionType* outType) override; + private: +#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER class CommandWriter : public CommandWriterBase { public: explicit CommandWriter(uint32_t initialMaxSize); @@ -433,6 +445,13 @@ private: void writeBufferMetadata( const IVrComposerClient::BufferMetadata& metadata); }; +#else + class CommandWriter : public CommandWriterBase { + public: + explicit CommandWriter(uint32_t initialMaxSize) : CommandWriterBase(initialMaxSize) {} + ~CommandWriter() override {} + }; +#endif // defined(USE_VR_COMPOSER) && USE_VR_COMPOSER // Many public functions above simply write a command into the command // queue to batch the calls. validateDisplay and presentDisplay will call @@ -443,7 +462,8 @@ private: sp<V2_1::IComposerClient> mClient; sp<V2_2::IComposerClient> mClient_2_2; - sp<IComposerClient> mClient_2_3; + sp<V2_3::IComposerClient> mClient_2_3; + sp<IComposerClient> mClient_2_4; // 64KiB minus a small space for metadata such as read/write pointers static constexpr size_t kWriterInitialSize = diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index c463c4e40a..6f7428a44a 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -169,20 +169,8 @@ void Device::onHotplug(hwc2_display_t displayId, Connection connection) { } mDisplays.erase(displayId); - DisplayType displayType; - auto intError = mComposer->getDisplayType(displayId, - reinterpret_cast<Hwc2::IComposerClient::DisplayType *>( - &displayType)); - auto error = static_cast<Error>(intError); - if (error != Error::None) { - ALOGE("getDisplayType(%" PRIu64 ") failed: %s (%d). " - "Aborting hotplug attempt.", - displayId, to_string(error).c_str(), intError); - return; - } - auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities, - displayId, displayType); + displayId, DisplayType::Physical); newDisplay->setConnected(true); mDisplays.emplace(displayId, std::move(newDisplay)); } else if (connection == Connection::Disconnected) { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 1099041b4b..d480f7ce29 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -20,6 +20,8 @@ #define LOG_TAG "HWComposer" #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include "HWComposer.h" + #include <compositionengine/Output.h> #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> @@ -29,12 +31,10 @@ #include <utils/Errors.h> #include <utils/Trace.h> -#include "HWComposer.h" -#include "HWC2.h" -#include "ComposerHal.h" - -#include "../Layer.h" // needed only for debugging +#include "../Layer.h" // needed only for debugging #include "../SurfaceFlinger.h" +#include "ComposerHal.h" +#include "HWC2.h" #define LOG_HWC_DISPLAY_ERROR(hwcDisplayId, msg) \ ALOGE("%s failed for HWC display %" PRIu64 ": %s", __FUNCTION__, hwcDisplayId, msg) @@ -113,31 +113,6 @@ bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId, return mDisplayData.at(*displayId).hwcDisplay->getCapabilities().count(capability) > 0; } -void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) { - bool valid = true; - switch (from) { - case HWC2::Composition::Client: - valid = false; - break; - case HWC2::Composition::Device: - case HWC2::Composition::SolidColor: - valid = (to == HWC2::Composition::Client); - break; - case HWC2::Composition::Cursor: - case HWC2::Composition::Sideband: - valid = (to == HWC2::Composition::Client || - to == HWC2::Composition::Device); - break; - default: - break; - } - - if (!valid) { - ALOGE("Invalid layer type change: %s --> %s", to_string(from).c_str(), - to_string(to).c_str()); - } -} - std::optional<DisplayIdentificationInfo> HWComposer::onHotplug(hwc2_display_t hwcDisplayId, HWC2::Connection connection) { std::optional<DisplayIdentificationInfo> info; @@ -399,7 +374,9 @@ status_t HWComposer::setClientTarget(DisplayId displayId, uint32_t slot, return NO_ERROR; } -status_t HWComposer::prepare(DisplayId displayId, const compositionengine::Output& output) { +status_t HWComposer::getDeviceCompositionChanges( + DisplayId displayId, bool frameUsesClientComposition, + std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) { ATRACE_CALL(); RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); @@ -419,12 +396,8 @@ status_t HWComposer::prepare(DisplayId displayId, const compositionengine::Outpu // composition. When there is client composition, since we haven't // rendered to the client target yet, we should not attempt to skip // validate. - // - // displayData.hasClientComposition hasn't been updated for this frame. - // The check below is incorrect. We actually rely on HWC here to fall - // back to validate when there is any client layer. displayData.validateWasSkipped = false; - if (!displayData.hasClientComposition) { + if (!frameUsesClientComposition) { sp<Fence> outPresentFence; uint32_t state = UINT32_MAX; error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state); @@ -449,58 +422,19 @@ status_t HWComposer::prepare(DisplayId displayId, const compositionengine::Outpu RETURN_IF_HWC_ERROR_FOR("validate", error, displayId, BAD_INDEX); } - std::unordered_map<HWC2::Layer*, HWC2::Composition> changedTypes; + android::HWComposer::DeviceRequestedChanges::ChangedTypes changedTypes; changedTypes.reserve(numTypes); error = hwcDisplay->getChangedCompositionTypes(&changedTypes); RETURN_IF_HWC_ERROR_FOR("getChangedCompositionTypes", error, displayId, BAD_INDEX); - displayData.displayRequests = static_cast<HWC2::DisplayRequest>(0); - std::unordered_map<HWC2::Layer*, HWC2::LayerRequest> layerRequests; + auto displayRequests = static_cast<HWC2::DisplayRequest>(0); + android::HWComposer::DeviceRequestedChanges::LayerRequests layerRequests; layerRequests.reserve(numRequests); - error = hwcDisplay->getRequests(&displayData.displayRequests, - &layerRequests); + error = hwcDisplay->getRequests(&displayRequests, &layerRequests); RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX); - displayData.hasClientComposition = false; - displayData.hasDeviceComposition = false; - for (auto& outputLayer : output.getOutputLayersOrderedByZ()) { - auto& state = outputLayer->editState(); - LOG_FATAL_IF(!state.hwc.); - auto hwcLayer = (*state.hwc).hwcLayer; - - if (auto it = changedTypes.find(hwcLayer.get()); it != changedTypes.end()) { - auto newCompositionType = it->second; - validateChange(static_cast<HWC2::Composition>((*state.hwc).hwcCompositionType), - newCompositionType); - (*state.hwc).hwcCompositionType = - static_cast<Hwc2::IComposerClient::Composition>(newCompositionType); - } - - switch ((*state.hwc).hwcCompositionType) { - case Hwc2::IComposerClient::Composition::CLIENT: - displayData.hasClientComposition = true; - break; - case Hwc2::IComposerClient::Composition::DEVICE: - case Hwc2::IComposerClient::Composition::SOLID_COLOR: - case Hwc2::IComposerClient::Composition::CURSOR: - case Hwc2::IComposerClient::Composition::SIDEBAND: - displayData.hasDeviceComposition = true; - break; - default: - break; - } - - state.clearClientTarget = false; - if (auto it = layerRequests.find(hwcLayer.get()); it != layerRequests.end()) { - auto request = it->second; - if (request == HWC2::LayerRequest::ClearClientTarget) { - state.clearClientTarget = true; - } else { - LOG_DISPLAY_ERROR(displayId, - ("Unknown layer request " + to_string(request)).c_str()); - } - } - } + outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests), + std::move(layerRequests)}); error = hwcDisplay->acceptChanges(); RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX); @@ -508,40 +442,6 @@ status_t HWComposer::prepare(DisplayId displayId, const compositionengine::Outpu return NO_ERROR; } -bool HWComposer::hasDeviceComposition(const std::optional<DisplayId>& displayId) const { - if (!displayId) { - // Displays without a corresponding HWC display are never composed by - // the device - return false; - } - - RETURN_IF_INVALID_DISPLAY(*displayId, false); - return mDisplayData.at(*displayId).hasDeviceComposition; -} - -bool HWComposer::hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const { - if (!displayId) { - // Displays without a corresponding HWC display are never composed by - // the device - return false; - } - - RETURN_IF_INVALID_DISPLAY(*displayId, false); - return ((static_cast<uint32_t>(mDisplayData.at(*displayId).displayRequests) & - static_cast<uint32_t>(HWC2::DisplayRequest::FlipClientTarget)) != 0); -} - -bool HWComposer::hasClientComposition(const std::optional<DisplayId>& displayId) const { - if (!displayId) { - // Displays without a corresponding HWC display are always composed by - // the client - return true; - } - - RETURN_IF_INVALID_DISPLAY(*displayId, true); - return mDisplayData.at(*displayId).hasClientComposition; -} - sp<Fence> HWComposer::getPresentFence(DisplayId displayId) const { RETURN_IF_INVALID_DISPLAY(displayId, Fence::NO_FENCE); return mDisplayData.at(displayId).lastPresentFence; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index de863b8d6c..e87c5c3309 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -71,8 +71,26 @@ public: // Destroy a previously created layer virtual void destroyLayer(DisplayId displayId, HWC2::Layer* layer) = 0; - // Asks the HAL what it can do - virtual status_t prepare(DisplayId displayId, const compositionengine::Output&) = 0; + struct DeviceRequestedChanges { + using ChangedTypes = std::unordered_map<HWC2::Layer*, HWC2::Composition>; + using DisplayRequests = HWC2::DisplayRequest; + using LayerRequests = std::unordered_map<HWC2::Layer*, HWC2::LayerRequest>; + + ChangedTypes changedTypes; + DisplayRequests displayRequests; + LayerRequests layerRequests; + }; + + // Gets any required composition change requests from the HWC device. + // + // Note that frameUsesClientComposition must be set correctly based on + // whether the current frame appears to use client composition. If it is + // false some internal optimizations are allowed to present the display + // with fewer handshakes, but this does not work if client composition is + // expected. + virtual status_t getDeviceCompositionChanges( + DisplayId, bool frameUsesClientComposition, + std::optional<DeviceRequestedChanges>* outChanges) = 0; virtual status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target, @@ -93,15 +111,6 @@ public: // reset state when an external, non-virtual display is disconnected virtual void disconnectDisplay(DisplayId displayId) = 0; - // does this display have layers handled by HWC - virtual bool hasDeviceComposition(const std::optional<DisplayId>& displayId) const = 0; - - // does this display have pending request to flip client target - virtual bool hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const = 0; - - // does this display have layers handled by GLES - virtual bool hasClientComposition(const std::optional<DisplayId>& displayId) const = 0; - // get the present fence received from the last call to present. virtual sp<Fence> getPresentFence(DisplayId displayId) const = 0; @@ -210,8 +219,9 @@ public: // Destroy a previously created layer void destroyLayer(DisplayId displayId, HWC2::Layer* layer) override; - // Asks the HAL what it can do - status_t prepare(DisplayId displayId, const compositionengine::Output&) override; + status_t getDeviceCompositionChanges( + DisplayId, bool frameUsesClientComposition, + std::optional<DeviceRequestedChanges>* outChanges) override; status_t setClientTarget(DisplayId displayId, uint32_t slot, const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target, ui::Dataspace dataspace) override; @@ -231,15 +241,6 @@ public: // reset state when an external, non-virtual display is disconnected void disconnectDisplay(DisplayId displayId) override; - // does this display have layers handled by HWC - bool hasDeviceComposition(const std::optional<DisplayId>& displayId) const override; - - // does this display have pending request to flip client target - bool hasFlipClientTargetRequest(const std::optional<DisplayId>& displayId) const override; - - // does this display have layers handled by GLES - bool hasClientComposition(const std::optional<DisplayId>& displayId) const override; - // get the present fence received from the last call to present. sp<Fence> getPresentFence(DisplayId displayId) const override; @@ -326,14 +327,10 @@ private: std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId); - static void validateChange(HWC2::Composition from, HWC2::Composition to); - struct DisplayData { bool isVirtual = false; - bool hasClientComposition = false; - bool hasDeviceComposition = false; + HWC2::Display* hwcDisplay = nullptr; - HWC2::DisplayRequest displayRequests; sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; buffer_handle_t outbufHandle = nullptr; diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp new file mode 100644 index 0000000000..006dbfe67e --- /dev/null +++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "FrameTracer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "FrameTracer.h" + +#include <android-base/stringprintf.h> + +#include <algorithm> +#include <mutex> + +PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(android::FrameTracer::FrameTracerDataSource); + +namespace android { + +void FrameTracer::initialize() { + std::call_once(mInitializationFlag, [this]() { + perfetto::TracingInitArgs args; + args.backends = perfetto::kSystemBackend; + perfetto::Tracing::Initialize(args); + registerDataSource(); + }); +} + +void FrameTracer::registerDataSource() { + perfetto::DataSourceDescriptor dsd; + dsd.set_name(kFrameTracerDataSource); + FrameTracerDataSource::Register(dsd); +} + +void FrameTracer::traceNewLayer(int32_t layerID, const std::string& layerName) { + FrameTracerDataSource::Trace([this, layerID, &layerName](FrameTracerDataSource::TraceContext) { + if (mTraceTracker.find(layerID) == mTraceTracker.end()) { + std::lock_guard<std::mutex> lock(mTraceMutex); + mTraceTracker[layerID].layerName = layerName; + } + }); +} + +void FrameTracer::traceTimestamp(int32_t layerID, uint64_t bufferID, uint64_t frameNumber, + nsecs_t timestamp, FrameEvent::BufferEventType type, + nsecs_t duration) { + FrameTracerDataSource::Trace([this, layerID, bufferID, frameNumber, timestamp, type, + duration](FrameTracerDataSource::TraceContext ctx) { + std::lock_guard<std::mutex> lock(mTraceMutex); + if (mTraceTracker.find(layerID) == mTraceTracker.end()) { + return; + } + + // Handle any pending fences for this buffer. + tracePendingFencesLocked(ctx, layerID, bufferID); + + // Complete current trace. + traceLocked(ctx, layerID, bufferID, frameNumber, timestamp, type, duration); + }); +} + +void FrameTracer::traceFence(int32_t layerID, uint64_t bufferID, uint64_t frameNumber, + const std::shared_ptr<FenceTime>& fence, + FrameEvent::BufferEventType type, nsecs_t startTime) { + FrameTracerDataSource::Trace([this, layerID, bufferID, frameNumber, &fence, type, + startTime](FrameTracerDataSource::TraceContext ctx) { + const nsecs_t signalTime = fence->getSignalTime(); + if (signalTime != Fence::SIGNAL_TIME_INVALID) { + std::lock_guard<std::mutex> lock(mTraceMutex); + if (mTraceTracker.find(layerID) == mTraceTracker.end()) { + return; + } + + // Handle any pending fences for this buffer. + tracePendingFencesLocked(ctx, layerID, bufferID); + + if (signalTime != Fence::SIGNAL_TIME_PENDING) { + traceSpanLocked(ctx, layerID, bufferID, frameNumber, type, startTime, signalTime); + } else { + mTraceTracker[layerID].pendingFences[bufferID].push_back( + {.frameNumber = frameNumber, + .type = type, + .fence = fence, + .startTime = startTime}); + } + } + }); +} + +void FrameTracer::tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx, + int32_t layerID, uint64_t bufferID) { + if (mTraceTracker[layerID].pendingFences.count(bufferID)) { + auto& pendingFences = mTraceTracker[layerID].pendingFences[bufferID]; + for (size_t i = 0; i < pendingFences.size(); ++i) { + auto& pendingFence = pendingFences[i]; + + nsecs_t signalTime = Fence::SIGNAL_TIME_INVALID; + if (pendingFence.fence && pendingFence.fence->isValid()) { + signalTime = pendingFence.fence->getSignalTime(); + if (signalTime == Fence::SIGNAL_TIME_PENDING) { + continue; + } + } + + if (signalTime != Fence::SIGNAL_TIME_INVALID && + systemTime() - signalTime < kFenceSignallingDeadline) { + traceSpanLocked(ctx, layerID, bufferID, pendingFence.frameNumber, pendingFence.type, + pendingFence.startTime, signalTime); + } + + pendingFences.erase(pendingFences.begin() + i); + --i; + } + } +} + +void FrameTracer::traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID, + uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp, + FrameEvent::BufferEventType type, nsecs_t duration) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp(timestamp); + auto* event = packet->set_graphics_frame_event()->set_buffer_event(); + event->set_buffer_id(static_cast<uint32_t>(bufferID)); + event->set_frame_number(frameNumber); + event->set_type(type); + + if (mTraceTracker.find(layerID) != mTraceTracker.end() && + !mTraceTracker[layerID].layerName.empty()) { + const std::string& layerName = mTraceTracker[layerID].layerName; + event->set_layer_name(layerName.c_str(), layerName.size()); + } + + if (duration > 0) { + event->set_duration_ns(duration); + } +} + +void FrameTracer::traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID, + uint64_t bufferID, uint64_t frameNumber, + FrameEvent::BufferEventType type, nsecs_t startTime, + nsecs_t endTime) { + nsecs_t timestamp = endTime; + nsecs_t duration = 0; + if (startTime > 0 && startTime < endTime) { + timestamp = startTime; + duration = endTime - startTime; + } + traceLocked(ctx, layerID, bufferID, frameNumber, timestamp, type, duration); +} + +void FrameTracer::onDestroy(int32_t layerID) { + std::lock_guard<std::mutex> traceLock(mTraceMutex); + mTraceTracker.erase(layerID); +} + +std::string FrameTracer::miniDump() { + std::string result = "FrameTracer miniDump:\n"; + std::lock_guard<std::mutex> lock(mTraceMutex); + android::base::StringAppendF(&result, "Number of layers currently being traced is %zu\n", + mTraceTracker.size()); + return result; +} + +} // namespace android diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.h b/services/surfaceflinger/FrameTracer/FrameTracer.h new file mode 100644 index 0000000000..d4dfab94eb --- /dev/null +++ b/services/surfaceflinger/FrameTracer/FrameTracer.h @@ -0,0 +1,106 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include <perfetto/trace/android/graphics_frame_event.pbzero.h> +#include <perfetto/tracing.h> +#include <ui/FenceTime.h> + +#include <mutex> +#include <unordered_map> + +namespace android { + +class FrameTracer { +public: + class FrameTracerDataSource : public perfetto::DataSource<FrameTracerDataSource> { + virtual void OnSetup(const SetupArgs&) override{}; + virtual void OnStart(const StartArgs&) override{}; + virtual void OnStop(const StopArgs&) override{}; + }; + + using FrameEvent = perfetto::protos::pbzero::GraphicsFrameEvent; + + ~FrameTracer() = default; + + // Sets up the perfetto tracing backend and data source. + void initialize(); + // Registers the data source with the perfetto backend. Called as part of initialize() + // and should not be called manually outside of tests. Public to allow for substituting a + // perfetto::kInProcessBackend in tests. + void registerDataSource(); + // Starts tracking a new layer for tracing. Needs to be called once before traceTimestamp() or + // traceFence() for each layer. + void traceNewLayer(int32_t layerID, const std::string& layerName); + // Creates a trace point at the timestamp provided. + void traceTimestamp(int32_t layerID, uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp, + FrameEvent::BufferEventType type, nsecs_t duration = 0); + // Creates a trace point after the provided fence has been signalled. If a startTime is provided + // the trace will have be timestamped from startTime until fence signalling time. If no + // startTime is provided, a durationless trace point will be created timestamped at fence + // signalling time. If the fence hasn't signalled yet, the trace point will be created the next + // time after signalling a trace call for this buffer occurs. + void traceFence(int32_t layerID, uint64_t bufferID, uint64_t frameNumber, + const std::shared_ptr<FenceTime>& fence, FrameEvent::BufferEventType type, + nsecs_t startTime = 0); + + // Takes care of cleanup when a layer is destroyed. + void onDestroy(int32_t layerID); + + std::string miniDump(); + + static constexpr char kFrameTracerDataSource[] = "android.surfaceflinger.frame"; + + // The maximum amount of time a fence has to signal before it is discarded. + // Used to avoid fences from previous traces generating new trace points in later ones. + // Public for testing. + static constexpr nsecs_t kFenceSignallingDeadline = 60'000'000'000; // 60 seconds + +private: + struct PendingFence { + uint64_t frameNumber; + FrameEvent::BufferEventType type; + std::shared_ptr<FenceTime> fence; + nsecs_t startTime; + }; + + struct TraceRecord { + std::string layerName; + using BufferID = uint64_t; + std::unordered_map<BufferID, std::vector<PendingFence>> pendingFences; + }; + + // Checks if any pending fences for a layer and buffer have signalled and, if they have, creates + // trace points for them. + void tracePendingFencesLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID, + uint64_t bufferID); + // Creates a trace point by translating a start time and an end time to a timestamp and + // duration. If startTime is later than end time it sets end time as the timestamp and the + // duration to 0. Used by traceFence(). + void traceSpanLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID, + uint64_t bufferID, uint64_t frameNumber, FrameEvent::BufferEventType type, + nsecs_t startTime, nsecs_t endTime); + void traceLocked(FrameTracerDataSource::TraceContext& ctx, int32_t layerID, uint64_t bufferID, + uint64_t frameNumber, nsecs_t timestamp, FrameEvent::BufferEventType type, + nsecs_t duration = 0); + + std::mutex mTraceMutex; + std::unordered_map<int32_t, TraceRecord> mTraceTracker; + std::once_flag mInitializationFlag; +}; + +} // namespace android diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 9f0b5d9ba4..7065460b2c 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -22,11 +22,11 @@ #include "Layer.h" #include <android-base/stringprintf.h> +#include <binder/IPCThreadState.h> #include <compositionengine/Display.h> #include <compositionengine/Layer.h> #include <compositionengine/LayerFECompositionState.h> #include <compositionengine/OutputLayer.h> -#include <compositionengine/impl/LayerCompositionState.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <cutils/compiler.h> #include <cutils/native_handle.h> @@ -57,6 +57,7 @@ #include "Colorizer.h" #include "DisplayDevice.h" #include "DisplayHardware/HWComposer.h" +#include "FrameTracer/FrameTracer.h" #include "LayerProtoHelper.h" #include "LayerRejecter.h" #include "MonitoredProducer.h" @@ -76,7 +77,6 @@ Layer::Layer(const LayerCreationArgs& args) mName(args.name), mClientRef(args.client), mWindowType(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0)) { - mCurrentCrop.makeInvalid(); uint32_t layerFlags = 0; if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden; @@ -105,7 +105,7 @@ Layer::Layer(const LayerCreationArgs& args) mCurrentState.acquireFence = new Fence(-1); mCurrentState.dataspace = ui::Dataspace::UNKNOWN; mCurrentState.hdrMetadata.validTypes = 0; - mCurrentState.surfaceDamageRegion.clear(); + mCurrentState.surfaceDamageRegion = Region::INVALID_REGION; mCurrentState.cornerRadius = 0.0f; mCurrentState.api = -1; mCurrentState.hasColorTransform = false; @@ -121,7 +121,8 @@ Layer::Layer(const LayerCreationArgs& args) mFrameTracker.setDisplayRefreshPeriod(compositorTiming.interval); mSchedulerLayerHandle = mFlinger->mScheduler->registerLayer(mName.c_str(), mWindowType); - + mCallingPid = args.callingPid; + mCallingUid = args.callingUid; mFlinger->onLayerCreated(); } @@ -135,6 +136,21 @@ Layer::~Layer() { mFlinger->onLayerDestroyed(this); } +LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, + const String8& name, uint32_t w, uint32_t h, uint32_t flags, + LayerMetadata metadata) + : flinger(flinger), + client(client), + name(name), + w(w), + h(h), + flags(flags), + metadata(std::move(metadata)) { + IPCThreadState* ipc = IPCThreadState::self(); + callingPid = ipc->getCallingPid(); + callingUid = ipc->getCallingUid(); +} + // --------------------------------------------------------------------------- // callbacks // --------------------------------------------------------------------------- @@ -241,37 +257,6 @@ sp<IBinder> Layer::getHandle() { // h/w composer set-up // --------------------------------------------------------------------------- -bool Layer::hasHwcLayer(const sp<const DisplayDevice>& displayDevice) { - auto outputLayer = findOutputLayerForDisplay(displayDevice); - LOG_FATAL_IF(!outputLayer); - return outputLayer->getState().hwc && (*outputLayer->getState().hwc).hwcLayer != nullptr; -} - -HWC2::Layer* Layer::getHwcLayer(const sp<const DisplayDevice>& displayDevice) { - auto outputLayer = findOutputLayerForDisplay(displayDevice); - if (!outputLayer || !outputLayer->getState().hwc) { - return nullptr; - } - return (*outputLayer->getState().hwc).hwcLayer.get(); -} - -Rect Layer::getContentCrop() const { - // this is the crop rectangle that applies to the buffer - // itself (as opposed to the window) - Rect crop; - if (!mCurrentCrop.isEmpty()) { - // if the buffer crop is defined, we use that - crop = mCurrentCrop; - } else if (mActiveBuffer != nullptr) { - // otherwise we use the whole buffer - crop = mActiveBuffer->getBounds(); - } else { - // if we don't have a buffer yet, we use an empty/invalid crop - crop.makeInvalid(); - } - return crop; -} - static Rect reduce(const Rect& win, const Region& exclude) { if (CC_LIKELY(exclude.isEmpty())) { return win; @@ -316,7 +301,7 @@ ui::Transform Layer::getBufferScaleTransform() const { // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g. // it isFixedSize) then there may be additional scaling not accounted // for in the layer transform. - if (!isFixedSize() || !mActiveBuffer) { + if (!isFixedSize() || getBuffer() == nullptr) { return {}; } @@ -328,10 +313,10 @@ ui::Transform Layer::getBufferScaleTransform() const { return {}; } - int bufferWidth = mActiveBuffer->getWidth(); - int bufferHeight = mActiveBuffer->getHeight(); + int bufferWidth = getBuffer()->getWidth(); + int bufferHeight = getBuffer()->getHeight(); - if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + if (getBufferTransform() & NATIVE_WINDOW_TRANSFORM_ROT_90) { std::swap(bufferWidth, bufferHeight); } @@ -346,7 +331,7 @@ ui::Transform Layer::getBufferScaleTransform() const { ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const { // We need to mirror this scaling to child surfaces or we will break the contract where WM can // treat child surfaces as pixels in the parent surface. - if (!isFixedSize() || !mActiveBuffer) { + if (!isFixedSize() || getBuffer() == nullptr) { return mEffectiveTransform; } return mEffectiveTransform * bufferScaleTransform; @@ -355,7 +340,7 @@ ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTrans FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const { // We need the pre scaled layer bounds when computing child bounds to make sure the child is // cropped to its parent layer after any buffer transform scaling is applied. - if (!isFixedSize() || !mActiveBuffer) { + if (!isFixedSize() || getBuffer() == nullptr) { return mBounds; } return bufferScaleTransform.inverse().transform(mBounds); @@ -413,15 +398,44 @@ void Layer::setupRoundedCornersCropCoordinates(Rect win, win.bottom -= roundedCornersCrop.top; } -void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const { +void Layer::latchBasicGeometry(compositionengine::LayerFECompositionState& compositionState) const { const auto& drawingState{getDrawingState()}; - auto alpha = static_cast<float>(getAlpha()); - auto blendMode = HWC2::BlendMode::None; - if (!isOpaque(drawingState) || alpha != 1.0f) { - blendMode = - mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage; + const uint32_t layerStack = getLayerStack(); + const auto alpha = static_cast<float>(getAlpha()); + const bool opaque = isOpaque(drawingState); + const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f; + + auto blendMode = Hwc2::IComposerClient::BlendMode::NONE; + if (!opaque || alpha != 1.0f) { + blendMode = mPremultipliedAlpha ? Hwc2::IComposerClient::BlendMode::PREMULTIPLIED + : Hwc2::IComposerClient::BlendMode::COVERAGE; } + // TODO(b/121291683): Instead of filling in a passed-in compositionState + // structure, switch to Layer owning the structure and have + // CompositionEngine be able to get a reference to it. + + compositionState.layerStackId = + (layerStack != ~0u) ? std::make_optional(layerStack) : std::nullopt; + compositionState.internalOnly = getPrimaryDisplayOnly(); + compositionState.isVisible = isVisible(); + compositionState.isOpaque = opaque && !usesRoundedCorners && alpha == 1.f; + + compositionState.contentDirty = contentDirty; + contentDirty = false; + + compositionState.geomLayerBounds = mBounds; + compositionState.geomLayerTransform = getTransform(); + compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse(); + compositionState.transparentRegionHint = getActiveTransparentRegion(drawingState); + + compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); + compositionState.alpha = alpha; +} + +void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const { + const auto& drawingState{getDrawingState()}; + int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0); int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0); sp<Layer> parent = mDrawingParent.promote(); @@ -429,160 +443,120 @@ void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositio auto& parentState = parent->getDrawingState(); const int parentType = parentState.metadata.getInt32(METADATA_WINDOW_TYPE, 0); const int parentAppId = parentState.metadata.getInt32(METADATA_OWNER_UID, 0); - if (parentType >= 0 || parentAppId >= 0) { + if (parentType > 0 && parentAppId > 0) { type = parentType; appId = parentAppId; } } - compositionState.geomLayerTransform = getTransform(); - compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse(); compositionState.geomBufferSize = getBufferSize(drawingState); - compositionState.geomContentCrop = getContentCrop(); + compositionState.geomContentCrop = getBufferCrop(); compositionState.geomCrop = getCrop(drawingState); - compositionState.geomBufferTransform = mCurrentTransform; + compositionState.geomBufferTransform = getBufferTransform(); compositionState.geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse(); - compositionState.geomActiveTransparentRegion = getActiveTransparentRegion(drawingState); - compositionState.geomLayerBounds = mBounds; compositionState.geomUsesSourceCrop = usesSourceCrop(); compositionState.isSecure = isSecure(); - compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); - compositionState.alpha = alpha; compositionState.type = type; compositionState.appId = appId; } -void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState, - bool includeGeometry) const { - if (includeGeometry) { - latchGeometry(compositionState); - } -} - -const char* Layer::getDebugName() const { - return mName.string(); -} - -void Layer::forceClientComposition(const sp<DisplayDevice>& display) { - const auto outputLayer = findOutputLayerForDisplay(display); - LOG_FATAL_IF(!outputLayer); - outputLayer->editState().forceClientComposition = true; -} +void Layer::latchPerFrameState(compositionengine::LayerFECompositionState& compositionState) const { + const auto& drawingState{getDrawingState()}; + compositionState.forceClientComposition = false; -bool Layer::getForceClientComposition(const sp<DisplayDevice>& display) { - const auto outputLayer = findOutputLayerForDisplay(display); - LOG_FATAL_IF(!outputLayer); - return outputLayer->getState().forceClientComposition; -} + compositionState.isColorspaceAgnostic = isColorSpaceAgnostic(); + compositionState.dataspace = getDataSpace(); + compositionState.colorTransform = getColorTransform(); + compositionState.colorTransformIsIdentity = !hasColorTransform(); + compositionState.surfaceDamage = surfaceDamageRegion; + compositionState.hasProtectedContent = isProtected(); -void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) { - const auto outputLayer = findOutputLayerForDisplay(display); - LOG_FATAL_IF(!outputLayer); + const bool usesRoundedCorners = getRoundedCornerState().radius != 0.f; + compositionState.isOpaque = + isOpaque(drawingState) && !usesRoundedCorners && getAlpha() == 1.0_hf; - if (!outputLayer->getState().hwc || - (*outputLayer->getState().hwc).hwcCompositionType != - Hwc2::IComposerClient::Composition::CURSOR) { - return; + // Force client composition for special cases known only to the front-end. + if (isHdrY410() || usesRoundedCorners) { + compositionState.forceClientComposition = true; } +} +void Layer::latchCursorCompositionState( + compositionengine::LayerFECompositionState& compositionState) const { // This gives us only the "orientation" component of the transform - const State& s(getDrawingState()); + const State& drawingState{getDrawingState()}; // Apply the layer's transform, followed by the display's global transform // Here we're guaranteed that the layer's transform preserves rects - Rect win = getCroppedBufferSize(s); + Rect win = getCroppedBufferSize(drawingState); // Subtract the transparent region and snap to the bounds - Rect bounds = reduce(win, getActiveTransparentRegion(s)); + Rect bounds = reduce(win, getActiveTransparentRegion(drawingState)); Rect frame(getTransform().transform(bounds)); - frame.intersect(display->getViewport(), &frame); - auto& displayTransform = display->getTransform(); - auto position = displayTransform.transform(frame); - auto error = - (*outputLayer->getState().hwc).hwcLayer->setCursorPosition(position.left, position.top); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set cursor position " - "to (%d, %d): %s (%d)", - mName.string(), position.left, position.top, to_string(error).c_str(), - static_cast<int32_t>(error)); + compositionState.cursorFrame = frame; } -// --------------------------------------------------------------------------- -// drawing... -// --------------------------------------------------------------------------- +bool Layer::onPreComposition(nsecs_t) { + return false; +} + +void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState, + compositionengine::LayerFE::StateSubset subset) const { + using StateSubset = compositionengine::LayerFE::StateSubset; -bool Layer::prepareClientLayer(const RenderArea& renderArea, const Region& clip, - Region& clearRegion, const bool supportProtectedContent, - renderengine::LayerSettings& layer) { - return prepareClientLayer(renderArea, clip, false, clearRegion, supportProtectedContent, layer); + switch (subset) { + case StateSubset::BasicGeometry: + latchBasicGeometry(compositionState); + break; + + case StateSubset::GeometryAndContent: + latchBasicGeometry(compositionState); + latchGeometry(compositionState); + latchPerFrameState(compositionState); + break; + + case StateSubset::Content: + latchPerFrameState(compositionState); + break; + } } -bool Layer::prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform, - Region& clearRegion, const bool supportProtectedContent, - renderengine::LayerSettings& layer) { - return prepareClientLayer(renderArea, Region(renderArea.getBounds()), useIdentityTransform, - clearRegion, supportProtectedContent, layer); +const char* Layer::getDebugName() const { + return mName.string(); } -bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/, - bool useIdentityTransform, Region& /*clearRegion*/, - const bool /*supportProtectedContent*/, - renderengine::LayerSettings& layer) { +// --------------------------------------------------------------------------- +// drawing... +// --------------------------------------------------------------------------- + +std::optional<renderengine::LayerSettings> Layer::prepareClientComposition( + compositionengine::LayerFE::ClientCompositionTargetSettings& targetSettings) { + if (!getCompositionLayer()) { + return {}; + } + FloatRect bounds = getBounds(); half alpha = getAlpha(); - layer.geometry.boundaries = bounds; - if (useIdentityTransform) { - layer.geometry.positionTransform = mat4(); + renderengine::LayerSettings layerSettings; + layerSettings.geometry.boundaries = bounds; + if (targetSettings.useIdentityTransform) { + layerSettings.geometry.positionTransform = mat4(); } else { - const ui::Transform transform = getTransform(); - mat4 m; - m[0][0] = transform[0][0]; - m[0][1] = transform[0][1]; - m[0][3] = transform[0][2]; - m[1][0] = transform[1][0]; - m[1][1] = transform[1][1]; - m[1][3] = transform[1][2]; - m[3][0] = transform[2][0]; - m[3][1] = transform[2][1]; - m[3][3] = transform[2][2]; - layer.geometry.positionTransform = m; + layerSettings.geometry.positionTransform = getTransform().asMatrix4(); } if (hasColorTransform()) { - layer.colorTransform = getColorTransform(); + layerSettings.colorTransform = getColorTransform(); } const auto roundedCornerState = getRoundedCornerState(); - layer.geometry.roundedCornersRadius = roundedCornerState.radius; - layer.geometry.roundedCornersCrop = roundedCornerState.cropRect; - - layer.alpha = alpha; - layer.sourceDataspace = mCurrentDataSpace; - return true; -} - -void Layer::setCompositionType(const sp<const DisplayDevice>& display, - Hwc2::IComposerClient::Composition type) { - const auto outputLayer = findOutputLayerForDisplay(display); - LOG_FATAL_IF(!outputLayer); - LOG_FATAL_IF(!outputLayer->getState().hwc); - auto& compositionState = outputLayer->editState(); - - ALOGV("setCompositionType(%" PRIx64 ", %s, %d)", ((*compositionState.hwc).hwcLayer)->getId(), - toString(type).c_str(), 1); - if ((*compositionState.hwc).hwcCompositionType != type) { - ALOGV(" actually setting"); - (*compositionState.hwc).hwcCompositionType = type; + layerSettings.geometry.roundedCornersRadius = roundedCornerState.radius; + layerSettings.geometry.roundedCornersCrop = roundedCornerState.cropRect; - auto error = (*compositionState.hwc) - .hwcLayer->setCompositionType(static_cast<HWC2::Composition>(type)); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set " - "composition type %s: %s (%d)", - mName.string(), toString(type).c_str(), to_string(error).c_str(), - static_cast<int32_t>(error)); - } + layerSettings.alpha = alpha; + layerSettings.sourceDataspace = getDataSpace(); + return layerSettings; } Hwc2::IComposerClient::Composition Layer::getCompositionType( @@ -618,58 +592,11 @@ bool Layer::addSyncPoint(const std::shared_ptr<SyncPoint>& point) { // local state // ---------------------------------------------------------------------------- -void Layer::computeGeometry(const RenderArea& renderArea, - renderengine::Mesh& mesh, - bool useIdentityTransform) const { - const ui::Transform renderAreaTransform(renderArea.getTransform()); - FloatRect win = getBounds(); - - vec2 lt = vec2(win.left, win.top); - vec2 lb = vec2(win.left, win.bottom); - vec2 rb = vec2(win.right, win.bottom); - vec2 rt = vec2(win.right, win.top); - - ui::Transform layerTransform = getTransform(); - if (!useIdentityTransform) { - lt = layerTransform.transform(lt); - lb = layerTransform.transform(lb); - rb = layerTransform.transform(rb); - rt = layerTransform.transform(rt); - } - - renderengine::Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); - position[0] = renderAreaTransform.transform(lt); - position[1] = renderAreaTransform.transform(lb); - position[2] = renderAreaTransform.transform(rb); - position[3] = renderAreaTransform.transform(rt); -} - bool Layer::isSecure() const { const State& s(mDrawingState); return (s.flags & layer_state_t::eLayerSecure); } -void Layer::setVisibleRegion(const Region& visibleRegion) { - // always called from main thread - this->visibleRegion = visibleRegion; -} - -void Layer::setCoveredRegion(const Region& coveredRegion) { - // always called from main thread - this->coveredRegion = coveredRegion; -} - -void Layer::setVisibleNonTransparentRegion(const Region& setVisibleNonTransparentRegion) { - // always called from main thread - this->visibleNonTransparentRegion = setVisibleNonTransparentRegion; -} - -void Layer::clearVisibilityRegions() { - visibleRegion.clear(); - visibleNonTransparentRegion.clear(); - coveredRegion.clear(); -} - // ---------------------------------------------------------------------------- // transaction // ---------------------------------------------------------------------------- @@ -790,7 +717,7 @@ uint32_t Layer::doTransactionResize(uint32_t flags, State* stateToCommit) { " requested={ wh={%4u,%4u} }}\n" " drawing={ active ={ wh={%4u,%4u} crop={%4d,%4d,%4d,%4d} (%4d,%4d) }\n" " requested={ wh={%4u,%4u} }}\n", - this, getName().string(), mCurrentTransform, getEffectiveScalingMode(), + this, getName().string(), getBufferTransform(), getEffectiveScalingMode(), stateToCommit->active_legacy.w, stateToCommit->active_legacy.h, stateToCommit->crop_legacy.left, stateToCommit->crop_legacy.top, stateToCommit->crop_legacy.right, stateToCommit->crop_legacy.bottom, @@ -822,7 +749,7 @@ uint32_t Layer::doTransactionResize(uint32_t flags, State* stateToCommit) { const bool resizePending = ((stateToCommit->requested_legacy.w != stateToCommit->active_legacy.w) || (stateToCommit->requested_legacy.h != stateToCommit->active_legacy.h)) && - (mActiveBuffer != nullptr); + (getBuffer() != nullptr); if (!isFixedSize()) { if (resizePending && mSidebandStream == nullptr) { flags |= eDontUpdateGeometryState; @@ -835,11 +762,6 @@ uint32_t Layer::doTransactionResize(uint32_t flags, State* stateToCommit) { if (!(flags & eDontUpdateGeometryState)) { State& editCurrentState(getCurrentState()); - // If mFreezeGeometryUpdates is true we are in the setGeometryAppliesWithResize - // mode, which causes attributes which normally latch regardless of scaling mode, - // to be delayed. We copy the requested state to the active state making sure - // to respect these rules (again see Layer.h for a detailed discussion). - // // There is an awkward asymmetry in the handling of the crop states in the position // states, as can be seen below. Largely this arises from position and transform // being stored in the same data structure while having different latching rules. @@ -847,16 +769,8 @@ uint32_t Layer::doTransactionResize(uint32_t flags, State* stateToCommit) { // // Careful that "stateToCommit" and editCurrentState may not begin as equivalent due to // applyPendingStates in the presence of deferred transactions. - if (mFreezeGeometryUpdates) { - float tx = stateToCommit->active_legacy.transform.tx(); - float ty = stateToCommit->active_legacy.transform.ty(); - stateToCommit->active_legacy = stateToCommit->requested_legacy; - stateToCommit->active_legacy.transform.set(tx, ty); - editCurrentState.active_legacy = stateToCommit->active_legacy; - } else { - editCurrentState.active_legacy = editCurrentState.requested_legacy; - stateToCommit->active_legacy = stateToCommit->requested_legacy; - } + editCurrentState.active_legacy = editCurrentState.requested_legacy; + stateToCommit->active_legacy = stateToCommit->requested_legacy; } return flags; @@ -866,6 +780,15 @@ uint32_t Layer::doTransaction(uint32_t flags) { ATRACE_CALL(); if (mLayerDetached) { + // Ensure BLAST buffer callbacks are processed. + // detachChildren and mLayerDetached were implemented to avoid geometry updates + // to layers in the cases of animation. For BufferQueue layers buffers are still + // consumed as normal. This is useful as otherwise the client could get hung + // inevitably waiting on a buffer to return. We recreate this semantic for BufferQueue + // even though it is a little consistent. detachChildren is shortly slated for removal + // by the hierarchy mirroring work so we don't need to worry about it too much. + mDrawingState.callbackHandles = mCurrentState.callbackHandles; + mCurrentState.callbackHandles = {}; return flags; } @@ -923,7 +846,7 @@ uint32_t Layer::setTransactionFlags(uint32_t flags) { return mTransactionFlags.fetch_or(flags); } -bool Layer::setPosition(float x, float y, bool immediate) { +bool Layer::setPosition(float x, float y) { if (mCurrentState.requested_legacy.transform.tx() == x && mCurrentState.requested_legacy.transform.ty() == y) return false; @@ -933,14 +856,11 @@ bool Layer::setPosition(float x, float y, bool immediate) { // we want to apply the position portion of the transform matrix immediately, // but still delay scaling when resizing a SCALING_MODE_FREEZE layer. mCurrentState.requested_legacy.transform.set(x, y); - if (immediate && !mFreezeGeometryUpdates) { - // Here we directly update the active state - // unlike other setters, because we store it within - // the transform, but use different latching rules. - // b/38182305 - mCurrentState.active_legacy.transform.set(x, y); - } - mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate; + // Here we directly update the active state + // unlike other setters, because we store it within + // the transform, but use different latching rules. + // b/38182305 + mCurrentState.active_legacy.transform.set(x, y); mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -1010,6 +930,8 @@ void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) { mCurrentState.zOrderRelativeOf = relativeOf; mCurrentState.sequence++; mCurrentState.modified = true; + mCurrentState.isRelativeOf = relativeOf != nullptr; + setTransactionFlags(eTransactionNeeded); } @@ -1078,7 +1000,7 @@ bool Layer::setBackgroundColor(const half3& color, float alpha, ui::Dataspace da // create background color layer if one does not yet exist uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor; const String8& name = mName + "BackgroundColorLayer"; - mCurrentState.bgColorLayer = new ColorLayer( + mCurrentState.bgColorLayer = mFlinger->getFactory().createColorLayer( LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags, LayerMetadata())); // add to child list @@ -1146,14 +1068,11 @@ bool Layer::setFlags(uint8_t flags, uint8_t mask) { return true; } -bool Layer::setCrop_legacy(const Rect& crop, bool immediate) { +bool Layer::setCrop_legacy(const Rect& crop) { if (mCurrentState.requestedCrop_legacy == crop) return false; mCurrentState.sequence++; mCurrentState.requestedCrop_legacy = crop; - if (immediate && !mFreezeGeometryUpdates) { - mCurrentState.crop_legacy = crop; - } - mFreezeGeometryUpdates = mFreezeGeometryUpdates || !immediate; + mCurrentState.crop_legacy = crop; mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -1282,9 +1201,10 @@ LayerDebugInfo Layer::getLayerDebugInfo() const { info.mName = getName(); sp<Layer> parent = getParent(); info.mParentName = (parent == nullptr ? std::string("none") : parent->getName().string()); - info.mType = std::string(getTypeId()); + info.mType = getType(); info.mTransparentRegion = ds.activeTransparentRegion_legacy; - info.mVisibleRegion = visibleRegion; + + info.mVisibleRegion = debugGetVisibleRegionOnDefaultDisplay(); info.mSurfaceDamageRegion = surfaceDamageRegion; info.mLayerStack = getLayerStack(); info.mX = ds.active_legacy.transform.tx(); @@ -1296,13 +1216,13 @@ LayerDebugInfo Layer::getLayerDebugInfo() const { info.mColor = ds.color; info.mFlags = ds.flags; info.mPixelFormat = getPixelFormat(); - info.mDataSpace = static_cast<android_dataspace>(mCurrentDataSpace); + info.mDataSpace = static_cast<android_dataspace>(getDataSpace()); info.mMatrix[0][0] = ds.active_legacy.transform[0][0]; info.mMatrix[0][1] = ds.active_legacy.transform[0][1]; info.mMatrix[1][0] = ds.active_legacy.transform[1][0]; info.mMatrix[1][1] = ds.active_legacy.transform[1][1]; { - sp<const GraphicBuffer> buffer = mActiveBuffer; + sp<const GraphicBuffer> buffer = getBuffer(); if (buffer != 0) { info.mActiveBufferWidth = buffer->getWidth(); info.mActiveBufferHeight = buffer->getHeight(); @@ -1399,16 +1319,23 @@ void Layer::getFrameStats(FrameStats* outStats) const { } void Layer::dumpFrameEvents(std::string& result) { - StringAppendF(&result, "- Layer %s (%s, %p)\n", getName().string(), getTypeId(), this); + StringAppendF(&result, "- Layer %s (%s, %p)\n", getName().string(), getType(), this); Mutex::Autolock lock(mFrameEventHistoryMutex); mFrameEventHistory.checkFencesForCompletion(); mFrameEventHistory.dump(result); } +void Layer::dumpCallingUidPid(std::string& result) const { + StringAppendF(&result, "Layer %s (%s) pid:%d uid:%d\n", getName().string(), getType(), + mCallingPid, mCallingUid); +} + void Layer::onDisconnect() { Mutex::Autolock lock(mFrameEventHistoryMutex); mFrameEventHistory.onDisconnect(); - mFlinger->mTimeStats->onDestroy(getSequence()); + const int32_t layerID = getSequence(); + mFlinger->mTimeStats->onDestroy(layerID); + mFlinger->mFrameTracer->onDestroy(layerID); } void Layer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, @@ -1608,8 +1535,9 @@ bool Layer::hasColorTransform() const { bool Layer::isLegacyDataSpace() const { // return true when no higher bits are set - return !(mCurrentDataSpace & (ui::Dataspace::STANDARD_MASK | - ui::Dataspace::TRANSFER_MASK | ui::Dataspace::RANGE_MASK)); + return !(getDataSpace() & + (ui::Dataspace::STANDARD_MASK | ui::Dataspace::TRANSFER_MASK | + ui::Dataspace::RANGE_MASK)); } void Layer::setParent(const sp<Layer>& layer) { @@ -1623,7 +1551,7 @@ int32_t Layer::getZ() const { bool Layer::usingRelativeZ(LayerVector::StateSet stateSet) const { const bool useDrawing = stateSet == LayerVector::StateSet::Drawing; const State& state = useDrawing ? mDrawingState : mCurrentState; - return state.zOrderRelativeOf != nullptr; + return state.isRelativeOf; } __attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::makeTraversalList( @@ -1648,8 +1576,7 @@ __attribute__((no_sanitize("unsigned-integer-overflow"))) LayerVector Layer::mak } for (const sp<Layer>& child : children) { - const State& childState = useDrawing ? child->mDrawingState : child->mCurrentState; - if (childState.zOrderRelativeOf != nullptr) { + if (child->usingRelativeZ(stateSet)) { continue; } traverse.add(child); @@ -1887,17 +1814,16 @@ void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags) } } - auto buffer = mActiveBuffer; + auto buffer = getBuffer(); if (buffer != nullptr) { LayerProtoHelper::writeToProto(buffer, [&]() { return layerInfo->mutable_active_buffer(); }); - LayerProtoHelper::writeToProto(ui::Transform(mCurrentTransform), + LayerProtoHelper::writeToProto(ui::Transform(getBufferTransform()), layerInfo->mutable_buffer_transform()); } layerInfo->set_invalidate(contentDirty); layerInfo->set_is_protected(isProtected()); - layerInfo->set_dataspace( - dataspaceDetails(static_cast<android_dataspace>(mCurrentDataSpace))); + layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace()))); layerInfo->set_queued_frames(getQueuedFrameCount()); layerInfo->set_refresh_pending(isBufferLatched()); layerInfo->set_curr_frame(mCurrentFrameNumber); @@ -1908,7 +1834,7 @@ void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags) LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), [&]() { return layerInfo->mutable_position(); }); LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); }); - LayerProtoHelper::writeToProto(visibleRegion, + LayerProtoHelper::writeToProto(debugGetVisibleRegionOnDefaultDisplay(), [&]() { return layerInfo->mutable_visible_region(); }); LayerProtoHelper::writeToProto(surfaceDamageRegion, [&]() { return layerInfo->mutable_damage_region(); }); @@ -1933,7 +1859,7 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet if (traceFlags & SurfaceTracing::TRACE_CRITICAL) { layerInfo->set_id(sequence); layerInfo->set_name(getName().c_str()); - layerInfo->set_type(String8(getTypeId())); + layerInfo->set_type(getType()); for (const auto& child : children) { layerInfo->add_children(child->sequence); @@ -2003,46 +1929,10 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet } } -void Layer::writeToProtoCompositionState(LayerProto* layerInfo, - const sp<DisplayDevice>& displayDevice, - uint32_t traceFlags) const { - auto outputLayer = findOutputLayerForDisplay(displayDevice); - if (!outputLayer) { - return; - } - - writeToProtoDrawingState(layerInfo, traceFlags); - writeToProtoCommonState(layerInfo, LayerVector::StateSet::Drawing, traceFlags); - - const auto& compositionState = outputLayer->getState(); - - const Rect& frame = compositionState.displayFrame; - LayerProtoHelper::writeToProto(frame, [&]() { return layerInfo->mutable_hwc_frame(); }); - - const FloatRect& crop = compositionState.sourceCrop; - LayerProtoHelper::writeToProto(crop, [&]() { return layerInfo->mutable_hwc_crop(); }); - - const int32_t transform = - getCompositionLayer() ? static_cast<int32_t>(compositionState.bufferTransform) : 0; - layerInfo->set_hwc_transform(transform); - - const int32_t compositionType = - static_cast<int32_t>(compositionState.hwc ? (*compositionState.hwc).hwcCompositionType - : Hwc2::IComposerClient::Composition::CLIENT); - layerInfo->set_hwc_composition_type(compositionType); -} - bool Layer::isRemovedFromCurrentState() const { return mRemovedFromCurrentState; } -// Debug helper for b/137560795 -#define INT32_MIGHT_OVERFLOW(n) (((n) >= INT32_MAX / 2) || ((n) <= INT32_MIN / 2)) - -#define RECT_BOUNDS_INVALID(rect) \ - (INT32_MIGHT_OVERFLOW((rect).left) || INT32_MIGHT_OVERFLOW((rect).right) || \ - INT32_MIGHT_OVERFLOW((rect).bottom) || INT32_MIGHT_OVERFLOW((rect).top)) - InputWindowInfo Layer::fillInputInfo() { InputWindowInfo info = mDrawingState.inputInfo; @@ -2053,14 +1943,14 @@ InputWindowInfo Layer::fillInputInfo() { ui::Transform t = getTransform(); const float xScale = t.sx(); const float yScale = t.sy(); - float xSurfaceInset = info.surfaceInset; - float ySurfaceInset = info.surfaceInset; + int32_t xSurfaceInset = info.surfaceInset; + int32_t ySurfaceInset = info.surfaceInset; if (xScale != 1.0f || yScale != 1.0f) { - info.windowXScale *= 1.0f / xScale; - info.windowYScale *= 1.0f / yScale; + info.windowXScale *= (xScale != 0.0f) ? 1.0f / xScale : 0.0f; + info.windowYScale *= (yScale != 0.0f) ? 1.0f / yScale : 0.0f; info.touchableRegion.scaleSelf(xScale, yScale); - xSurfaceInset *= xScale; - ySurfaceInset *= yScale; + xSurfaceInset = std::round(xSurfaceInset * xScale); + ySurfaceInset = std::round(ySurfaceInset * yScale); } // Transform layer size to screen space and inset it by surface insets. @@ -2073,25 +1963,10 @@ InputWindowInfo Layer::fillInputInfo() { } layerBounds = t.transform(layerBounds); - // debug check for b/137560795 - { - if (RECT_BOUNDS_INVALID(layerBounds)) { - ALOGE("layer %s bounds are invalid (%" PRIi32 ", %" PRIi32 ", %" PRIi32 ", %" PRIi32 - ")", - mName.c_str(), layerBounds.left, layerBounds.top, layerBounds.right, - layerBounds.bottom); - std::string out; - getTransform().dump(out, "Transform"); - ALOGE("%s", out.c_str()); - layerBounds.left = layerBounds.top = layerBounds.right = layerBounds.bottom = 0; - } + // clamp inset to layer bounds + xSurfaceInset = (xSurfaceInset >= 0) ? std::min(xSurfaceInset, layerBounds.getWidth() / 2) : 0; + ySurfaceInset = (ySurfaceInset >= 0) ? std::min(ySurfaceInset, layerBounds.getHeight() / 2) : 0; - if (INT32_MIGHT_OVERFLOW(xSurfaceInset) || INT32_MIGHT_OVERFLOW(ySurfaceInset)) { - ALOGE("layer %s surface inset are invalid (%" PRIi32 ", %" PRIi32 ")", mName.c_str(), - int32_t(xSurfaceInset), int32_t(ySurfaceInset)); - xSurfaceInset = ySurfaceInset = 0; - } - } layerBounds.inset(xSurfaceInset, ySurfaceInset, xSurfaceInset, ySurfaceInset); // Input coordinate should match the layer bounds. @@ -2128,7 +2003,7 @@ std::shared_ptr<compositionengine::Layer> Layer::getCompositionLayer() const { } bool Layer::canReceiveInput() const { - return isVisible(); + return !isHiddenByPolicy(); } compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( @@ -2136,6 +2011,141 @@ compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionLayer().get()); } +Region Layer::debugGetVisibleRegionOnDefaultDisplay() const { + sp<DisplayDevice> displayDevice = mFlinger->getDefaultDisplayDeviceLocked(); + if (displayDevice == nullptr) { + return {}; + } + + auto outputLayer = findOutputLayerForDisplay(displayDevice); + if (outputLayer == nullptr) { + return {}; + } + + return outputLayer->getState().visibleRegion; +} + +void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) { + // copy drawing state from cloned layer + mDrawingState = clonedFrom->mDrawingState; + mClonedFrom = clonedFrom; + + // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple + // InputWindows per client token yet. + mDrawingState.inputInfo.token = nullptr; +} + +void Layer::updateMirrorInfo() { + if (mClonedChild == nullptr || !mClonedChild->isClonedFromAlive()) { + // If mClonedChild is null, there is nothing to mirror. If isClonedFromAlive returns false, + // it means that there is a clone, but the layer it was cloned from has been destroyed. In + // that case, we want to delete the reference to the clone since we want it to get + // destroyed. The root, this layer, will still be around since the client can continue + // to hold a reference, but no cloned layers will be displayed. + mClonedChild = nullptr; + return; + } + + std::map<sp<Layer>, sp<Layer>> clonedLayersMap; + // If the real layer exists and is in current state, add the clone as a child of the root. + // There's no need to remove from drawingState when the layer is offscreen since currentState is + // copied to drawingState for the root layer. So the clonedChild is always removed from + // drawingState and then needs to be added back each traversal. + if (!mClonedChild->getClonedFrom()->isRemovedFromCurrentState()) { + addChildToDrawing(mClonedChild); + } + + mClonedChild->updateClonedDrawingState(clonedLayersMap); + mClonedChild->updateClonedChildren(this, clonedLayersMap); + mClonedChild->updateClonedRelatives(clonedLayersMap); +} + +void Layer::updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { + // If the layer the clone was cloned from is alive, copy the content of the drawingState + // to the clone. If the real layer is no longer alive, continue traversing the children + // since we may be able to pull out other children that are still alive. + if (isClonedFromAlive()) { + sp<Layer> clonedFrom = getClonedFrom(); + mDrawingState = clonedFrom->mDrawingState; + // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple + // InputWindows per client token yet. + mDrawingState.inputInfo.token = nullptr; + clonedLayersMap.emplace(clonedFrom, this); + } + + // The clone layer may have children in drawingState since they may have been created and + // added from a previous request to updateMirorInfo. This is to ensure we don't recreate clones + // that already exist, since we can just re-use them. + // The drawingChildren will not get overwritten by the currentChildren since the clones are + // not updated in the regular traversal. They are skipped since the root will lose the + // reference to them when it copies its currentChildren to drawing. + for (sp<Layer>& child : mDrawingChildren) { + child->updateClonedDrawingState(clonedLayersMap); + } +} + +void Layer::updateClonedChildren(const sp<Layer>& mirrorRoot, + std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) { + mDrawingChildren.clear(); + + if (!isClonedFromAlive()) { + return; + } + + sp<Layer> clonedFrom = getClonedFrom(); + for (sp<Layer>& child : clonedFrom->mDrawingChildren) { + if (child == mirrorRoot) { + // This is to avoid cyclical mirroring. + continue; + } + sp<Layer> clonedChild = clonedLayersMap[child]; + if (clonedChild == nullptr) { + clonedChild = child->createClone(); + clonedLayersMap[child] = clonedChild; + } + addChildToDrawing(clonedChild); + clonedChild->updateClonedChildren(mirrorRoot, clonedLayersMap); + } +} + +void Layer::updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> clonedLayersMap) { + mDrawingState.zOrderRelativeOf = nullptr; + mDrawingState.zOrderRelatives.clear(); + + if (!isClonedFromAlive()) { + return; + } + + sp<Layer> clonedFrom = getClonedFrom(); + for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) { + sp<Layer> relative = relativeWeak.promote(); + auto clonedRelative = clonedLayersMap[relative]; + if (clonedRelative != nullptr) { + mDrawingState.zOrderRelatives.add(clonedRelative); + } + } + + // Check if the relativeLayer for the real layer is part of the cloned hierarchy. + // It's possible that the layer it's relative to is outside the requested cloned hierarchy. + // In that case, we treat the layer as if the relativeOf has been removed. This way, it will + // still traverse the children, but the layer with the missing relativeOf will not be shown + // on screen. + sp<Layer> relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote(); + sp<Layer> clonedRelativeOf = clonedLayersMap[relativeOf]; + if (clonedRelativeOf != nullptr) { + mDrawingState.zOrderRelativeOf = clonedRelativeOf; + } + + for (sp<Layer>& child : mDrawingChildren) { + child->updateClonedRelatives(clonedLayersMap); + } +} + +void Layer::addChildToDrawing(const sp<Layer>& layer) { + mDrawingChildren.add(layer); + layer->mDrawingParent = this; +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 3b4d8733c7..3023cf5df3 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -80,9 +80,7 @@ class SurfaceInterceptor; struct LayerCreationArgs { LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata) - : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags), - metadata(std::move(metadata)) {} + uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata); SurfaceFlinger* flinger; const sp<Client>& client; @@ -91,17 +89,17 @@ struct LayerCreationArgs { uint32_t h; uint32_t flags; LayerMetadata metadata; + pid_t callingPid; + uid_t callingUid; + sp<const DisplayDevice> displayDevice; + uint32_t textureName; }; -class Layer : public virtual compositionengine::LayerFE { +class Layer : public compositionengine::LayerFE { static std::atomic<int32_t> sSequence; public: mutable bool contentDirty{false}; - // regions below are in window-manager space - Region visibleRegion; - Region coveredRegion; - Region visibleNonTransparentRegion; Region surfaceDamageRegion; // Layer serial number. This gives layers an explicit ordering, so we @@ -174,6 +172,7 @@ public: // If non-null, a Surface this Surface's Z-order is interpreted relative to. wp<Layer> zOrderRelativeOf; + bool isRelativeOf{false}; // A list of surfaces whose Z-order is interpreted relative to ours. SortedVector<wp<Layer>> zOrderRelatives; @@ -217,6 +216,7 @@ public: // recent callback handle. std::deque<sp<CallbackHandle>> callbackHandles; bool colorSpaceAgnostic; + nsecs_t desiredPresentTime = -1; }; explicit Layer(const LayerCreationArgs& args); @@ -267,9 +267,9 @@ public: // setPosition operates in parent buffer space (pre parent-transform) or display // space for top-level layers. - virtual bool setPosition(float x, float y, bool immediate); + virtual bool setPosition(float x, float y); // Buffer space - virtual bool setCrop_legacy(const Rect& crop, bool immediate); + virtual bool setCrop_legacy(const Rect& crop); // TODO(b/38182121): Could we eliminate the various latching modes by // using the layer hierarchy? @@ -329,7 +329,7 @@ public: virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace); virtual bool setColorSpaceAgnostic(const bool agnostic); - ui::Dataspace getDataSpace() const { return mCurrentDataSpace; } + virtual ui::Dataspace getDataSpace() const { return ui::Dataspace::UNKNOWN; } // Before color management is introduced, contents on Android have to be // desaturated in order to match what they appears like visually. @@ -357,8 +357,6 @@ public: return getLayerStack() == layerStack && (!mPrimaryDisplayOnly || isPrimaryDisplay); } - void computeGeometry(const RenderArea& renderArea, renderengine::Mesh& mesh, - bool useIdentityTransform) const; FloatRect getBounds(const Region& activeTransparentRegion) const; FloatRect getBounds() const; @@ -377,9 +375,19 @@ public: int32_t getSequence() const { return sequence; } + // For tracing. + // TODO: Replace with raw buffer id from buffer metadata when that becomes available. + // GraphicBuffer::getId() does not provide a reliable global identifier. Since the traces + // creates its tracks by buffer id and has no way of associating a buffer back to the process + // that created it, the current implementation is only sufficient for cases where a buffer is + // only used within a single layer. + uint64_t getCurrentBufferId() const { return getBuffer() ? getBuffer()->getId() : 0; } + // ----------------------------------------------------------------------- // Virtuals - virtual const char* getTypeId() const = 0; + + // Provide unique string for each class type in the Layer hierarchy + virtual const char* getType() const = 0; /* * isOpaque - true if this surface is opaque @@ -442,11 +450,6 @@ public: // thread. void writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; - // Write states that are modified by the main thread. This includes drawing - // state as well as buffer data and composition data for layers on the specified - // display. This should be called in the main or tracing thread. - void writeToProtoCompositionState(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice, - uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; // Write drawing or current state. If writing current state, the caller should hold the // external mStateLock. If writing drawing state, this function should be called on the // main or tracing thread. @@ -463,56 +466,63 @@ public: return s.activeTransparentRegion_legacy; } virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; } + virtual bool needsFiltering(const sp<const DisplayDevice>&) const { return false; } + + // This layer is not a clone, but it's the parent to the cloned hierarchy. The + // variable mClonedChild represents the top layer that will be cloned so this + // layer will be the parent of mClonedChild. + // The layers in the cloned hierarchy will match the lifetime of the real layers. That is + // if the real layer is destroyed, then the clone layer will also be destroyed. + sp<Layer> mClonedChild; + + virtual sp<Layer> createClone() = 0; + void updateMirrorInfo(); + virtual void updateCloneBufferInfo(){}; protected: - virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform, Region& clearRegion, - const bool supportProtectedContent, - renderengine::LayerSettings& layer); + sp<Layer> getClonedFrom() { return mClonedFrom != nullptr ? mClonedFrom.promote() : nullptr; } + bool isClone() { return mClonedFrom != nullptr; } + bool isClonedFromAlive() { return getClonedFrom() != nullptr; } + + virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom); + + void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); + void updateClonedChildren(const sp<Layer>& mirrorRoot, + std::map<sp<Layer>, sp<Layer>>& clonedLayersMap); + void updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> clonedLayersMap); + void addChildToDrawing(const sp<Layer>& layer); public: /* * compositionengine::LayerFE overrides */ + bool onPreComposition(nsecs_t) override; void latchCompositionState(compositionengine::LayerFECompositionState&, - bool includeGeometry) const override; + compositionengine::LayerFE::StateSubset subset) const override; + void latchCursorCompositionState(compositionengine::LayerFECompositionState&) const override; + std::optional<renderengine::LayerSettings> prepareClientComposition( + compositionengine::LayerFE::ClientCompositionTargetSettings&) override; void onLayerDisplayed(const sp<Fence>& releaseFence) override; const char* getDebugName() const override; protected: + void latchBasicGeometry(compositionengine::LayerFECompositionState& outState) const; void latchGeometry(compositionengine::LayerFECompositionState& outState) const; + virtual void latchPerFrameState(compositionengine::LayerFECompositionState& outState) const; public: virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {} virtual bool isHdrY410() const { return false; } - void forceClientComposition(const sp<DisplayDevice>& display); - bool getForceClientComposition(const sp<DisplayDevice>& display); - virtual void setPerFrameData(const sp<const DisplayDevice>& display, - const ui::Transform& transform, const Rect& viewport, - int32_t supportedPerFrameMetadata, - const ui::Dataspace targetDataspace) = 0; - - // callIntoHwc exists so we can update our local state and call - // acceptDisplayChanges without unnecessarily updating the device's state - void setCompositionType(const sp<const DisplayDevice>& display, - Hwc2::IComposerClient::Composition type); Hwc2::IComposerClient::Composition getCompositionType( const sp<const DisplayDevice>& display) const; bool getClearClientTarget(const sp<const DisplayDevice>& display) const; - void updateCursorPosition(const sp<const DisplayDevice>& display); virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; } virtual void setTransformHint(uint32_t /*orientation*/) const { } /* - * called before composition. - * returns true if the layer has pending updates. - */ - virtual bool onPreComposition(nsecs_t refreshStartTime) = 0; - - /* * called after composition. * returns true if the layer latched a new buffer this frame. */ @@ -527,58 +537,26 @@ public: virtual void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/) { } /* - * prepareClientLayer - populates a renderengine::LayerSettings to passed to - * RenderEngine::drawLayers. Returns true if the layer can be used, and - * false otherwise. - */ - bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, Region& clearRegion, - const bool supportProtectedContent, renderengine::LayerSettings& layer); - bool prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform, - Region& clearRegion, const bool supportProtectedContent, - renderengine::LayerSettings& layer); - - /* * doTransaction - process the transaction. This is a good place to figure * out which attributes of the surface have changed. */ uint32_t doTransaction(uint32_t transactionFlags); /* - * setVisibleRegion - called to set the new visible region. This gives - * a chance to update the new visible region or record the fact it changed. - */ - void setVisibleRegion(const Region& visibleRegion); - - /* - * setCoveredRegion - called when the covered region changes. The covered - * region corresponds to any area of the surface that is covered - * (transparently or not) by another surface. - */ - void setCoveredRegion(const Region& coveredRegion); - - /* - * setVisibleNonTransparentRegion - called when the visible and - * non-transparent region changes. - */ - void setVisibleNonTransparentRegion(const Region& visibleNonTransparentRegion); - - /* - * Clear the visible, covered, and non-transparent regions. - */ - void clearVisibilityRegions(); - - /* * latchBuffer - called each time the screen is redrawn and returns whether * the visible regions need to be recomputed (this is a fairly heavy * operation, so this should be set only if needed). Typically this is used * to figure out if the content or size of a surface has changed. */ - virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) { - return {}; + virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/, + nsecs_t /*expectedPresentTime*/) { + return false; } virtual bool isBufferLatched() const { return false; } + virtual void latchAndReleaseBuffer() {} + /* * Remove relative z for the layer if its relative parent is not part of the * provided layer tree. @@ -609,7 +587,14 @@ public: * returns the rectangle that crops the content of the layer and scales it * to the layer's size. */ - Rect getContentCrop() const; + virtual Rect getBufferCrop() const { return Rect(); } + + /* + * Returns the transform applied to the buffer. + */ + virtual uint32_t getBufferTransform() const { return 0; } + + virtual sp<GraphicBuffer> getBuffer() const { return nullptr; } /* * Returns if a frame is ready @@ -619,10 +604,6 @@ public: virtual int32_t getQueuedFrameCount() const { return 0; } // ----------------------------------------------------------------------- - - bool hasHwcLayer(const sp<const DisplayDevice>& displayDevice); - HWC2::Layer* getHwcLayer(const sp<const DisplayDevice>& displayDevice); - inline const State& getDrawingState() const { return mDrawingState; } inline const State& getCurrentState() const { return mCurrentState; } inline State& getCurrentState() { return mCurrentState; } @@ -634,6 +615,7 @@ public: void miniDump(std::string& result, const sp<DisplayDevice>& display) const; void dumpFrameStats(std::string& result) const; void dumpFrameEvents(std::string& result); + void dumpCallingUidPid(std::string& result) const; void clearFrameStats(); void logFrameStats(); void getFrameStats(FrameStats* outStats) const; @@ -710,6 +692,8 @@ public: compositionengine::OutputLayer* findOutputLayerForDisplay( const sp<const DisplayDevice>& display) const; + Region debugGetVisibleRegionOnDefaultDisplay() const; + protected: // constant sp<SurfaceFlinger> mFlinger; @@ -821,7 +805,7 @@ public: // this to be called once. sp<IBinder> getHandle(); const String8& getName() const; - virtual void notifyAvailableFrames() {} + virtual void notifyAvailableFrames(nsecs_t /*expectedPresentTime*/) {} virtual PixelFormat getPixelFormat() const { return PIXEL_FORMAT_NONE; } bool getPremultipledAlpha() const; @@ -864,20 +848,13 @@ protected: // main thread sp<NativeHandle> mSidebandStream; - // Active buffer fields - sp<GraphicBuffer> mActiveBuffer; - sp<Fence> mActiveBufferFence; // False if the buffer and its contents have been previously used for GPU // composition, true otherwise. bool mIsActiveBufferUpdatedForGpu = true; - ui::Dataspace mCurrentDataSpace = ui::Dataspace::UNKNOWN; - Rect mCurrentCrop; - uint32_t mCurrentTransform{0}; // We encode unset as -1. int32_t mOverrideScalingMode{-1}; std::atomic<uint64_t> mCurrentFrameNumber{0}; - bool mFrameLatencyNeeded{false}; // Whether filtering is needed b/c of the drawingstate bool mNeedsFiltering{false}; @@ -894,8 +871,6 @@ protected: // This layer can be a cursor on some displays. bool mPotentialCursor{false}; - bool mFreezeGeometryUpdates{false}; - // Child list about to be committed/used for editing. LayerVector mCurrentChildren{LayerVector::StateSet::Current}; // Child list used for rendering. @@ -958,6 +933,17 @@ private: bool mGetHandleCalled = false; void removeRemoteSyncPoints(); + + // Tracks the process and user id of the caller when creating this layer + // to help debugging. + pid_t mCallingPid; + uid_t mCallingUid; + + // The current layer is a clone of mClonedFrom. This means that this layer will update it's + // properties based on mClonedFrom. When mClonedFrom latches a new buffer for BufferLayers, + // this layer will update it's buffer. When mClonedFrom updates it's drawing state, children, + // and relatives, this layer will update as well. + wp<Layer> mClonedFrom; }; } // namespace android diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp index 72abea8b2d..8a221837ac 100644 --- a/services/surfaceflinger/LayerRejecter.cpp +++ b/services/surfaceflinger/LayerRejecter.cpp @@ -23,22 +23,16 @@ namespace android { -LayerRejecter::LayerRejecter(Layer::State& front, - Layer::State& current, - bool& recomputeVisibleRegions, - bool stickySet, - const char* name, - int32_t overrideScalingMode, - bool transformToDisplayInverse, - bool& freezePositionUpdates) - : mFront(front), - mCurrent(current), - mRecomputeVisibleRegions(recomputeVisibleRegions), - mStickyTransformSet(stickySet), - mName(name), - mOverrideScalingMode(overrideScalingMode), - mTransformToDisplayInverse(transformToDisplayInverse), - mFreezeGeometryUpdates(freezePositionUpdates) {} +LayerRejecter::LayerRejecter(Layer::State& front, Layer::State& current, + bool& recomputeVisibleRegions, bool stickySet, const char* name, + int32_t overrideScalingMode, bool transformToDisplayInverse) + : mFront(front), + mCurrent(current), + mRecomputeVisibleRegions(recomputeVisibleRegions), + mStickyTransformSet(stickySet), + mName(name), + mOverrideScalingMode(overrideScalingMode), + mTransformToDisplayInverse(transformToDisplayInverse) {} bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) { if (buf == nullptr) { @@ -83,8 +77,6 @@ bool LayerRejecter::reject(const sp<GraphicBuffer>& buf, const BufferItem& item) // recompute visible region mRecomputeVisibleRegions = true; - mFreezeGeometryUpdates = false; - if (mFront.crop_legacy != mFront.requestedCrop_legacy) { mFront.crop_legacy = mFront.requestedCrop_legacy; mCurrent.crop_legacy = mFront.requestedCrop_legacy; diff --git a/services/surfaceflinger/LayerRejecter.h b/services/surfaceflinger/LayerRejecter.h index 63d51de0ca..1bd0c26dc6 100644 --- a/services/surfaceflinger/LayerRejecter.h +++ b/services/surfaceflinger/LayerRejecter.h @@ -23,14 +23,9 @@ namespace android { class LayerRejecter : public BufferLayerConsumer::BufferRejecter { public: - LayerRejecter(Layer::State &front, - Layer::State ¤t, - bool &recomputeVisibleRegions, - bool stickySet, - const char *name, - int32_t overrideScalingMode, - bool transformToDisplayInverse, - bool &freezePositionUpdates); + LayerRejecter(Layer::State &front, Layer::State ¤t, bool &recomputeVisibleRegions, + bool stickySet, const char *name, int32_t overrideScalingMode, + bool transformToDisplayInverse); virtual bool reject(const sp<GraphicBuffer> &buf, const BufferItem &item); @@ -42,7 +37,6 @@ namespace android { const char *mName; int32_t mOverrideScalingMode; bool mTransformToDisplayInverse; - bool &mFreezeGeometryUpdates; }; } // namespace android diff --git a/services/surfaceflinger/LayerStats.cpp b/services/surfaceflinger/LayerStats.cpp deleted file mode 100644 index a2d1feb40d..0000000000 --- a/services/surfaceflinger/LayerStats.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#undef LOG_TAG -#define LOG_TAG "LayerStats" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "LayerStats.h" -#include "DisplayHardware/HWComposer.h" -#include "ui/DebugUtils.h" - -#include <android-base/stringprintf.h> -#include <log/log.h> -#include <utils/Trace.h> - -namespace android { - -using base::StringAppendF; -using base::StringPrintf; - -void LayerStats::enable() { - ATRACE_CALL(); - std::lock_guard<std::mutex> lock(mMutex); - if (mEnabled) return; - mLayerShapeStatsMap.clear(); - mEnabled = true; - ALOGD("Logging enabled"); -} - -void LayerStats::disable() { - ATRACE_CALL(); - std::lock_guard<std::mutex> lock(mMutex); - if (!mEnabled) return; - mEnabled = false; - ALOGD("Logging disabled"); -} - -void LayerStats::clear() { - ATRACE_CALL(); - std::lock_guard<std::mutex> lock(mMutex); - mLayerShapeStatsMap.clear(); - ALOGD("Cleared current layer stats"); -} - -bool LayerStats::isEnabled() { - return mEnabled; -} - -void LayerStats::traverseLayerTreeStatsLocked( - const std::vector<LayerProtoParser::Layer*>& layerTree, - const LayerProtoParser::LayerGlobal& layerGlobal, - std::vector<std::string>* const outLayerShapeVec) { - for (const auto& layer : layerTree) { - if (!layer) continue; - traverseLayerTreeStatsLocked(layer->children, layerGlobal, outLayerShapeVec); - std::string key = ""; - StringAppendF(&key, ",%s", layer->type.c_str()); - StringAppendF(&key, ",%s", layerCompositionType(layer->hwcCompositionType)); - StringAppendF(&key, ",%d", layer->isProtected); - StringAppendF(&key, ",%s", layerTransform(layer->hwcTransform)); - StringAppendF(&key, ",%s", layerPixelFormat(layer->activeBuffer.format).c_str()); - StringAppendF(&key, ",%s", layer->dataspace.c_str()); - StringAppendF(&key, ",%s", - destinationLocation(layer->hwcFrame.left, layerGlobal.resolution[0], true)); - StringAppendF(&key, ",%s", - destinationLocation(layer->hwcFrame.top, layerGlobal.resolution[1], false)); - StringAppendF(&key, ",%s", - destinationSize(layer->hwcFrame.right - layer->hwcFrame.left, - layerGlobal.resolution[0], true)); - StringAppendF(&key, ",%s", - destinationSize(layer->hwcFrame.bottom - layer->hwcFrame.top, - layerGlobal.resolution[1], false)); - StringAppendF(&key, ",%s", scaleRatioWH(layer).c_str()); - StringAppendF(&key, ",%s", alpha(static_cast<float>(layer->color.a))); - - outLayerShapeVec->push_back(key); - ALOGV("%s", key.c_str()); - } -} - -void LayerStats::logLayerStats(const LayersProto& layersProto) { - ATRACE_CALL(); - ALOGV("Logging"); - auto layerGlobal = LayerProtoParser::generateLayerGlobalInfo(layersProto); - auto layerTree = LayerProtoParser::generateLayerTree(layersProto); - std::vector<std::string> layerShapeVec; - - std::lock_guard<std::mutex> lock(mMutex); - traverseLayerTreeStatsLocked(layerTree.topLevelLayers, layerGlobal, &layerShapeVec); - - std::string layerShapeKey = - StringPrintf("%d,%s,%s,%s", static_cast<int32_t>(layerShapeVec.size()), - layerGlobal.colorMode.c_str(), layerGlobal.colorTransform.c_str(), - layerTransform(layerGlobal.globalTransform)); - ALOGV("%s", layerShapeKey.c_str()); - - std::sort(layerShapeVec.begin(), layerShapeVec.end(), std::greater<std::string>()); - for (auto const& s : layerShapeVec) { - layerShapeKey += s; - } - - mLayerShapeStatsMap[layerShapeKey]++; -} - -void LayerStats::dump(std::string& result) { - ATRACE_CALL(); - ALOGD("Dumping"); - std::lock_guard<std::mutex> lock(mMutex); - result.append("Frequency,LayerCount,ColorMode,ColorTransform,Orientation\n"); - result.append("LayerType,CompositionType,IsProtected,Transform,PixelFormat,Dataspace,"); - result.append("DstX,DstY,DstWidth,DstHeight,WScale,HScale,Alpha\n"); - for (auto& u : mLayerShapeStatsMap) { - StringAppendF(&result, "%u,%s\n", u.second, u.first.c_str()); - } -} - -const char* LayerStats::destinationLocation(int32_t location, int32_t range, bool isHorizontal) { - static const char* locationArray[8] = {"0", "1/8", "1/4", "3/8", "1/2", "5/8", "3/4", "7/8"}; - int32_t ratio = location * 8 / range; - if (ratio < 0) return "N/A"; - if (isHorizontal) { - // X location is divided into 4 buckets {"0", "1/4", "1/2", "3/4"} - if (ratio > 6) return "3/4"; - // use index 0, 2, 4, 6 - return locationArray[ratio & ~1]; - } - if (ratio > 7) return "7/8"; - return locationArray[ratio]; -} - -const char* LayerStats::destinationSize(int32_t size, int32_t range, bool isWidth) { - static const char* sizeArray[8] = {"1/8", "1/4", "3/8", "1/2", "5/8", "3/4", "7/8", "1"}; - int32_t ratio = size * 8 / range; - if (ratio < 0) return "N/A"; - if (isWidth) { - // width is divided into 4 buckets {"1/4", "1/2", "3/4", "1"} - if (ratio > 6) return "1"; - // use index 1, 3, 5, 7 - return sizeArray[ratio | 1]; - } - if (ratio > 7) return "1"; - return sizeArray[ratio]; -} - -const char* LayerStats::layerTransform(int32_t transform) { - return getTransformName(static_cast<hwc_transform_t>(transform)); -} - -const char* LayerStats::layerCompositionType(int32_t compositionType) { - return getCompositionName(static_cast<hwc2_composition_t>(compositionType)); -} - -std::string LayerStats::layerPixelFormat(int32_t pixelFormat) { - return decodePixelFormat(pixelFormat); -} - -std::string LayerStats::scaleRatioWH(const LayerProtoParser::Layer* layer) { - if (!layer->type.compare("ColorLayer")) return "N/A,N/A"; - std::string ret = ""; - if (isRotated(layer->hwcTransform)) { - ret += scaleRatio(layer->hwcFrame.right - layer->hwcFrame.left, - static_cast<int32_t>(layer->hwcCrop.bottom - layer->hwcCrop.top)); - ret += ","; - ret += scaleRatio(layer->hwcFrame.bottom - layer->hwcFrame.top, - static_cast<int32_t>(layer->hwcCrop.right - layer->hwcCrop.left)); - } else { - ret += scaleRatio(layer->hwcFrame.right - layer->hwcFrame.left, - static_cast<int32_t>(layer->hwcCrop.right - layer->hwcCrop.left)); - ret += ","; - ret += scaleRatio(layer->hwcFrame.bottom - layer->hwcFrame.top, - static_cast<int32_t>(layer->hwcCrop.bottom - layer->hwcCrop.top)); - } - return ret; -} - -const char* LayerStats::scaleRatio(int32_t destinationScale, int32_t sourceScale) { - // Make scale buckets from <1/64 to >= 16, to avoid floating point - // calculation, x64 on destinationScale first - int32_t scale = destinationScale * 64 / sourceScale; - if (!scale) return "<1/64"; - if (scale < 2) return "1/64"; - if (scale < 4) return "1/32"; - if (scale < 8) return "1/16"; - if (scale < 16) return "1/8"; - if (scale < 32) return "1/4"; - if (scale < 64) return "1/2"; - if (scale < 128) return "1"; - if (scale < 256) return "2"; - if (scale < 512) return "4"; - if (scale < 1024) return "8"; - return ">=16"; -} - -const char* LayerStats::alpha(float a) { - if (a == 1.0f) return "1.0"; - if (a > 0.9f) return "0.99"; - if (a > 0.8f) return "0.9"; - if (a > 0.7f) return "0.8"; - if (a > 0.6f) return "0.7"; - if (a > 0.5f) return "0.6"; - if (a > 0.4f) return "0.5"; - if (a > 0.3f) return "0.4"; - if (a > 0.2f) return "0.3"; - if (a > 0.1f) return "0.2"; - if (a > 0.0f) return "0.1"; - return "0.0"; -} - -bool LayerStats::isRotated(int32_t transform) { - return transform & HWC_TRANSFORM_ROT_90; -} - -bool LayerStats::isVFlipped(int32_t transform) { - return transform & HWC_TRANSFORM_FLIP_V; -} - -bool LayerStats::isHFlipped(int32_t transform) { - return transform & HWC_TRANSFORM_FLIP_H; -} - -} // namespace android diff --git a/services/surfaceflinger/LayerStats.h b/services/surfaceflinger/LayerStats.h deleted file mode 100644 index 62b2688936..0000000000 --- a/services/surfaceflinger/LayerStats.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2018 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. - */ - -#pragma once - -#include <layerproto/LayerProtoHeader.h> -#include <layerproto/LayerProtoParser.h> -#include <mutex> -#include <unordered_map> - -using namespace android::surfaceflinger; - -namespace android { - -class LayerStats { -public: - void enable(); - void disable(); - void clear(); - bool isEnabled(); - void logLayerStats(const LayersProto& layersProto); - void dump(std::string& result); - -private: - // Traverse layer tree to get all visible layers' stats - void traverseLayerTreeStatsLocked( - const std::vector<LayerProtoParser::Layer*>& layerTree, - const LayerProtoParser::LayerGlobal& layerGlobal, - std::vector<std::string>* const outLayerShapeVec); - // Convert layer's top-left position into 8x8 percentage of the display - static const char* destinationLocation(int32_t location, int32_t range, bool isHorizontal); - // Convert layer's size into 8x8 percentage of the display - static const char* destinationSize(int32_t size, int32_t range, bool isWidth); - // Return the name of the transform - static const char* layerTransform(int32_t transform); - // Return the name of the composition type - static const char* layerCompositionType(int32_t compositionType); - // Return the name of the pixel format - static std::string layerPixelFormat(int32_t pixelFormat); - // Calculate scale ratios of layer's width/height with rotation information - static std::string scaleRatioWH(const LayerProtoParser::Layer* layer); - // Calculate scale ratio from source to destination and convert to string - static const char* scaleRatio(int32_t destinationScale, int32_t sourceScale); - // Bucket the alpha into designed buckets - static const char* alpha(float a); - // Return whether the original buffer is rotated in final composition - static bool isRotated(int32_t transform); - // Return whether the original buffer is V-flipped in final composition - static bool isVFlipped(int32_t transform); - // Return whether the original buffer is H-flipped in final composition - static bool isHFlipped(int32_t transform); - - bool mEnabled = false; - // Protect mLayersStatsMap - std::mutex mMutex; - // Hashmap for tracking the frame(layer shape) stats - // KEY is a concatenation of all layers' properties within a frame - // VALUE is the number of times this particular set has been scanned out - std::unordered_map<std::string, uint32_t> mLayerShapeStatsMap; -}; - -} // namespace android diff --git a/services/surfaceflinger/LayerVector.cpp b/services/surfaceflinger/LayerVector.cpp index 84945247a6..8271fd97ea 100644 --- a/services/surfaceflinger/LayerVector.cpp +++ b/services/surfaceflinger/LayerVector.cpp @@ -64,7 +64,7 @@ void LayerVector::traverseInZOrder(StateSet stateSet, const Visitor& visitor) co const auto& layer = (*this)[i]; auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState() : layer->getDrawingState(); - if (state.zOrderRelativeOf != nullptr) { + if (state.isRelativeOf) { continue; } layer->traverseInZOrder(stateSet, visitor); @@ -76,7 +76,7 @@ void LayerVector::traverseInReverseZOrder(StateSet stateSet, const Visitor& visi const auto& layer = (*this)[i]; auto& state = (stateSet == StateSet::Current) ? layer->getCurrentState() : layer->getDrawingState(); - if (state.zOrderRelativeOf != nullptr) { + if (state.isRelativeOf) { continue; } layer->traverseInReverseZOrder(stateSet, visitor); diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp index c60421b538..7a959f7b19 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -154,6 +154,10 @@ status_t MonitoredProducer::getConsumerUsage(uint64_t* outUsage) const { return mProducer->getConsumerUsage(outUsage); } +status_t MonitoredProducer::setAutoPrerotation(bool autoPrerotation) { + return mProducer->setAutoPrerotation(autoPrerotation); +} + IBinder* MonitoredProducer::onAsBinder() { return this; } diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h index d346f821d3..788919b3da 100644 --- a/services/surfaceflinger/MonitoredProducer.h +++ b/services/surfaceflinger/MonitoredProducer.h @@ -70,6 +70,7 @@ public: virtual void getFrameTimestamps(FrameEventHistoryDelta *outDelta) override; virtual status_t getUniqueId(uint64_t* outId) const override; virtual status_t getConsumerUsage(uint64_t* outUsage) const override; + virtual status_t setAutoPrerotation(bool autoPrerotation) override; // The Layer which created this producer, and on which queued Buffer's will be displayed. sp<Layer> getLayer() const; diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 5b4bec96ea..976fedb58a 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -39,7 +39,7 @@ bool RefreshRateOverlay::createLayer() { Mutex::Autolock _l(mFlinger.mStateLock); mLayer = mClient->getLayerUser(mIBinder); - mLayer->setCrop_legacy(Rect(50, 70, 200, 100), true); + mLayer->setCrop_legacy(Rect(50, 70, 200, 100)); // setting Layer's Z requires resorting layersSortedByZ ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer); diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 07fdead310..1c1367c916 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -21,15 +21,18 @@ #include "RegionSamplingThread.h" +#include <compositionengine/Display.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <cutils/properties.h> #include <gui/IRegionSamplingListener.h> +#include <ui/DisplayStatInfo.h> #include <utils/Trace.h> + #include <string> -#include <compositionengine/Display.h> -#include <compositionengine/impl/OutputCompositionState.h> #include "DisplayDevice.h" #include "Layer.h" +#include "Scheduler/DispSync.h" #include "SurfaceFlinger.h" namespace android { @@ -105,9 +108,8 @@ struct SamplingOffsetCallback : DispSync::Callback { if (mVsyncListening) return; mPhaseIntervalSetting = Phase::ZERO; - mScheduler.withPrimaryDispSync([this](android::DispSync& sync) { - sync.addEventListener("SamplingThreadDispSyncListener", 0, this, mLastCallbackTime); - }); + mScheduler.getPrimaryDispSync().addEventListener("SamplingThreadDispSyncListener", 0, this, + mLastCallbackTime); mVsyncListening = true; } @@ -120,9 +122,7 @@ private: void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ { if (!mVsyncListening) return; - mScheduler.withPrimaryDispSync([this](android::DispSync& sync) { - sync.removeEventListener(this, &mLastCallbackTime); - }); + mScheduler.getPrimaryDispSync().removeEventListener(this, &mLastCallbackTime); mVsyncListening = false; } @@ -132,16 +132,13 @@ private: if (mPhaseIntervalSetting == Phase::ZERO) { ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase)); mPhaseIntervalSetting = Phase::SAMPLING; - mScheduler.withPrimaryDispSync([this](android::DispSync& sync) { - sync.changePhaseOffset(this, mTargetSamplingOffset.count()); - }); + mScheduler.getPrimaryDispSync().changePhaseOffset(this, mTargetSamplingOffset.count()); return; } if (mPhaseIntervalSetting == Phase::SAMPLING) { mPhaseIntervalSetting = Phase::ZERO; - mScheduler.withPrimaryDispSync( - [this](android::DispSync& sync) { sync.changePhaseOffset(this, 0); }); + mScheduler.getPrimaryDispSync().changePhaseOffset(this, 0); stopVsyncListenerLocked(); lock.unlock(); mRegionSamplingThread.notifySamplingOffset(); diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h index 96ffe207e4..99c07c288e 100644 --- a/services/surfaceflinger/RegionSamplingThread.h +++ b/services/surfaceflinger/RegionSamplingThread.h @@ -27,7 +27,7 @@ #include <ui/GraphicBuffer.h> #include <ui/Rect.h> #include <utils/StrongPointer.h> -#include "Scheduler/IdleTimer.h" +#include "Scheduler/OneShotTimer.h" namespace android { @@ -107,7 +107,7 @@ private: SurfaceFlinger& mFlinger; Scheduler& mScheduler; const TimingTunables mTunables; - scheduler::IdleTimer mIdleTimer; + scheduler::OneShotTimer mIdleTimer; std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback; diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp index 0c94052979..4bdfad94eb 100644 --- a/services/surfaceflinger/Scheduler/DispSync.cpp +++ b/services/surfaceflinger/Scheduler/DispSync.cpp @@ -64,7 +64,7 @@ public: DispSyncThread(const char* name, bool showTraceDetailedInfo) : mName(name), mStop(false), - mModelLocked(false), + mModelLocked("DispSync:ModelLocked", false), mPeriod(0), mPhase(0), mReferenceTime(0), @@ -121,13 +121,11 @@ public: void lockModel() { Mutex::Autolock lock(mMutex); mModelLocked = true; - ATRACE_INT("DispSync:ModelLocked", mModelLocked); } void unlockModel() { Mutex::Autolock lock(mMutex); mModelLocked = false; - ATRACE_INT("DispSync:ModelLocked", mModelLocked); } virtual bool threadLoop() { @@ -431,7 +429,7 @@ private: const char* const mName; bool mStop; - bool mModelLocked; + TracedOrdinal<bool> mModelLocked; nsecs_t mPeriod; nsecs_t mPhase; @@ -454,33 +452,24 @@ private: class ZeroPhaseTracer : public DispSync::Callback { public: - ZeroPhaseTracer() : mParity(false) {} + ZeroPhaseTracer() : mParity("ZERO_PHASE_VSYNC", false) {} virtual void onDispSyncEvent(nsecs_t /*when*/) { mParity = !mParity; - ATRACE_INT("ZERO_PHASE_VSYNC", mParity ? 1 : 0); } private: - bool mParity; + TracedOrdinal<bool> mParity; }; -DispSync::DispSync(const char* name) : mName(name), mRefreshSkipCount(0) { +DispSync::DispSync(const char* name, bool hasSyncFramework) + : mName(name), mIgnorePresentFences(!hasSyncFramework) { // This flag offers the ability to turn on systrace logging from the shell. char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.dispsync_trace_detailed_info", value, "0"); mTraceDetailedInfo = atoi(value); - mThread = new DispSyncThread(name, mTraceDetailedInfo); -} - -DispSync::~DispSync() { - mThread->stop(); - mThread->requestExitAndWait(); -} -void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { - mIgnorePresentFences = !hasSyncFramework; - mPresentTimeOffset = dispSyncPresentTimeOffset; + mThread = new DispSyncThread(name, mTraceDetailedInfo); mThread->run("DispSync", PRIORITY_URGENT_DISPLAY + PRIORITY_MORE_FAVORABLE); // set DispSync to SCHED_FIFO to minimize jitter @@ -498,6 +487,11 @@ void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { } } +DispSync::~DispSync() { + mThread->stop(); + mThread->requestExitAndWait(); +} + void DispSync::reset() { Mutex::Autolock lock(mMutex); resetLocked(); @@ -623,13 +617,6 @@ status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* c return mThread->addEventListener(name, phase, callback, lastCallbackTime); } -void DispSync::setRefreshSkipCount(int count) { - Mutex::Autolock lock(mMutex); - ALOGD("setRefreshSkipCount(%d)", count); - mRefreshSkipCount = count; - updateModelLocked(); -} - status_t DispSync::removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) { Mutex::Autolock lock(mMutex); return mThread->removeEventListener(callback, outLastCallbackTime); @@ -712,9 +699,6 @@ void DispSync::updateModelLocked() { ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase)); } - // Artificially inflate the period if requested. - mPeriod += mPeriod * mRefreshSkipCount; - mThread->updateModel(mPeriod, mPhase, mReferenceTime); mModelUpdated = true; } @@ -725,10 +709,6 @@ void DispSync::updateErrorLocked() { return; } - // Need to compare present fences against the un-adjusted refresh period, - // since they might arrive between two events. - nsecs_t period = mPeriod / (1 + mRefreshSkipCount); - int numErrSamples = 0; nsecs_t sqErrSum = 0; @@ -747,9 +727,9 @@ void DispSync::updateErrorLocked() { continue; } - nsecs_t sampleErr = (sample - mPhase) % period; - if (sampleErr > period / 2) { - sampleErr -= period; + nsecs_t sampleErr = (sample - mPhase) % mPeriod; + if (sampleErr > mPeriod / 2) { + sampleErr -= mPeriod; } sqErrSum += sampleErr * sampleErr; numErrSamples++; @@ -804,8 +784,7 @@ void DispSync::setIgnorePresentFences(bool ignore) { void DispSync::dump(std::string& result) const { Mutex::Autolock lock(mMutex); StringAppendF(&result, "present fences are %s\n", mIgnorePresentFences ? "ignored" : "used"); - StringAppendF(&result, "mPeriod: %" PRId64 " ns (%.3f fps; skipCount=%d)\n", mPeriod, - 1000000000.0 / mPeriod, mRefreshSkipCount); + StringAppendF(&result, "mPeriod: %" PRId64 " ns (%.3f fps)\n", mPeriod, 1000000000.0 / mPeriod); StringAppendF(&result, "mPhase: %" PRId64 " ns\n", mPhase); StringAppendF(&result, "mError: %" PRId64 " ns (sqrt=%.1f)\n", mError, sqrt(mError)); StringAppendF(&result, "mNumResyncSamplesSincePresent: %d (limit %d)\n", diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h index 3e33c7edc0..c6aadbb928 100644 --- a/services/surfaceflinger/Scheduler/DispSync.h +++ b/services/surfaceflinger/Scheduler/DispSync.h @@ -53,7 +53,6 @@ public: virtual void endResync() = 0; virtual void setPeriod(nsecs_t period) = 0; virtual nsecs_t getPeriod() = 0; - virtual void setRefreshSkipCount(int count) = 0; virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback, nsecs_t lastCallbackTime) = 0; virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0; @@ -88,11 +87,10 @@ class DispSyncThread; // needed. class DispSync : public android::DispSync { public: - explicit DispSync(const char* name); + // hasSyncFramework specifies whether the platform supports present fences. + DispSync(const char* name, bool hasSyncFramework); ~DispSync() override; - void init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset); - // reset clears the resync samples and error value. void reset() override; @@ -138,12 +136,6 @@ public: // The getPeriod method returns the current vsync period. nsecs_t getPeriod() override; - // setRefreshSkipCount specifies an additional number of refresh - // cycles to skip. For example, on a 60Hz display, a skip count of 1 - // will result in events happening at 30Hz. Default is zero. The idea - // is to sacrifice smoothness for battery life. - void setRefreshSkipCount(int count) override; - // addEventListener registers a callback to be called repeatedly at the // given phase offset from the hardware vsync events. The callback is // called from a separate thread and it should return reasonably quickly @@ -252,18 +244,12 @@ private: std::shared_ptr<FenceTime> mPresentFences[NUM_PRESENT_SAMPLES]{FenceTime::NO_FENCE}; size_t mPresentSampleOffset; - int mRefreshSkipCount; - // mThread is the thread from which all the callbacks are called. sp<DispSyncThread> mThread; // mMutex is used to protect access to all member variables. mutable Mutex mMutex; - // This is the offset from the present fence timestamps to the corresponding - // vsync event. - int64_t mPresentTimeOffset; - // Ignore present (retire) fences if the device doesn't have support for the // sync framework bool mIgnorePresentFences; diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp index 5faf46e31e..571c9ca362 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp +++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp @@ -31,19 +31,16 @@ DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync, bool traceVsync, const char* name) : mName(name), + mValue(base::StringPrintf("VSYNC-%s", name), 0), mTraceVsync(traceVsync), mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)), - mVsyncEventLabel(base::StringPrintf("VSYNC-%s", name)), - mVsyncOffsetLabel(base::StringPrintf("VsyncOffset-%s", name)), - mVsyncNegativeOffsetLabel(base::StringPrintf("VsyncNegativeOffset-%s", name)), mDispSync(dispSync), - mPhaseOffset(phaseOffset), + mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset), mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {} void DispSyncSource::setVSyncEnabled(bool enable) { std::lock_guard lock(mVsyncMutex); if (enable) { - tracePhaseOffset(); status_t err = mDispSync->addEventListener(mName, mPhaseOffset, static_cast<DispSync::Callback*>(this), mLastCallbackTime); @@ -83,7 +80,6 @@ void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) { } mPhaseOffset = phaseOffset; - tracePhaseOffset(); // If we're not enabled, we don't need to mess with the listeners if (!mEnabled) { @@ -106,7 +102,6 @@ void DispSyncSource::onDispSyncEvent(nsecs_t when) { if (mTraceVsync) { mValue = (mValue + 1) % 2; - ATRACE_INT(mVsyncEventLabel.c_str(), mValue); } if (callback != nullptr) { @@ -114,14 +109,4 @@ void DispSyncSource::onDispSyncEvent(nsecs_t when) { } } -void DispSyncSource::tracePhaseOffset() { - if (mPhaseOffset > 0) { - ATRACE_INT(mVsyncOffsetLabel.c_str(), mPhaseOffset); - ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), 0); - } else { - ATRACE_INT(mVsyncOffsetLabel.c_str(), 0); - ATRACE_INT(mVsyncNegativeOffsetLabel.c_str(), -mPhaseOffset); - } -} - } // namespace android diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h index 50560a5a2b..536464e8fb 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.h +++ b/services/surfaceflinger/Scheduler/DispSyncSource.h @@ -20,6 +20,7 @@ #include "DispSync.h" #include "EventThread.h" +#include "TracedOrdinal.h" namespace android { @@ -31,6 +32,7 @@ public: ~DispSyncSource() override = default; // The following methods are implementation of VSyncSource. + const char* getName() const override { return mName; } void setVSyncEnabled(bool enable) override; void setCallback(VSyncSource::Callback* callback) override; void setPhaseOffset(nsecs_t phaseOffset) override; @@ -39,16 +41,11 @@ private: // The following method is the implementation of the DispSync::Callback. virtual void onDispSyncEvent(nsecs_t when); - void tracePhaseOffset() REQUIRES(mVsyncMutex); - const char* const mName; - int mValue = 0; + TracedOrdinal<int> mValue; const bool mTraceVsync; const std::string mVsyncOnLabel; - const std::string mVsyncEventLabel; - const std::string mVsyncOffsetLabel; - const std::string mVsyncNegativeOffsetLabel; nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0; DispSync* mDispSync; @@ -57,7 +54,7 @@ private: VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr; std::mutex mVsyncMutex; - nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex); + TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex); const nsecs_t mOffsetThresholdForNextVsync; bool mEnabled GUARDED_BY(mVsyncMutex) = false; }; diff --git a/services/surfaceflinger/Scheduler/EventControlThread.cpp b/services/surfaceflinger/Scheduler/EventControlThread.cpp index fb6cff5705..85a7f82a3c 100644 --- a/services/surfaceflinger/Scheduler/EventControlThread.cpp +++ b/services/surfaceflinger/Scheduler/EventControlThread.cpp @@ -31,7 +31,7 @@ EventControlThread::~EventControlThread() = default; namespace impl { EventControlThread::EventControlThread(EventControlThread::SetVSyncEnabledFunction function) - : mSetVSyncEnabled(function) { + : mSetVSyncEnabled(std::move(function)) { pthread_setname_np(mThread.native_handle(), "EventControlThread"); pid_t tid = pthread_gettid_np(mThread.native_handle()); diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 9d1f777859..8d9adc8525 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -154,23 +154,11 @@ EventThread::~EventThread() = default; namespace impl { -EventThread::EventThread(std::unique_ptr<VSyncSource> src, - InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) - : EventThread(nullptr, std::move(src), std::move(interceptVSyncsCallback), threadName) {} - -EventThread::EventThread(VSyncSource* src, InterceptVSyncsCallback interceptVSyncsCallback, - const char* threadName) - : EventThread(src, nullptr, std::move(interceptVSyncsCallback), threadName) {} - -EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc, - InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName) - : mVSyncSource(src), - mVSyncSourceUnique(std::move(uniqueSrc)), +EventThread::EventThread(std::unique_ptr<VSyncSource> vsyncSource, + InterceptVSyncsCallback interceptVSyncsCallback) + : mVSyncSource(std::move(vsyncSource)), mInterceptVSyncsCallback(std::move(interceptVSyncsCallback)), - mThreadName(threadName) { - if (src == nullptr) { - mVSyncSource = mVSyncSourceUnique.get(); - } + mThreadName(mVSyncSource->getName()) { mVSyncSource->setCallback(this); mThread = std::thread([this]() NO_THREAD_SAFETY_ANALYSIS { @@ -178,7 +166,7 @@ EventThread::EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSr threadMain(lock); }); - pthread_setname_np(mThread.native_handle(), threadName); + pthread_setname_np(mThread.native_handle(), mThreadName); pid_t tid = pthread_gettid_np(mThread.native_handle()); diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index dd23b88726..a029586088 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -62,6 +62,8 @@ public: }; virtual ~VSyncSource() {} + + virtual const char* getName() const = 0; virtual void setVSyncEnabled(bool enable) = 0; virtual void setCallback(Callback* callback) = 0; virtual void setPhaseOffset(nsecs_t phaseOffset) = 0; @@ -126,9 +128,7 @@ class EventThread : public android::EventThread, private VSyncSource::Callback { public: using InterceptVSyncsCallback = std::function<void(nsecs_t)>; - // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete. - EventThread(VSyncSource*, InterceptVSyncsCallback, const char* threadName); - EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback, const char* threadName); + EventThread(std::unique_ptr<VSyncSource>, InterceptVSyncsCallback); ~EventThread(); sp<EventThreadConnection> createEventConnection( @@ -157,10 +157,6 @@ private: using DisplayEventConsumers = std::vector<sp<EventThreadConnection>>; - // TODO(b/128863962): Once the Scheduler is complete this constructor will become obsolete. - EventThread(VSyncSource* src, std::unique_ptr<VSyncSource> uniqueSrc, - InterceptVSyncsCallback interceptVSyncsCallback, const char* threadName); - void threadMain(std::unique_lock<std::mutex>& lock) REQUIRES(mMutex); bool shouldConsumeEvent(const DisplayEventReceiver::Event& event, @@ -174,9 +170,7 @@ private: // Implements VSyncSource::Callback void onVSyncEvent(nsecs_t timestamp) override; - // TODO(b/128863962): Once the Scheduler is complete this pointer will become obsolete. - VSyncSource* mVSyncSource GUARDED_BY(mMutex) = nullptr; - std::unique_ptr<VSyncSource> mVSyncSourceUnique GUARDED_BY(mMutex) = nullptr; + const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex); const InterceptVSyncsCallback mInterceptVSyncsCallback; const char* const mThreadName; diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h index 90609afaa7..6c502e6d98 100644 --- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h +++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h @@ -42,6 +42,7 @@ public: } } + const char* getName() const override { return "inject"; } void setVSyncEnabled(bool) override {} void setPhaseOffset(nsecs_t) override {} void pauseVsyncCallback(bool) {} @@ -51,4 +52,4 @@ private: VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr; }; -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index fcb307f213..5318b00983 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -85,24 +85,6 @@ void MessageQueue::init(const sp<SurfaceFlinger>& flinger) { mHandler = new Handler(*this); } -void MessageQueue::setEventThread(android::EventThread* eventThread, - ResyncCallback resyncCallback) { - if (mEventThread == eventThread) { - return; - } - - if (mEventTube.getFd() >= 0) { - mLooper->removeFd(mEventTube.getFd()); - } - - mEventThread = eventThread; - mEvents = eventThread->createEventConnection(std::move(resyncCallback), - ISurfaceComposer::eConfigChangedSuppress); - mEvents->stealReceiveChannel(&mEventTube); - mLooper->addFd(mEventTube.getFd(), 0, Looper::EVENT_INPUT, MessageQueue::cb_eventReceiver, - this); -} - void MessageQueue::setEventConnection(const sp<EventThreadConnection>& connection) { if (mEventTube.getFd() >= 0) { mLooper->removeFd(mEventTube.getFd()); diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index 0b2206d2af..fcfc4aa3f7 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -85,8 +85,6 @@ public: virtual ~MessageQueue(); virtual void init(const sp<SurfaceFlinger>& flinger) = 0; - // TODO(b/128863962): Remove this function once everything is migrated to Scheduler. - virtual void setEventThread(EventThread* events, ResyncCallback resyncCallback) = 0; virtual void setEventConnection(const sp<EventThreadConnection>& connection) = 0; virtual void waitMessage() = 0; virtual status_t postMessage(const sp<MessageBase>& message, nsecs_t reltime = 0) = 0; @@ -115,7 +113,6 @@ class MessageQueue final : public android::MessageQueue { sp<SurfaceFlinger> mFlinger; sp<Looper> mLooper; - android::EventThread* mEventThread; sp<EventThreadConnection> mEvents; gui::BitTube mEventTube; sp<Handler> mHandler; @@ -126,7 +123,6 @@ class MessageQueue final : public android::MessageQueue { public: ~MessageQueue() override = default; void init(const sp<SurfaceFlinger>& flinger) override; - void setEventThread(android::EventThread* events, ResyncCallback resyncCallback) override; void setEventConnection(const sp<EventThreadConnection>& connection) override; void waitMessage() override; diff --git a/services/surfaceflinger/Scheduler/IdleTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp index 37fdfc7c62..4870a3bbdd 100644 --- a/services/surfaceflinger/Scheduler/IdleTimer.cpp +++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "IdleTimer.h" +#include "OneShotTimer.h" #include <chrono> #include <thread> @@ -22,23 +22,23 @@ namespace android { namespace scheduler { -IdleTimer::IdleTimer(const Interval& interval, const ResetCallback& resetCallback, - const TimeoutCallback& timeoutCallback) +OneShotTimer::OneShotTimer(const Interval& interval, const ResetCallback& resetCallback, + const TimeoutCallback& timeoutCallback) : mInterval(interval), mResetCallback(resetCallback), mTimeoutCallback(timeoutCallback) {} -IdleTimer::~IdleTimer() { +OneShotTimer::~OneShotTimer() { stop(); } -void IdleTimer::start() { +void OneShotTimer::start() { { std::lock_guard<std::mutex> lock(mMutex); mState = TimerState::RESET; } - mThread = std::thread(&IdleTimer::loop, this); + mThread = std::thread(&OneShotTimer::loop, this); } -void IdleTimer::stop() { +void OneShotTimer::stop() { { std::lock_guard<std::mutex> lock(mMutex); mState = TimerState::STOPPED; @@ -49,7 +49,7 @@ void IdleTimer::stop() { } } -void IdleTimer::loop() { +void OneShotTimer::loop() { while (true) { bool triggerReset = false; bool triggerTimeout = false; @@ -100,7 +100,7 @@ void IdleTimer::loop() { } } // namespace scheduler -void IdleTimer::reset() { +void OneShotTimer::reset() { { std::lock_guard<std::mutex> lock(mMutex); mState = TimerState::RESET; diff --git a/services/surfaceflinger/Scheduler/IdleTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h index 2646688860..921631e009 100644 --- a/services/surfaceflinger/Scheduler/IdleTimer.h +++ b/services/surfaceflinger/Scheduler/OneShotTimer.h @@ -29,15 +29,17 @@ namespace scheduler { * Class that sets off a timer for a given interval, and fires a callback when the * interval expires. */ -class IdleTimer { +class OneShotTimer { public: using Interval = std::chrono::milliseconds; using ResetCallback = std::function<void()>; using TimeoutCallback = std::function<void()>; - IdleTimer(const Interval& interval, const ResetCallback& resetCallback, - const TimeoutCallback& timeoutCallback); - ~IdleTimer(); + OneShotTimer(const Interval& interval, const ResetCallback& resetCallback, + const TimeoutCallback& timeoutCallback); + ~OneShotTimer(); + + const Interval& interval() const { return mInterval; } // Initializes and turns on the idle timer. void start(); diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp index 8a2604f4a3..6be88f89f9 100644 --- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp +++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp @@ -18,115 +18,102 @@ #include <cutils/properties.h> -#include "SurfaceFlingerProperties.h" - -namespace android { -using namespace android::sysprop; - -namespace scheduler { - -using RefreshRateType = RefreshRateConfigs::RefreshRateType; -PhaseOffsets::~PhaseOffsets() = default; +#include <optional> -namespace impl { -PhaseOffsets::PhaseOffsets() { - int64_t vsyncPhaseOffsetNs = vsync_event_phase_offset_ns(1000000); +#include "SurfaceFlingerProperties.h" - int64_t sfVsyncPhaseOffsetNs = vsync_sf_event_phase_offset_ns(1000000); +namespace { +std::optional<nsecs_t> getProperty(const char* name) { char value[PROPERTY_VALUE_MAX]; - property_get("debug.sf.early_phase_offset_ns", value, "-1"); - const int earlySfOffsetNs = atoi(value); - - property_get("debug.sf.early_gl_phase_offset_ns", value, "-1"); - const int earlyGlSfOffsetNs = atoi(value); - - property_get("debug.sf.early_app_phase_offset_ns", value, "-1"); - const int earlyAppOffsetNs = atoi(value); - - property_get("debug.sf.early_gl_app_phase_offset_ns", value, "-1"); - const int earlyGlAppOffsetNs = atoi(value); - - property_get("debug.sf.high_fps_early_phase_offset_ns", value, "-1"); - const int highFpsEarlySfOffsetNs = atoi(value); - - property_get("debug.sf.high_fps_early_gl_phase_offset_ns", value, "-1"); - const int highFpsEarlyGlSfOffsetNs = atoi(value); + property_get(name, value, "-1"); + if (const int i = atoi(value); i != -1) return i; + return std::nullopt; +} - property_get("debug.sf.high_fps_early_app_phase_offset_ns", value, "-1"); - const int highFpsEarlyAppOffsetNs = atoi(value); +} // namespace - property_get("debug.sf.high_fps_early_gl_app_phase_offset_ns", value, "-1"); - const int highFpsEarlyGlAppOffsetNs = atoi(value); +namespace android::scheduler { - // TODO(b/122905996): Define these in device.mk. - property_get("debug.sf.high_fps_late_app_phase_offset_ns", value, "2000000"); - const int highFpsLateAppOffsetNs = atoi(value); +PhaseOffsets::~PhaseOffsets() = default; - property_get("debug.sf.high_fps_late_sf_phase_offset_ns", value, "1000000"); - const int highFpsLateSfOffsetNs = atoi(value); +namespace impl { +PhaseOffsets::PhaseOffsets() { // Below defines the threshold when an offset is considered to be negative, i.e. targeting // for the N+2 vsync instead of N+1. This means that: // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync. // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync. - property_get("debug.sf.phase_offset_threshold_for_next_vsync_ns", value, "-1"); - const int phaseOffsetThresholdForNextVsyncNs = atoi(value); - - Offsets defaultOffsets; - Offsets highFpsOffsets; - defaultOffsets.early = {RefreshRateType::DEFAULT, - earlySfOffsetNs != -1 ? earlySfOffsetNs : sfVsyncPhaseOffsetNs, - earlyAppOffsetNs != -1 ? earlyAppOffsetNs : vsyncPhaseOffsetNs}; - defaultOffsets.earlyGl = {RefreshRateType::DEFAULT, - earlyGlSfOffsetNs != -1 ? earlyGlSfOffsetNs : sfVsyncPhaseOffsetNs, - earlyGlAppOffsetNs != -1 ? earlyGlAppOffsetNs : vsyncPhaseOffsetNs}; - defaultOffsets.late = {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs}; - - highFpsOffsets.early = {RefreshRateType::PERFORMANCE, - highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs - : highFpsLateSfOffsetNs, - highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs - : highFpsLateAppOffsetNs}; - highFpsOffsets.earlyGl = {RefreshRateType::PERFORMANCE, - highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs - : highFpsLateSfOffsetNs, - highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs - : highFpsLateAppOffsetNs}; - highFpsOffsets.late = {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs, - highFpsLateAppOffsetNs}; - - mOffsets.insert({RefreshRateType::POWER_SAVING, defaultOffsets}); + const nsecs_t thresholdForNextVsync = + getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns") + .value_or(std::numeric_limits<nsecs_t>::max()); + + const Offsets defaultOffsets = getDefaultOffsets(thresholdForNextVsync); + const Offsets highFpsOffsets = getHighFpsOffsets(thresholdForNextVsync); + mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets}); mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets}); - - mOffsetThresholdForNextVsync = phaseOffsetThresholdForNextVsyncNs != -1 - ? phaseOffsetThresholdForNextVsyncNs - : std::numeric_limits<nsecs_t>::max(); } PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate( - android::scheduler::RefreshRateConfigs::RefreshRateType refreshRateType) const { + RefreshRateType refreshRateType) const { return mOffsets.at(refreshRateType); } void PhaseOffsets::dump(std::string& result) const { - const auto [early, earlyGl, late] = getCurrentOffsets(); - base::StringAppendF(&result, - " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n" - " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n" - "GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n", - late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf); + const auto [early, earlyGl, late, threshold] = getCurrentOffsets(); + using base::StringAppendF; + StringAppendF(&result, + " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n" + " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n" + " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n" + "next VSYNC threshold: %9" PRId64 " ns\n", + late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf, threshold); } -nsecs_t PhaseOffsets::getCurrentAppOffset() { - return getCurrentOffsets().late.app; +PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t thresholdForNextVsync) { + const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000); + const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000); + + const auto earlySfOffsetNs = getProperty("debug.sf.early_phase_offset_ns"); + const auto earlyGlSfOffsetNs = getProperty("debug.sf.early_gl_phase_offset_ns"); + const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns"); + const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns"); + + return {{RefreshRateType::DEFAULT, earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs), + earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs)}, + + {RefreshRateType::DEFAULT, earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs), + earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs)}, + + {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs}, + + thresholdForNextVsync}; } -nsecs_t PhaseOffsets::getCurrentSfOffset() { - return getCurrentOffsets().late.sf; +PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t thresholdForNextVsync) { + // TODO(b/122905996): Define these in device.mk. + const int highFpsLateAppOffsetNs = + getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000); + const int highFpsLateSfOffsetNs = + getProperty("debug.sf.high_fps_late_sf_phase_offset_ns").value_or(1000000); + + const auto highFpsEarlySfOffsetNs = getProperty("debug.sf.high_fps_early_phase_offset_ns"); + const auto highFpsEarlyGlSfOffsetNs = getProperty("debug.sf.high_fps_early_gl_phase_offset_ns"); + const auto highFpsEarlyAppOffsetNs = getProperty("debug.sf.high_fps_early_app_phase_offset_ns"); + const auto highFpsEarlyGlAppOffsetNs = + getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns"); + + return {{RefreshRateType::PERFORMANCE, highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs), + highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs)}, + + {RefreshRateType::PERFORMANCE, highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs), + highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)}, + + {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs, highFpsLateAppOffsetNs}, + + thresholdForNextVsync}; } } // namespace impl -} // namespace scheduler -} // namespace android +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h index 2b5c2f10f1..2c52432448 100644 --- a/services/surfaceflinger/Scheduler/PhaseOffsets.h +++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h @@ -16,14 +16,12 @@ #pragma once -#include <cinttypes> #include <unordered_map> #include "RefreshRateConfigs.h" #include "VSyncModulator.h" -namespace android { -namespace scheduler { +namespace android::scheduler { /* * This class encapsulates offsets for different refresh rates. Depending @@ -33,35 +31,33 @@ namespace scheduler { */ class PhaseOffsets { public: - struct Offsets { - VSyncModulator::Offsets early; - VSyncModulator::Offsets earlyGl; - VSyncModulator::Offsets late; - }; + using Offsets = VSyncModulator::OffsetsConfig; + using RefreshRateType = RefreshRateConfigs::RefreshRateType; virtual ~PhaseOffsets(); - virtual nsecs_t getCurrentAppOffset() = 0; - virtual nsecs_t getCurrentSfOffset() = 0; - virtual Offsets getOffsetsForRefreshRate( - RefreshRateConfigs::RefreshRateType refreshRateType) const = 0; + nsecs_t getCurrentAppOffset() const { return getCurrentOffsets().late.app; } + nsecs_t getCurrentSfOffset() const { return getCurrentOffsets().late.sf; } + nsecs_t getOffsetThresholdForNextVsync() const { + return getCurrentOffsets().thresholdForNextVsync; + } + virtual Offsets getCurrentOffsets() const = 0; - virtual void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) = 0; - virtual nsecs_t getOffsetThresholdForNextVsync() const = 0; + virtual Offsets getOffsetsForRefreshRate(RefreshRateType) const = 0; + + virtual void setRefreshRateType(RefreshRateType) = 0; + virtual void dump(std::string& result) const = 0; }; namespace impl { + class PhaseOffsets : public scheduler::PhaseOffsets { public: PhaseOffsets(); - nsecs_t getCurrentAppOffset() override; - nsecs_t getCurrentSfOffset() override; - // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate. - Offsets getOffsetsForRefreshRate( - RefreshRateConfigs::RefreshRateType refreshRateType) const override; + Offsets getOffsetsForRefreshRate(RefreshRateType) const override; // Returns early, early GL, and late offsets for Apps and SF. Offsets getCurrentOffsets() const override { @@ -70,23 +66,21 @@ public: // This function should be called when the device is switching between different // refresh rates, to properly update the offsets. - void setRefreshRateType(RefreshRateConfigs::RefreshRateType refreshRateType) override { + void setRefreshRateType(RefreshRateType refreshRateType) override { mRefreshRateType = refreshRateType; } - nsecs_t getOffsetThresholdForNextVsync() const override { return mOffsetThresholdForNextVsync; } - // Returns current offsets in human friendly format. void dump(std::string& result) const override; private: - std::atomic<RefreshRateConfigs::RefreshRateType> mRefreshRateType = - RefreshRateConfigs::RefreshRateType::DEFAULT; + static Offsets getDefaultOffsets(nsecs_t thresholdForNextVsync); + static Offsets getHighFpsOffsets(nsecs_t thresholdForNextVsync); + + std::atomic<RefreshRateType> mRefreshRateType = RefreshRateType::DEFAULT; - std::unordered_map<RefreshRateConfigs::RefreshRateType, Offsets> mOffsets; - nsecs_t mOffsetThresholdForNextVsync; + std::unordered_map<RefreshRateType, Offsets> mOffsets; }; -} // namespace impl -} // namespace scheduler -} // namespace android +} // namespace impl +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index d7300583c3..2fd100f876 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -16,16 +16,23 @@ #pragma once +#include <android-base/stringprintf.h> + #include <algorithm> #include <numeric> - -#include "android-base/stringprintf.h" +#include <type_traits> #include "DisplayHardware/HWComposer.h" #include "Scheduler/SchedulerUtils.h" -namespace android { -namespace scheduler { +namespace android::scheduler { + +enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 }; + +inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateConfigEvent rhs) { + using T = std::underlying_type_t<RefreshRateConfigEvent>; + return static_cast<RefreshRateConfigEvent>(static_cast<T>(lhs) | static_cast<T>(rhs)); +} /** * This class is used to encapsulate configuration for refresh rates. It holds information @@ -34,10 +41,9 @@ namespace scheduler { */ class RefreshRateConfigs { public: - // Enum to indicate which vsync rate to run at. Power saving is intended to be the lowest - // (eg. when the screen is in AOD mode or off), default is the old 60Hz, and performance + // Enum to indicate which vsync rate to run at. Default is the old 60Hz, and performance // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs. - enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE }; + enum class RefreshRateType { DEFAULT, PERFORMANCE }; struct RefreshRate { // This config ID corresponds to the position of the config in the vector that is stored @@ -47,26 +53,57 @@ public: std::string name; // Refresh rate in frames per second, rounded to the nearest integer. uint32_t fps = 0; - // config Id (returned from HWC2::Display::Config::getId()) - hwc2_config_t id; + // Vsync period in nanoseconds. + nsecs_t vsyncPeriod; + // Hwc config Id (returned from HWC2::Display::Config::getId()) + hwc2_config_t hwcId; }; + // Returns true if this device is doing refresh rate switching. This won't change at runtime. + bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; } + + // Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access + // from multiple threads. This can only be called if refreshRateSwitching() returns true. // TODO(b/122916473): Get this information from configs prepared by vendors, instead of // baking them in. - const std::map<RefreshRateType, std::shared_ptr<RefreshRate>>& getRefreshRates() const { - return mRefreshRates; + const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const { + LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported); + return mRefreshRateMap; } - std::shared_ptr<RefreshRate> getRefreshRate(RefreshRateType type) const { - const auto& refreshRate = mRefreshRates.find(type); - if (refreshRate != mRefreshRates.end()) { + + const RefreshRate& getRefreshRateFromType(RefreshRateType type) const { + if (!mRefreshRateSwitchingSupported) { + return getCurrentRefreshRate().second; + } else { + auto refreshRate = mRefreshRateMap.find(type); + LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end()); return refreshRate->second; } - return nullptr; } - RefreshRateType getRefreshRateType(hwc2_config_t id) const { - for (const auto& [type, refreshRate] : mRefreshRates) { - if (refreshRate->id == id) { + std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const { + int currentConfig = mCurrentConfig; + if (mRefreshRateSwitchingSupported) { + for (const auto& [type, refresh] : mRefreshRateMap) { + if (refresh.configId == currentConfig) { + return {type, refresh}; + } + } + LOG_ALWAYS_FATAL(); + } + return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]}; + } + + const RefreshRate& getRefreshRateFromConfigId(int configId) const { + LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size()); + return mRefreshRates[configId]; + } + + RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const { + if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT; + + for (const auto& [type, refreshRate] : mRefreshRateMap) { + if (refreshRate.hwcId == hwcId) { return type; } } @@ -74,65 +111,102 @@ public: return RefreshRateType::DEFAULT; } - void populate(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) { - mRefreshRates.clear(); + void setCurrentConfig(int config) { + LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size()); + mCurrentConfig = config; + } - // This is the rate that HWC encapsulates right now when the device is in DOZE mode. - mRefreshRates.emplace(RefreshRateType::POWER_SAVING, - std::make_shared<RefreshRate>( - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, - HWC2_SCREEN_OFF_CONFIG_ID})); + struct InputConfig { + hwc2_config_t hwcId = 0; + nsecs_t vsyncPeriod = 0; + }; - if (configs.size() < 1) { - ALOGE("Device does not have valid configs. Config size is 0."); - return; + RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs, + int currentConfig) { + init(refreshRateSwitching, configs, currentConfig); + } + + RefreshRateConfigs(bool refreshRateSwitching, + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, + int currentConfig) { + std::vector<InputConfig> inputConfigs; + for (const auto& config : configs) { + inputConfigs.push_back({config->getId(), config->getVsyncPeriod()}); } + init(refreshRateSwitching, inputConfigs, currentConfig); + } + +private: + void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs, + int currentConfig) { + mRefreshRateSwitchingSupported = refreshRateSwitching; + LOG_ALWAYS_FATAL_IF(configs.empty()); + LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size()); + mCurrentConfig = currentConfig; + + auto buildRefreshRate = [&](int configId) -> RefreshRate { + const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod; + const float fps = 1e9 / vsyncPeriod; + return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps), + vsyncPeriod, configs[configId].hwcId}; + }; - // Create a map between config index and vsync period. This is all the info we need - // from the configs. - std::vector<std::pair<int, nsecs_t>> configIdToVsyncPeriod; for (int i = 0; i < configs.size(); ++i) { - configIdToVsyncPeriod.emplace_back(i, configs.at(i)->getVsyncPeriod()); + mRefreshRates.push_back(buildRefreshRate(i)); } - std::sort(configIdToVsyncPeriod.begin(), configIdToVsyncPeriod.end(), - [](const std::pair<int, nsecs_t>& a, const std::pair<int, nsecs_t>& b) { - return a.second > b.second; - }); + if (!mRefreshRateSwitchingSupported) return; - // When the configs are ordered by the resync rate. We assume that the first one is DEFAULT. - nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second; - if (vsyncPeriod != 0) { - const float fps = 1e9 / vsyncPeriod; - const int configId = configIdToVsyncPeriod[0].first; - mRefreshRates.emplace(RefreshRateType::DEFAULT, - std::make_shared<RefreshRate>( - RefreshRate{configId, base::StringPrintf("%2.ffps", fps), - static_cast<uint32_t>(fps), - configs.at(configId)->getId()})); - } + auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> { + if (configs.size() < 2) { + return {}; + } + + std::vector<const RefreshRate*> sortedRefreshRates; + for (const auto& refreshRate : mRefreshRates) { + sortedRefreshRates.push_back(&refreshRate); + } + std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(), + [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) { + return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod; + }); + + // When the configs are ordered by the resync rate, we assume that + // the first one is DEFAULT and the second one is PERFORMANCE, + // i.e. the higher rate. + if (sortedRefreshRates[0]->vsyncPeriod == 0 || + sortedRefreshRates[1]->vsyncPeriod == 0) { + return {}; + } - if (configs.size() < 2) { + return std::pair<int, int>(sortedRefreshRates[0]->configId, + sortedRefreshRates[1]->configId); + }; + + auto defaultAndPerfConfigs = findDefaultAndPerfConfigs(); + if (!defaultAndPerfConfigs) { + mRefreshRateSwitchingSupported = false; return; } - // When the configs are ordered by the resync rate. We assume that the second one is - // PERFORMANCE, eg. the higher rate. - vsyncPeriod = configIdToVsyncPeriod[1].second; - if (vsyncPeriod != 0) { - const float fps = 1e9 / vsyncPeriod; - const int configId = configIdToVsyncPeriod[1].first; - mRefreshRates.emplace(RefreshRateType::PERFORMANCE, - std::make_shared<RefreshRate>( - RefreshRate{configId, base::StringPrintf("%2.ffps", fps), - static_cast<uint32_t>(fps), - configs.at(configId)->getId()})); - } + mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first]; + mRefreshRateMap[RefreshRateType::PERFORMANCE] = + mRefreshRates[defaultAndPerfConfigs->second]; } -private: - std::map<RefreshRateType, std::shared_ptr<RefreshRate>> mRefreshRates; + // Whether this device is doing refresh rate switching or not. This must not change after this + // object is initialized. + bool mRefreshRateSwitchingSupported; + // The list of refresh rates, indexed by display config ID. This must not change after this + // object is initialized. + std::vector<RefreshRate> mRefreshRates; + // The mapping of refresh rate type to RefreshRate. This must not change after this object is + // initialized. + std::map<RefreshRateType, RefreshRate> mRefreshRateMap; + // The ID of the current config. This will change at runtime. This is set by SurfaceFlinger on + // the main thread, and read by the Scheduler (and other objects) on other threads, so it's + // atomic. + std::atomic<int> mCurrentConfig; }; -} // namespace scheduler -} // namespace android +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h index 7e7c6307a4..8afc93e8db 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateStats.h +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -41,21 +41,18 @@ class RefreshRateStats { static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR; public: - RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats) - : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats) {} - - // Sets power mode. We only collect the information when the power mode is not - // HWC_POWER_MODE_NORMAL. When power mode is HWC_POWER_MODE_NORMAL, we collect the stats based - // on config mode. + RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats, + int currentConfigMode, int currentPowerMode) + : mRefreshRateConfigs(refreshRateConfigs), + mTimeStats(timeStats), + mCurrentConfigMode(currentConfigMode), + mCurrentPowerMode(currentPowerMode) {} + + // Sets power mode. void setPowerMode(int mode) { if (mCurrentPowerMode == mode) { return; } - // If power mode is normal, the time is going to be recorded under config modes. - if (mode == HWC_POWER_MODE_NORMAL) { - mCurrentPowerMode = mode; - return; - } flushTime(); mCurrentPowerMode = mode; } @@ -79,57 +76,50 @@ public: flushTime(); std::unordered_map<std::string, int64_t> totalTime; - for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) { - int64_t totalTimeForConfig = 0; - if (!config) { - continue; - } - if (mConfigModesTotalTime.find(config->configId) != mConfigModesTotalTime.end()) { - totalTimeForConfig = mConfigModesTotalTime.at(config->configId); - } - totalTime[config->name] = totalTimeForConfig; + // Multiple configs may map to the same name, e.g. "60fps". Add the + // times for such configs together. + for (const auto& [config, time] : mConfigModesTotalTime) { + totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] = 0; } + for (const auto& [config, time] : mConfigModesTotalTime) { + totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time; + } + totalTime["ScreenOff"] = mScreenOffTime; return totalTime; } // Traverses through the map of config modes and returns how long they've been running in easy // to read format. - std::string doDump() const { - std::ostringstream stream; - stream << "+ Refresh rate: running time in seconds\n"; + void dump(std::string& result) const { + std::ostringstream stream("+ Refresh rate: running time in seconds\n"); + for (const auto& [name, time] : const_cast<RefreshRateStats*>(this)->getTotalTimes()) { stream << name << ": " << getDateFormatFromMs(time) << '\n'; } - return stream.str(); + result.append(stream.str()); } private: - void flushTime() { - // Normal power mode is counted under different config modes. - if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) { - flushTimeForMode(mCurrentConfigMode); - } else { - flushTimeForMode(SCREEN_OFF_CONFIG_ID); - } - } - // Calculates the time that passed in ms between the last time we recorded time and the time // this method was called. - void flushTimeForMode(int mode) { + void flushTime() { nsecs_t currentTime = systemTime(); nsecs_t timeElapsed = currentTime - mPreviousRecordedTime; int64_t timeElapsedMs = ns2ms(timeElapsed); mPreviousRecordedTime = currentTime; - mConfigModesTotalTime[mode] += timeElapsedMs; - for (const auto& [type, config] : mRefreshRateConfigs.getRefreshRates()) { - if (!config) { - continue; - } - if (config->configId == mode) { - mTimeStats.recordRefreshRate(config->fps, timeElapsed); + uint32_t fps = 0; + if (mCurrentPowerMode == HWC_POWER_MODE_NORMAL) { + // Normal power mode is counted under different config modes. + if (mConfigModesTotalTime.find(mCurrentConfigMode) == mConfigModesTotalTime.end()) { + mConfigModesTotalTime[mCurrentConfigMode] = 0; } + mConfigModesTotalTime[mCurrentConfigMode] += timeElapsedMs; + fps = mRefreshRateConfigs.getRefreshRateFromConfigId(mCurrentConfigMode).fps; + } else { + mScreenOffTime += timeElapsedMs; } + mTimeStats.recordRefreshRate(fps, timeElapsed); } // Formats the time in milliseconds into easy to read format. @@ -149,10 +139,11 @@ private: // Aggregate refresh rate statistics for telemetry. TimeStats& mTimeStats; - int64_t mCurrentConfigMode = SCREEN_OFF_CONFIG_ID; - int32_t mCurrentPowerMode = HWC_POWER_MODE_OFF; + int mCurrentConfigMode; + int32_t mCurrentPowerMode; - std::unordered_map<int /* power mode */, int64_t /* duration in ms */> mConfigModesTotalTime; + std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime; + int64_t mScreenOffTime = 0; nsecs_t mPreviousRecordedTime = systemTime(); }; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 8da5612b5b..d60e101fb4 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#undef LOG_TAG +#define LOG_TAG "Scheduler" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "Scheduler.h" @@ -21,6 +23,7 @@ #include <algorithm> #include <cinttypes> #include <cstdint> +#include <functional> #include <memory> #include <numeric> @@ -38,185 +41,156 @@ #include "DispSyncSource.h" #include "EventControlThread.h" #include "EventThread.h" -#include "IdleTimer.h" #include "InjectVSyncSource.h" -#include "LayerInfo.h" +#include "OneShotTimer.h" #include "SchedulerUtils.h" #include "SurfaceFlingerProperties.h" -namespace android { - -using namespace android::hardware::configstore; -using namespace android::hardware::configstore::V1_0; -using namespace android::sysprop; - -#define RETURN_VALUE_IF_INVALID(value) \ - if (handle == nullptr || mConnections.count(handle->id) == 0) return value -#define RETURN_IF_INVALID() \ - if (handle == nullptr || mConnections.count(handle->id) == 0) return +#define RETURN_IF_INVALID_HANDLE(handle, ...) \ + do { \ + if (mConnections.count(handle) == 0) { \ + ALOGE("Invalid connection handle %" PRIuPTR, handle.id); \ + return __VA_ARGS__; \ + } \ + } while (false) -std::atomic<int64_t> Scheduler::sNextId = 0; +namespace android { Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, const scheduler::RefreshRateConfigs& refreshRateConfig) - : mHasSyncFramework(running_without_sync_framework(true)), - mDispSyncPresentTimeOffset(present_time_offset_from_vsync_ns(0)), - mPrimaryHWVsyncEnabled(false), - mHWVsyncAvailable(false), + : mPrimaryDispSync(new impl::DispSync("SchedulerDispSync", + sysprop::running_without_sync_framework(true))), + mEventControlThread(new impl::EventControlThread(std::move(function))), + mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)), mRefreshRateConfigs(refreshRateConfig) { - // Note: We create a local temporary with the real DispSync implementation - // type temporarily so we can initialize it with the configured values, - // before storing it for more generic use using the interface type. - auto primaryDispSync = std::make_unique<impl::DispSync>("SchedulerDispSync"); - primaryDispSync->init(mHasSyncFramework, mDispSyncPresentTimeOffset); - mPrimaryDispSync = std::move(primaryDispSync); - mEventControlThread = std::make_unique<impl::EventControlThread>(function); - - mSetIdleTimerMs = set_idle_timer_ms(0); - mSupportKernelTimer = support_kernel_idle_timer(false); - - mSetTouchTimerMs = set_touch_timer_ms(0); - mSetDisplayPowerTimerMs = set_display_power_timer_ms(0); + using namespace sysprop; char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.set_idle_timer_ms", value, "0"); - int int_value = atoi(value); - if (int_value) { - mSetIdleTimerMs = atoi(value); - } - - if (mSetIdleTimerMs > 0) { - if (mSupportKernelTimer) { - mIdleTimer = - std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds( - mSetIdleTimerMs), - [this] { resetKernelTimerCallback(); }, - [this] { - expiredKernelTimerCallback(); - }); - } else { - mIdleTimer = std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds( - mSetIdleTimerMs), - [this] { resetTimerCallback(); }, - [this] { expiredTimerCallback(); }); - } + const int setIdleTimerMs = atoi(value); + + if (const auto millis = setIdleTimerMs ? setIdleTimerMs : set_idle_timer_ms(0); millis > 0) { + const auto callback = mSupportKernelTimer ? &Scheduler::kernelIdleTimerCallback + : &Scheduler::idleTimerCallback; + + mIdleTimer.emplace( + std::chrono::milliseconds(millis), + [this, callback] { std::invoke(callback, this, TimerState::Reset); }, + [this, callback] { std::invoke(callback, this, TimerState::Expired); }); mIdleTimer->start(); } - if (mSetTouchTimerMs > 0) { + if (const int64_t millis = set_touch_timer_ms(0); millis > 0) { // Touch events are coming to SF every 100ms, so the timer needs to be higher than that - mTouchTimer = - std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds(mSetTouchTimerMs), - [this] { resetTouchTimerCallback(); }, - [this] { expiredTouchTimerCallback(); }); + mTouchTimer.emplace( + std::chrono::milliseconds(millis), + [this] { touchTimerCallback(TimerState::Reset); }, + [this] { touchTimerCallback(TimerState::Expired); }); mTouchTimer->start(); } - if (mSetDisplayPowerTimerMs > 0) { - mDisplayPowerTimer = - std::make_unique<scheduler::IdleTimer>(std::chrono::milliseconds( - mSetDisplayPowerTimerMs), - [this] { resetDisplayPowerTimerCallback(); }, - [this] { - expiredDisplayPowerTimerCallback(); - }); + if (const int64_t millis = set_display_power_timer_ms(0); millis > 0) { + mDisplayPowerTimer.emplace( + std::chrono::milliseconds(millis), + [this] { displayPowerTimerCallback(TimerState::Reset); }, + [this] { displayPowerTimerCallback(TimerState::Expired); }); mDisplayPowerTimer->start(); } } +Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync, + std::unique_ptr<EventControlThread> eventControlThread, + const scheduler::RefreshRateConfigs& configs) + : mPrimaryDispSync(std::move(primaryDispSync)), + mEventControlThread(std::move(eventControlThread)), + mSupportKernelTimer(false), + mRefreshRateConfigs(configs) {} + Scheduler::~Scheduler() { - // Ensure the IdleTimer thread is joined before we start destroying state. + // Ensure the OneShotTimer threads are joined before we start destroying state. mDisplayPowerTimer.reset(); mTouchTimer.reset(); mIdleTimer.reset(); } -sp<Scheduler::ConnectionHandle> Scheduler::createConnection( +DispSync& Scheduler::getPrimaryDispSync() { + return *mPrimaryDispSync; +} + +std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource( + const char* name, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync) { + return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs, + offsetThresholdForNextVsync, true /* traceVsync */, + name); +} + +Scheduler::ConnectionHandle Scheduler::createConnection( const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync, - ResyncCallback resyncCallback, - impl::EventThread::InterceptVSyncsCallback interceptCallback) { - const int64_t id = sNextId++; - ALOGV("Creating a connection handle with ID: %" PRId64 "\n", id); - - std::unique_ptr<EventThread> eventThread = - makeEventThread(connectionName, mPrimaryDispSync.get(), phaseOffsetNs, - offsetThresholdForNextVsync, std::move(interceptCallback)); - - auto eventThreadConnection = - createConnectionInternal(eventThread.get(), std::move(resyncCallback), - ISurfaceComposer::eConfigChangedSuppress); - mConnections.emplace(id, - std::make_unique<Connection>(new ConnectionHandle(id), - eventThreadConnection, - std::move(eventThread))); - return mConnections[id]->handle; -} - -std::unique_ptr<EventThread> Scheduler::makeEventThread( - const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs, - nsecs_t offsetThresholdForNextVsync, impl::EventThread::InterceptVSyncsCallback interceptCallback) { - std::unique_ptr<VSyncSource> eventThreadSource = - std::make_unique<DispSyncSource>(dispSync, phaseOffsetNs, offsetThresholdForNextVsync, - true, connectionName); - return std::make_unique<impl::EventThread>(std::move(eventThreadSource), - std::move(interceptCallback), connectionName); + auto vsyncSource = + makePrimaryDispSyncSource(connectionName, phaseOffsetNs, offsetThresholdForNextVsync); + auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource), + std::move(interceptCallback)); + return createConnection(std::move(eventThread)); } -sp<EventThreadConnection> Scheduler::createConnectionInternal( - EventThread* eventThread, ResyncCallback&& resyncCallback, - ISurfaceComposer::ConfigChanged configChanged) { - return eventThread->createEventConnection(std::move(resyncCallback), configChanged); +Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) { + const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++}; + ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id); + + auto connection = + createConnectionInternal(eventThread.get(), ISurfaceComposer::eConfigChangedSuppress); + + mConnections.emplace(handle, Connection{connection, std::move(eventThread)}); + return handle; } -sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection( - const sp<Scheduler::ConnectionHandle>& handle, ResyncCallback resyncCallback, - ISurfaceComposer::ConfigChanged configChanged) { - RETURN_VALUE_IF_INVALID(nullptr); - return createConnectionInternal(mConnections[handle->id]->thread.get(), - std::move(resyncCallback), configChanged); +sp<EventThreadConnection> Scheduler::createConnectionInternal( + EventThread* eventThread, ISurfaceComposer::ConfigChanged configChanged) { + return eventThread->createEventConnection([&] { resync(); }, configChanged); } -EventThread* Scheduler::getEventThread(const sp<Scheduler::ConnectionHandle>& handle) { - RETURN_VALUE_IF_INVALID(nullptr); - return mConnections[handle->id]->thread.get(); +sp<IDisplayEventConnection> Scheduler::createDisplayEventConnection( + ConnectionHandle handle, ISurfaceComposer::ConfigChanged configChanged) { + RETURN_IF_INVALID_HANDLE(handle, nullptr); + return createConnectionInternal(mConnections[handle].thread.get(), configChanged); } -sp<EventThreadConnection> Scheduler::getEventConnection(const sp<ConnectionHandle>& handle) { - RETURN_VALUE_IF_INVALID(nullptr); - return mConnections[handle->id]->eventConnection; +sp<EventThreadConnection> Scheduler::getEventConnection(ConnectionHandle handle) { + RETURN_IF_INVALID_HANDLE(handle, nullptr); + return mConnections[handle].connection; } -void Scheduler::hotplugReceived(const sp<Scheduler::ConnectionHandle>& handle, - PhysicalDisplayId displayId, bool connected) { - RETURN_IF_INVALID(); - mConnections[handle->id]->thread->onHotplugReceived(displayId, connected); +void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId displayId, + bool connected) { + RETURN_IF_INVALID_HANDLE(handle); + mConnections[handle].thread->onHotplugReceived(displayId, connected); } -void Scheduler::onScreenAcquired(const sp<Scheduler::ConnectionHandle>& handle) { - RETURN_IF_INVALID(); - mConnections[handle->id]->thread->onScreenAcquired(); +void Scheduler::onScreenAcquired(ConnectionHandle handle) { + RETURN_IF_INVALID_HANDLE(handle); + mConnections[handle].thread->onScreenAcquired(); } -void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle) { - RETURN_IF_INVALID(); - mConnections[handle->id]->thread->onScreenReleased(); +void Scheduler::onScreenReleased(ConnectionHandle handle) { + RETURN_IF_INVALID_HANDLE(handle); + mConnections[handle].thread->onScreenReleased(); } -void Scheduler::onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId, +void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId, int32_t configId) { - RETURN_IF_INVALID(); - mConnections[handle->id]->thread->onConfigChanged(displayId, configId); + RETURN_IF_INVALID_HANDLE(handle); + mConnections[handle].thread->onConfigChanged(displayId, configId); } -void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, std::string& result) const { - RETURN_IF_INVALID(); - mConnections.at(handle->id)->thread->dump(result); +void Scheduler::dump(ConnectionHandle handle, std::string& result) const { + RETURN_IF_INVALID_HANDLE(handle); + mConnections.at(handle).thread->dump(result); } -void Scheduler::setPhaseOffset(const sp<Scheduler::ConnectionHandle>& handle, nsecs_t phaseOffset) { - RETURN_IF_INVALID(); - mConnections[handle->id]->thread->setPhaseOffset(phaseOffset); +void Scheduler::setPhaseOffset(ConnectionHandle handle, nsecs_t phaseOffset) { + RETURN_IF_INVALID_HANDLE(handle); + mConnections[handle].thread->setPhaseOffset(phaseOffset); } void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) { @@ -224,6 +198,37 @@ void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) { stats->vsyncPeriod = mPrimaryDispSync->getPeriod(); } +Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) { + if (mInjectVSyncs == enable) { + return {}; + } + + ALOGV("%s VSYNC injection", enable ? "Enabling" : "Disabling"); + + if (!mInjectorConnectionHandle) { + auto vsyncSource = std::make_unique<InjectVSyncSource>(); + mVSyncInjector = vsyncSource.get(); + + auto eventThread = + std::make_unique<impl::EventThread>(std::move(vsyncSource), + impl::EventThread::InterceptVSyncsCallback()); + + mInjectorConnectionHandle = createConnection(std::move(eventThread)); + } + + mInjectVSyncs = enable; + return mInjectorConnectionHandle; +} + +bool Scheduler::injectVSync(nsecs_t when) { + if (!mInjectVSyncs || !mVSyncInjector) { + return false; + } + + mVSyncInjector->onInjectSyncEvent(when); + return true; +} + void Scheduler::enableHardwareVsync() { std::lock_guard<std::mutex> lock(mHWVsyncLock); if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) { @@ -264,31 +269,19 @@ void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) { setVsyncPeriod(period); } -ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) { - std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState; - return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() { - if (const auto vsync = ptr.lock()) { - vsync->resync(getVsyncPeriod); - } - }; -} - -void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) { +void Scheduler::resync() { static constexpr nsecs_t kIgnoreDelay = ms2ns(750); const nsecs_t now = systemTime(); - const nsecs_t last = lastResyncTime.exchange(now); + const nsecs_t last = mLastResyncTime.exchange(now); if (now - last > kIgnoreDelay) { - scheduler.resyncToHardwareVsync(false, getVsyncPeriod()); + resyncToHardwareVsync(false, + mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod); } } -void Scheduler::setRefreshSkipCount(int count) { - mPrimaryDispSync->setRefreshSkipCount(count); -} - -void Scheduler::setVsyncPeriod(const nsecs_t period) { +void Scheduler::setVsyncPeriod(nsecs_t period) { std::lock_guard<std::mutex> lock(mHWVsyncLock); mPrimaryDispSync->setPeriod(period); @@ -299,7 +292,7 @@ void Scheduler::setVsyncPeriod(const nsecs_t period) { } } -void Scheduler::addResyncSample(const nsecs_t timestamp, bool* periodFlushed) { +void Scheduler::addResyncSample(nsecs_t timestamp, bool* periodFlushed) { bool needsHwVsync = false; *periodFlushed = false; { // Scope for the lock @@ -332,21 +325,21 @@ nsecs_t Scheduler::getDispSyncExpectedPresentTime() { return mPrimaryDispSync->expectedPresentTime(); } -void Scheduler::dumpPrimaryDispSync(std::string& result) const { - mPrimaryDispSync->dump(result); -} - std::unique_ptr<scheduler::LayerHistory::LayerHandle> Scheduler::registerLayer( std::string const& name, int windowType) { - RefreshRateType refreshRateType = (windowType == InputWindowInfo::TYPE_WALLPAPER) - ? RefreshRateType::DEFAULT - : RefreshRateType::PERFORMANCE; - - const auto refreshRate = mRefreshRateConfigs.getRefreshRate(refreshRateType); - const uint32_t performanceFps = (refreshRate) ? refreshRate->fps : 0; - - const auto defaultRefreshRate = mRefreshRateConfigs.getRefreshRate(RefreshRateType::DEFAULT); - const uint32_t defaultFps = (defaultRefreshRate) ? defaultRefreshRate->fps : 0; + uint32_t defaultFps, performanceFps; + if (mRefreshRateConfigs.refreshRateSwitchingSupported()) { + defaultFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps; + performanceFps = + mRefreshRateConfigs + .getRefreshRateFromType((windowType == InputWindowInfo::TYPE_WALLPAPER) + ? RefreshRateType::DEFAULT + : RefreshRateType::PERFORMANCE) + .fps; + } else { + defaultFps = mRefreshRateConfigs.getCurrentRefreshRate().second.fps; + performanceFps = defaultFps; + } return mLayerHistory.createLayer(name, defaultFps, performanceFps); } @@ -361,61 +354,35 @@ void Scheduler::setLayerVisibility( mLayerHistory.setVisibility(layerHandle, visible); } -void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) { - fn(*mPrimaryDispSync); -} - void Scheduler::updateFpsBasedOnContent() { auto [refreshRate, isHDR] = mLayerHistory.getDesiredRefreshRateAndHDR(); const uint32_t refreshRateRound = std::round(refreshRate); RefreshRateType newRefreshRateType; { std::lock_guard<std::mutex> lock(mFeatureStateLock); - if (mContentRefreshRate == refreshRateRound && mIsHDRContent == isHDR) { + if (mFeatures.contentRefreshRate == refreshRateRound && mFeatures.isHDRContent == isHDR) { return; } - mContentRefreshRate = refreshRateRound; - ATRACE_INT("ContentFPS", mContentRefreshRate); + mFeatures.contentRefreshRate = refreshRateRound; + ATRACE_INT("ContentFPS", refreshRateRound); - mIsHDRContent = isHDR; - ATRACE_INT("ContentHDR", mIsHDRContent); + mFeatures.isHDRContent = isHDR; + ATRACE_INT("ContentHDR", isHDR); - mCurrentContentFeatureState = refreshRateRound > 0 - ? ContentFeatureState::CONTENT_DETECTION_ON - : ContentFeatureState::CONTENT_DETECTION_OFF; + mFeatures.contentDetection = + refreshRateRound > 0 ? ContentDetectionState::On : ContentDetectionState::Off; newRefreshRateType = calculateRefreshRateType(); - if (mRefreshRateType == newRefreshRateType) { + if (mFeatures.refreshRateType == newRefreshRateType) { return; } - mRefreshRateType = newRefreshRateType; + mFeatures.refreshRateType = newRefreshRateType; } changeRefreshRate(newRefreshRateType, ConfigEvent::Changed); } -void Scheduler::setChangeRefreshRateCallback( - const ChangeRefreshRateCallback&& changeRefreshRateCallback) { - std::lock_guard<std::mutex> lock(mCallbackLock); - mChangeRefreshRateCallback = changeRefreshRateCallback; -} - -void Scheduler::setGetCurrentRefreshRateTypeCallback( - const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateTypeCallback) { +void Scheduler::setChangeRefreshRateCallback(ChangeRefreshRateCallback&& callback) { std::lock_guard<std::mutex> lock(mCallbackLock); - mGetCurrentRefreshRateTypeCallback = getCurrentRefreshRateTypeCallback; -} - -void Scheduler::setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod) { - std::lock_guard<std::mutex> lock(mCallbackLock); - mGetVsyncPeriod = getVsyncPeriod; -} - -void Scheduler::updateFrameSkipping(const int64_t skipCount) { - ATRACE_INT("FrameSkipCount", skipCount); - if (mSkipCount != skipCount) { - // Only update DispSync if it hasn't been updated yet. - mPrimaryDispSync->setRefreshSkipCount(skipCount); - mSkipCount = skipCount; - } + mChangeRefreshRateCallback = std::move(callback); } void Scheduler::resetIdleTimer() { @@ -429,8 +396,8 @@ void Scheduler::notifyTouchEvent() { mTouchTimer->reset(); } - if (mSupportKernelTimer) { - resetIdleTimer(); + if (mSupportKernelTimer && mIdleTimer) { + mIdleTimer->reset(); } // Touch event will boost the refresh rate to performance. @@ -441,7 +408,7 @@ void Scheduler::notifyTouchEvent() { void Scheduler::setDisplayPowerState(bool normal) { { std::lock_guard<std::mutex> lock(mFeatureStateLock); - mIsDisplayPowerStateNormal = normal; + mFeatures.isDisplayPowerStateNormal = normal; } if (mDisplayPowerTimer) { @@ -453,67 +420,50 @@ void Scheduler::setDisplayPowerState(bool normal) { mLayerHistory.clearHistory(); } -void Scheduler::resetTimerCallback() { - handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::RESET, false); - ATRACE_INT("ExpiredIdleTimer", 0); -} +void Scheduler::kernelIdleTimerCallback(TimerState state) { + ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state)); -void Scheduler::resetKernelTimerCallback() { - ATRACE_INT("ExpiredKernelIdleTimer", 0); - std::lock_guard<std::mutex> lock(mCallbackLock); - if (mGetVsyncPeriod && mGetCurrentRefreshRateTypeCallback) { + const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate(); + if (state == TimerState::Reset && refreshRate.first == RefreshRateType::PERFORMANCE) { // If we're not in performance mode then the kernel timer shouldn't do // anything, as the refresh rate during DPU power collapse will be the // same. - if (mGetCurrentRefreshRateTypeCallback() == Scheduler::RefreshRateType::PERFORMANCE) { - resyncToHardwareVsync(true, mGetVsyncPeriod()); - } + resyncToHardwareVsync(true /* makeAvailable */, refreshRate.second.vsyncPeriod); + } else if (state == TimerState::Expired && refreshRate.first != RefreshRateType::PERFORMANCE) { + // Disable HW VSYNC if the timer expired, as we don't need it enabled if + // we're not pushing frames, and if we're in PERFORMANCE mode then we'll + // need to update the DispSync model anyway. + disableHardwareVsync(false /* makeUnavailable */); } } -void Scheduler::expiredTimerCallback() { - handleTimerStateChanged(&mCurrentIdleTimerState, IdleTimerState::EXPIRED, false); - ATRACE_INT("ExpiredIdleTimer", 1); +void Scheduler::idleTimerCallback(TimerState state) { + handleTimerStateChanged(&mFeatures.idleTimer, state, false /* eventOnContentDetection */); + ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state)); } -void Scheduler::resetTouchTimerCallback() { - handleTimerStateChanged(&mCurrentTouchState, TouchState::ACTIVE, true); - ATRACE_INT("TouchState", 1); +void Scheduler::touchTimerCallback(TimerState state) { + const TouchState touch = state == TimerState::Reset ? TouchState::Active : TouchState::Inactive; + handleTimerStateChanged(&mFeatures.touch, touch, true /* eventOnContentDetection */); + ATRACE_INT("TouchState", static_cast<int>(touch)); } -void Scheduler::expiredTouchTimerCallback() { - handleTimerStateChanged(&mCurrentTouchState, TouchState::INACTIVE, true); - ATRACE_INT("TouchState", 0); +void Scheduler::displayPowerTimerCallback(TimerState state) { + handleTimerStateChanged(&mFeatures.displayPowerTimer, state, + true /* eventOnContentDetection */); + ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state)); } -void Scheduler::resetDisplayPowerTimerCallback() { - handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::RESET, true); - ATRACE_INT("ExpiredDisplayPowerTimer", 0); -} - -void Scheduler::expiredDisplayPowerTimerCallback() { - handleTimerStateChanged(&mDisplayPowerTimerState, DisplayPowerTimerState::EXPIRED, true); - ATRACE_INT("ExpiredDisplayPowerTimer", 1); -} - -void Scheduler::expiredKernelTimerCallback() { - std::lock_guard<std::mutex> lock(mCallbackLock); - ATRACE_INT("ExpiredKernelIdleTimer", 1); - if (mGetCurrentRefreshRateTypeCallback) { - if (mGetCurrentRefreshRateTypeCallback() != Scheduler::RefreshRateType::PERFORMANCE) { - // Disable HW Vsync if the timer expired, as we don't need it - // enabled if we're not pushing frames, and if we're in PERFORMANCE - // mode then we'll need to re-update the DispSync model anyways. - disableHardwareVsync(false); - } +void Scheduler::dump(std::string& result) const { + std::ostringstream stream; + if (mIdleTimer) { + stream << "+ Idle timer interval: " << mIdleTimer->interval().count() << " ms\n"; + } + if (mTouchTimer) { + stream << "+ Touch timer interval: " << mTouchTimer->interval().count() << " ms\n"; } -} -std::string Scheduler::doDump() { - std::ostringstream stream; - stream << "+ Idle timer interval: " << mSetIdleTimerMs << " ms" << std::endl; - stream << "+ Touch timer interval: " << mSetTouchTimerMs << " ms" << std::endl; - return stream.str(); + result.append(stream.str()); } template <class T> @@ -527,12 +477,11 @@ void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventO } *currentState = newState; newRefreshRateType = calculateRefreshRateType(); - if (mRefreshRateType == newRefreshRateType) { + if (mFeatures.refreshRateType == newRefreshRateType) { return; } - mRefreshRateType = newRefreshRateType; - if (eventOnContentDetection && - mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_ON) { + mFeatures.refreshRateType = newRefreshRateType; + if (eventOnContentDetection && mFeatures.contentDetection == ContentDetectionState::On) { event = ConfigEvent::Changed; } } @@ -540,43 +489,44 @@ void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventO } Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() { + if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) { + return RefreshRateType::DEFAULT; + } + // HDR content is not supported on PERFORMANCE mode - if (mForceHDRContentToDefaultRefreshRate && mIsHDRContent) { + if (mForceHDRContentToDefaultRefreshRate && mFeatures.isHDRContent) { return RefreshRateType::DEFAULT; } // If Display Power is not in normal operation we want to be in performance mode. // When coming back to normal mode, a grace period is given with DisplayPowerTimer - if (!mIsDisplayPowerStateNormal || mDisplayPowerTimerState == DisplayPowerTimerState::RESET) { + if (!mFeatures.isDisplayPowerStateNormal || mFeatures.displayPowerTimer == TimerState::Reset) { return RefreshRateType::PERFORMANCE; } // As long as touch is active we want to be in performance mode - if (mCurrentTouchState == TouchState::ACTIVE) { + if (mFeatures.touch == TouchState::Active) { return RefreshRateType::PERFORMANCE; } // If timer has expired as it means there is no new content on the screen - if (mCurrentIdleTimerState == IdleTimerState::EXPIRED) { + if (mFeatures.idleTimer == TimerState::Expired) { return RefreshRateType::DEFAULT; } // If content detection is off we choose performance as we don't know the content fps - if (mCurrentContentFeatureState == ContentFeatureState::CONTENT_DETECTION_OFF) { + if (mFeatures.contentDetection == ContentDetectionState::Off) { return RefreshRateType::PERFORMANCE; } // Content detection is on, find the appropriate refresh rate with minimal error - auto begin = mRefreshRateConfigs.getRefreshRates().cbegin(); - - // Skip POWER_SAVING config as it is not a real config - if (begin->first == RefreshRateType::POWER_SAVING) { - ++begin; - } - auto iter = min_element(begin, mRefreshRateConfigs.getRefreshRates().cend(), - [rate = mContentRefreshRate](const auto& l, const auto& r) -> bool { - return std::abs(l.second->fps - static_cast<float>(rate)) < - std::abs(r.second->fps - static_cast<float>(rate)); + // TODO(b/139751853): Scan allowed refresh rates only (SurfaceFlinger::mAllowedDisplayConfigs) + const float rate = static_cast<float>(mFeatures.contentRefreshRate); + auto iter = min_element(mRefreshRateConfigs.getRefreshRateMap().cbegin(), + mRefreshRateConfigs.getRefreshRateMap().cend(), + [rate](const auto& lhs, const auto& rhs) -> bool { + return std::abs(lhs.second.fps - rate) < + std::abs(rhs.second.fps - rate); }); RefreshRateType currRefreshRateType = iter->first; @@ -584,11 +534,10 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() { // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't // align well with both constexpr float MARGIN = 0.05f; - float ratio = mRefreshRateConfigs.getRefreshRate(currRefreshRateType)->fps / - float(mContentRefreshRate); + float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps / rate; if (std::abs(std::round(ratio) - ratio) > MARGIN) { - while (iter != mRefreshRateConfigs.getRefreshRates().cend()) { - ratio = iter->second->fps / float(mContentRefreshRate); + while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) { + ratio = iter->second.fps / rate; if (std::abs(std::round(ratio) - ratio) <= MARGIN) { currRefreshRateType = iter->first; @@ -601,6 +550,11 @@ Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() { return currRefreshRateType; } +Scheduler::RefreshRateType Scheduler::getPreferredRefreshRateType() { + std::lock_guard<std::mutex> lock(mFeatureStateLock); + return mFeatures.refreshRateType; +} + void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) { std::lock_guard<std::mutex> lock(mCallbackLock); if (mChangeRefreshRateCallback) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 5d8bb4cd2f..a5971fed23 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -16,140 +16,88 @@ #pragma once -#include <cstdint> +#include <atomic> #include <functional> #include <memory> +#include <mutex> +#include <optional> +#include <unordered_map> -#include <ui/DisplayStatInfo.h> #include <ui/GraphicTypes.h> -#include "DispSync.h" #include "EventControlThread.h" #include "EventThread.h" -#include "IdleTimer.h" -#include "InjectVSyncSource.h" #include "LayerHistory.h" +#include "OneShotTimer.h" #include "RefreshRateConfigs.h" #include "SchedulerUtils.h" namespace android { -class EventControlThread; +class DispSync; +class FenceTime; +class InjectVSyncSource; +struct DisplayStateInfo; class Scheduler { public: - // Enum to keep track of whether we trigger event to notify choreographer of config changes. - enum class ConfigEvent { None, Changed }; - - // logical or operator with the semantics of at least one of the events is Changed - friend ConfigEvent operator|(const ConfigEvent& first, const ConfigEvent& second) { - if (first == ConfigEvent::Changed) return ConfigEvent::Changed; - if (second == ConfigEvent::Changed) return ConfigEvent::Changed; - return ConfigEvent::None; - } - using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType; - using GetCurrentRefreshRateTypeCallback = std::function<RefreshRateType()>; + using ConfigEvent = scheduler::RefreshRateConfigEvent; + using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>; - using GetVsyncPeriod = std::function<nsecs_t()>; - // Enum to indicate whether to start the transaction early, or at vsync time. + // Indicates whether to start the transaction early, or at vsync time. enum class TransactionStart { EARLY, NORMAL }; - /* The scheduler handle is a BBinder object passed to the client from which we can extract - * an ID for subsequent operations. - */ - class ConnectionHandle : public BBinder { - public: - ConnectionHandle(int64_t id) : id(id) {} - - ~ConnectionHandle() = default; - - const int64_t id; - }; - - class Connection { - public: - Connection(sp<ConnectionHandle> handle, sp<EventThreadConnection> eventConnection, - std::unique_ptr<EventThread> eventThread) - : handle(handle), eventConnection(eventConnection), thread(std::move(eventThread)) {} - - ~Connection() = default; - - sp<ConnectionHandle> handle; - sp<EventThreadConnection> eventConnection; - const std::unique_ptr<EventThread> thread; - }; - - // Stores per-display state about VSYNC. - struct VsyncState { - explicit VsyncState(Scheduler& scheduler) : scheduler(scheduler) {} - - void resync(const GetVsyncPeriod&); - - Scheduler& scheduler; - std::atomic<nsecs_t> lastResyncTime = 0; - }; - - explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function, - const scheduler::RefreshRateConfigs& refreshRateConfig); + Scheduler(impl::EventControlThread::SetVSyncEnabledFunction, + const scheduler::RefreshRateConfigs&); virtual ~Scheduler(); - /** Creates an EventThread connection. */ - sp<ConnectionHandle> createConnection(const char* connectionName, nsecs_t phaseOffsetNs, - nsecs_t offsetThresholdForNextVsync, ResyncCallback, - impl::EventThread::InterceptVSyncsCallback); - - sp<IDisplayEventConnection> createDisplayEventConnection( - const sp<ConnectionHandle>& handle, ResyncCallback, - ISurfaceComposer::ConfigChanged configChanged); - - // Getter methods. - EventThread* getEventThread(const sp<ConnectionHandle>& handle); + DispSync& getPrimaryDispSync(); - // Provides access to the DispSync object for the primary display. - void withPrimaryDispSync(std::function<void(DispSync&)> const& fn); + using ConnectionHandle = scheduler::ConnectionHandle; + ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs, + nsecs_t offsetThresholdForNextVsync, + impl::EventThread::InterceptVSyncsCallback); - sp<EventThreadConnection> getEventConnection(const sp<ConnectionHandle>& handle); + sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle, + ISurfaceComposer::ConfigChanged); - // Should be called when receiving a hotplug event. - void hotplugReceived(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId, - bool connected); + sp<EventThreadConnection> getEventConnection(ConnectionHandle); - // Should be called after the screen is turned on. - void onScreenAcquired(const sp<ConnectionHandle>& handle); + void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); + void onConfigChanged(ConnectionHandle, PhysicalDisplayId, int32_t configId); - // Should be called before the screen is turned off. - void onScreenReleased(const sp<ConnectionHandle>& handle); + void onScreenAcquired(ConnectionHandle); + void onScreenReleased(ConnectionHandle); - // Should be called when display config changed - void onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId, - int32_t configId); + // Modifies phase offset in the event thread. + void setPhaseOffset(ConnectionHandle, nsecs_t phaseOffset); - // Should be called when dumpsys command is received. - void dump(const sp<ConnectionHandle>& handle, std::string& result) const; + void getDisplayStatInfo(DisplayStatInfo* stats); - // Offers ability to modify phase offset in the event thread. - void setPhaseOffset(const sp<ConnectionHandle>& handle, nsecs_t phaseOffset); + // Returns injector handle if injection has toggled, or an invalid handle otherwise. + ConnectionHandle enableVSyncInjection(bool enable); - void getDisplayStatInfo(DisplayStatInfo* stats); + // Returns false if injection is disabled. + bool injectVSync(nsecs_t when); void enableHardwareVsync(); void disableHardwareVsync(bool makeUnavailable); + // Resyncs the scheduler to hardware vsync. // If makeAvailable is true, then hardware vsync will be turned on. // Otherwise, if hardware vsync is not already enabled then this method will // no-op. // The period is the vsync period from the current display configuration. void resyncToHardwareVsync(bool makeAvailable, nsecs_t period); - // Creates a callback for resyncing. - ResyncCallback makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod); - void setRefreshSkipCount(int count); + void resync(); + // Passes a vsync sample to DispSync. periodFlushed will be true if // DispSync detected that the vsync period changed, and false otherwise. - void addResyncSample(const nsecs_t timestamp, bool* periodFlushed); - void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime); + void addResyncSample(nsecs_t timestamp, bool* periodFlushed); + void addPresentFence(const std::shared_ptr<FenceTime>&); void setIgnorePresentFences(bool ignore); nsecs_t getDispSyncExpectedPresentTime(); // Registers the layer in the scheduler, and returns the handle for future references. @@ -165,155 +113,115 @@ public: const std::unique_ptr<scheduler::LayerHistory::LayerHandle>& layerHandle, bool visible); // Updates FPS based on the most content presented. void updateFpsBasedOnContent(); - // Callback that gets invoked when Scheduler wants to change the refresh rate. - void setChangeRefreshRateCallback(const ChangeRefreshRateCallback&& changeRefreshRateCallback); - void setGetCurrentRefreshRateTypeCallback( - const GetCurrentRefreshRateTypeCallback&& getCurrentRefreshRateType); - void setGetVsyncPeriodCallback(const GetVsyncPeriod&& getVsyncPeriod); - // Returns whether idle timer is enabled or not - bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; } + // Called by Scheduler to change refresh rate. + void setChangeRefreshRateCallback(ChangeRefreshRateCallback&&); - // Function that resets the idle timer. + bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); } void resetIdleTimer(); // Function that resets the touch timer. void notifyTouchEvent(); - // Function that sets whether display power mode is normal or not. void setDisplayPowerState(bool normal); - // Returns relevant information about Scheduler for dumpsys purposes. - std::string doDump(); - - // calls DispSync::dump() on primary disp sync - void dumpPrimaryDispSync(std::string& result) const; + void dump(std::string&) const; + void dump(ConnectionHandle, std::string&) const; -protected: - virtual std::unique_ptr<EventThread> makeEventThread( - const char* connectionName, DispSync* dispSync, nsecs_t phaseOffsetNs, - nsecs_t offsetThresholdForNextVsync, - impl::EventThread::InterceptVSyncsCallback interceptCallback); + // Get the appropriate refresh type for current conditions. + RefreshRateType getPreferredRefreshRateType(); private: friend class TestableScheduler; // In order to make sure that the features don't override themselves, we need a state machine // to keep track which feature requested the config change. - enum class ContentFeatureState { CONTENT_DETECTION_ON, CONTENT_DETECTION_OFF }; - enum class IdleTimerState { EXPIRED, RESET }; - enum class TouchState { INACTIVE, ACTIVE }; - enum class DisplayPowerTimerState { EXPIRED, RESET }; + enum class ContentDetectionState { Off, On }; + enum class TimerState { Reset, Expired }; + enum class TouchState { Inactive, Active }; - // Creates a connection on the given EventThread and forwards the given callbacks. - sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&, + // Used by tests to inject mocks. + Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>, + const scheduler::RefreshRateConfigs&); + + std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs, + nsecs_t offsetThresholdForNextVsync); + + // Create a connection on the given EventThread. + ConnectionHandle createConnection(std::unique_ptr<EventThread>); + sp<EventThreadConnection> createConnectionInternal(EventThread*, ISurfaceComposer::ConfigChanged); - nsecs_t calculateAverage() const; - void updateFrameSkipping(const int64_t skipCount); - - // Function that is called when the timer resets. - void resetTimerCallback(); - // Function that is called when the timer expires. - void expiredTimerCallback(); - // Function that is called when the timer resets when paired with a display - // driver timeout in the kernel. This enables hardware vsync when we move - // out from idle. - void resetKernelTimerCallback(); - // Function that is called when the timer expires when paired with a display - // driver timeout in the kernel. This disables hardware vsync when we move - // into idle. - void expiredKernelTimerCallback(); - // Function that is called when the touch timer resets. - void resetTouchTimerCallback(); - // Function that is called when the touch timer expires. - void expiredTouchTimerCallback(); - // Function that is called when the display power timer resets. - void resetDisplayPowerTimerCallback(); - // Function that is called when the display power timer expires. - void expiredDisplayPowerTimerCallback(); - // Sets vsync period. - void setVsyncPeriod(const nsecs_t period); + // Update feature state machine to given state when corresponding timer resets or expires. + void kernelIdleTimerCallback(TimerState); + void idleTimerCallback(TimerState); + void touchTimerCallback(TimerState); + void displayPowerTimerCallback(TimerState); + // handles various timer features to change the refresh rate. template <class T> void handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection); - // Calculate the new refresh rate type - RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock); - // Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters. - void changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent); - // Helper function to calculate error frames - float getErrorFrames(float contentFps, float configFps); + void setVsyncPeriod(nsecs_t period); - // If fences from sync Framework are supported. - const bool mHasSyncFramework; + RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock); + // Acquires a lock and calls the ChangeRefreshRateCallback with given parameters. + void changeRefreshRate(RefreshRateType, ConfigEvent); - // The offset in nanoseconds to use, when DispSync timestamps present fence - // signaling time. - nsecs_t mDispSyncPresentTimeOffset; + // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. + struct Connection { + sp<EventThreadConnection> connection; + std::unique_ptr<EventThread> thread; + }; - // Each connection has it's own ID. This variable keeps track of the count. - static std::atomic<int64_t> sNextId; + ConnectionHandle::Id mNextConnectionHandleId = 0; + std::unordered_map<ConnectionHandle, Connection> mConnections; - // Connections are stored in a map <connection ID, connection> for easy retrieval. - std::unordered_map<int64_t, std::unique_ptr<Connection>> mConnections; + bool mInjectVSyncs = false; + InjectVSyncSource* mVSyncInjector = nullptr; + ConnectionHandle mInjectorConnectionHandle; std::mutex mHWVsyncLock; - bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock); - bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock); - const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)}; + bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock) = false; + bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock) = false; + + std::atomic<nsecs_t> mLastResyncTime = 0; std::unique_ptr<DispSync> mPrimaryDispSync; std::unique_ptr<EventControlThread> mEventControlThread; - // TODO(b/113612090): The following set of variables needs to be revised. For now, this is - // a proof of concept. We turn on frame skipping if the difference between the timestamps - // is between 32 and 34ms. We expect this currently for 30fps videos, so we render them at 30Hz. - nsecs_t mPreviousFrameTimestamp = 0; - // Keeping track of whether we are skipping the refresh count. If we want to - // simulate 30Hz rendering, we skip every other frame, and this variable is set - // to 1. - int64_t mSkipCount = 0; - std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{}; - size_t mCounter = 0; - // Historical information about individual layers. Used for predicting the refresh rate. scheduler::LayerHistory mLayerHistory; - // Timer that records time between requests for next vsync. If the time is higher than a given - // interval, a callback is fired. Set this variable to >0 to use this feature. - int64_t mSetIdleTimerMs = 0; - std::unique_ptr<scheduler::IdleTimer> mIdleTimer; - // Enables whether to use idle timer callbacks that support the kernel - // timer. - bool mSupportKernelTimer; + // Whether to use idle timer callbacks that support the kernel timer. + const bool mSupportKernelTimer; + // Timer that records time between requests for next vsync. + std::optional<scheduler::OneShotTimer> mIdleTimer; // Timer used to monitor touch events. - int64_t mSetTouchTimerMs = 0; - std::unique_ptr<scheduler::IdleTimer> mTouchTimer; - + std::optional<scheduler::OneShotTimer> mTouchTimer; // Timer used to monitor display power mode. - int64_t mSetDisplayPowerTimerMs = 0; - std::unique_ptr<scheduler::IdleTimer> mDisplayPowerTimer; + std::optional<scheduler::OneShotTimer> mDisplayPowerTimer; std::mutex mCallbackLock; - GetCurrentRefreshRateTypeCallback mGetCurrentRefreshRateTypeCallback GUARDED_BY(mCallbackLock); ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock); - GetVsyncPeriod mGetVsyncPeriod GUARDED_BY(mCallbackLock); // In order to make sure that the features don't override themselves, we need a state machine // to keep track which feature requested the config change. std::mutex mFeatureStateLock; - ContentFeatureState mCurrentContentFeatureState GUARDED_BY(mFeatureStateLock) = - ContentFeatureState::CONTENT_DETECTION_OFF; - IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET; - TouchState mCurrentTouchState GUARDED_BY(mFeatureStateLock) = TouchState::INACTIVE; - DisplayPowerTimerState mDisplayPowerTimerState GUARDED_BY(mFeatureStateLock) = - DisplayPowerTimerState::EXPIRED; - uint32_t mContentRefreshRate GUARDED_BY(mFeatureStateLock); - RefreshRateType mRefreshRateType GUARDED_BY(mFeatureStateLock); - bool mIsHDRContent GUARDED_BY(mFeatureStateLock) = false; - bool mIsDisplayPowerStateNormal GUARDED_BY(mFeatureStateLock) = true; + + struct { + ContentDetectionState contentDetection = ContentDetectionState::Off; + TimerState idleTimer = TimerState::Reset; + TouchState touch = TouchState::Inactive; + TimerState displayPowerTimer = TimerState::Expired; + + RefreshRateType refreshRateType = RefreshRateType::DEFAULT; + uint32_t contentRefreshRate = 0; + + bool isHDRContent = false; + bool isDisplayPowerStateNormal = true; + } mFeatures GUARDED_BY(mFeatureStateLock); const scheduler::RefreshRateConfigs& mRefreshRateConfigs; diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h index ac10f83ad9..3b7567ce8b 100644 --- a/services/surfaceflinger/Scheduler/SchedulerUtils.h +++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h @@ -22,19 +22,23 @@ #include <unordered_map> #include <vector> -namespace android { -namespace scheduler { -using namespace std::chrono_literals; +namespace android::scheduler { + +// Opaque handle to scheduler connection. +struct ConnectionHandle { + using Id = std::uintptr_t; + static constexpr Id INVALID_ID = static_cast<Id>(-1); + + Id id = INVALID_ID; -// This number is used to set the size of the arrays in scheduler that hold information -// about layers. -static constexpr size_t ARRAY_SIZE = 30; + explicit operator bool() const { return id != INVALID_ID; } +}; + +inline bool operator==(ConnectionHandle lhs, ConnectionHandle rhs) { + return lhs.id == rhs.id; +} -// This number is used to have a place holder for when the screen is not NORMAL/ON. Currently -// the config is not visible to SF, and is completely maintained by HWC. However, we would -// still like to keep track of time when the device is in this config. -static constexpr int SCREEN_OFF_CONFIG_ID = -1; -static constexpr uint32_t HWC2_SCREEN_OFF_CONFIG_ID = 0xffffffff; +using namespace std::chrono_literals; // This number is used when we try to determine how long do we keep layer information around // before we remove it. It is also used to determine how long the layer stays relevant. @@ -82,5 +86,15 @@ auto calculate_mode(const T& v) { return static_cast<int>(std::max_element(counts.begin(), counts.end(), compareCounts)->first); } -} // namespace scheduler -} // namespace android
\ No newline at end of file +} // namespace android::scheduler + +namespace std { + +template <> +struct hash<android::scheduler::ConnectionHandle> { + size_t operator()(android::scheduler::ConnectionHandle handle) const { + return hash<android::scheduler::ConnectionHandle::Id>()(handle.id); + } +}; + +} // namespace std diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp index 7a3bf8edaf..27fd76cba3 100644 --- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp +++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp @@ -24,25 +24,24 @@ #include <cinttypes> #include <mutex> -namespace android { - -using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType; -VSyncModulator::VSyncModulator() { +namespace android::scheduler { + +VSyncModulator::VSyncModulator(Scheduler& scheduler, + Scheduler::ConnectionHandle appConnectionHandle, + Scheduler::ConnectionHandle sfConnectionHandle, + const OffsetsConfig& config) + : mScheduler(scheduler), + mAppConnectionHandle(appConnectionHandle), + mSfConnectionHandle(sfConnectionHandle), + mOffsetsConfig(config) { char value[PROPERTY_VALUE_MAX]; property_get("debug.sf.vsync_trace_detailed_info", value, "0"); mTraceDetailedInfo = atoi(value); - // Populate the offset map with some default offsets. - const Offsets defaultOffsets = {RefreshRateType::DEFAULT, 0, 0}; - setPhaseOffsets(defaultOffsets, defaultOffsets, defaultOffsets, 0); } -void VSyncModulator::setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late, - nsecs_t thresholdForNextVsync) { +void VSyncModulator::setPhaseOffsets(const OffsetsConfig& config) { std::lock_guard<std::mutex> lock(mMutex); - mOffsetMap.insert_or_assign(OffsetType::Early, early); - mOffsetMap.insert_or_assign(OffsetType::EarlyGl, earlyGl); - mOffsetMap.insert_or_assign(OffsetType::Late, late); - mThresholdForNextVsync = thresholdForNextVsync; + mOffsetsConfig = config; updateOffsetsLocked(); } @@ -100,25 +99,21 @@ void VSyncModulator::onRefreshed(bool usedRenderEngine) { } } -VSyncModulator::Offsets VSyncModulator::getOffsets() { +VSyncModulator::Offsets VSyncModulator::getOffsets() const { std::lock_guard<std::mutex> lock(mMutex); return mOffsets; } -VSyncModulator::Offsets VSyncModulator::getNextOffsets() { - return mOffsetMap.at(getNextOffsetType()); -} - -VSyncModulator::OffsetType VSyncModulator::getNextOffsetType() { +const VSyncModulator::Offsets& VSyncModulator::getNextOffsets() const { // Early offsets are used if we're in the middle of a refresh rate // change, or if we recently begin a transaction. if (mTransactionStart == Scheduler::TransactionStart::EARLY || mRemainingEarlyFrameCount > 0 || mRefreshRateChangePending) { - return OffsetType::Early; + return mOffsetsConfig.early; } else if (mRemainingRenderEngineUsageCount > 0) { - return OffsetType::EarlyGl; + return mOffsetsConfig.earlyGl; } else { - return OffsetType::Late; + return mOffsetsConfig.late; } } @@ -128,37 +123,29 @@ void VSyncModulator::updateOffsets() { } void VSyncModulator::updateOffsetsLocked() { - const Offsets desired = getNextOffsets(); - - if (mSfConnectionHandle != nullptr) { - mScheduler->setPhaseOffset(mSfConnectionHandle, desired.sf); - } + const Offsets& offsets = getNextOffsets(); - if (mAppConnectionHandle != nullptr) { - mScheduler->setPhaseOffset(mAppConnectionHandle, desired.app); - } + mScheduler.setPhaseOffset(mSfConnectionHandle, offsets.sf); + mScheduler.setPhaseOffset(mAppConnectionHandle, offsets.app); - flushOffsets(); -} + mOffsets = offsets; -void VSyncModulator::flushOffsets() { - OffsetType type = getNextOffsetType(); - mOffsets = mOffsetMap.at(type); if (!mTraceDetailedInfo) { return; } - ATRACE_INT("Vsync-EarlyOffsetsOn", - mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Early); - ATRACE_INT("Vsync-EarlyGLOffsetsOn", - mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::EarlyGl); - ATRACE_INT("Vsync-LateOffsetsOn", - mOffsets.fpsMode == RefreshRateType::DEFAULT && type == OffsetType::Late); - ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn", - mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Early); - ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn", - mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::EarlyGl); - ATRACE_INT("Vsync-HighFpsLateOffsetsOn", - mOffsets.fpsMode == RefreshRateType::PERFORMANCE && type == OffsetType::Late); + + const bool isDefault = mOffsets.fpsMode == RefreshRateType::DEFAULT; + const bool isPerformance = mOffsets.fpsMode == RefreshRateType::PERFORMANCE; + const bool isEarly = &offsets == &mOffsetsConfig.early; + const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl; + const bool isLate = &offsets == &mOffsetsConfig.late; + + ATRACE_INT("Vsync-EarlyOffsetsOn", isDefault && isEarly); + ATRACE_INT("Vsync-EarlyGLOffsetsOn", isDefault && isEarlyGl); + ATRACE_INT("Vsync-LateOffsetsOn", isDefault && isLate); + ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn", isPerformance && isEarly); + ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn", isPerformance && isEarlyGl); + ATRACE_INT("Vsync-HighFpsLateOffsetsOn", isPerformance && isLate); } -} // namespace android +} // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h index ddbd221ef1..727cef26fe 100644 --- a/services/surfaceflinger/Scheduler/VSyncModulator.h +++ b/services/surfaceflinger/Scheduler/VSyncModulator.h @@ -16,12 +16,11 @@ #pragma once -#include <cinttypes> #include <mutex> #include "Scheduler.h" -namespace android { +namespace android::scheduler { /* * Modulates the vsync-offsets depending on current SurfaceFlinger state. @@ -31,51 +30,36 @@ private: // Number of frames we'll keep the early phase offsets once they are activated for a // transaction. This acts as a low-pass filter in case the client isn't quick enough in // sending new transactions. - const int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2; + static constexpr int MIN_EARLY_FRAME_COUNT_TRANSACTION = 2; // Number of frames we'll keep the early gl phase offsets once they are activated. // This acts as a low-pass filter to avoid scenarios where we rapidly // switch in and out of gl composition. - const int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2; + static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2; -public: - VSyncModulator(); + using RefreshRateType = RefreshRateConfigs::RefreshRateType; +public: // Wrapper for a collection of surfaceflinger/app offsets for a particular - // configuration . + // configuration. struct Offsets { - scheduler::RefreshRateConfigs::RefreshRateType fpsMode; + RefreshRateType fpsMode; nsecs_t sf; nsecs_t app; }; - enum class OffsetType { - Early, - EarlyGl, - Late, + struct OffsetsConfig { + Offsets early; // For transactions with the eEarlyWakeup flag. + Offsets earlyGl; // As above but while compositing with GL. + Offsets late; // Default. + + nsecs_t thresholdForNextVsync; }; - // Sets the phase offsets - // - // sfEarly: The phase offset when waking up SF early, which happens when marking a transaction - // as early. May be the same as late, in which case we don't shift offsets. - // sfEarlyGl: Like sfEarly, but only if we used GL composition. If we use both GL composition - // and the transaction was marked as early, we'll use sfEarly. - // sfLate: The regular SF vsync phase offset. - // appEarly: Like sfEarly, but for the app-vsync - // appEarlyGl: Like sfEarlyGl, but for the app-vsync. - // appLate: The regular app vsync phase offset. - void setPhaseOffsets(Offsets early, Offsets earlyGl, Offsets late, - nsecs_t thresholdForNextVsync) EXCLUDES(mMutex); - - // Sets the scheduler and vsync connection handlers. - void setSchedulerAndHandles(Scheduler* scheduler, - Scheduler::ConnectionHandle* appConnectionHandle, - Scheduler::ConnectionHandle* sfConnectionHandle) { - mScheduler = scheduler; - mAppConnectionHandle = appConnectionHandle; - mSfConnectionHandle = sfConnectionHandle; - } + VSyncModulator(Scheduler&, ConnectionHandle appConnectionHandle, + ConnectionHandle sfConnectionHandle, const OffsetsConfig&); + + void setPhaseOffsets(const OffsetsConfig&) EXCLUDES(mMutex); // Signals that a transaction has started, and changes offsets accordingly. void setTransactionStart(Scheduler::TransactionStart transactionStart); @@ -98,28 +82,23 @@ public: void onRefreshed(bool usedRenderEngine); // Returns the offsets that we are currently using - Offsets getOffsets() EXCLUDES(mMutex); + Offsets getOffsets() const EXCLUDES(mMutex); private: // Returns the next offsets that we should be using - Offsets getNextOffsets() REQUIRES(mMutex); - // Returns the next offset type that we should use. - OffsetType getNextOffsetType(); + const Offsets& getNextOffsets() const REQUIRES(mMutex); // Updates offsets and persists them into the scheduler framework. void updateOffsets() EXCLUDES(mMutex); void updateOffsetsLocked() REQUIRES(mMutex); - // Updates the internal offsets and offset type. - void flushOffsets() REQUIRES(mMutex); - mutable std::mutex mMutex; - std::unordered_map<OffsetType, Offsets> mOffsetMap GUARDED_BY(mMutex); - nsecs_t mThresholdForNextVsync; + Scheduler& mScheduler; + const ConnectionHandle mAppConnectionHandle; + const ConnectionHandle mSfConnectionHandle; - Scheduler* mScheduler = nullptr; - Scheduler::ConnectionHandle* mAppConnectionHandle = nullptr; - Scheduler::ConnectionHandle* mSfConnectionHandle = nullptr; + mutable std::mutex mMutex; + OffsetsConfig mOffsetsConfig GUARDED_BY(mMutex); - Offsets mOffsets GUARDED_BY(mMutex) = {Scheduler::RefreshRateType::DEFAULT, 0, 0}; + Offsets mOffsets GUARDED_BY(mMutex){mOffsetsConfig.late}; std::atomic<Scheduler::TransactionStart> mTransactionStart = Scheduler::TransactionStart::NORMAL; @@ -130,4 +109,4 @@ private: bool mTraceDetailedInfo = false; }; -} // namespace android +} // namespace android::scheduler diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 200da2e814..54e20659a7 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -38,14 +38,12 @@ #include <binder/PermissionCache.h> #include <compositionengine/CompositionEngine.h> +#include <compositionengine/CompositionRefreshArgs.h> #include <compositionengine/Display.h> #include <compositionengine/DisplayColorProfile.h> -#include <compositionengine/Layer.h> #include <compositionengine/OutputLayer.h> #include <compositionengine/RenderSurface.h> -#include <compositionengine/impl/LayerCompositionState.h> #include <compositionengine/impl/OutputCompositionState.h> -#include <compositionengine/impl/OutputLayerCompositionState.h> #include <dvr/vr_flinger.h> #include <gui/BufferQueue.h> #include <gui/DebugEGLImageTracker.h> @@ -97,12 +95,12 @@ #include "DisplayHardware/HWComposer.h" #include "DisplayHardware/VirtualDisplaySurface.h" #include "Effects/Daltonizer.h" +#include "FrameTracer/FrameTracer.h" #include "RegionSamplingThread.h" #include "Scheduler/DispSync.h" #include "Scheduler/DispSyncSource.h" #include "Scheduler/EventControlThread.h" #include "Scheduler/EventThread.h" -#include "Scheduler/InjectVSyncSource.h" #include "Scheduler/MessageQueue.h" #include "Scheduler/PhaseOffsets.h" #include "Scheduler/Scheduler.h" @@ -110,6 +108,7 @@ #include <cutils/compiler.h> +#include "android-base/parseint.h" #include "android-base/stringprintf.h" #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> @@ -162,28 +161,6 @@ bool isWideColorMode(const ColorMode colorMode) { return false; } -bool isHdrColorMode(const ColorMode colorMode) { - switch (colorMode) { - case ColorMode::BT2100_PQ: - case ColorMode::BT2100_HLG: - return true; - case ColorMode::DISPLAY_P3: - case ColorMode::ADOBE_RGB: - case ColorMode::DCI_P3: - case ColorMode::BT2020: - case ColorMode::DISPLAY_BT2020: - case ColorMode::NATIVE: - case ColorMode::STANDARD_BT601_625: - case ColorMode::STANDARD_BT601_625_UNADJUSTED: - case ColorMode::STANDARD_BT601_525: - case ColorMode::STANDARD_BT601_525_UNADJUSTED: - case ColorMode::STANDARD_BT709: - case ColorMode::SRGB: - return false; - } - return false; -} - ui::Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) { switch (rotation) { case ISurfaceComposer::eRotateNone: @@ -260,11 +237,11 @@ bool useTrebleTestingOverride() { std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) { switch(displayColorSetting) { - case DisplayColorSetting::MANAGED: + case DisplayColorSetting::kManaged: return std::string("Managed"); - case DisplayColorSetting::UNMANAGED: + case DisplayColorSetting::kUnmanaged: return std::string("Unmanaged"); - case DisplayColorSetting::ENHANCED: + case DisplayColorSetting::kEnhanced: return std::string("Enhanced"); default: return std::string("Unknown ") + @@ -276,11 +253,13 @@ SurfaceFlingerBE::SurfaceFlingerBE() : mHwcServiceName(getHwcServiceName()) {} SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag) : mFactory(factory), - mPhaseOffsets(mFactory.createPhaseOffsets()), mInterceptor(mFactory.createSurfaceInterceptor(this)), - mTimeStats(mFactory.createTimeStats()), + mTimeStats(std::make_shared<impl::TimeStats>()), + mFrameTracer(std::make_unique<FrameTracer>()), mEventQueue(mFactory.createMessageQueue()), - mCompositionEngine(mFactory.createCompositionEngine()) {} + mCompositionEngine(mFactory.createCompositionEngine()), + mPhaseOffsets(mFactory.createPhaseOffsets()), + mPendingSyncInputWindows(false) {} SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) { ALOGI("SurfaceFlinger is starting"); @@ -386,10 +365,6 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI property_get("debug.sf.luma_sampling", value, "1"); mLumaSampling = atoi(value); - const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); - mVsyncModulator.setPhaseOffsets(early, gl, late, - mPhaseOffsets->getOffsetThresholdForNextVsync()); - // We should be reading 'persist.sys.sf.color_saturation' here // but since /data may be encrypted, we need to wait until after vold // comes online to attempt to read the property. The property is @@ -538,6 +513,8 @@ void SurfaceFlinger::bootFinished() const nsecs_t duration = now - mBootTime; ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); + mFrameTracer->initialize(); + // wait patiently for the window manager death const String16 name("window"); mWindowManager = defaultServiceManager()->getService(name); @@ -569,14 +546,16 @@ void SurfaceFlinger::bootFinished() readPersistentProperties(); mBootStage = BootStage::FINISHED; - // set the refresh rate according to the policy - const auto& performanceRefreshRate = - mRefreshRateConfigs.getRefreshRate(RefreshRateType::PERFORMANCE); + if (mRefreshRateConfigs->refreshRateSwitchingSupported()) { + // set the refresh rate according to the policy + const auto& performanceRefreshRate = + mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE); - if (performanceRefreshRate && isDisplayConfigAllowed(performanceRefreshRate->configId)) { - setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None); - } else { - setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None); + if (isDisplayConfigAllowed(performanceRefreshRate.configId)) { + setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None); + } else { + setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None); + } } })); } @@ -615,36 +594,9 @@ void SurfaceFlinger::deleteTextureAsync(uint32_t texture) { void SurfaceFlinger::init() { ALOGI( "SurfaceFlinger's main thread ready to run. " "Initializing graphics H/W..."); - - ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset()); + ALOGI("Phase offset: %" PRId64 " ns", mPhaseOffsets->getCurrentAppOffset()); Mutex::Autolock _l(mStateLock); - // start the EventThread - mScheduler = - getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); }, - mRefreshRateConfigs); - auto resyncCallback = - mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this)); - - mAppConnectionHandle = - mScheduler->createConnection("app", mVsyncModulator.getOffsets().app, - mPhaseOffsets->getOffsetThresholdForNextVsync(), - resyncCallback, - impl::EventThread::InterceptVSyncsCallback()); - mSfConnectionHandle = - mScheduler->createConnection("sf", mVsyncModulator.getOffsets().sf, - mPhaseOffsets->getOffsetThresholdForNextVsync(), - resyncCallback, [this](nsecs_t timestamp) { - mInterceptor->saveVSyncEvent(timestamp); - }); - - mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); - mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(), - mSfConnectionHandle.get()); - - mRegionSamplingThread = - new RegionSamplingThread(*this, *mScheduler, - RegionSamplingThread::EnvironmentTimingTunables()); // Get a RenderEngine for the given display / config (can't fail) int32_t renderEngineFeature = 0; @@ -703,7 +655,11 @@ void SurfaceFlinger::init() { // set initial conditions (e.g. unblank default device) initializeDisplays(); - getRenderEngine().primeCache(); + char primeShaderCache[PROPERTY_VALUE_MAX]; + property_get("service.sf.prime_shader_cache", primeShaderCache, "1"); + if (atoi(primeShaderCache)) { + getRenderEngine().primeCache(); + } // Inform native graphics APIs whether the present timestamp is supported: @@ -715,37 +671,6 @@ void SurfaceFlinger::init() { ALOGE("Run StartPropertySetThread failed!"); } - mScheduler->setChangeRefreshRateCallback( - [this](RefreshRateType type, Scheduler::ConfigEvent event) { - Mutex::Autolock lock(mStateLock); - setRefreshRateTo(type, event); - }); - mScheduler->setGetCurrentRefreshRateTypeCallback([this] { - Mutex::Autolock lock(mStateLock); - const auto display = getDefaultDisplayDeviceLocked(); - if (!display) { - // If we don't have a default display the fallback to the default - // refresh rate type - return RefreshRateType::DEFAULT; - } - - const int configId = display->getActiveConfig(); - for (const auto& [type, refresh] : mRefreshRateConfigs.getRefreshRates()) { - if (refresh && refresh->configId == configId) { - return type; - } - } - // This should never happen, but just gracefully fallback to default. - return RefreshRateType::DEFAULT; - }); - mScheduler->setGetVsyncPeriodCallback([this] { - Mutex::Autolock lock(mStateLock); - return getVsyncPeriod(); - }); - - mRefreshRateConfigs.populate(getHwComposer().getConfigs(*display->getId())); - mRefreshRateStats.setConfigMode(getHwComposer().getActiveConfigIndex(*display->getId())); - ALOGV("Done initializing"); } @@ -889,17 +814,22 @@ status_t SurfaceFlinger::getDisplayConfigs(const sp<IBinder>& displayToken, info.viewportW = uint32_t(viewport.getWidth()); info.viewportH = uint32_t(viewport.getHeight()); } + info.layerStack = display->getLayerStack(); } else { // TODO: where should this value come from? static const int TV_DENSITY = 213; info.density = TV_DENSITY / 160.0f; info.orientation = 0; + + const auto display = getDisplayDeviceLocked(displayToken); + info.layerStack = display->getLayerStack(); } info.xdpi = xdpi; info.ydpi = ydpi; info.fps = 1e9 / hwConfig->getVsyncPeriod(); - const auto refreshRateType = mRefreshRateConfigs.getRefreshRateType(hwConfig->getId()); + const auto refreshRateType = + mRefreshRateConfigs->getRefreshRateTypeFromHwcConfigId(hwConfig->getId()); const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType); info.appVsyncOffset = offset.late.app; @@ -947,7 +877,16 @@ int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) { return BAD_VALUE; } - return display->getActiveConfig(); + if (display->isPrimary()) { + std::lock_guard<std::mutex> lock(mActiveConfigLock); + if (mDesiredActiveConfigChanged) { + return mDesiredActiveConfig.configId; + } else { + return display->getActiveConfig(); + } + } else { + return display->getActiveConfig(); + } } void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) { @@ -969,14 +908,11 @@ void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) { mScheduler->resyncToHardwareVsync(true, getVsyncPeriod()); // As we called to set period, we will call to onRefreshRateChangeCompleted once // DispSync model is locked. - mVsyncModulator.onRefreshRateChangeInitiated(); + mVSyncModulator->onRefreshRateChangeInitiated(); mPhaseOffsets->setRefreshRateType(info.type); - const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); - mVsyncModulator.setPhaseOffsets(early, gl, late, - mPhaseOffsets->getOffsetThresholdForNextVsync()); + mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets()); } mDesiredActiveConfigChanged = true; - ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged); if (mRefreshRateOverlay) { mRefreshRateOverlay->changeRefreshRate(mDesiredActiveConfig.type); @@ -1001,14 +937,13 @@ void SurfaceFlinger::setActiveConfigInternal() { } std::lock_guard<std::mutex> lock(mActiveConfigLock); - mRefreshRateStats.setConfigMode(mUpcomingActiveConfig.configId); + mRefreshRateConfigs->setCurrentConfig(mUpcomingActiveConfig.configId); + mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId); display->setActiveConfig(mUpcomingActiveConfig.configId); mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type); - const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); - mVsyncModulator.setPhaseOffsets(early, gl, late, - mPhaseOffsets->getOffsetThresholdForNextVsync()); + mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets()); ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId); if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) { @@ -1021,13 +956,10 @@ void SurfaceFlinger::desiredActiveConfigChangeDone() { std::lock_guard<std::mutex> lock(mActiveConfigLock); mDesiredActiveConfig.event = Scheduler::ConfigEvent::None; mDesiredActiveConfigChanged = false; - ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged); mScheduler->resyncToHardwareVsync(true, getVsyncPeriod()); mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type); - const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); - mVsyncModulator.setPhaseOffsets(early, gl, late, - mPhaseOffsets->getOffsetThresholdForNextVsync()); + mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets()); } bool SurfaceFlinger::performSetActiveConfig() { @@ -1159,8 +1091,10 @@ status_t SurfaceFlinger::setActiveColorMode(const sp<IBinder>& displayToken, Col ALOGW("Attempt to set active color mode %s (%d) for virtual display", decodeColorMode(mode).c_str(), mode); } else { - display->getCompositionDisplay()->setColorMode(mode, Dataspace::UNKNOWN, - RenderIntent::COLORIMETRIC); + display->getCompositionDisplay()->setColorProfile( + compositionengine::Output::ColorProfile{mode, Dataspace::UNKNOWN, + RenderIntent::COLORIMETRIC, + Dataspace::UNKNOWN}); } })); @@ -1274,51 +1208,20 @@ status_t SurfaceFlinger::isWideColorDisplay(const sp<IBinder>& displayToken, status_t SurfaceFlinger::enableVSyncInjections(bool enable) { postMessageSync(new LambdaMessage([&] { - Mutex::Autolock _l(mStateLock); - - if (mInjectVSyncs == enable) { - return; - } - - auto resyncCallback = - mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this)); + Mutex::Autolock lock(mStateLock); - // TODO(b/128863962): Part of the Injector should be refactored, so that it - // can be passed to Scheduler. - if (enable) { - ALOGV("VSync Injections enabled"); - if (mVSyncInjector.get() == nullptr) { - mVSyncInjector = std::make_unique<InjectVSyncSource>(); - mInjectorEventThread = std::make_unique< - impl::EventThread>(mVSyncInjector.get(), - impl::EventThread::InterceptVSyncsCallback(), - "injEventThread"); - } - mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback)); - } else { - ALOGV("VSync Injections disabled"); - mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle), - std::move(resyncCallback)); + if (const auto handle = mScheduler->enableVSyncInjection(enable)) { + mEventQueue->setEventConnection( + mScheduler->getEventConnection(enable ? handle : mSfConnectionHandle)); } - - mInjectVSyncs = enable; })); return NO_ERROR; } status_t SurfaceFlinger::injectVSync(nsecs_t when) { - Mutex::Autolock _l(mStateLock); - - if (!mInjectVSyncs) { - ALOGE("VSync Injections not enabled"); - return BAD_VALUE; - } - if (mInjectVSyncs && mInjectorEventThread.get() != nullptr) { - ALOGV("Injecting VSync inside SurfaceFlinger"); - mVSyncInjector->onInjectSyncEvent(when); - } - return NO_ERROR; + Mutex::Autolock lock(mStateLock); + return mScheduler->injectVSync(when) ? NO_ERROR : BAD_VALUE; } status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) const @@ -1409,16 +1312,10 @@ status_t SurfaceFlinger::notifyPowerHint(int32_t hintId) { sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( ISurfaceComposer::VsyncSource vsyncSource, ISurfaceComposer::ConfigChanged configChanged) { - auto resyncCallback = mScheduler->makeResyncCallback([this] { - Mutex::Autolock lock(mStateLock); - return getVsyncPeriod(); - }); - const auto& handle = vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle; - return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback), - configChanged); + return mScheduler->createDisplayEventConnection(handle, configChanged); } // ---------------------------------------------------------------------------- @@ -1494,7 +1391,7 @@ void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDispl bool periodFlushed = false; mScheduler->addResyncSample(timestamp, &periodFlushed); if (periodFlushed) { - mVsyncModulator.onRefreshRateChangeCompleted(); + mVSyncModulator->onRefreshRateChangeCompleted(); } } @@ -1503,7 +1400,7 @@ void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { *compositorTiming = getBE().mCompositorTiming; } -bool SurfaceFlinger::isDisplayConfigAllowed(int32_t configId) { +bool SurfaceFlinger::isDisplayConfigAllowed(int32_t configId) const { return mAllowedDisplayConfigs.empty() || mAllowedDisplayConfigs.count(configId); } @@ -1515,13 +1412,8 @@ void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::Co ATRACE_CALL(); // Don't do any updating if the current fps is the same as the new one. - const auto& refreshRateConfig = mRefreshRateConfigs.getRefreshRate(refreshRate); - if (!refreshRateConfig) { - ALOGV("Skipping refresh rate change request for unsupported rate."); - return; - } - - const int desiredConfigId = refreshRateConfig->configId; + const auto& refreshRateConfig = mRefreshRateConfigs->getRefreshRateFromType(refreshRate); + const int desiredConfigId = refreshRateConfig.configId; if (!isDisplayConfigAllowed(desiredConfigId)) { ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId); @@ -1627,8 +1519,7 @@ void SurfaceFlinger::updateVrFlinger() { // any HWC layers are destroyed through that interface before it becomes // invalid. for (const auto& [token, displayDevice] : mDisplays) { - displayDevice->getCompositionDisplay()->setOutputLayersOrderedByZ( - compositionengine::Output::OutputLayers()); + displayDevice->getCompositionDisplay()->clearOutputLayers(); } // This DisplayDevice will no longer be relevant once resetDisplayState() is @@ -1687,7 +1578,7 @@ bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALY // woken up before the actual vsync but targeting the next vsync, we need to check // fence N-2 const sp<Fence>& fence = - mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync() + mVSyncModulator->getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync() ? mPreviousPresentFences[0] : mPreviousPresentFences[1]; @@ -1702,15 +1593,15 @@ bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALY return (fence->getStatus() == Fence::Status::Unsignaled); } -void SurfaceFlinger::populateExpectedPresentTime() NO_THREAD_SAFETY_ANALYSIS { +void SurfaceFlinger::populateExpectedPresentTime() { DisplayStatInfo stats; mScheduler->getDisplayStatInfo(&stats); const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime(); // Inflate the expected present time if we're targetting the next vsync. - mExpectedPresentTime = - mVsyncModulator.getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync() - ? presentTime - : presentTime + stats.vsyncPeriod; + mExpectedPresentTime.store(mVSyncModulator->getOffsets().sf < + mPhaseOffsets->getOffsetThresholdForNextVsync() + ? presentTime + : presentTime + stats.vsyncPeriod); } void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS { @@ -1730,12 +1621,14 @@ void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS { (mPropagateBackpressureClientComposition || !mHadClientComposition)) ? 1 : 0; - bool frameMissed = previousFrameMissed(graceTimeForPresentFenceMs); - bool hwcFrameMissed = mHadDeviceComposition && frameMissed; - bool gpuFrameMissed = mHadClientComposition && frameMissed; - ATRACE_INT("FrameMissed", static_cast<int>(frameMissed)); - ATRACE_INT("HwcFrameMissed", static_cast<int>(hwcFrameMissed)); - ATRACE_INT("GpuFrameMissed", static_cast<int>(gpuFrameMissed)); + const TracedOrdinal<bool> frameMissed = {"FrameMissed", + previousFrameMissed( + graceTimeForPresentFenceMs)}; + const TracedOrdinal<bool> hwcFrameMissed = {"HwcFrameMissed", + mHadDeviceComposition && frameMissed}; + const TracedOrdinal<bool> gpuFrameMissed = {"GpuFrameMissed", + mHadClientComposition && frameMissed}; + if (frameMissed) { mFrameMissedCount++; mTimeStats->incrementMissedFrames(); @@ -1821,34 +1714,62 @@ void SurfaceFlinger::handleMessageRefresh() { mRefreshPending = false; - const bool repaintEverything = mRepaintEverything.exchange(false); - preComposition(); - rebuildLayerStacks(); - calculateWorkingSet(); - for (const auto& [token, display] : mDisplays) { - beginFrame(display); - prepareFrame(display); - doDebugFlashRegions(display, repaintEverything); - doComposition(display, repaintEverything); + compositionengine::CompositionRefreshArgs refreshArgs; + refreshArgs.outputs.reserve(mDisplays.size()); + for (const auto& [_, display] : mDisplays) { + refreshArgs.outputs.push_back(display->getCompositionDisplay()); + } + mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) { + auto compositionLayer = layer->getCompositionLayer(); + if (compositionLayer) refreshArgs.layers.push_back(compositionLayer); + }); + refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size()); + for (sp<Layer> layer : mLayersWithQueuedFrames) { + auto compositionLayer = layer->getCompositionLayer(); + if (compositionLayer) refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get()); } - logLayerStats(); + refreshArgs.repaintEverything = mRepaintEverything.exchange(false); + refreshArgs.outputColorSetting = useColorManagement + ? mDisplayColorSetting + : compositionengine::OutputColorSetting::kUnmanaged; + refreshArgs.colorSpaceAgnosticDataspace = mColorSpaceAgnosticDataspace; + refreshArgs.forceOutputColorMode = mForceColorMode; + + refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty; + refreshArgs.updatingGeometryThisFrame = mGeometryInvalid || mVisibleRegionsDirty; + + if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) { + refreshArgs.colorTransformMatrix = mDrawingState.colorMatrix; + mDrawingState.colorMatrixChanged = false; + } + + refreshArgs.devOptForceClientComposition = mDebugDisableHWC || mDebugRegion; + + if (mDebugRegion != 0) { + refreshArgs.devOptFlashDirtyRegionsDelay = + std::chrono::milliseconds(mDebugRegion > 1 ? mDebugRegion : 0); + } + + mGeometryInvalid = false; + + mCompositionEngine->present(refreshArgs); postFrame(); postComposition(); - mHadClientComposition = false; - mHadDeviceComposition = false; - for (const auto& [token, displayDevice] : mDisplays) { - auto display = displayDevice->getCompositionDisplay(); - const auto displayId = display->getId(); - mHadClientComposition = - mHadClientComposition || getHwComposer().hasClientComposition(displayId); - mHadDeviceComposition = - mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId); - } + mHadClientComposition = + std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) { + auto& displayDevice = tokenDisplayPair.second; + return displayDevice->getCompositionDisplay()->getState().usesClientComposition; + }); + mHadDeviceComposition = + std::any_of(mDisplays.cbegin(), mDisplays.cend(), [](const auto& tokenDisplayPair) { + auto& displayDevice = tokenDisplayPair.second; + return displayDevice->getCompositionDisplay()->getState().usesDeviceComposition; + }); - mVsyncModulator.onRefreshed(mHadClientComposition); + mVSyncModulator->onRefreshed(mHadClientComposition); mLayersWithQueuedFrames.clear(); if (mVisibleRegionsDirty) { @@ -1857,6 +1778,10 @@ void SurfaceFlinger::handleMessageRefresh() { mTracing.notify("visibleRegionsDirty"); } } + + if (mCompositionEngine->needsAnotherUpdate()) { + signalLayerUpdate(); + } } @@ -1877,174 +1802,6 @@ bool SurfaceFlinger::handleMessageInvalidate() { return refreshNeeded; } -void SurfaceFlinger::calculateWorkingSet() { - ATRACE_CALL(); - ALOGV(__FUNCTION__); - - // build the h/w work list - if (CC_UNLIKELY(mGeometryInvalid)) { - mGeometryInvalid = false; - for (const auto& [token, displayDevice] : mDisplays) { - auto display = displayDevice->getCompositionDisplay(); - - uint32_t zOrder = 0; - - for (auto& layer : display->getOutputLayersOrderedByZ()) { - auto& compositionState = layer->editState(); - compositionState.forceClientComposition = false; - if (!compositionState.hwc || mDebugDisableHWC || mDebugRegion) { - compositionState.forceClientComposition = true; - } - - // The output Z order is set here based on a simple counter. - compositionState.z = zOrder++; - - // Update the display independent composition state. This goes - // to the general composition layer state structure. - // TODO: Do this once per compositionengine::CompositionLayer. - layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd, - true); - - // Recalculate the geometry state of the output layer. - layer->updateCompositionState(true); - - // Write the updated geometry state to the HWC - layer->writeStateToHWC(true); - } - } - } - - // Set the per-frame data - for (const auto& [token, displayDevice] : mDisplays) { - auto display = displayDevice->getCompositionDisplay(); - const auto displayId = display->getId(); - if (!displayId) { - continue; - } - auto* profile = display->getDisplayColorProfile(); - - if (mDrawingState.colorMatrixChanged) { - display->setColorTransform(mDrawingState.colorMatrix); - } - Dataspace targetDataspace = Dataspace::UNKNOWN; - if (useColorManagement) { - ColorMode colorMode; - RenderIntent renderIntent; - pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent); - display->setColorMode(colorMode, targetDataspace, renderIntent); - - if (isHdrColorMode(colorMode)) { - targetDataspace = Dataspace::UNKNOWN; - } else if (mColorSpaceAgnosticDataspace != Dataspace::UNKNOWN) { - targetDataspace = mColorSpaceAgnosticDataspace; - } - } - - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { - if (layer->isHdrY410()) { - layer->forceClientComposition(displayDevice); - } else if ((layer->getDataSpace() == Dataspace::BT2020_PQ || - layer->getDataSpace() == Dataspace::BT2020_ITU_PQ) && - !profile->hasHDR10Support()) { - layer->forceClientComposition(displayDevice); - } else if ((layer->getDataSpace() == Dataspace::BT2020_HLG || - layer->getDataSpace() == Dataspace::BT2020_ITU_HLG) && - !profile->hasHLGSupport()) { - layer->forceClientComposition(displayDevice); - } - - if (layer->getRoundedCornerState().radius > 0.0f) { - layer->forceClientComposition(displayDevice); - } - - if (layer->getForceClientComposition(displayDevice)) { - ALOGV("[%s] Requesting Client composition", layer->getName().string()); - layer->setCompositionType(displayDevice, - Hwc2::IComposerClient::Composition::CLIENT); - continue; - } - - const auto& displayState = display->getState(); - layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport, - displayDevice->getSupportedPerFrameMetadata(), targetDataspace); - } - } - - mDrawingState.colorMatrixChanged = false; - - for (const auto& [token, displayDevice] : mDisplays) { - auto display = displayDevice->getCompositionDisplay(); - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { - auto& layerState = layer->getCompositionLayer()->editState().frontEnd; - layerState.compositionType = static_cast<Hwc2::IComposerClient::Composition>( - layer->getCompositionType(displayDevice)); - } - } -} - -void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& displayDevice, - bool repaintEverything) { - auto display = displayDevice->getCompositionDisplay(); - const auto& displayState = display->getState(); - - // is debugging enabled - if (CC_LIKELY(!mDebugRegion)) - return; - - if (displayState.isEnabled) { - // transform the dirty region into this screen's coordinate space - const Region dirtyRegion = display->getDirtyRegion(repaintEverything); - if (!dirtyRegion.isEmpty()) { - base::unique_fd readyFence; - // redraw the whole screen - doComposeSurfaces(displayDevice, dirtyRegion, &readyFence); - - display->getRenderSurface()->queueBuffer(std::move(readyFence)); - } - } - - postFramebuffer(displayDevice); - - if (mDebugRegion > 1) { - usleep(mDebugRegion * 1000); - } - - prepareFrame(displayDevice); -} - -void SurfaceFlinger::logLayerStats() { - ATRACE_CALL(); - if (CC_UNLIKELY(mLayerStats.isEnabled())) { - for (const auto& [token, display] : mDisplays) { - if (display->isPrimary()) { - mLayerStats.logLayerStats(dumpVisibleLayersProtoInfo(display)); - return; - } - } - - ALOGE("logLayerStats: no primary display"); - } -} - -void SurfaceFlinger::preComposition() -{ - ATRACE_CALL(); - ALOGV("preComposition"); - - mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC); - - bool needExtraInvalidate = false; - mDrawingState.traverseInZOrder([&](Layer* layer) { - if (layer->onPreComposition(mRefreshStartTime)) { - needExtraInvalidate = true; - } - }); - - if (needExtraInvalidate) { - signalLayerUpdate(); - } -} - void SurfaceFlinger::updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime, std::shared_ptr<FenceTime>& presentFenceTime) { // Update queue of past composite+present times and determine the @@ -2117,7 +1874,7 @@ void SurfaceFlinger::postComposition() getBE().mGlCompositionDoneTimeline.updateSignalTimes(); std::shared_ptr<FenceTime> glCompositionDoneFenceTime; - if (displayDevice && getHwComposer().hasClientComposition(displayDevice->getId())) { + if (displayDevice && displayDevice->getCompositionDisplay()->getState().usesClientComposition) { glCompositionDoneFenceTime = std::make_shared<FenceTime>(displayDevice->getCompositionDisplay() ->getRenderSurface() @@ -2138,10 +1895,11 @@ void SurfaceFlinger::postComposition() DisplayStatInfo stats; mScheduler->getDisplayStatInfo(&stats); - // We use the mRefreshStartTime which might be sampled a little later than - // when we started doing work for this frame, but that should be okay - // since updateCompositorTiming has snapping logic. - updateCompositorTiming(stats, mRefreshStartTime, presentFenceTime); + // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might + // be sampled a little later than when we started doing work for this frame, + // but that should be okay since updateCompositorTiming has snapping logic. + updateCompositorTiming(stats, mCompositionEngine->getLastFrameRefreshTimestamp(), + presentFenceTime); CompositorTiming compositorTiming; { std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock); @@ -2230,14 +1988,7 @@ void SurfaceFlinger::postComposition() } mTransactionCompletedThread.addPresentFence(mPreviousPresentFences[0]); - - // Lock the mStateLock in case SurfaceFlinger is in the middle of applying a transaction. - // If we do not lock here, a callback could be sent without all of its SurfaceControls and - // metrics. - { - Mutex::Autolock _l(mStateLock); - mTransactionCompletedThread.sendCallbacks(); - } + mTransactionCompletedThread.sendCallbacks(); if (mLumaSampling && mRegionSamplingThread) { mRegionSamplingThread->notifyNewContent(); @@ -2265,258 +2016,6 @@ void SurfaceFlinger::computeLayerBounds() { } } -void SurfaceFlinger::rebuildLayerStacks() { - ATRACE_CALL(); - ALOGV("rebuildLayerStacks"); - - // rebuild the visible layer list per screen - if (CC_UNLIKELY(mVisibleRegionsDirty)) { - ATRACE_NAME("rebuildLayerStacks VR Dirty"); - invalidateHwcGeometry(); - - for (const auto& pair : mDisplays) { - const auto& displayDevice = pair.second; - auto display = displayDevice->getCompositionDisplay(); - const auto& displayState = display->getState(); - Region opaqueRegion; - Region dirtyRegion; - compositionengine::Output::OutputLayers layersSortedByZ; - Vector<sp<Layer>> deprecated_layersSortedByZ; - Vector<sp<Layer>> layersNeedingFences; - const ui::Transform& tr = displayState.transform; - const Rect bounds = displayState.bounds; - if (displayState.isEnabled) { - computeVisibleRegions(displayDevice, dirtyRegion, opaqueRegion); - - mDrawingState.traverseInZOrder([&](Layer* layer) { - auto compositionLayer = layer->getCompositionLayer(); - if (compositionLayer == nullptr) { - return; - } - - const auto displayId = displayDevice->getId(); - sp<compositionengine::LayerFE> layerFE = compositionLayer->getLayerFE(); - LOG_ALWAYS_FATAL_IF(layerFE.get() == nullptr); - - bool needsOutputLayer = false; - - if (display->belongsInOutput(layer->getLayerStack(), - layer->getPrimaryDisplayOnly())) { - Region drawRegion(tr.transform( - layer->visibleNonTransparentRegion)); - drawRegion.andSelf(bounds); - if (!drawRegion.isEmpty()) { - needsOutputLayer = true; - } - } - - if (needsOutputLayer) { - layersSortedByZ.emplace_back( - display->getOrCreateOutputLayer(displayId, compositionLayer, - layerFE)); - deprecated_layersSortedByZ.add(layer); - - auto& outputLayerState = layersSortedByZ.back()->editState(); - outputLayerState.visibleRegion = - tr.transform(layer->visibleRegion.intersect(displayState.viewport)); - } else if (displayId) { - // For layers that are being removed from a HWC display, - // and that have queued frames, add them to a a list of - // released layers so we can properly set a fence. - bool hasExistingOutputLayer = - display->getOutputLayerForLayer(compositionLayer.get()) != nullptr; - bool hasQueuedFrames = std::find(mLayersWithQueuedFrames.cbegin(), - mLayersWithQueuedFrames.cend(), - layer) != mLayersWithQueuedFrames.cend(); - - if (hasExistingOutputLayer && hasQueuedFrames) { - layersNeedingFences.add(layer); - } - } - }); - } - - display->setOutputLayersOrderedByZ(std::move(layersSortedByZ)); - - displayDevice->setVisibleLayersSortedByZ(deprecated_layersSortedByZ); - displayDevice->setLayersNeedingFences(layersNeedingFences); - - Region undefinedRegion{bounds}; - undefinedRegion.subtractSelf(tr.transform(opaqueRegion)); - - display->editState().undefinedRegion = undefinedRegion; - display->editState().dirtyRegion.orSelf(dirtyRegion); - } - } -} - -// Returns a data space that fits all visible layers. The returned data space -// can only be one of -// - Dataspace::SRGB (use legacy dataspace and let HWC saturate when colors are enhanced) -// - Dataspace::DISPLAY_P3 -// - Dataspace::DISPLAY_BT2020 -// The returned HDR data space is one of -// - Dataspace::UNKNOWN -// - Dataspace::BT2020_HLG -// - Dataspace::BT2020_PQ -Dataspace SurfaceFlinger::getBestDataspace(const sp<DisplayDevice>& display, - Dataspace* outHdrDataSpace, - bool* outIsHdrClientComposition) const { - Dataspace bestDataSpace = Dataspace::V0_SRGB; - *outHdrDataSpace = Dataspace::UNKNOWN; - - for (const auto& layer : display->getVisibleLayersSortedByZ()) { - switch (layer->getDataSpace()) { - case Dataspace::V0_SCRGB: - case Dataspace::V0_SCRGB_LINEAR: - case Dataspace::BT2020: - case Dataspace::BT2020_ITU: - case Dataspace::BT2020_LINEAR: - case Dataspace::DISPLAY_BT2020: - bestDataSpace = Dataspace::DISPLAY_BT2020; - break; - case Dataspace::DISPLAY_P3: - bestDataSpace = Dataspace::DISPLAY_P3; - break; - case Dataspace::BT2020_PQ: - case Dataspace::BT2020_ITU_PQ: - bestDataSpace = Dataspace::DISPLAY_P3; - *outHdrDataSpace = Dataspace::BT2020_PQ; - *outIsHdrClientComposition = layer->getForceClientComposition(display); - break; - case Dataspace::BT2020_HLG: - case Dataspace::BT2020_ITU_HLG: - bestDataSpace = Dataspace::DISPLAY_P3; - // When there's mixed PQ content and HLG content, we set the HDR - // data space to be BT2020_PQ and convert HLG to PQ. - if (*outHdrDataSpace == Dataspace::UNKNOWN) { - *outHdrDataSpace = Dataspace::BT2020_HLG; - } - break; - default: - break; - } - } - - return bestDataSpace; -} - -// Pick the ColorMode / Dataspace for the display device. -void SurfaceFlinger::pickColorMode(const sp<DisplayDevice>& display, ColorMode* outMode, - Dataspace* outDataSpace, RenderIntent* outRenderIntent) const { - if (mDisplayColorSetting == DisplayColorSetting::UNMANAGED) { - *outMode = ColorMode::NATIVE; - *outDataSpace = Dataspace::UNKNOWN; - *outRenderIntent = RenderIntent::COLORIMETRIC; - return; - } - - Dataspace hdrDataSpace; - bool isHdrClientComposition = false; - Dataspace bestDataSpace = getBestDataspace(display, &hdrDataSpace, &isHdrClientComposition); - - auto* profile = display->getCompositionDisplay()->getDisplayColorProfile(); - - switch (mForceColorMode) { - case ColorMode::SRGB: - bestDataSpace = Dataspace::V0_SRGB; - break; - case ColorMode::DISPLAY_P3: - bestDataSpace = Dataspace::DISPLAY_P3; - break; - default: - break; - } - - // respect hdrDataSpace only when there is no legacy HDR support - const bool isHdr = hdrDataSpace != Dataspace::UNKNOWN && - !profile->hasLegacyHdrSupport(hdrDataSpace) && !isHdrClientComposition; - if (isHdr) { - bestDataSpace = hdrDataSpace; - } - - RenderIntent intent; - switch (mDisplayColorSetting) { - case DisplayColorSetting::MANAGED: - case DisplayColorSetting::UNMANAGED: - intent = isHdr ? RenderIntent::TONE_MAP_COLORIMETRIC : RenderIntent::COLORIMETRIC; - break; - case DisplayColorSetting::ENHANCED: - intent = isHdr ? RenderIntent::TONE_MAP_ENHANCE : RenderIntent::ENHANCE; - break; - default: // vendor display color setting - intent = static_cast<RenderIntent>(mDisplayColorSetting); - break; - } - - profile->getBestColorMode(bestDataSpace, intent, outDataSpace, outMode, outRenderIntent); -} - -void SurfaceFlinger::beginFrame(const sp<DisplayDevice>& displayDevice) { - auto display = displayDevice->getCompositionDisplay(); - const auto& displayState = display->getState(); - - bool dirty = !display->getDirtyRegion(false).isEmpty(); - bool empty = displayDevice->getVisibleLayersSortedByZ().size() == 0; - bool wasEmpty = !displayState.lastCompositionHadVisibleLayers; - - // If nothing has changed (!dirty), don't recompose. - // If something changed, but we don't currently have any visible layers, - // and didn't when we last did a composition, then skip it this time. - // The second rule does two things: - // - When all layers are removed from a display, we'll emit one black - // frame, then nothing more until we get new layers. - // - When a display is created with a private layer stack, we won't - // emit any black frames until a layer is added to the layer stack. - bool mustRecompose = dirty && !(empty && wasEmpty); - - const char flagPrefix[] = {'-', '+'}; - static_cast<void>(flagPrefix); - ALOGV_IF(displayDevice->isVirtual(), "%s: %s composition for %s (%cdirty %cempty %cwasEmpty)", - __FUNCTION__, mustRecompose ? "doing" : "skipping", - displayDevice->getDebugName().c_str(), flagPrefix[dirty], flagPrefix[empty], - flagPrefix[wasEmpty]); - - display->getRenderSurface()->beginFrame(mustRecompose); - - if (mustRecompose) { - display->editState().lastCompositionHadVisibleLayers = !empty; - } -} - -void SurfaceFlinger::prepareFrame(const sp<DisplayDevice>& displayDevice) { - auto display = displayDevice->getCompositionDisplay(); - const auto& displayState = display->getState(); - - if (!displayState.isEnabled) { - return; - } - - status_t result = display->getRenderSurface()->prepareFrame(); - ALOGE_IF(result != NO_ERROR, "prepareFrame failed for %s: %d (%s)", - displayDevice->getDebugName().c_str(), result, strerror(-result)); -} - -void SurfaceFlinger::doComposition(const sp<DisplayDevice>& displayDevice, bool repaintEverything) { - ATRACE_CALL(); - ALOGV("doComposition"); - - auto display = displayDevice->getCompositionDisplay(); - const auto& displayState = display->getState(); - - if (displayState.isEnabled) { - // transform the dirty region into this screen's coordinate space - const Region dirtyRegion = display->getDirtyRegion(repaintEverything); - - // repaint the framebuffer (if needed) - doDisplayComposition(displayDevice, dirtyRegion); - - display->editState().dirtyRegion.clear(); - display->getRenderSurface()->flip(); - } - postFramebuffer(displayDevice); -} - void SurfaceFlinger::postFrame() { // |mStateLock| not needed as we are on the main thread @@ -2529,65 +2028,6 @@ void SurfaceFlinger::postFrame() } } -void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& displayDevice) { - ATRACE_CALL(); - ALOGV("postFramebuffer"); - - auto display = displayDevice->getCompositionDisplay(); - const auto& displayState = display->getState(); - const auto displayId = display->getId(); - - if (displayState.isEnabled) { - if (displayId) { - getHwComposer().presentAndGetReleaseFences(*displayId); - } - display->getRenderSurface()->onPresentDisplayCompleted(); - for (auto& layer : display->getOutputLayersOrderedByZ()) { - sp<Fence> releaseFence = Fence::NO_FENCE; - bool usedClientComposition = true; - - // The layer buffer from the previous frame (if any) is released - // by HWC only when the release fence from this frame (if any) is - // signaled. Always get the release fence from HWC first. - if (layer->getState().hwc) { - const auto& hwcState = *layer->getState().hwc; - releaseFence = - getHwComposer().getLayerReleaseFence(*displayId, hwcState.hwcLayer.get()); - usedClientComposition = - hwcState.hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT; - } - - // If the layer was client composited in the previous frame, we - // need to merge with the previous client target acquire fence. - // Since we do not track that, always merge with the current - // client target acquire fence when it is available, even though - // this is suboptimal. - if (usedClientComposition) { - releaseFence = - Fence::merge("LayerRelease", releaseFence, - display->getRenderSurface()->getClientTargetAcquireFence()); - } - - layer->getLayerFE().onLayerDisplayed(releaseFence); - } - - // We've got a list of layers needing fences, that are disjoint with - // display->getVisibleLayersSortedByZ. The best we can do is to - // supply them with the present fence. - if (!displayDevice->getLayersNeedingFences().isEmpty()) { - sp<Fence> presentFence = - displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE; - for (auto& layer : displayDevice->getLayersNeedingFences()) { - layer->getCompositionLayer()->getLayerFE()->onLayerDisplayed(presentFence); - } - } - - if (displayId) { - getHwComposer().clearReleaseFences(*displayId); - } - } -} - void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) { ATRACE_CALL(); @@ -2607,7 +2047,7 @@ void SurfaceFlinger::handleTransaction(uint32_t transactionFlags) // with mStateLock held to guarantee that mCurrentState won't change // until the transaction is committed. - mVsyncModulator.onTransactionHandled(); + mVSyncModulator->onTransactionHandled(); transactionFlags = getTransactionFlags(eTransactionMask); handleTransactionLocked(transactionFlags); @@ -2628,6 +2068,9 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { if (event.connection == HWC2::Connection::Connected) { if (!mPhysicalDisplayTokens.count(info->id)) { ALOGV("Creating display %s", to_string(info->id).c_str()); + if (event.hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) { + initScheduler(info->id); + } mPhysicalDisplayTokens[info->id] = new BBinder(); DisplayDeviceState state; state.displayId = info->id; @@ -2655,8 +2098,8 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { } void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) { - mScheduler->hotplugReceived(mAppConnectionHandle, displayId, connected); - mScheduler->hotplugReceived(mSfConnectionHandle, displayId, connected); + mScheduler->onHotplugReceived(mAppConnectionHandle, displayId, connected); + mScheduler->onHotplugReceived(mSfConnectionHandle, displayId, connected); } sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( @@ -2670,6 +2113,7 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( creationArgs.displaySurface = dispSurface; creationArgs.hasWideColorGamut = false; creationArgs.supportedPerFrameMetadata = 0; + creationArgs.powerAdvisor = displayId ? &mPowerAdvisor : nullptr; const bool isInternalDisplay = displayId && displayId == getInternalDisplayIdLocked(); creationArgs.isPrimary = isInternalDisplay; @@ -2722,8 +2166,10 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( defaultColorMode = ColorMode::SRGB; defaultDataSpace = Dataspace::V0_SRGB; } - display->getCompositionDisplay()->setColorMode(defaultColorMode, defaultDataSpace, - RenderIntent::COLORIMETRIC); + display->getCompositionDisplay()->setColorProfile( + compositionengine::Output::ColorProfile{defaultColorMode, defaultDataSpace, + RenderIntent::COLORIMETRIC, + Dataspace::UNKNOWN}); if (!state.isVirtual()) { LOG_ALWAYS_FATAL_IF(!displayId); display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId)); @@ -2880,9 +2326,11 @@ void SurfaceFlinger::processDisplayChangesLocked() { void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { + const nsecs_t expectedPresentTime = mExpectedPresentTime.load(); + // Notify all layers of available frames - mCurrentState.traverseInZOrder([](Layer* layer) { - layer->notifyAvailableFrames(); + mCurrentState.traverseInZOrder([expectedPresentTime](Layer* layer) { + layer->notifyAvailableFrames(expectedPresentTime); }); /* @@ -2927,7 +2375,7 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) // display is used to calculate the hint, otherwise we use the // default display. // - // NOTE: we do this here, rather than in rebuildLayerStacks() so that + // NOTE: we do this here, rather than when presenting the display so that // the hint is set before we acquire a buffer from the surface texture. // // NOTE: layer transactions have taken place already, so we use their @@ -3028,7 +2476,7 @@ void SurfaceFlinger::updateInputFlinger() { setInputWindowsFinished(); } - executeInputWindowCommands(); + mInputWindowCommands.clear(); } void SurfaceFlinger::updateInputWindowInfo() { @@ -3052,51 +2500,85 @@ void SurfaceFlinger::commitInputWindowCommands() { mPendingInputWindowCommands.clear(); } -void SurfaceFlinger::executeInputWindowCommands() { - for (const auto& transferTouchFocusCommand : mInputWindowCommands.transferTouchFocusCommands) { - if (transferTouchFocusCommand.fromToken != nullptr && - transferTouchFocusCommand.toToken != nullptr && - transferTouchFocusCommand.fromToken != transferTouchFocusCommand.toToken) { - mInputFlinger->transferTouchFocus(transferTouchFocusCommand.fromToken, - transferTouchFocusCommand.toToken); - } - } - - mInputWindowCommands.clear(); -} - void SurfaceFlinger::updateCursorAsync() { - for (const auto& [token, display] : mDisplays) { - if (!display->getId()) { - continue; - } - - for (auto& layer : display->getVisibleLayersSortedByZ()) { - layer->updateCursorPosition(display); + compositionengine::CompositionRefreshArgs refreshArgs; + for (const auto& [_, display] : mDisplays) { + if (display->getId()) { + refreshArgs.outputs.push_back(display->getCompositionDisplay()); } } + + mCompositionEngine->updateCursorAsync(refreshArgs); } -void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) { - if (layer->hasReadyFrame()) { - bool ignored = false; - layer->latchBuffer(ignored, systemTime()); +void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) { + if (mScheduler) { + // In practice it's not allowed to hotplug in/out the primary display once it's been + // connected during startup, but some tests do it, so just warn and return. + ALOGW("Can't re-init scheduler"); + return; } - layer->releasePendingBuffer(systemTime()); + + int currentConfig = getHwComposer().getActiveConfigIndex(primaryDisplayId); + mRefreshRateConfigs = + std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false), + getHwComposer().getConfigs( + primaryDisplayId), + currentConfig); + mRefreshRateStats = + std::make_unique<scheduler::RefreshRateStats>(*mRefreshRateConfigs, *mTimeStats, + currentConfig, HWC_POWER_MODE_OFF); + mRefreshRateStats->setConfigMode(currentConfig); + + // start the EventThread + mScheduler = + getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); }, + *mRefreshRateConfigs); + mAppConnectionHandle = + mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(), + mPhaseOffsets->getOffsetThresholdForNextVsync(), + impl::EventThread::InterceptVSyncsCallback()); + mSfConnectionHandle = + mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(), + mPhaseOffsets->getOffsetThresholdForNextVsync(), + [this](nsecs_t timestamp) { + mInterceptor->saveVSyncEvent(timestamp); + }); + + mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); + mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle, + mPhaseOffsets->getCurrentOffsets()); + + mRegionSamplingThread = + new RegionSamplingThread(*this, *mScheduler, + RegionSamplingThread::EnvironmentTimingTunables()); + + mScheduler->setChangeRefreshRateCallback( + [this](RefreshRateType type, Scheduler::ConfigEvent event) { + Mutex::Autolock lock(mStateLock); + setRefreshRateTo(type, event); + }); } void SurfaceFlinger::commitTransaction() { + withTracingLock([this]() { commitTransactionLocked(); }); + + mTransactionPending = false; + mAnimTransactionPending = false; + mTransactionCV.broadcast(); +} + +void SurfaceFlinger::commitTransactionLocked() { if (!mLayersPendingRemoval.isEmpty()) { // Notify removed layers now that they can't be drawn from for (const auto& l : mLayersPendingRemoval) { - recordBufferingStats(l->getName().string(), - l->getOccupancyHistory(true)); + recordBufferingStats(l->getName().string(), l->getOccupancyHistory(true)); // Ensure any buffers set to display on any children are released. if (l->isRemovedFromCurrentState()) { - latchAndReleaseBuffer(l); + l->latchAndReleaseBuffer(); } // If the layer has been removed and has no parent, then it will not be reachable @@ -3113,27 +2595,22 @@ void SurfaceFlinger::commitTransaction() // we composite should be considered an animation as well. mAnimCompositionPending = mAnimTransactionPending; - withTracingLock([&]() { - mDrawingState = mCurrentState; - // clear the "changed" flags in current state - mCurrentState.colorMatrixChanged = false; - - mDrawingState.traverseInZOrder([&](Layer* layer) { - layer->commitChildList(); + mDrawingState = mCurrentState; + // clear the "changed" flags in current state + mCurrentState.colorMatrixChanged = false; - // If the layer can be reached when traversing mDrawingState, then the layer is no - // longer offscreen. Remove the layer from the offscreenLayer set. - if (mOffscreenLayers.count(layer)) { - mOffscreenLayers.erase(layer); - } - }); + mDrawingState.traverseInZOrder([&](Layer* layer) { + layer->commitChildList(); - commitOffscreenLayers(); + // If the layer can be reached when traversing mDrawingState, then the layer is no + // longer offscreen. Remove the layer from the offscreenLayer set. + if (mOffscreenLayers.count(layer)) { + mOffscreenLayers.erase(layer); + } }); - mTransactionPending = false; - mAnimTransactionPending = false; - mTransactionCV.broadcast(); + commitOffscreenLayers(); + mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateMirrorInfo(); }); } void SurfaceFlinger::withTracingLock(std::function<void()> lockedOperation) { @@ -3168,147 +2645,6 @@ void SurfaceFlinger::commitOffscreenLayers() { } } -void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displayDevice, - Region& outDirtyRegion, Region& outOpaqueRegion) { - ATRACE_CALL(); - ALOGV("computeVisibleRegions"); - - auto display = displayDevice->getCompositionDisplay(); - - Region aboveOpaqueLayers; - Region aboveCoveredLayers; - Region dirty; - - outDirtyRegion.clear(); - - mDrawingState.traverseInReverseZOrder([&](Layer* layer) { - // start with the whole surface at its current location - const Layer::State& s(layer->getDrawingState()); - - // only consider the layers on the given layer stack - if (!display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) { - return; - } - - /* - * opaqueRegion: area of a surface that is fully opaque. - */ - Region opaqueRegion; - - /* - * visibleRegion: area of a surface that is visible on screen - * and not fully transparent. This is essentially the layer's - * footprint minus the opaque regions above it. - * Areas covered by a translucent surface are considered visible. - */ - Region visibleRegion; - - /* - * coveredRegion: area of a surface that is covered by all - * visible regions above it (which includes the translucent areas). - */ - Region coveredRegion; - - /* - * transparentRegion: area of a surface that is hinted to be completely - * transparent. This is only used to tell when the layer has no visible - * non-transparent regions and can be removed from the layer list. It - * does not affect the visibleRegion of this layer or any layers - * beneath it. The hint may not be correct if apps don't respect the - * SurfaceView restrictions (which, sadly, some don't). - */ - Region transparentRegion; - - - // handle hidden surfaces by setting the visible region to empty - if (CC_LIKELY(layer->isVisible())) { - const bool translucent = !layer->isOpaque(s); - Rect bounds(layer->getScreenBounds()); - - visibleRegion.set(bounds); - ui::Transform tr = layer->getTransform(); - if (!visibleRegion.isEmpty()) { - // Remove the transparent area from the visible region - if (translucent) { - if (tr.preserveRects()) { - // transform the transparent region - transparentRegion = tr.transform(layer->getActiveTransparentRegion(s)); - } else { - // transformation too complex, can't do the - // transparent region optimization. - transparentRegion.clear(); - } - } - - // compute the opaque region - const int32_t layerOrientation = tr.getOrientation(); - if (layer->getAlpha() == 1.0f && !translucent && - layer->getRoundedCornerState().radius == 0.0f && - ((layerOrientation & ui::Transform::ROT_INVALID) == false)) { - // the opaque region is the layer's footprint - opaqueRegion = visibleRegion; - } - } - } - - if (visibleRegion.isEmpty()) { - layer->clearVisibilityRegions(); - return; - } - - // Clip the covered region to the visible region - coveredRegion = aboveCoveredLayers.intersect(visibleRegion); - - // Update aboveCoveredLayers for next (lower) layer - aboveCoveredLayers.orSelf(visibleRegion); - - // subtract the opaque region covered by the layers above us - visibleRegion.subtractSelf(aboveOpaqueLayers); - - // compute this layer's dirty region - if (layer->contentDirty) { - // we need to invalidate the whole region - dirty = visibleRegion; - // as well, as the old visible region - dirty.orSelf(layer->visibleRegion); - layer->contentDirty = false; - } else { - /* compute the exposed region: - * the exposed region consists of two components: - * 1) what's VISIBLE now and was COVERED before - * 2) what's EXPOSED now less what was EXPOSED before - * - * note that (1) is conservative, we start with the whole - * visible region but only keep what used to be covered by - * something -- which mean it may have been exposed. - * - * (2) handles areas that were not covered by anything but got - * exposed because of a resize. - */ - const Region newExposed = visibleRegion - coveredRegion; - const Region oldVisibleRegion = layer->visibleRegion; - const Region oldCoveredRegion = layer->coveredRegion; - const Region oldExposed = oldVisibleRegion - oldCoveredRegion; - dirty = (visibleRegion&oldCoveredRegion) | (newExposed-oldExposed); - } - dirty.subtractSelf(aboveOpaqueLayers); - - // accumulate to the screen dirty region - outDirtyRegion.orSelf(dirty); - - // Update aboveOpaqueLayers for next (lower) layer - aboveOpaqueLayers.orSelf(opaqueRegion); - - // Store the visible region in screen space - layer->setVisibleRegion(visibleRegion); - layer->setCoveredRegion(coveredRegion); - layer->setVisibleNonTransparentRegion( - visibleRegion.subtract(transparentRegion)); - }); - - outOpaqueRegion = aboveOpaqueLayers; -} - void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty) { for (const auto& [token, displayDevice] : mDisplays) { auto display = displayDevice->getCompositionDisplay(); @@ -3329,6 +2665,8 @@ bool SurfaceFlinger::handlePageFlip() bool frameQueued = false; bool newDataLatched = false; + const nsecs_t expectedPresentTime = mExpectedPresentTime.load(); + // Store the set of layers that need updates. This set must not change as // buffers are being latched, as this could result in a deadlock. // Example: Two producers share the same command stream and: @@ -3341,7 +2679,6 @@ bool SurfaceFlinger::handlePageFlip() mDrawingState.traverseInZOrder([&](Layer* layer) { if (layer->hasReadyFrame()) { frameQueued = true; - const nsecs_t expectedPresentTime = getExpectedPresentTime(); if (layer->shouldPresentNow(expectedPresentTime)) { mLayersWithQueuedFrames.push_back(layer); } else { @@ -3353,13 +2690,21 @@ bool SurfaceFlinger::handlePageFlip() } }); + // The client can continue submitting buffers for offscreen layers, but they will not + // be shown on screen. Therefore, we need to latch and release buffers of offscreen + // layers to ensure dequeueBuffer doesn't block indefinitely. + for (Layer* offscreenLayer : mOffscreenLayers) { + offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, + [&](Layer* l) { l->latchAndReleaseBuffer(); }); + } + if (!mLayersWithQueuedFrames.empty()) { // mStateLock is needed for latchBuffer as LayerRejecter::reject() // writes to Layer current state. See also b/119481871 Mutex::Autolock lock(mStateLock); for (auto& layer : mLayersWithQueuedFrames) { - if (layer->latchBuffer(visibleRegions, latchTime)) { + if (layer->latchBuffer(visibleRegions, latchTime, expectedPresentTime)) { mLayersPendingRefresh.push_back(layer); } layer->useSurfaceDamage(); @@ -3384,6 +2729,8 @@ bool SurfaceFlinger::handlePageFlip() mBootStage = BootStage::BOOTANIMATION; } + mDrawingState.traverseInZOrder([&](Layer* layer) { layer->updateCloneBufferInfo(); }); + // Only continue with the refresh if there is actually new work to do return !mLayersWithQueuedFrames.empty() && newDataLatched; } @@ -3393,207 +2740,6 @@ void SurfaceFlinger::invalidateHwcGeometry() mGeometryInvalid = true; } -void SurfaceFlinger::doDisplayComposition(const sp<DisplayDevice>& displayDevice, - const Region& inDirtyRegion) { - auto display = displayDevice->getCompositionDisplay(); - // We only need to actually compose the display if: - // 1) It is being handled by hardware composer, which may need this to - // keep its virtual display state machine in sync, or - // 2) There is work to be done (the dirty region isn't empty) - if (!displayDevice->getId() && inDirtyRegion.isEmpty()) { - ALOGV("Skipping display composition"); - return; - } - - ALOGV("doDisplayComposition"); - base::unique_fd readyFence; - if (!doComposeSurfaces(displayDevice, Region::INVALID_REGION, &readyFence)) return; - - // swap buffers (presentation) - display->getRenderSurface()->queueBuffer(std::move(readyFence)); -} - -bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice, - const Region& debugRegion, base::unique_fd* readyFence) { - ATRACE_CALL(); - ALOGV("doComposeSurfaces"); - - auto display = displayDevice->getCompositionDisplay(); - const auto& displayState = display->getState(); - const auto displayId = display->getId(); - auto& renderEngine = getRenderEngine(); - const bool supportProtectedContent = renderEngine.supportsProtectedContent(); - - const Region bounds(displayState.bounds); - const DisplayRenderArea renderArea(displayDevice); - const bool hasClientComposition = getHwComposer().hasClientComposition(displayId); - ATRACE_INT("hasClientComposition", hasClientComposition); - - bool applyColorMatrix = false; - - renderengine::DisplaySettings clientCompositionDisplay; - std::vector<renderengine::LayerSettings> clientCompositionLayers; - sp<GraphicBuffer> buf; - base::unique_fd fd; - - if (hasClientComposition) { - ALOGV("hasClientComposition"); - - if (displayDevice->isPrimary() && supportProtectedContent) { - bool needsProtected = false; - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { - // If the layer is a protected layer, mark protected context is needed. - if (layer->isProtected()) { - needsProtected = true; - break; - } - } - if (needsProtected != renderEngine.isProtected()) { - renderEngine.useProtectedContext(needsProtected); - } - if (needsProtected != display->getRenderSurface()->isProtected() && - needsProtected == renderEngine.isProtected()) { - display->getRenderSurface()->setProtected(needsProtected); - } - } - - buf = display->getRenderSurface()->dequeueBuffer(&fd); - - if (buf == nullptr) { - ALOGW("Dequeuing buffer for display [%s] failed, bailing out of " - "client composition for this frame", - displayDevice->getDisplayName().c_str()); - return false; - } - - clientCompositionDisplay.physicalDisplay = displayState.scissor; - clientCompositionDisplay.clip = displayState.scissor; - const ui::Transform& displayTransform = displayState.transform; - clientCompositionDisplay.globalTransform = displayTransform.asMatrix4(); - clientCompositionDisplay.orientation = displayState.orientation; - - const auto* profile = display->getDisplayColorProfile(); - Dataspace outputDataspace = Dataspace::UNKNOWN; - if (profile->hasWideColorGamut()) { - outputDataspace = displayState.dataspace; - } - clientCompositionDisplay.outputDataspace = outputDataspace; - clientCompositionDisplay.maxLuminance = - profile->getHdrCapabilities().getDesiredMaxLuminance(); - - const bool hasDeviceComposition = getHwComposer().hasDeviceComposition(displayId); - const bool skipClientColorTransform = - getHwComposer() - .hasDisplayCapability(displayId, - HWC2::DisplayCapability::SkipClientColorTransform); - - // Compute the global color transform matrix. - applyColorMatrix = !hasDeviceComposition && !skipClientColorTransform; - if (applyColorMatrix) { - clientCompositionDisplay.colorTransform = displayState.colorTransformMat; - } - } - - /* - * and then, render the layers targeted at the framebuffer - */ - - ALOGV("Rendering client layers"); - bool firstLayer = true; - Region clearRegion = Region::INVALID_REGION; - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { - const Region viewportRegion(displayState.viewport); - const Region clip(viewportRegion.intersect(layer->visibleRegion)); - ALOGV("Layer: %s", layer->getName().string()); - ALOGV(" Composition type: %s", toString(layer->getCompositionType(displayDevice)).c_str()); - if (!clip.isEmpty()) { - switch (layer->getCompositionType(displayDevice)) { - case Hwc2::IComposerClient::Composition::CURSOR: - case Hwc2::IComposerClient::Composition::DEVICE: - case Hwc2::IComposerClient::Composition::SIDEBAND: - case Hwc2::IComposerClient::Composition::SOLID_COLOR: { - LOG_ALWAYS_FATAL_IF(!displayId); - const Layer::State& state(layer->getDrawingState()); - if (layer->getClearClientTarget(displayDevice) && !firstLayer && - layer->isOpaque(state) && (layer->getAlpha() == 1.0f) && - layer->getRoundedCornerState().radius == 0.0f && hasClientComposition) { - // never clear the very first layer since we're - // guaranteed the FB is already cleared - renderengine::LayerSettings layerSettings; - Region dummyRegion; - bool prepared = - layer->prepareClientLayer(renderArea, clip, dummyRegion, - supportProtectedContent, layerSettings); - - if (prepared) { - layerSettings.source.buffer.buffer = nullptr; - layerSettings.source.solidColor = half3(0.0, 0.0, 0.0); - layerSettings.alpha = half(0.0); - layerSettings.disableBlending = true; - clientCompositionLayers.push_back(layerSettings); - } - } - break; - } - case Hwc2::IComposerClient::Composition::CLIENT: { - renderengine::LayerSettings layerSettings; - bool prepared = - layer->prepareClientLayer(renderArea, clip, clearRegion, - supportProtectedContent, layerSettings); - if (prepared) { - clientCompositionLayers.push_back(layerSettings); - } - break; - } - default: - break; - } - } else { - ALOGV(" Skipping for empty clip"); - } - firstLayer = false; - } - - // Perform some cleanup steps if we used client composition. - if (hasClientComposition) { - clientCompositionDisplay.clearRegion = clearRegion; - - // We boost GPU frequency here because there will be color spaces conversion - // and it's expensive. We boost the GPU frequency so that GPU composition can - // finish in time. We must reset GPU frequency afterwards, because high frequency - // consumes extra battery. - const bool expensiveRenderingExpected = - clientCompositionDisplay.outputDataspace == Dataspace::DISPLAY_P3; - if (expensiveRenderingExpected && displayId) { - mPowerAdvisor.setExpensiveRenderingExpected(*displayId, true); - } - if (!debugRegion.isEmpty()) { - Region::const_iterator it = debugRegion.begin(); - Region::const_iterator end = debugRegion.end(); - while (it != end) { - const Rect& rect = *it++; - renderengine::LayerSettings layerSettings; - layerSettings.source.buffer.buffer = nullptr; - layerSettings.source.solidColor = half3(1.0, 0.0, 1.0); - layerSettings.geometry.boundaries = rect.toFloatRect(); - layerSettings.alpha = half(1.0); - clientCompositionLayers.push_back(layerSettings); - } - } - renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers, - buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd), - readyFence); - } else if (displayId) { - mPowerAdvisor.setExpensiveRenderingExpected(*displayId, false); - } - return true; -} - -void SurfaceFlinger::drawWormhole(const Region& region) const { - auto& engine(getRenderEngine()); - engine.fillRegionWithColor(region, 0, 0, 0, 0); -} - status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle, const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc, const sp<IBinder>& parentHandle, @@ -3612,7 +2758,7 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind } if (mNumLayers >= MAX_LAYERS) { - ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers, + ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(), MAX_LAYERS); return NO_MEMORY; } @@ -3636,7 +2782,7 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBind mMaxGraphicBufferProducerListSize, "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers", mGraphicBufferProducerList.size(), - mMaxGraphicBufferProducerListSize, mNumLayers); + mMaxGraphicBufferProducerListSize, mNumLayers.load()); } mLayersAdded = true; } @@ -3662,7 +2808,7 @@ uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags) { uint32_t SurfaceFlinger::setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart) { uint32_t old = mTransactionFlags.fetch_or(flags); - mVsyncModulator.setTransactionStart(transactionStart); + mVSyncModulator->setTransactionStart(transactionStart); if ((old & flags)==0) { // wake the server up signalTransaction(); } @@ -3685,6 +2831,7 @@ bool SurfaceFlinger::flushTransactionQueues() { while (!transactionQueue.empty()) { const auto& transaction = transactionQueue.front(); if (!transactionIsReadyToBeApplied(transaction.desiredPresentTime, + true /* useCachedExpectedPresentTime */, transaction.states)) { setTransactionFlags(eTransactionFlushNeeded); break; @@ -3692,9 +2839,9 @@ bool SurfaceFlinger::flushTransactionQueues() { transactions.push_back(transaction); applyTransactionState(transaction.states, transaction.displays, transaction.flags, mPendingInputWindowCommands, transaction.desiredPresentTime, - transaction.buffer, transaction.callback, - transaction.postTime, transaction.privileged, - /*isMainThread*/ true); + transaction.buffer, transaction.postTime, + transaction.privileged, transaction.hasListenerCallbacks, + transaction.listenerCallbacks, /*isMainThread*/ true); transactionQueue.pop(); flushedATransaction = true; } @@ -3714,31 +2861,14 @@ bool SurfaceFlinger::transactionFlushNeeded() { return !mTransactionQueues.empty(); } -bool SurfaceFlinger::containsAnyInvalidClientState(const Vector<ComposerState>& states) { - for (const ComposerState& state : states) { - // Here we need to check that the interface we're given is indeed - // one of our own. A malicious client could give us a nullptr - // IInterface, or one of its own or even one of our own but a - // different type. All these situations would cause us to crash. - if (state.client == nullptr) { - return true; - } - - sp<IBinder> binder = IInterface::asBinder(state.client); - if (binder == nullptr) { - return true; - } - - if (binder->queryLocalInterface(ISurfaceComposerClient::descriptor) == nullptr) { - return true; - } - } - return false; -} bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime, + bool useCachedExpectedPresentTime, const Vector<ComposerState>& states) { - nsecs_t expectedPresentTime = getExpectedPresentTime(); + if (!useCachedExpectedPresentTime) + populateExpectedPresentTime(); + + const nsecs_t expectedPresentTime = mExpectedPresentTime.load(); // Do not present if the desiredPresentTime has not passed unless it is more than one second // in the future. We ignore timestamps more than 1 second in the future for stability reasons. if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime && @@ -3758,13 +2888,11 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime, return true; } -void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states, - const Vector<DisplayState>& displays, uint32_t flags, - const sp<IBinder>& applyToken, - const InputWindowCommands& inputWindowCommands, - int64_t desiredPresentTime, - const client_cache_t& uncacheBuffer, - const std::vector<ListenerCallbacks>& listenerCallbacks) { +void SurfaceFlinger::setTransactionState( + const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, + const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, + int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks, + const std::vector<ListenerCallbacks>& listenerCallbacks) { ATRACE_CALL(); const int64_t postTime = systemTime(); @@ -3773,10 +2901,6 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states, Mutex::Autolock _l(mStateLock); - if (containsAnyInvalidClientState(states)) { - return; - } - // If its TransactionQueue already has a pending TransactionState or if it is pending auto itr = mTransactionQueues.find(applyToken); // if this is an animation frame, wait until prior animation frame has @@ -3793,27 +2917,28 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states, itr = mTransactionQueues.find(applyToken); } } - if (itr != mTransactionQueues.end() || - !transactionIsReadyToBeApplied(desiredPresentTime, states)) { + + // Expected present time is computed and cached on invalidate, so it may be stale. + if (itr != mTransactionQueues.end() || !transactionIsReadyToBeApplied( + desiredPresentTime, false /* useCachedExpectedPresentTime */, states)) { mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime, - uncacheBuffer, listenerCallbacks, postTime, - privileged); + uncacheBuffer, postTime, privileged, + hasListenerCallbacks, listenerCallbacks); setTransactionFlags(eTransactionFlushNeeded); return; } applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime, - uncacheBuffer, listenerCallbacks, postTime, privileged); + uncacheBuffer, postTime, privileged, hasListenerCallbacks, + listenerCallbacks); } -void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states, - const Vector<DisplayState>& displays, uint32_t flags, - const InputWindowCommands& inputWindowCommands, - const int64_t desiredPresentTime, - const client_cache_t& uncacheBuffer, - const std::vector<ListenerCallbacks>& listenerCallbacks, - const int64_t postTime, bool privileged, - bool isMainThread) { +void SurfaceFlinger::applyTransactionState( + const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, + const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, + const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged, + bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, + bool isMainThread) { uint32_t transactionFlags = 0; if (flags & eAnimation) { @@ -3836,24 +2961,27 @@ void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states, transactionFlags |= setDisplayStateLocked(display); } - // In case the client has sent a Transaction that should receive callbacks but without any - // SurfaceControls that should be included in the callback, send the listener and callbackIds - // to the callback thread so it can send an empty callback - if (!listenerCallbacks.empty()) { - mTransactionCompletedThread.run(); - } - for (const auto& [listener, callbackIds] : listenerCallbacks) { - mTransactionCompletedThread.addCallback(listener, callbackIds); + // start and end registration for listeners w/ no surface so they can get their callback. Note + // that listeners with SurfaceControls will start registration during setClientStateLocked + // below. + for (const auto& listener : listenerCallbacks) { + mTransactionCompletedThread.startRegistration(listener); + mTransactionCompletedThread.endRegistration(listener); } + std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> listenerCallbacksWithSurfaces; uint32_t clientStateFlags = 0; for (const ComposerState& state : states) { - clientStateFlags |= setClientStateLocked(state, desiredPresentTime, listenerCallbacks, - postTime, privileged); + clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged, + listenerCallbacksWithSurfaces); + } + + for (const auto& listenerCallback : listenerCallbacksWithSurfaces) { + mTransactionCompletedThread.endRegistration(listenerCallback); } // If the state doesn't require a traversal and there are callbacks, send them now - if (!(clientStateFlags & eTraversalNeeded) && !listenerCallbacks.empty()) { + if (!(clientStateFlags & eTraversalNeeded) && hasListenerCallbacks) { mTransactionCompletedThread.sendCallbacks(); } transactionFlags |= clientStateFlags; @@ -3981,22 +3109,30 @@ bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess() { } uint32_t SurfaceFlinger::setClientStateLocked( - const ComposerState& composerState, int64_t desiredPresentTime, - const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime, - bool privileged) { + const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime, + bool privileged, + std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) { const layer_state_t& s = composerState.state; - sp<Client> client(static_cast<Client*>(composerState.client.get())); - sp<Layer> layer(client->getLayerUser(s.surface)); + for (auto& listener : s.listeners) { + // note that startRegistration will not re-register if the listener has + // already be registered for a prior surface control + mTransactionCompletedThread.startRegistration(listener); + listenerCallbacks.insert(listener); + } + + sp<Layer> layer(fromHandle(s.surface)); if (layer == nullptr) { + for (auto& [listener, callbackIds] : s.listeners) { + mTransactionCompletedThread.registerUnpresentedCallbackHandle( + new CallbackHandle(listener, callbackIds, s.surface)); + } return 0; } uint32_t flags = 0; const uint64_t what = s.what; - bool geometryAppliesWithResize = - what & layer_state_t::eGeometryAppliesWithResize; // If we are deferring transaction, make sure to push the pending state, as otherwise the // pending state will also be deferred. @@ -4005,7 +3141,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( } if (what & layer_state_t::ePositionChanged) { - if (layer->setPosition(s.x, s.y, !geometryAppliesWithResize)) { + if (layer->setPosition(s.x, s.y)) { flags |= eTraversalNeeded; } } @@ -4096,8 +3232,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( flags |= eTraversalNeeded; } if (what & layer_state_t::eCropChanged_legacy) { - if (layer->setCrop_legacy(s.crop_legacy, !geometryAppliesWithResize)) - flags |= eTraversalNeeded; + if (layer->setCrop_legacy(s.crop_legacy)) flags |= eTraversalNeeded; } if (what & layer_state_t::eCornerRadiusChanged) { if (layer->setCornerRadius(s.cornerRadius)) @@ -4139,15 +3274,6 @@ uint32_t SurfaceFlinger::setClientStateLocked( // We don't trigger a traversal here because if no other state is // changed, we don't want this to cause any more work } - if (what & layer_state_t::eReparent) { - bool hadParent = layer->hasParent(); - if (layer->reparent(s.parentHandleForChild)) { - if (!hadParent) { - mCurrentState.layersSortedByZ.remove(layer); - } - flags |= eTransactionNeeded|eTraversalNeeded; - } - } if (what & layer_state_t::eReparentChildren) { if (layer->reparentChildren(s.reparentHandle)) { flags |= eTransactionNeeded|eTraversalNeeded; @@ -4208,9 +3334,22 @@ uint32_t SurfaceFlinger::setClientStateLocked( flags |= eTraversalNeeded; } } + // This has to happen after we reparent children because when we reparent to null we remove + // child layers from current state and remove its relative z. If the children are reparented in + // the same transaction, then we have to make sure we reparent the children first so we do not + // lose its relative z order. + if (what & layer_state_t::eReparent) { + bool hadParent = layer->hasParent(); + if (layer->reparent(s.parentHandleForChild)) { + if (!hadParent) { + mCurrentState.layersSortedByZ.remove(layer); + } + flags |= eTransactionNeeded | eTraversalNeeded; + } + } std::vector<sp<CallbackHandle>> callbackHandles; - if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!listenerCallbacks.empty())) { - for (const auto& [listener, callbackIds] : listenerCallbacks) { + if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!s.listeners.empty())) { + for (auto& [listener, callbackIds] : s.listeners) { callbackHandles.emplace_back(new CallbackHandle(listener, callbackIds, s.surface)); } } @@ -4259,6 +3398,35 @@ uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& input return flags; } +status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle, + sp<IBinder>* outHandle) { + if (!mirrorFromHandle) { + return NAME_NOT_FOUND; + } + + sp<Layer> mirrorLayer; + sp<Layer> mirrorFrom; + String8 uniqueName = getUniqueLayerName(String8("MirrorRoot")); + + { + Mutex::Autolock _l(mStateLock); + mirrorFrom = fromHandle(mirrorFromHandle); + if (!mirrorFrom) { + return NAME_NOT_FOUND; + } + + status_t result = createContainerLayer(client, uniqueName, -1, -1, 0, LayerMetadata(), + outHandle, &mirrorLayer); + if (result != NO_ERROR) { + return result; + } + + mirrorLayer->mClonedChild = mirrorFrom->createClone(); + } + + return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false); +} + status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h, PixelFormat format, uint32_t flags, LayerMetadata metadata, sp<IBinder>* handle, @@ -4394,8 +3562,18 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const break; } - sp<BufferQueueLayer> layer = getFactory().createBufferQueueLayer( - LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); + sp<BufferQueueLayer> layer; + LayerCreationArgs args = + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)); + args.textureName = getNewTexture(); + { + // Grab the SF state lock during this since it's the only safe way to access + // RenderEngine when creating a BufferLayerConsumer + // TODO: Check if this lock is still needed here + Mutex::Autolock lock(mStateLock); + layer = getFactory().createBufferQueueLayer(args); + } + status_t err = layer->setDefaultBufferProperties(w, h, format); if (err == NO_ERROR) { *handle = layer->getHandle(); @@ -4411,8 +3589,11 @@ status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, const uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata, sp<IBinder>* handle, sp<Layer>* outLayer) { - sp<BufferStateLayer> layer = getFactory().createBufferStateLayer( - LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); + LayerCreationArgs args = + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata)); + args.displayDevice = getDefaultDisplayDevice(); + args.textureName = getNewTexture(); + sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args); *handle = layer->getHandle(); *outLayer = layer; @@ -4492,7 +3673,8 @@ void SurfaceFlinger::onInitializeDisplays() { d.width = 0; d.height = 0; displays.add(d); - setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, {}); + setTransactionState(state, displays, 0, nullptr, mPendingInputWindowCommands, -1, {}, false, + {}); setPowerModeInternal(display, HWC_POWER_MODE_NORMAL); @@ -4598,7 +3780,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int if (display->isPrimary()) { mTimeStats->setPowerMode(mode); - mRefreshRateStats.setPowerMode(mode); + mRefreshRateStats->setPowerMode(mode); mScheduler->setDisplayPowerState(mode == HWC_POWER_MODE_NORMAL); } @@ -4649,14 +3831,10 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, using namespace std::string_literals; static const std::unordered_map<std::string, Dumper> dumpers = { - {"--clear-layer-stats"s, dumper([this](std::string&) { mLayerStats.clear(); })}, - {"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })}, {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)}, - {"--dispsync"s, dumper([this](std::string& s) { - mScheduler->dumpPrimaryDispSync(s); - })}, - {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })}, - {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })}, + {"--dispsync"s, + dumper([this](std::string& s) { mScheduler->getPrimaryDispSync().dump(s); })}, + {"--edid"s, argsDumper(&SurfaceFlinger::dumpRawDisplayIdentificationData)}, {"--frame-events"s, dumper(&SurfaceFlinger::dumpFrameEventsLocked)}, {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)}, {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)}, @@ -4669,7 +3847,8 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, const auto flag = args.empty() ? ""s : std::string(String8(args[0])); - if (const auto it = dumpers.find(flag); it != dumpers.end()) { + const auto it = dumpers.find(flag); + if (it != dumpers.end()) { (it->second)(args, asProto, result); } else if (!asProto) { dumpAllLocked(args, result); @@ -4679,13 +3858,17 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, mStateLock.unlock(); } - LayersProto layersProto = dumpProtoFromMainThread(); - if (asProto) { - result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize()); - } else { - auto layerTree = LayerProtoParser::generateLayerTree(layersProto); - result.append(LayerProtoParser::layerTreeToString(layerTree)); - result.append("\n"); + if (it == dumpers.end()) { + const LayersProto layersProto = dumpProtoFromMainThread(); + if (asProto) { + result.append(layersProto.SerializeAsString()); + } else { + // Dump info that we need to access from the main thread + const auto layerTree = LayerProtoParser::generateLayerTree(layersProto); + result.append(LayerProtoParser::layerTreeToString(layerTree)); + result.append("\n"); + dumpOffscreenLayers(result); + } } } write(fd, result.c_str(), result.size()); @@ -4760,24 +3943,27 @@ void SurfaceFlinger::appendSfConfigString(std::string& result) const { } void SurfaceFlinger::dumpVSync(std::string& result) const { + mScheduler->dump(result); + StringAppendF(&result, "+ Refresh rate switching: %s\n", + mRefreshRateConfigs->refreshRateSwitchingSupported() ? "on" : "off"); + StringAppendF(&result, "+ Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off"); + + mRefreshRateStats->dump(result); + result.append("\n"); + mPhaseOffsets->dump(result); StringAppendF(&result, - " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", + " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", dispSyncPresentTimeOffset, getVsyncPeriod()); - StringAppendF(&result, "Scheduler enabled."); - StringAppendF(&result, "+ Smart 90 for video detection: %s\n\n", - mUseSmart90ForVideo ? "on" : "off"); StringAppendF(&result, "Allowed Display Configs: "); for (int32_t configId : mAllowedDisplayConfigs) { - for (auto refresh : mRefreshRateConfigs.getRefreshRates()) { - if (refresh.second && refresh.second->configId == configId) { - StringAppendF(&result, "%dHz, ", refresh.second->fps); - } - } + StringAppendF(&result, "%" PRIu32 " Hz, ", + mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps); } StringAppendF(&result, "(config override by backdoor: %s)\n\n", mDebugDisplayConfigSetByBackdoor ? "yes" : "no"); + mScheduler->dump(mAppConnectionHandle, result); } @@ -4878,21 +4064,13 @@ void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const { } if (!isEdid(data)) { - result.append("unknown identification data: "); - for (uint8_t byte : data) { - StringAppendF(&result, "%x ", byte); - } - result.append("\n"); + result.append("unknown identification data\n"); continue; } const auto edid = parseEdid(data); if (!edid) { - result.append("invalid EDID: "); - for (uint8_t byte : data) { - StringAppendF(&result, "%x ", byte); - } - result.append("\n"); + result.append("invalid EDID\n"); continue; } @@ -4902,6 +4080,18 @@ void SurfaceFlinger::dumpDisplayIdentificationData(std::string& result) const { } } +void SurfaceFlinger::dumpRawDisplayIdentificationData(const DumpArgs& args, + std::string& result) const { + hwc2_display_t hwcDisplayId; + uint8_t port; + DisplayIdentificationData data; + + if (args.size() > 1 && base::ParseUint(String8(args[1]), &hwcDisplayId) && + getHwComposer().getDisplayIdentificationData(hwcDisplayId, &port, &data)) { + result.append(reinterpret_cast<const char*>(data.data()), data.size()); + } +} + void SurfaceFlinger::dumpWideColorInfo(std::string& result) const { StringAppendF(&result, "Device has wide color built-in display: %d\n", hasWideColorDisplay); StringAppendF(&result, "Device uses color management: %d\n", useColorManagement); @@ -4940,37 +4130,53 @@ LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const { return layersProto; } +void SurfaceFlinger::dumpOffscreenLayersProto(LayersProto& layersProto, uint32_t traceFlags) const { + // Add a fake invisible root layer to the proto output and parent all the offscreen layers to + // it. + LayerProto* rootProto = layersProto.add_layers(); + const int32_t offscreenRootLayerId = INT32_MAX - 2; + rootProto->set_id(offscreenRootLayerId); + rootProto->set_name("Offscreen Root"); + rootProto->set_parent(-1); + + for (Layer* offscreenLayer : mOffscreenLayers) { + // Add layer as child of the fake root + rootProto->add_children(offscreenLayer->sequence); + + // Add layer + LayerProto* layerProto = layersProto.add_layers(); + offscreenLayer->writeToProtoDrawingState(layerProto, traceFlags); + offscreenLayer->writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, + traceFlags); + layerProto->set_parent(offscreenRootLayerId); + + // Add children + offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { + if (layer == offscreenLayer) { + return; + } + LayerProto* childProto = layersProto.add_layers(); + layer->writeToProtoDrawingState(childProto, traceFlags); + layer->writeToProtoCommonState(childProto, LayerVector::StateSet::Drawing, traceFlags); + }); + } +} + LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) { LayersProto layersProto; postMessageSync(new LambdaMessage([&]() { layersProto = dumpDrawingStateProto(traceFlags); })); return layersProto; } -LayersProto SurfaceFlinger::dumpVisibleLayersProtoInfo( - const sp<DisplayDevice>& displayDevice) const { - LayersProto layersProto; - - SizeProto* resolution = layersProto.mutable_resolution(); - resolution->set_w(displayDevice->getWidth()); - resolution->set_h(displayDevice->getHeight()); - - auto display = displayDevice->getCompositionDisplay(); - const auto& displayState = display->getState(); - - layersProto.set_color_mode(decodeColorMode(displayState.colorMode)); - layersProto.set_color_transform(decodeColorTransform(displayState.colorTransform)); - layersProto.set_global_transform(displayState.orientation); - - const auto displayId = displayDevice->getId(); - LOG_ALWAYS_FATAL_IF(!displayId); - mDrawingState.traverseInZOrder([&](Layer* layer) { - if (!layer->visibleRegion.isEmpty() && !display->getOutputLayersOrderedByZ().empty()) { - LayerProto* layerProto = layersProto.add_layers(); - layer->writeToProtoCompositionState(layerProto, displayDevice); +void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { + result.append("Offscreen Layers:\n"); + postMessageSync(new LambdaMessage([&]() { + for (Layer* offscreenLayer : mOffscreenLayers) { + offscreenLayer->traverseInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) { + layer->dumpCallingUidPid(result); + }); } - }); - - return layersProto; + })); } void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const { @@ -5007,7 +4213,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("\n\n"); colorizer.bold(result); - result.append("VSYNC configuration:\n"); + result.append("Scheduler:\n"); colorizer.reset(result); dumpVSync(result); result.append("\n"); @@ -5025,7 +4231,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co * Dump the visible layer list */ colorizer.bold(result); - StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers); + StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load()); StringAppendF(&result, "GraphicBufferProducers: %zu, max %zu\n", mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize); colorizer.reset(result); @@ -5051,6 +4257,12 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("\n"); /* + * Dump CompositionEngine state + */ + + mCompositionEngine->dump(result); + + /* * Dump SurfaceFlinger global state */ @@ -5134,31 +4346,10 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("\n"); } - /** - * Scheduler dump state. - */ - result.append("\nScheduler state:\n"); - result.append(mScheduler->doDump() + "\n"); - StringAppendF(&result, "+ Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off"); - result.append(mRefreshRateStats.doDump() + "\n"); - result.append(mTimeStats->miniDump()); result.append("\n"); } -const Vector<sp<Layer>>& SurfaceFlinger::getLayerSortedByZForHwcDisplay(DisplayId displayId) { - // Note: mStateLock is held here - for (const auto& [token, display] : mDisplays) { - if (display->getId() == displayId) { - return getDisplayDeviceLocked(token)->getVisibleLayersSortedByZ(); - } - } - - ALOGE("%s: Invalid display %s", __FUNCTION__, to_string(displayId).c_str()); - static const Vector<sp<Layer>> empty; - return empty; -} - void SurfaceFlinger::updateColorMatrixLocked() { mat4 colorMatrix; if (mGlobalSaturationFactor != 1.0f) { @@ -5251,7 +4442,18 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case SET_DISPLAY_BRIGHTNESS: { return OK; } - case CAPTURE_LAYERS: + case CAPTURE_LAYERS: { + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + // allow media to capture layer for video thumbnails + if ((uid != AID_GRAPHICS && uid != AID_MEDIA) && + !PermissionCache::checkPermission(sReadFramebuffer, pid, uid)) { + ALOGE("Permission Denial: can't capture layer pid=%d, uid=%d", pid, uid); + return PERMISSION_DENIED; + } + return OK; + } case CAPTURE_SCREEN: case ADD_REGION_SAMPLING_LISTENER: case REMOVE_REGION_SAMPLING_LISTENER: { @@ -5427,13 +4629,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r updateColorMatrixLocked(); return NO_ERROR; } - // This is an experimental interface - // Needs to be shifted to proper binder interface when we productize - case 1016: { - n = data.readInt32(); - // TODO(b/113612090): Evaluate if this can be removed. - mScheduler->setRefreshSkipCount(n); - return NO_ERROR; + case 1016: { // Unused. + return NAME_NOT_FOUND; } case 1017: { n = data.readInt32(); @@ -5527,13 +4724,13 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r DisplayColorSetting setting = static_cast<DisplayColorSetting>(data.readInt32()); switch (setting) { - case DisplayColorSetting::MANAGED: + case DisplayColorSetting::kManaged: reply->writeBool(useColorManagement); break; - case DisplayColorSetting::UNMANAGED: + case DisplayColorSetting::kUnmanaged: reply->writeBool(true); break; - case DisplayColorSetting::ENHANCED: + case DisplayColorSetting::kEnhanced: reply->writeBool(display->hasRenderIntent(RenderIntent::ENHANCE)); break; default: // vendor display color setting @@ -5611,7 +4808,8 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r case 1034: { // TODO(b/129297325): expose this via developer menu option n = data.readInt32(); - if (n && !mRefreshRateOverlay) { + if (n && !mRefreshRateOverlay && + mRefreshRateConfigs->refreshRateSwitchingSupported()) { RefreshRateType type; { std::lock_guard<std::mutex> lock(mActiveConfigLock); @@ -5833,10 +5031,13 @@ status_t SurfaceFlinger::captureLayers( drawLayers(); } else { Rect bounds = getBounds(); - screenshotParentLayer = mFlinger->getFactory().createContainerLayer( - LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"), - bounds.getWidth(), bounds.getHeight(), 0, - LayerMetadata())); + // In the "childrenOnly" case we reparent the children to a screenshot + // layer which has no properties set and which does not draw. + sp<ContainerLayer> screenshotParentLayer = + mFlinger->getFactory().createContainerLayer( + LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"), + bounds.getWidth(), bounds.getHeight(), 0, + LayerMetadata())); ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop); drawLayers(); @@ -5847,9 +5048,6 @@ status_t SurfaceFlinger::captureLayers( const sp<Layer> mLayer; const Rect mCrop; - // In the "childrenOnly" case we reparent the children to a screenshot - // layer which has no properties set and which does not draw. - sp<ContainerLayer> screenshotParentLayer; ui::Transform mTransform; bool mNeedsFiltering; @@ -6108,11 +5306,19 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, Region clearRegion = Region::INVALID_REGION; traverseLayers([&](Layer* layer) { - renderengine::LayerSettings layerSettings; - bool prepared = layer->prepareClientLayer(renderArea, useIdentityTransform, clearRegion, - false, layerSettings); - if (prepared) { - clientCompositionLayers.push_back(layerSettings); + const bool supportProtectedContent = false; + Region clip(renderArea.getBounds()); + compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{ + clip, + useIdentityTransform, + layer->needsFiltering(renderArea.getDisplayDevice()) || renderArea.needsFiltering(), + renderArea.isSecure(), + supportProtectedContent, + clearRegion, + }; + auto result = layer->prepareClientComposition(targetSettings); + if (result) { + clientCompositionLayers.push_back(*result); } }); @@ -6210,15 +5416,28 @@ void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& d mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, display->getActiveConfig()); - // Set the highest allowed config by iterating backwards on available refresh rates - const auto& refreshRates = mRefreshRateConfigs.getRefreshRates(); - for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) { - if (iter->second && isDisplayConfigAllowed(iter->second->configId)) { - ALOGV("switching to config %d", iter->second->configId); - setDesiredActiveConfig( - {iter->first, iter->second->configId, Scheduler::ConfigEvent::Changed}); - break; + if (mRefreshRateConfigs->refreshRateSwitchingSupported()) { + const auto& type = mScheduler->getPreferredRefreshRateType(); + const auto& config = mRefreshRateConfigs->getRefreshRateFromType(type); + if (isDisplayConfigAllowed(config.configId)) { + ALOGV("switching to Scheduler preferred config %d", config.configId); + setDesiredActiveConfig({type, config.configId, Scheduler::ConfigEvent::Changed}); + } else { + // Set the highest allowed config by iterating backwards on available refresh rates + const auto& refreshRates = mRefreshRateConfigs->getRefreshRateMap(); + for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) { + if (isDisplayConfigAllowed(iter->second.configId)) { + ALOGV("switching to allowed config %d", iter->second.configId); + setDesiredActiveConfig( + {iter->first, iter->second.configId, Scheduler::ConfigEvent::Changed}); + break; + } + } } + } else if (!allowedConfigs.empty()) { + ALOGV("switching to config %d", allowedConfigs[0]); + setDesiredActiveConfig( + {RefreshRateType::DEFAULT, allowedConfigs[0], Scheduler::ConfigEvent::Changed}); } } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 5778be6900..9f3a914366 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -23,12 +23,14 @@ */ #include <android-base/thread_annotations.h> +#include <compositionengine/OutputColorSetting.h> #include <cutils/atomic.h> #include <cutils/compiler.h> #include <gui/BufferQueue.h> #include <gui/FrameTimestamps.h> #include <gui/ISurfaceComposer.h> #include <gui/ISurfaceComposerClient.h> +#include <gui/ITransactionCompletedListener.h> #include <gui/LayerState.h> #include <gui/OccupancyTracker.h> #include <hardware/hwcomposer_defs.h> @@ -52,7 +54,6 @@ #include "DisplayHardware/PowerAdvisor.h" #include "Effects/Daltonizer.h" #include "FrameTracker.h" -#include "LayerStats.h" #include "LayerVector.h" #include "Scheduler/RefreshRateConfigs.h" #include "Scheduler/RefreshRateStats.h" @@ -60,6 +61,7 @@ #include "Scheduler/VSyncModulator.h" #include "SurfaceFlingerFactory.h" #include "SurfaceTracing.h" +#include "TracedOrdinal.h" #include "TransactionCompletedThread.h" #include <atomic> @@ -68,6 +70,7 @@ #include <map> #include <memory> #include <mutex> +#include <optional> #include <queue> #include <set> #include <string> @@ -86,15 +89,18 @@ class EventThread; class HWComposer; class IGraphicBufferProducer; class IInputFlinger; -class InjectVSyncSource; class Layer; class MessageBase; class RefreshRateOverlay; class RegionSamplingThread; class TimeStats; +class FrameTracer; namespace compositionengine { class DisplaySurface; +class OutputLayer; + +struct CompositionRefreshArgs; } // namespace compositionengine namespace renderengine { @@ -114,11 +120,7 @@ enum { eTransactionMask = 0x1f, }; -enum class DisplayColorSetting : int32_t { - MANAGED = 0, - UNMANAGED = 1, - ENHANCED = 2, -}; +using DisplayColorSetting = compositionengine::OutputColorSetting; class SurfaceFlingerBE { @@ -296,22 +298,13 @@ public: // main thread function to enable/disable h/w composer event void setPrimaryVsyncEnabledInternal(bool enabled); + void setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled); // called on the main thread by MessageQueue when an internal message // is received // TODO: this should be made accessible only to MessageQueue void onMessageReceived(int32_t what); - // populates the expected present time for this frame. - // When we are in negative offsets, we perform a correction so that the - // predicted vsync for the *next* frame is used instead. - void populateExpectedPresentTime(); - nsecs_t getExpectedPresentTime() const { return mExpectedPresentTime; } - - // for debugging only - // TODO: this should be made accessible only to HWComposer - const Vector<sp<Layer>>& getLayerSortedByZForHwcDisplay(DisplayId displayId); - renderengine::RenderEngine& getRenderEngine() const; bool authenticateSurfaceTextureLocked( @@ -345,6 +338,7 @@ private: // For unit tests friend class TestableSurfaceFlinger; + friend class TransactionApplicationTest; // This value is specified in number of frames. Log frame stats at most // every half hour. @@ -353,9 +347,11 @@ private: static const size_t MAX_LAYERS = 4096; static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB +protected: // We're reference counted, never destroy SurfaceFlinger directly virtual ~SurfaceFlinger(); +private: /* ------------------------------------------------------------------------ * Internal data structures */ @@ -406,6 +402,7 @@ private: const sp<IBinder>& applyToken, const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, + bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks) override; void bootFinished() override; bool authenticateSurfaceTexture( @@ -554,9 +551,9 @@ private: void updateInputFlinger(); void updateInputWindowInfo(); void commitInputWindowCommands() REQUIRES(mStateLock); - void executeInputWindowCommands(); void setInputWindowsFinished(); void updateCursorAsync(); + void initScheduler(DisplayId primaryDisplayId); /* handlePageFlip - latch a new buffer if available and compute the dirty * region. Returns whether a new buffer has been latched, i.e., whether it @@ -571,10 +568,10 @@ private: const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, - const client_cache_t& uncacheBuffer, + const client_cache_t& uncacheBuffer, const int64_t postTime, + bool privileged, bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, - const int64_t postTime, bool privileged, bool isMainThread = false) - REQUIRES(mStateLock); + bool isMainThread = false) REQUIRES(mStateLock); // Returns true if at least one transaction was flushed bool flushTransactionQueues(); // Returns true if there is at least one transaction that needs to be flushed @@ -584,19 +581,24 @@ private: // Can only be called from the main thread or with mStateLock held uint32_t setTransactionFlags(uint32_t flags); uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart); - void latchAndReleaseBuffer(const sp<Layer>& layer); void commitTransaction() REQUIRES(mStateLock); void commitOffscreenLayers(); - bool containsAnyInvalidClientState(const Vector<ComposerState>& states); bool transactionIsReadyToBeApplied(int64_t desiredPresentTime, + bool useCachedExpectedPresentTime, const Vector<ComposerState>& states); - uint32_t setClientStateLocked(const ComposerState& composerState, int64_t desiredPresentTime, - const std::vector<ListenerCallbacks>& listenerCallbacks, - int64_t postTime, bool privileged) REQUIRES(mStateLock); uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock); uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands) REQUIRES(mStateLock); +protected: + virtual uint32_t setClientStateLocked( + const ComposerState& composerState, int64_t desiredPresentTime, int64_t postTime, + bool privileged, + std::unordered_set<ListenerCallbacks, ListenerCallbacksHash>& listenerCallbacks) + REQUIRES(mStateLock); + virtual void commitTransactionLocked(); + +private: /* ------------------------------------------------------------------------ * Layer management */ @@ -622,6 +624,9 @@ private: uint32_t h, uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle, sp<Layer>* outLayer); + status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle, + sp<IBinder>* outHandle); + String8 getUniqueLayerName(const String8& name); // called when all clients have released all their references to @@ -749,53 +754,15 @@ private: * Compositing */ void invalidateHwcGeometry(); - void computeVisibleRegions(const sp<const DisplayDevice>& display, Region& dirtyRegion, - Region& opaqueRegion); - void preComposition(); void postComposition(); void getCompositorTiming(CompositorTiming* compositorTiming); void updateCompositorTiming(const DisplayStatInfo& stats, nsecs_t compositeTime, std::shared_ptr<FenceTime>& presentFenceTime); void setCompositorTimingSnapped(const DisplayStatInfo& stats, nsecs_t compositeToPresentLatency); - void rebuildLayerStacks(); - - ui::Dataspace getBestDataspace(const sp<DisplayDevice>& display, ui::Dataspace* outHdrDataSpace, - bool* outIsHdrClientComposition) const; - // Returns the appropriate ColorMode, Dataspace and RenderIntent for the - // DisplayDevice. The function only returns the supported ColorMode, - // Dataspace and RenderIntent. - void pickColorMode(const sp<DisplayDevice>& display, ui::ColorMode* outMode, - ui::Dataspace* outDataSpace, ui::RenderIntent* outRenderIntent) const; - - void calculateWorkingSet(); - /* - * beginFrame - This function handles any pre-frame processing that needs to be - * prior to any CompositionInfo handling and is not dependent on data in - * CompositionInfo - */ - void beginFrame(const sp<DisplayDevice>& display); - /* prepareFrame - This function will call into the DisplayDevice to prepare a - * frame after CompositionInfo has been programmed. This provides a mechanism - * to prepare the hardware composer - */ - void prepareFrame(const sp<DisplayDevice>& display); - void doComposition(const sp<DisplayDevice>& display, bool repainEverything); - void doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything); - void logLayerStats(); - void doDisplayComposition(const sp<DisplayDevice>& display, const Region& dirtyRegion); - - // This fails if using GL and the surface has been destroyed. readyFence - // will be populated if using GL and native fence sync is supported, to - // signal when drawing has completed. - bool doComposeSurfaces(const sp<DisplayDevice>& display, const Region& debugRegionm, - base::unique_fd* readyFence); - - void postFramebuffer(const sp<DisplayDevice>& display); void postFrame(); - void drawWormhole(const Region& region) const; /* ------------------------------------------------------------------------ * Display management @@ -819,7 +786,13 @@ private: // the desired refresh rate. void setRefreshRateTo(RefreshRateType, Scheduler::ConfigEvent event) REQUIRES(mStateLock); - bool isDisplayConfigAllowed(int32_t configId) REQUIRES(mStateLock); + bool isDisplayConfigAllowed(int32_t configId) const REQUIRES(mStateLock); + + bool previousFrameMissed(int graceTimeMs = 0); + + // Populates the expected present time for this frame. For negative offsets, performs a + // correction using the predicted vsync for the next frame instead. + void populateExpectedPresentTime(); /* * Display identification @@ -849,9 +822,6 @@ private: return hwcDisplayId ? getHwComposer().toPhysicalDisplayId(*hwcDisplayId) : std::nullopt; } - bool previousFrameMissed(int graceTimeMs = 0); - void setVsyncEnabledInHWC(DisplayId displayId, HWC2::Vsync enabled); - /* * Debugging & dumpsys */ @@ -900,12 +870,15 @@ private: std::vector<OccupancyTracker::Segment>&& history); void dumpBufferingStats(std::string& result) const; void dumpDisplayIdentificationData(std::string& result) const; + void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const; void dumpWideColorInfo(std::string& result) const; LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; + void dumpOffscreenLayersProto(LayersProto& layersProto, + uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const; LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) EXCLUDES(mStateLock); + void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock); void withTracingLock(std::function<void()> operation) REQUIRES(mStateLock); - LayersProto dumpVisibleLayersProtoInfo(const sp<DisplayDevice>& display) const; bool isLayerTripleBufferingDisabled() const { return this->mLayerTripleBufferingDisabled; @@ -966,13 +939,6 @@ private: // constant members (no synchronization needed for access) const nsecs_t mBootTime = systemTime(); bool mGpuToCpuSupported = false; - std::unique_ptr<EventThread> mInjectorEventThread; - std::unique_ptr<InjectVSyncSource> mVSyncInjector; - - // Calculates correct offsets. - VSyncModulator mVsyncModulator; - // Keeps track of all available phase offsets for different refresh types. - const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets; // Can only accessed from the main thread, these members // don't need synchronization @@ -1027,8 +993,8 @@ private: SurfaceTracing mTracing{*this}; bool mTracingEnabled = false; bool mTracingEnabledChanged GUARDED_BY(mStateLock) = false; - LayerStats mLayerStats; const std::shared_ptr<TimeStats> mTimeStats; + const std::unique_ptr<FrameTracer> mFrameTracer; bool mUseHwcVirtualDisplays = false; std::atomic<uint32_t> mFrameMissedCount = 0; std::atomic<uint32_t> mHwcFrameMissedCount = 0; @@ -1058,47 +1024,42 @@ private: uint32_t mTexturePoolSize = 0; std::vector<uint32_t> mTexturePool; - struct IBinderHash { - std::size_t operator()(const sp<IBinder>& strongPointer) const { - return std::hash<IBinder*>{}(strongPointer.get()); - } - }; struct TransactionState { TransactionState(const Vector<ComposerState>& composerStates, const Vector<DisplayState>& displayStates, uint32_t transactionFlags, int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, - const std::vector<ListenerCallbacks>& listenerCallbacks, int64_t postTime, - bool privileged) + int64_t postTime, bool privileged, bool hasListenerCallbacks, + std::vector<ListenerCallbacks> listenerCallbacks) : states(composerStates), displays(displayStates), flags(transactionFlags), desiredPresentTime(desiredPresentTime), buffer(uncacheBuffer), - callback(listenerCallbacks), postTime(postTime), - privileged(privileged) {} + privileged(privileged), + hasListenerCallbacks(hasListenerCallbacks), + listenerCallbacks(listenerCallbacks) {} Vector<ComposerState> states; Vector<DisplayState> displays; uint32_t flags; const int64_t desiredPresentTime; client_cache_t buffer; - std::vector<ListenerCallbacks> callback; const int64_t postTime; bool privileged; + bool hasListenerCallbacks; + std::vector<ListenerCallbacks> listenerCallbacks; }; - std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues; + std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues; /* ------------------------------------------------------------------------ * Feature prototyping */ - bool mInjectVSyncs = false; - // Static screen stats bool mHasPoweredOff = false; - size_t mNumLayers = 0; + std::atomic<size_t> mNumLayers = 0; // Verify that transaction is being called by an approved process: // either AID_GRAPHICS or AID_SYSTEM. @@ -1112,7 +1073,7 @@ private: static bool useVrFlinger; std::thread::id mMainThreadId = std::this_thread::get_id(); - DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::ENHANCED; + DisplayColorSetting mDisplayColorSetting = DisplayColorSetting::kEnhanced; // Color mode forced by setting persist.sys.sf.color_mode, it must: // 1. not be NATIVE color mode, NATIVE color mode means no forced color mode; @@ -1135,11 +1096,19 @@ private: */ bool mUseSmart90ForVideo = false; std::unique_ptr<Scheduler> mScheduler; - sp<Scheduler::ConnectionHandle> mAppConnectionHandle; - sp<Scheduler::ConnectionHandle> mSfConnectionHandle; + scheduler::ConnectionHandle mAppConnectionHandle; + scheduler::ConnectionHandle mSfConnectionHandle; + + // Stores phase offsets configured per refresh rate. + const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets; + + // Optional to defer construction until scheduler connections are created. + std::optional<scheduler::VSyncModulator> mVSyncModulator; - scheduler::RefreshRateConfigs mRefreshRateConfigs; - scheduler::RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, *mTimeStats}; + std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; + std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; + + std::atomic<nsecs_t> mExpectedPresentTime = 0; // All configs are allowed if the set is empty. using DisplayConfigs = std::set<int32_t>; @@ -1154,7 +1123,8 @@ private: ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock); // below flags are set by main thread only - bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false; + TracedOrdinal<bool> mDesiredActiveConfigChanged + GUARDED_BY(mActiveConfigLock) = {"DesiredActiveConfigChanged", false}; bool mCheckPendingFence = false; bool mLumaSampling = true; @@ -1194,8 +1164,6 @@ private: // Flags to capture the state of Vsync in HWC HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable; HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable; - - nsecs_t mExpectedPresentTime; }; } // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp new file mode 100644 index 0000000000..f0457e3e8a --- /dev/null +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp @@ -0,0 +1,120 @@ +/* + * Copyright 2019 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 <compositionengine/impl/CompositionEngine.h> +#include <ui/GraphicBuffer.h> + +#include "BufferQueueLayer.h" +#include "BufferStateLayer.h" +#include "ColorLayer.h" +#include "ContainerLayer.h" +#include "DisplayDevice.h" +#include "Layer.h" +#include "NativeWindowSurface.h" +#include "StartPropertySetThread.h" +#include "SurfaceFlingerDefaultFactory.h" +#include "SurfaceInterceptor.h" + +#include "DisplayHardware/ComposerHal.h" +#include "Scheduler/DispSync.h" +#include "Scheduler/EventControlThread.h" +#include "Scheduler/MessageQueue.h" +#include "Scheduler/PhaseOffsets.h" +#include "Scheduler/Scheduler.h" + +namespace android::surfaceflinger { + +DefaultFactory::~DefaultFactory() = default; + +std::unique_ptr<DispSync> DefaultFactory::createDispSync(const char* name, bool hasSyncFramework) { + return std::make_unique<android::impl::DispSync>(name, hasSyncFramework); +} + +std::unique_ptr<EventControlThread> DefaultFactory::createEventControlThread( + SetVSyncEnabled setVSyncEnabled) { + return std::make_unique<android::impl::EventControlThread>(std::move(setVSyncEnabled)); +} + +std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& serviceName) { + return std::make_unique<android::impl::HWComposer>( + std::make_unique<Hwc2::impl::Composer>(serviceName)); +} + +std::unique_ptr<MessageQueue> DefaultFactory::createMessageQueue() { + return std::make_unique<android::impl::MessageQueue>(); +} + +std::unique_ptr<scheduler::PhaseOffsets> DefaultFactory::createPhaseOffsets() { + return std::make_unique<scheduler::impl::PhaseOffsets>(); +} + +std::unique_ptr<Scheduler> DefaultFactory::createScheduler( + SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs) { + return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs); +} + +std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor( + SurfaceFlinger* flinger) { + return std::make_unique<android::impl::SurfaceInterceptor>(flinger); +} + +sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread( + bool timestampPropertyValue) { + return new StartPropertySetThread(timestampPropertyValue); +} + +sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) { + return new DisplayDevice(std::move(creationArgs)); +} + +sp<GraphicBuffer> DefaultFactory::createGraphicBuffer(uint32_t width, uint32_t height, + PixelFormat format, uint32_t layerCount, + uint64_t usage, std::string requestorName) { + return new GraphicBuffer(width, height, format, layerCount, usage, requestorName); +} + +void DefaultFactory::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer, + bool consumerIsSurfaceFlinger) { + BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); +} + +std::unique_ptr<surfaceflinger::NativeWindowSurface> DefaultFactory::createNativeWindowSurface( + const sp<IGraphicBufferProducer>& producer) { + return surfaceflinger::impl::createNativeWindowSurface(producer); +} + +std::unique_ptr<compositionengine::CompositionEngine> DefaultFactory::createCompositionEngine() { + return compositionengine::impl::createCompositionEngine(); +} + +sp<ContainerLayer> DefaultFactory::createContainerLayer(const LayerCreationArgs& args) { + return new ContainerLayer(args); +} + +sp<BufferQueueLayer> DefaultFactory::createBufferQueueLayer(const LayerCreationArgs& args) { + return new BufferQueueLayer(args); +} + +sp<BufferStateLayer> DefaultFactory::createBufferStateLayer(const LayerCreationArgs& args) { + return new BufferStateLayer(args); +} + +sp<ColorLayer> DefaultFactory::createColorLayer(const LayerCreationArgs& args) { + return new ColorLayer(args); +} + +} // namespace android::surfaceflinger diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h new file mode 100644 index 0000000000..89e0679c83 --- /dev/null +++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h @@ -0,0 +1,54 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include "SurfaceFlingerFactory.h" + +namespace android::surfaceflinger { + +// A default implementation of the factory which creates the standard +// implementation types for each interface. +class DefaultFactory : public surfaceflinger::Factory { +public: + virtual ~DefaultFactory(); + + std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) override; + std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) override; + std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override; + std::unique_ptr<MessageQueue> createMessageQueue() override; + std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override; + std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled, + const scheduler::RefreshRateConfigs&) override; + std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override; + sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override; + sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) override; + sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, + uint32_t layerCount, uint64_t usage, + std::string requestorName) override; + void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, + sp<IGraphicBufferConsumer>* outConsumer, + bool consumerIsSurfaceFlinger) override; + std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( + const sp<IGraphicBufferProducer>&) override; + std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override; + sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) override; + sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override; + sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) override; + sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override; +}; + +} // namespace android::surfaceflinger diff --git a/services/surfaceflinger/SurfaceFlingerFactory.cpp b/services/surfaceflinger/SurfaceFlingerFactory.cpp index e425b2a953..9b1f658661 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.cpp +++ b/services/surfaceflinger/SurfaceFlingerFactory.cpp @@ -14,127 +14,13 @@ * limitations under the License. */ -#include <compositionengine/impl/CompositionEngine.h> -#include <ui/GraphicBuffer.h> - -#include "BufferQueueLayer.h" -#include "BufferStateLayer.h" -#include "ColorLayer.h" -#include "ContainerLayer.h" -#include "DisplayDevice.h" -#include "Layer.h" -#include "NativeWindowSurface.h" -#include "StartPropertySetThread.h" #include "SurfaceFlinger.h" -#include "SurfaceFlingerFactory.h" -#include "SurfaceInterceptor.h" - -#include "DisplayHardware/ComposerHal.h" -#include "Scheduler/DispSync.h" -#include "Scheduler/EventControlThread.h" -#include "Scheduler/MessageQueue.h" -#include "Scheduler/PhaseOffsets.h" -#include "Scheduler/Scheduler.h" -#include "TimeStats/TimeStats.h" +#include "SurfaceFlingerDefaultFactory.h" namespace android::surfaceflinger { sp<SurfaceFlinger> createSurfaceFlinger() { - class Factory final : public surfaceflinger::Factory { - public: - Factory() = default; - ~Factory() = default; - - std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework, - int64_t dispSyncPresentTimeOffset) override { - // Note: We create a local temporary with the real DispSync implementation - // type temporarily so we can initialize it with the configured values, - // before storing it for more generic use using the interface type. - auto primaryDispSync = std::make_unique<android::impl::DispSync>(name); - primaryDispSync->init(hasSyncFramework, dispSyncPresentTimeOffset); - return primaryDispSync; - } - - std::unique_ptr<EventControlThread> createEventControlThread( - std::function<void(bool)> setVSyncEnabled) override { - return std::make_unique<android::impl::EventControlThread>(setVSyncEnabled); - } - - std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override { - return std::make_unique<android::impl::HWComposer>( - std::make_unique<Hwc2::impl::Composer>(serviceName)); - } - - std::unique_ptr<MessageQueue> createMessageQueue() override { - return std::make_unique<android::impl::MessageQueue>(); - } - - std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override { - return std::make_unique<scheduler::impl::PhaseOffsets>(); - } - - std::unique_ptr<Scheduler> createScheduler( - std::function<void(bool)> callback, - const scheduler::RefreshRateConfigs& refreshRateConfig) override { - return std::make_unique<Scheduler>(callback, refreshRateConfig); - } - - std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor( - SurfaceFlinger* flinger) override { - return std::make_unique<android::impl::SurfaceInterceptor>(flinger); - } - - sp<StartPropertySetThread> createStartPropertySetThread( - bool timestampPropertyValue) override { - return new StartPropertySetThread(timestampPropertyValue); - } - - sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override { - return new DisplayDevice(std::move(creationArgs)); - } - - sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format, - uint32_t layerCount, uint64_t usage, - std::string requestorName) override { - return new GraphicBuffer(width, height, format, layerCount, usage, requestorName); - } - - void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, - sp<IGraphicBufferConsumer>* outConsumer, - bool consumerIsSurfaceFlinger) override { - BufferQueue::createBufferQueue(outProducer, outConsumer, consumerIsSurfaceFlinger); - } - - std::unique_ptr<surfaceflinger::NativeWindowSurface> createNativeWindowSurface( - const sp<IGraphicBufferProducer>& producer) override { - return surfaceflinger::impl::createNativeWindowSurface(producer); - } - - std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override { - return compositionengine::impl::createCompositionEngine(); - } - - sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) override { - return new ContainerLayer(args); - } - - sp<BufferQueueLayer> createBufferQueueLayer(const LayerCreationArgs& args) override { - return new BufferQueueLayer(args); - } - - sp<BufferStateLayer> createBufferStateLayer(const LayerCreationArgs& args) override { - return new BufferStateLayer(args); - } - - sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) override { - return new ColorLayer(args); - } - - std::shared_ptr<TimeStats> createTimeStats() override { - return std::make_shared<android::impl::TimeStats>(); - } - }; - static Factory factory; + static DefaultFactory factory; return new SurfaceFlinger(factory); } diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h index c2bc808c8c..3fd4de8170 100644 --- a/services/surfaceflinger/SurfaceFlingerFactory.h +++ b/services/surfaceflinger/SurfaceFlingerFactory.h @@ -44,7 +44,6 @@ class Scheduler; class StartPropertySetThread; class SurfaceFlinger; class SurfaceInterceptor; -class TimeStats; struct DisplayDeviceCreationArgs; struct LayerCreationArgs; @@ -55,7 +54,9 @@ class CompositionEngine; namespace scheduler { class PhaseOffsets; +class RefreshRateConfigs; } // namespace scheduler + namespace surfaceflinger { class NativeWindowSurface; @@ -64,16 +65,15 @@ class NativeWindowSurface; // of each interface. class Factory { public: - virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework, - int64_t dispSyncPresentTimeOffset) = 0; - virtual std::unique_ptr<EventControlThread> createEventControlThread( - std::function<void(bool)> setVSyncEnabled) = 0; + using SetVSyncEnabled = std::function<void(bool)>; + + virtual std::unique_ptr<DispSync> createDispSync(const char* name, bool hasSyncFramework) = 0; + virtual std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) = 0; virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0; virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0; virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0; - virtual std::unique_ptr<Scheduler> createScheduler( - std::function<void(bool)> callback, - const scheduler::RefreshRateConfigs& refreshRateConfig) = 0; + virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled, + const scheduler::RefreshRateConfigs&) = 0; virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0; virtual sp<StartPropertySetThread> createStartPropertySetThread( @@ -95,8 +95,6 @@ public: virtual sp<ColorLayer> createColorLayer(const LayerCreationArgs& args) = 0; virtual sp<ContainerLayer> createContainerLayer(const LayerCreationArgs& args) = 0; - virtual std::shared_ptr<TimeStats> createTimeStats() = 0; - protected: ~Factory() = default; }; diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index 768074a6cd..b4716eb61e 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -226,6 +226,14 @@ int64_t color_space_agnostic_dataspace(Dataspace defaultValue) { return static_cast<int64_t>(defaultValue); } +bool refresh_rate_switching(bool defaultValue) { + auto temp = SurfaceFlingerProperties::refresh_rate_switching(); + if (temp.has_value()) { + return *temp; + } + return defaultValue; +} + int32_t set_idle_timer_ms(int32_t defaultValue) { auto temp = SurfaceFlingerProperties::set_idle_timer_ms(); if (temp.has_value()) { diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index 5f88322f71..e394ccab7b 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -73,6 +73,8 @@ int32_t wcg_composition_pixel_format( int64_t color_space_agnostic_dataspace( android::hardware::graphics::common::V1_2::Dataspace defaultValue); +bool refresh_rate_switching(bool defaultValue); + int32_t set_idle_timer_ms(int32_t defaultValue); int32_t set_touch_timer_ms(int32_t defaultValue); diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp index 7bfe0338f7..a02d14cc4d 100644 --- a/services/surfaceflinger/SurfaceInterceptor.cpp +++ b/services/surfaceflinger/SurfaceInterceptor.cpp @@ -116,7 +116,14 @@ void SurfaceInterceptor::addInitialSurfaceStateLocked(Increment* increment, layer->mCurrentState.frameNumber_legacy); } addOverrideScalingModeLocked(transaction, layerId, layer->getEffectiveScalingMode()); - addFlagsLocked(transaction, layerId, layer->mCurrentState.flags); + addFlagsLocked(transaction, layerId, layer->mCurrentState.flags, + layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque | + layer_state_t::eLayerSecure); + addReparentLocked(transaction, layerId, getLayerIdFromWeakRef(layer->mCurrentParent)); + addDetachChildrenLocked(transaction, layerId, layer->isLayerDetached()); + addRelativeParentLocked(transaction, layerId, + getLayerIdFromWeakRef(layer->mCurrentState.zOrderRelativeOf), + layer->mCurrentState.z); } void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment, @@ -150,7 +157,7 @@ status_t SurfaceInterceptor::writeProtoFileLocked() { return NO_ERROR; } -const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) { +const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weakHandle) const { const sp<const IBinder>& handle(weakHandle.promote()); const auto layerHandle(static_cast<const Layer::Handle*>(handle.get())); const sp<const Layer> layer(layerHandle->owner.promote()); @@ -158,14 +165,31 @@ const sp<const Layer> SurfaceInterceptor::getLayer(const wp<const IBinder>& weak return layer; } -const std::string SurfaceInterceptor::getLayerName(const sp<const Layer>& layer) { +const std::string SurfaceInterceptor::getLayerName(const sp<const Layer>& layer) const { return layer->getName().string(); } -int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) { +int32_t SurfaceInterceptor::getLayerId(const sp<const Layer>& layer) const { return layer->sequence; } +int32_t SurfaceInterceptor::getLayerIdFromWeakRef(const wp<const Layer>& layer) const { + if (layer == nullptr) { + return -1; + } + auto strongLayer = layer.promote(); + return strongLayer == nullptr ? -1 : getLayerId(strongLayer); +} + +int32_t SurfaceInterceptor::getLayerIdFromHandle(const sp<const IBinder>& handle) const { + if (handle == nullptr) { + return -1; + } + const auto layerHandle(static_cast<const Layer::Handle*>(handle.get())); + const sp<const Layer> layer(layerHandle->owner.promote()); + return layer == nullptr ? -1 : getLayerId(layer); +} + Increment* SurfaceInterceptor::createTraceIncrementLocked() { Increment* increment(mTrace.add_increment()); increment->set_time_stamp(systemTime()); @@ -252,24 +276,23 @@ void SurfaceInterceptor::addTransparentRegionLocked(Transaction* transaction, } } -void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId, - uint8_t flags) -{ +void SurfaceInterceptor::addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, + uint8_t mask) { // There can be multiple flags changed - if (flags & layer_state_t::eLayerHidden) { + if (mask & layer_state_t::eLayerHidden) { SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); HiddenFlagChange* flagChange(change->mutable_hidden_flag()); - flagChange->set_hidden_flag(true); + flagChange->set_hidden_flag(flags & layer_state_t::eLayerHidden); } - if (flags & layer_state_t::eLayerOpaque) { + if (mask & layer_state_t::eLayerOpaque) { SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); OpaqueFlagChange* flagChange(change->mutable_opaque_flag()); - flagChange->set_opaque_flag(true); + flagChange->set_opaque_flag(flags & layer_state_t::eLayerOpaque); } - if (flags & layer_state_t::eLayerSecure) { + if (mask & layer_state_t::eLayerSecure) { SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); SecureFlagChange* flagChange(change->mutable_secure_flag()); - flagChange->set_secure_flag(true); + flagChange->set_secure_flag(flags & layer_state_t::eLayerSecure); } } @@ -320,6 +343,35 @@ void SurfaceInterceptor::addOverrideScalingModeLocked(Transaction* transaction, overrideChange->set_override_scaling_mode(overrideScalingMode); } +void SurfaceInterceptor::addReparentLocked(Transaction* transaction, int32_t layerId, + int32_t parentId) { + SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); + ReparentChange* overrideChange(change->mutable_reparent()); + overrideChange->set_parent_id(parentId); +} + +void SurfaceInterceptor::addReparentChildrenLocked(Transaction* transaction, int32_t layerId, + int32_t parentId) { + SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); + ReparentChildrenChange* overrideChange(change->mutable_reparent_children()); + overrideChange->set_parent_id(parentId); +} + +void SurfaceInterceptor::addDetachChildrenLocked(Transaction* transaction, int32_t layerId, + bool detached) { + SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); + DetachChildrenChange* overrideChange(change->mutable_detach_children()); + overrideChange->set_detach_children(detached); +} + +void SurfaceInterceptor::addRelativeParentLocked(Transaction* transaction, int32_t layerId, + int32_t parentId, int z) { + SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId)); + RelativeParentChange* overrideChange(change->mutable_relative_parent()); + overrideChange->set_relative_parent_id(parentId); + overrideChange->set_z(z); +} + void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, const layer_state_t& state) { @@ -351,7 +403,7 @@ void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, addTransparentRegionLocked(transaction, layerId, state.transparentRegion); } if (state.what & layer_state_t::eFlagsChanged) { - addFlagsLocked(transaction, layerId, state.flags); + addFlagsLocked(transaction, layerId, state.flags, state.mask); } if (state.what & layer_state_t::eLayerStackChanged) { addLayerStackLocked(transaction, layerId, state.layerStack); @@ -380,6 +432,19 @@ void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction, if (state.what & layer_state_t::eOverrideScalingModeChanged) { addOverrideScalingModeLocked(transaction, layerId, state.overrideScalingMode); } + if (state.what & layer_state_t::eReparent) { + addReparentLocked(transaction, layerId, getLayerIdFromHandle(state.parentHandleForChild)); + } + if (state.what & layer_state_t::eReparentChildren) { + addReparentChildrenLocked(transaction, layerId, getLayerIdFromHandle(state.reparentHandle)); + } + if (state.what & layer_state_t::eDetachChildren) { + addDetachChildrenLocked(transaction, layerId, true); + } + if (state.what & layer_state_t::eRelativeLayerChanged) { + addRelativeParentLocked(transaction, layerId, + getLayerIdFromHandle(state.relativeLayerHandle), state.z); + } } void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction, diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h index 563a44c088..6858c4d5a6 100644 --- a/services/surfaceflinger/SurfaceInterceptor.h +++ b/services/surfaceflinger/SurfaceInterceptor.h @@ -39,8 +39,14 @@ struct ComposerState; struct DisplayDeviceState; struct DisplayState; struct layer_state_t; +using Transaction = surfaceflinger::Transaction; +using Trace = surfaceflinger::Trace; +using Rectangle = surfaceflinger::Rectangle; +using SurfaceChange = surfaceflinger::SurfaceChange; +using Increment = surfaceflinger::Increment; +using DisplayChange = surfaceflinger::DisplayChange; -constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat"; +constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb"; class SurfaceInterceptor { public: @@ -116,9 +122,11 @@ private: void addInitialDisplayStateLocked(Increment* increment, const DisplayDeviceState& display); status_t writeProtoFileLocked(); - const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle); - const std::string getLayerName(const sp<const Layer>& layer); - int32_t getLayerId(const sp<const Layer>& layer); + const sp<const Layer> getLayer(const wp<const IBinder>& weakHandle) const; + const std::string getLayerName(const sp<const Layer>& layer) const; + int32_t getLayerId(const sp<const Layer>& layer) const; + int32_t getLayerIdFromWeakRef(const wp<const Layer>& layer) const; + int32_t getLayerIdFromHandle(const sp<const IBinder>& weakHandle) const; Increment* createTraceIncrementLocked(); void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer); @@ -141,7 +149,7 @@ private: const layer_state_t::matrix22_t& matrix); void addTransparentRegionLocked(Transaction* transaction, int32_t layerId, const Region& transRegion); - void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags); + void addFlagsLocked(Transaction* transaction, int32_t layerId, uint8_t flags, uint8_t mask); void addLayerStackLocked(Transaction* transaction, int32_t layerId, uint32_t layerStack); void addCropLocked(Transaction* transaction, int32_t layerId, const Rect& rect); void addCornerRadiusLocked(Transaction* transaction, int32_t layerId, float cornerRadius); @@ -153,6 +161,11 @@ private: void addTransactionLocked(Increment* increment, const Vector<ComposerState>& stateUpdates, const DefaultKeyedVector< wp<IBinder>, DisplayDeviceState>& displays, const Vector<DisplayState>& changedDisplays, uint32_t transactionFlags); + void addReparentLocked(Transaction* transaction, int32_t layerId, int32_t parentId); + void addReparentChildrenLocked(Transaction* transaction, int32_t layerId, int32_t parentId); + void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached); + void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId, + int z); // Add display transactions to the trace DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId); @@ -176,6 +189,7 @@ private: }; } // namespace impl + } // namespace android #endif // ANDROID_SURFACEINTERCEPTOR_H diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp index 9053f2c7de..eb26cd0379 100644 --- a/services/surfaceflinger/SurfaceTracing.cpp +++ b/services/surfaceflinger/SurfaceTracing.cpp @@ -163,6 +163,7 @@ LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) { entry.set_elapsed_realtime_nanos(elapsedRealtimeNano()); entry.set_where(where); LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags)); + mFlinger.dumpOffscreenLayersProto(layers); entry.mutable_layers()->Swap(&layers); return entry; diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h index 4773307a65..18524f02d8 100644 --- a/services/surfaceflinger/SurfaceTracing.h +++ b/services/surfaceflinger/SurfaceTracing.h @@ -61,7 +61,7 @@ public: void setTraceFlags(uint32_t flags); private: - static constexpr auto kDefaultBufferCapInByte = 100_MB; + static constexpr auto kDefaultBufferCapInByte = 5_MB; static constexpr auto kDefaultFileName = "/data/misc/wmtrace/layers_trace.pb"; class LayersTraceBuffer { // ring buffer diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp new file mode 100644 index 0000000000..2080a3847e --- /dev/null +++ b/services/surfaceflinger/TimeStats/Android.bp @@ -0,0 +1,12 @@ +cc_library_static { + name: "libtimestats", + defaults: ["surfaceflinger_defaults"], + srcs: [ + "TimeStats.cpp", + ], + export_include_dirs: ["."], + shared_libs: [ + "libtimestats_proto", + "libui", + ], +} diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp index c97a19b39b..3e47ec6856 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.cpp +++ b/services/surfaceflinger/TimeStats/TimeStats.cpp @@ -28,12 +28,19 @@ #include <utils/Trace.h> #include <algorithm> -#include <regex> namespace android { namespace impl { +TimeStats::TimeStats() { + // Temporarily enable TimeStats by default. Telemetry is disabled while + // we move onto statsd, so TimeStats is currently not exercised at all + // during testing. + // TODO: remove this. + enable(); +} + void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) { ATRACE_CALL(); @@ -72,8 +79,10 @@ std::string TimeStats::miniDump() { std::string result = "TimeStats miniDump:\n"; std::lock_guard<std::mutex> lock(mMutex); - android::base::StringAppendF(&result, "Number of tracked layers is %zu\n", + android::base::StringAppendF(&result, "Number of layers currently being tracked is %zu\n", mTimeStatsTracker.size()); + android::base::StringAppendF(&result, "Number of layers in the stats pool is %zu\n", + mTimeStats.stats.size()); return result; } @@ -146,22 +155,6 @@ static int32_t msBetween(nsecs_t start, nsecs_t end) { return static_cast<int32_t>(delta); } -// This regular expression captures the following for instance: -// StatusBar in StatusBar#0 -// com.appname in com.appname/com.appname.activity#0 -// com.appname in SurfaceView - com.appname/com.appname.activity#0 -static const std::regex packageNameRegex("(?:SurfaceView[-\\s\\t]+)?([^/]+).*#\\d+"); - -static std::string getPackageName(const std::string& layerName) { - std::smatch match; - if (std::regex_match(layerName.begin(), layerName.end(), match, packageNameRegex)) { - // There must be a match for group 1 otherwise the whole string is not - // matched and the above will return false - return match[1]; - } - return ""; -} - void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) { ATRACE_CALL(); @@ -173,11 +166,10 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) { ALOGV("[%d]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerID, timeRecords[0].frameTime.frameNumber, timeRecords[0].frameTime.presentTime); - const std::string& layerName = layerRecord.layerName; if (prevTimeRecord.ready) { + const std::string& layerName = layerRecord.layerName; if (!mTimeStats.stats.count(layerName)) { mTimeStats.stats[layerName].layerName = layerName; - mTimeStats.stats[layerName].packageName = getPackageName(layerName); } TimeStatsHelper::TimeStatsLayer& timeStatsLayer = mTimeStats.stats[layerName]; timeStatsLayer.totalFrames++; @@ -220,37 +212,19 @@ void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerID) { timeRecords[0].frameTime.frameNumber, presentToPresentMs); timeStatsLayer.deltas["present2present"].insert(presentToPresentMs); } - - // Output additional trace points to track frame time. - ATRACE_INT64(("TimeStats-Post - " + layerName).c_str(), timeRecords[0].frameTime.postTime); - ATRACE_INT64(("TimeStats-Acquire - " + layerName).c_str(), - timeRecords[0].frameTime.acquireTime); - ATRACE_INT64(("TimeStats-Latch - " + layerName).c_str(), - timeRecords[0].frameTime.latchTime); - ATRACE_INT64(("TimeStats-Desired - " + layerName).c_str(), - timeRecords[0].frameTime.desiredTime); - ATRACE_INT64(("TimeStats-Present - " + layerName).c_str(), - timeRecords[0].frameTime.presentTime); - prevTimeRecord = timeRecords[0]; timeRecords.pop_front(); layerRecord.waitData--; } } -// This regular expression captures the following layer names for instance: -// 1) StatusBat#0 -// 2) NavigationBar#1 -// 3) co(m).*#0 -// 4) SurfaceView - co(m).*#0 -// Using [-\\s\t]+ for the conjunction part between SurfaceView and co(m).* -// is a bit more robust in case there's a slight change. -// The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases. -static const std::regex layerNameRegex( - "(((SurfaceView[-\\s\\t]+)?com?\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+"); +static constexpr const char* kPopupWindowPrefix = "PopupWindow"; +static const size_t kMinLenLayerName = std::strlen(kPopupWindowPrefix); +// Avoid tracking the "PopupWindow:<random hash>#<number>" layers static bool layerNameIsValid(const std::string& layerName) { - return std::regex_match(layerName.begin(), layerName.end(), layerNameRegex); + return layerName.length() >= kMinLenLayerName && + layerName.compare(0, kMinLenLayerName, kPopupWindowPrefix) != 0; } void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, const std::string& layerName, @@ -262,6 +236,9 @@ void TimeStats::setPostTime(int32_t layerID, uint64_t frameNumber, const std::st postTime); std::lock_guard<std::mutex> lock(mMutex); + if (!mTimeStats.stats.count(layerName) && mTimeStats.stats.size() >= MAX_NUM_LAYER_STATS) { + return; + } if (!mTimeStatsTracker.count(layerID) && mTimeStatsTracker.size() < MAX_NUM_LAYER_RECORDS && layerNameIsValid(layerName)) { mTimeStatsTracker[layerID].layerName = layerName; @@ -414,13 +391,9 @@ void TimeStats::setPresentFence(int32_t layerID, uint64_t frameNumber, } void TimeStats::onDestroy(int32_t layerID) { - if (!mEnabled.load()) return; - ATRACE_CALL(); ALOGV("[%d]-onDestroy", layerID); - std::lock_guard<std::mutex> lock(mMutex); - if (!mTimeStatsTracker.count(layerID)) return; mTimeStatsTracker.erase(layerID); } @@ -613,7 +586,7 @@ void TimeStats::dump(bool asProto, std::optional<uint32_t> maxLayers, std::strin if (asProto) { ALOGD("Dumping TimeStats as proto"); SFTimeStatsGlobalProto timeStatsProto = mTimeStats.toProto(maxLayers); - result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize()); + result.append(timeStatsProto.SerializeAsString()); } else { ALOGD("Dumping TimeStats as text"); result.append(mTimeStats.toString(maxLayers)); diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h index 2bcb5682b0..1313132920 100644 --- a/services/surfaceflinger/TimeStats/TimeStats.h +++ b/services/surfaceflinger/TimeStats/TimeStats.h @@ -16,13 +16,10 @@ #pragma once +#include <hardware/hwcomposer_defs.h> #include <timestatsproto/TimeStatsHelper.h> #include <timestatsproto/TimeStatsProtoHeader.h> - -#include <hardware/hwcomposer_defs.h> - #include <ui/FenceTime.h> - #include <utils/String16.h> #include <utils/Vector.h> @@ -109,7 +106,7 @@ class TimeStats : public android::TimeStats { }; public: - TimeStats() = default; + TimeStats(); void parseArgs(bool asProto, const Vector<String16>& args, std::string& result) override; bool isEnabled() override; @@ -161,6 +158,7 @@ private: GlobalRecord mGlobalRecord; static const size_t MAX_NUM_LAYER_RECORDS = 200; + static const size_t MAX_NUM_LAYER_STATS = 200; }; } // namespace impl diff --git a/services/surfaceflinger/TracedOrdinal.h b/services/surfaceflinger/TracedOrdinal.h new file mode 100644 index 0000000000..c145a396af --- /dev/null +++ b/services/surfaceflinger/TracedOrdinal.h @@ -0,0 +1,64 @@ +/* + * Copyright 2019 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. + */ + +#pragma once +#include <android-base/stringprintf.h> +#include <utils/Trace.h> +#include <cmath> +#include <string> + +template <typename T> +class TracedOrdinal { +public: + static_assert(std::is_same<bool, T>() || (std::is_signed<T>() && std::is_integral<T>()), + "Type is not supported. Please test it with systrace before adding " + "it to the list."); + + TracedOrdinal(const std::string& name, T initialValue) + : mName(name), + mNameNegative(android::base::StringPrintf("%sNegative", name.c_str())), + mHasGoneNegative(std::signbit(initialValue)), + mData(initialValue) { + trace(); + } + + operator T() const { return mData; } + + TracedOrdinal& operator=(T other) { + mData = other; + mHasGoneNegative = mHasGoneNegative || std::signbit(mData); + trace(); + return *this; + } + +private: + void trace() { + if (!std::signbit(mData)) { + ATRACE_INT64(mName.c_str(), int64_t(mData)); + if (mHasGoneNegative) { + ATRACE_INT64(mNameNegative.c_str(), 0); + } + } else { + ATRACE_INT64(mNameNegative.c_str(), -int64_t(mData)); + ATRACE_INT64(mName.c_str(), 0); + } + } + + const std::string mName; + const std::string mNameNegative; + bool mHasGoneNegative; + T mData; +}; diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp index fd466dedff..92e59d7745 100644 --- a/services/surfaceflinger/TransactionCompletedThread.cpp +++ b/services/surfaceflinger/TransactionCompletedThread.cpp @@ -24,7 +24,6 @@ #include <cinttypes> #include <binder/IInterface.h> -#include <gui/ITransactionCompletedListener.h> #include <utils/RefBase.h> namespace android { @@ -58,7 +57,7 @@ TransactionCompletedThread::~TransactionCompletedThread() { { std::lock_guard lock(mMutex); for (const auto& [listener, transactionStats] : mCompletedTransactions) { - IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient); + listener->unlinkToDeath(mDeathRecipient); } } } @@ -75,27 +74,59 @@ void TransactionCompletedThread::run() { mThread = std::thread(&TransactionCompletedThread::threadMain, this); } -status_t TransactionCompletedThread::addCallback(const sp<ITransactionCompletedListener>& listener, - const std::vector<CallbackId>& callbackIds) { +status_t TransactionCompletedThread::startRegistration(const ListenerCallbacks& listenerCallbacks) { + // begin running if not already running + run(); std::lock_guard lock(mMutex); if (!mRunning) { ALOGE("cannot add callback because the callback thread isn't running"); return BAD_VALUE; } - if (mCompletedTransactions.count(listener) == 0) { - status_t err = IInterface::asBinder(listener)->linkToDeath(mDeathRecipient); - if (err != NO_ERROR) { - ALOGE("cannot add callback because linkToDeath failed, err: %d", err); - return err; + auto [itr, inserted] = mRegisteringTransactions.insert(listenerCallbacks); + auto& [listener, callbackIds] = listenerCallbacks; + + if (inserted) { + if (mCompletedTransactions.count(listener) == 0) { + status_t err = listener->linkToDeath(mDeathRecipient); + if (err != NO_ERROR) { + ALOGE("cannot add callback because linkToDeath failed, err: %d", err); + return err; + } } + auto& transactionStatsDeque = mCompletedTransactions[listener]; + transactionStatsDeque.emplace_back(callbackIds); } - auto& transactionStatsDeque = mCompletedTransactions[listener]; - transactionStatsDeque.emplace_back(callbackIds); return NO_ERROR; } +status_t TransactionCompletedThread::endRegistration(const ListenerCallbacks& listenerCallbacks) { + std::lock_guard lock(mMutex); + if (!mRunning) { + ALOGE("cannot add callback because the callback thread isn't running"); + return BAD_VALUE; + } + + auto itr = mRegisteringTransactions.find(listenerCallbacks); + if (itr == mRegisteringTransactions.end()) { + ALOGE("cannot end a registration that does not exist"); + return BAD_VALUE; + } + + mRegisteringTransactions.erase(itr); + + return NO_ERROR; +} + +bool TransactionCompletedThread::isRegisteringTransaction( + const sp<IBinder>& transactionListener, const std::vector<CallbackId>& callbackIds) { + ListenerCallbacks listenerCallbacks(transactionListener, callbackIds); + + auto itr = mRegisteringTransactions.find(listenerCallbacks); + return itr != mRegisteringTransactions.end(); +} + status_t TransactionCompletedThread::registerPendingCallbackHandle( const sp<CallbackHandle>& handle) { std::lock_guard lock(mMutex); @@ -105,7 +136,7 @@ status_t TransactionCompletedThread::registerPendingCallbackHandle( } // If we can't find the transaction stats something has gone wrong. The client should call - // addCallback before trying to register a pending callback handle. + // startRegistration before trying to register a pending callback handle. TransactionStats* transactionStats; status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats); if (err != NO_ERROR) { @@ -117,7 +148,7 @@ status_t TransactionCompletedThread::registerPendingCallbackHandle( return NO_ERROR; } -status_t TransactionCompletedThread::addPresentedCallbackHandles( +status_t TransactionCompletedThread::finalizePendingCallbackHandles( const std::deque<sp<CallbackHandle>>& handles) { std::lock_guard lock(mMutex); if (!mRunning) { @@ -158,7 +189,7 @@ status_t TransactionCompletedThread::addPresentedCallbackHandles( return NO_ERROR; } -status_t TransactionCompletedThread::addUnpresentedCallbackHandle( +status_t TransactionCompletedThread::registerUnpresentedCallbackHandle( const sp<CallbackHandle>& handle) { std::lock_guard lock(mMutex); if (!mRunning) { @@ -170,8 +201,8 @@ status_t TransactionCompletedThread::addUnpresentedCallbackHandle( } status_t TransactionCompletedThread::findTransactionStats( - const sp<ITransactionCompletedListener>& listener, - const std::vector<CallbackId>& callbackIds, TransactionStats** outTransactionStats) { + const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds, + TransactionStats** outTransactionStats) { auto& transactionStatsDeque = mCompletedTransactions[listener]; // Search back to front because the most recent transactions are at the back of the deque @@ -189,7 +220,7 @@ status_t TransactionCompletedThread::findTransactionStats( status_t TransactionCompletedThread::addCallbackHandle(const sp<CallbackHandle>& handle) { // If we can't find the transaction stats something has gone wrong. The client should call - // addCallback before trying to add a presnted callback handle. + // startRegistration before trying to add a callback handle. TransactionStats* transactionStats; status_t err = findTransactionStats(handle->listener, handle->callbackIds, &transactionStats); if (err != NO_ERROR) { @@ -239,6 +270,13 @@ void TransactionCompletedThread::threadMain() { while (transactionStatsItr != transactionStatsDeque.end()) { auto& transactionStats = *transactionStatsItr; + // If this transaction is still registering, it is not safe to send a callback + // because there could be surface controls that haven't been added to + // transaction stats or mPendingTransactions. + if (isRegisteringTransaction(listener, transactionStats.callbackIds)) { + break; + } + // If we are still waiting on the callback handles for this transaction, stop // here because all transaction callbacks for the same listener must come in order auto pendingTransactions = mPendingTransactions.find(listener); @@ -262,10 +300,16 @@ void TransactionCompletedThread::threadMain() { // If the listener has completed transactions if (!listenerStats.transactionStats.empty()) { // If the listener is still alive - if (IInterface::asBinder(listener)->isBinderAlive()) { - // Send callback - listenerStats.listener->onTransactionCompleted(listenerStats); - IInterface::asBinder(listener)->unlinkToDeath(mDeathRecipient); + if (listener->isBinderAlive()) { + // Send callback. The listener stored in listenerStats + // comes from the cross-process setTransactionState call to + // SF. This MUST be an ITransactionCompletedListener. We + // keep it as an IBinder due to consistency reasons: if we + // interface_cast at the IPC boundary when reading a Parcel, + // we get pointers that compare unequal in the SF process. + interface_cast<ITransactionCompletedListener>(listenerStats.listener) + ->onTransactionCompleted(listenerStats); + listener->unlinkToDeath(mDeathRecipient); } completedTransactionsItr = mCompletedTransactions.erase(completedTransactionsItr); } else { @@ -297,7 +341,7 @@ void TransactionCompletedThread::threadMain() { // ----------------------------------------------------------------------- -CallbackHandle::CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener, +CallbackHandle::CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids, const sp<IBinder>& sc) : listener(transactionListener), callbackIds(ids), surfaceControl(sc) {} diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h index e849f714d0..a85ad1e63c 100644 --- a/services/surfaceflinger/TransactionCompletedThread.h +++ b/services/surfaceflinger/TransactionCompletedThread.h @@ -21,6 +21,7 @@ #include <mutex> #include <thread> #include <unordered_map> +#include <unordered_set> #include <android-base/thread_annotations.h> @@ -30,24 +31,12 @@ namespace android { -struct CallbackIdsHash { - // CallbackId vectors have several properties that let us get away with this simple hash. - // 1) CallbackIds are never 0 so if something has gone wrong and our CallbackId vector is - // empty we can still hash 0. - // 2) CallbackId vectors for the same listener either are identical or contain none of the - // same members. It is sufficient to just check the first CallbackId in the vectors. If - // they match, they are the same. If they do not match, they are not the same. - std::size_t operator()(const std::vector<CallbackId>& callbackIds) const { - return std::hash<CallbackId>{}((callbackIds.empty()) ? 0 : callbackIds.front()); - } -}; - class CallbackHandle : public RefBase { public: - CallbackHandle(const sp<ITransactionCompletedListener>& transactionListener, - const std::vector<CallbackId>& ids, const sp<IBinder>& sc); + CallbackHandle(const sp<IBinder>& transactionListener, const std::vector<CallbackId>& ids, + const sp<IBinder>& sc); - sp<ITransactionCompletedListener> listener; + sp<IBinder> listener; std::vector<CallbackId> callbackIds; wp<IBinder> surfaceControl; @@ -64,10 +53,12 @@ public: void run(); // Adds listener and callbackIds in case there are no SurfaceControls that are supposed - // to be included in the callback. This functions should be call before attempting to add any - // callback handles. - status_t addCallback(const sp<ITransactionCompletedListener>& transactionListener, - const std::vector<CallbackId>& callbackIds); + // to be included in the callback. This functions should be call before attempting to register + // any callback handles. + status_t startRegistration(const ListenerCallbacks& listenerCallbacks); + // Ends the registration. After this is called, no more CallbackHandles will be registered. + // It is safe to send a callback if the Transaction doesn't have any Pending callback handles. + status_t endRegistration(const ListenerCallbacks& listenerCallbacks); // Informs the TransactionCompletedThread that there is a Transaction with a CallbackHandle // that needs to be latched and presented this frame. This function should be called once the @@ -76,11 +67,11 @@ public: // presented. status_t registerPendingCallbackHandle(const sp<CallbackHandle>& handle); // Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented. - status_t addPresentedCallbackHandles(const std::deque<sp<CallbackHandle>>& handles); + status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles); // Adds the Transaction CallbackHandle from a layer that does not need to be relatched and // presented this frame. - status_t addUnpresentedCallbackHandle(const sp<CallbackHandle>& handle); + status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle); void addPresentFence(const sp<Fence>& presentFence); @@ -89,7 +80,10 @@ public: private: void threadMain(); - status_t findTransactionStats(const sp<ITransactionCompletedListener>& listener, + bool isRegisteringTransaction(const sp<IBinder>& transactionListener, + const std::vector<CallbackId>& callbackIds) REQUIRES(mMutex); + + status_t findTransactionStats(const sp<IBinder>& listener, const std::vector<CallbackId>& callbackIds, TransactionStats** outTransactionStats) REQUIRES(mMutex); @@ -106,13 +100,6 @@ private: }; sp<ThreadDeathRecipient> mDeathRecipient; - struct ITransactionCompletedListenerHash { - std::size_t operator()(const sp<ITransactionCompletedListener>& listener) const { - return std::hash<IBinder*>{}((listener) ? IInterface::asBinder(listener).get() - : nullptr); - } - }; - // Protects the creation and destruction of mThread std::mutex mThreadMutex; @@ -121,13 +108,16 @@ private: std::mutex mMutex; std::condition_variable_any mConditionVariable; + std::unordered_set<ListenerCallbacks, ListenerCallbacksHash> mRegisteringTransactions + GUARDED_BY(mMutex); + std::unordered_map< - sp<ITransactionCompletedListener>, + sp<IBinder>, std::unordered_map<std::vector<CallbackId>, uint32_t /*count*/, CallbackIdsHash>, - ITransactionCompletedListenerHash> + IListenerHash> mPendingTransactions GUARDED_BY(mMutex); - std::unordered_map<sp<ITransactionCompletedListener>, std::deque<TransactionStats>, - ITransactionCompletedListenerHash> + + std::unordered_map<sp<IBinder>, std::deque<TransactionStats>, IListenerHash> mCompletedTransactions GUARDED_BY(mMutex); bool mRunning GUARDED_BY(mMutex) = false; diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp index d3381e5757..ef488bd881 100644 --- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp +++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp @@ -37,16 +37,6 @@ bool sortLayers(LayerProtoParser::Layer* lhs, const LayerProtoParser::Layer* rhs return lhs->id < rhs->id; } -const LayerProtoParser::LayerGlobal LayerProtoParser::generateLayerGlobalInfo( - const LayersProto& layersProto) { - LayerGlobal layerGlobal; - layerGlobal.resolution = {layersProto.resolution().w(), layersProto.resolution().h()}; - layerGlobal.colorMode = layersProto.color_mode(); - layerGlobal.colorTransform = layersProto.color_transform(); - layerGlobal.globalTransform = layersProto.global_transform(); - return layerGlobal; -} - LayerProtoParser::LayerTree LayerProtoParser::generateLayerTree(const LayersProto& layersProto) { LayerTree layerTree; layerTree.allLayers = generateLayerList(layersProto); @@ -114,10 +104,6 @@ LayerProtoParser::Layer LayerProtoParser::generateLayer(const LayerProto& layerP layer.bufferTransform = generateTransform(layerProto.buffer_transform()); layer.queuedFrames = layerProto.queued_frames(); layer.refreshPending = layerProto.refresh_pending(); - layer.hwcFrame = generateRect(layerProto.hwc_frame()); - layer.hwcCrop = generateFloatRect(layerProto.hwc_crop()); - layer.hwcTransform = layerProto.hwc_transform(); - layer.hwcCompositionType = layerProto.hwc_composition_type(); layer.isProtected = layerProto.is_protected(); layer.cornerRadius = layerProto.corner_radius(); for (const auto& entry : layerProto.metadata()) { diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h index d1b2b1ffed..54e02cadee 100644 --- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h +++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h @@ -108,10 +108,6 @@ public: Transform bufferTransform; int32_t queuedFrames; bool refreshPending; - LayerProtoParser::Rect hwcFrame; - LayerProtoParser::FloatRect hwcCrop; - int32_t hwcTransform; - int32_t hwcCompositionType; bool isProtected; float cornerRadius; LayerMetadata metadata; @@ -119,14 +115,6 @@ public: std::string to_string() const; }; - class LayerGlobal { - public: - int2 resolution; - std::string colorMode; - std::string colorTransform; - int32_t globalTransform; - }; - class LayerTree { public: // all layers in LayersProto and in the original order @@ -136,7 +124,6 @@ public: std::vector<Layer*> topLevelLayers; }; - static const LayerGlobal generateLayerGlobalInfo(const LayersProto& layersProto); static LayerTree generateLayerTree(const LayersProto& layersProto); static std::string layerTreeToString(const LayerTree& layerTree); diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto index b097505bbc..c7fbff37da 100644 --- a/services/surfaceflinger/layerproto/layers.proto +++ b/services/surfaceflinger/layerproto/layers.proto @@ -7,10 +7,6 @@ package android.surfaceflinger; // Contains a list of all layers. message LayersProto { repeated LayerProto layers = 1; - SizeProto resolution = 2; - string color_mode = 3; - string color_transform = 4; - int32 global_transform = 5; } // Information about each layer. diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index 56ab4e3f33..51b20cb7e5 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -15,7 +15,7 @@ module: "android.sysprop.SurfaceFlingerProperties" owner: Platform -# The following two propertiess define (respectively): +# The following two properties define (respectively): # # - The phase offset between hardware vsync and when apps are woken up by the # Choreographer callback @@ -301,9 +301,18 @@ prop { prop_name: "ro.surface_flinger.display_primary_white" } -# setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is -# used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower -# refresh rate. Setting this property to 0 means there is no timer. +# refreshRateSwitching indicates whether SurfaceFlinger should use refresh rate +# switching on the device, e.g. to switch between 60 and 90 Hz. The settings +# below that are related to refresh rate switching will only have an effect if +# refresh_rate_switching is enabled. +prop { + api_name: "refresh_rate_switching" + type: Boolean + scope: System + access: Readonly + prop_name: "ro.surface_flinger.refresh_rate_switching" +} + prop { api_name: "set_idle_timer_ms" type: Integer diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index b66e56ecc7..2d525073b9 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -73,6 +73,10 @@ props { enum_values: "ORIENTATION_0|ORIENTATION_90|ORIENTATION_180|ORIENTATION_270" } prop { + api_name: "refresh_rate_switching" + prop_name: "ro.surface_flinger.refresh_rate_switching" + } + prop { api_name: "running_without_sync_framework" prop_name: "ro.surface_flinger.running_without_sync_framework" } diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp index 53a3611472..d021fc2859 100644 --- a/services/surfaceflinger/tests/Android.bp +++ b/services/surfaceflinger/tests/Android.bp @@ -19,10 +19,21 @@ cc_test { srcs: [ "BufferGenerator.cpp", "Credentials_test.cpp", + "DereferenceSurfaceControl_test.cpp", + "DisplayActiveConfig_test.cpp", "InvalidHandles_test.cpp", + "LayerCallback_test.cpp", + "LayerRenderTypeTransaction_test.cpp", + "LayerTransaction_test.cpp", + "LayerTypeAndRenderTypeTransaction_test.cpp", + "LayerTypeTransaction_test.cpp", + "LayerUpdate_test.cpp", + "MirrorLayer_test.cpp", + "MultiDisplayLayerBounds_test.cpp", + "RelativeZ_test.cpp", + "SetGeometry_test.cpp", "Stress_test.cpp", "SurfaceInterceptor_test.cpp", - "Transaction_test.cpp", "VirtualDisplay_test.cpp", ], data: ["SurfaceFlinger_test.filter"], @@ -43,13 +54,55 @@ cc_test { "libui", "libutils", ] +} + +cc_defaults { + name: "ipc_defaults", + cflags: [ + "-Wall", + "-Werror", + ], +} +cc_test { + name: "IPC_test", + defaults: ["ipc_defaults"], + test_suites: ["device-tests"], + srcs: [ + "BufferGenerator.cpp", + "IPC_test.cpp", + ], + cppflags: [ + "-Wall", + "-Werror", + "-Wformat", + "-Wthread-safety", + "-Wunused", + "-Wunreachable-code", + ], + shared_libs: [ + "libandroid", + "libbinder", + "libcutils", + "libEGL", + "libGLESv2", + "libgui", + "liblayers_proto", + "liblog", + "libprotobuf-cpp-full", + "libtimestats_proto", + "libui", + "libutils", + ], + cpp_std: "experimental", + gnu_extensions: false, } subdirs = [ "fakehwc", "hwc2", "unittests", + "utils", "vsync", "waitforvsync", ] diff --git a/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp new file mode 100644 index 0000000000..0cef0d1c87 --- /dev/null +++ b/services/surfaceflinger/tests/DereferenceSurfaceControl_test.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2019 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 "LayerTransactionTest.h" +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +::testing::Environment* const binderEnv = + ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); + +class DereferenceSurfaceControlTest : public LayerTransactionTest { +protected: + void SetUp() override { + LayerTransactionTest::SetUp(); + bgLayer = createLayer("BG layer", 20, 20); + fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20); + fgLayer = createLayer("FG layer", 20, 20); + fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20); + Transaction().setLayer(fgLayer, mLayerZBase + 1).apply(); + { + SCOPED_TRACE("before anything"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE); + } + } + void TearDown() override { + LayerTransactionTest::TearDown(); + bgLayer = 0; + fgLayer = 0; + } + + sp<SurfaceControl> bgLayer; + sp<SurfaceControl> fgLayer; +}; + +TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) { + fgLayer = nullptr; + { + SCOPED_TRACE("after setting null"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 20, 20), Color::RED); + } +} + +TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) { + auto transaction = Transaction().show(fgLayer); + fgLayer = nullptr; + { + SCOPED_TRACE("after setting null"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE); + } +} + +} // namespace android diff --git a/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp b/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp new file mode 100644 index 0000000000..2e3b760709 --- /dev/null +++ b/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 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 <thread> +#include "LayerTransactionTest.h" +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +::testing::Environment* const binderEnv = + ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); + +class DisplayActiveConfigTest : public ::testing::Test { +protected: + void SetUp() override { + mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); + SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &mDisplayconfigs); + EXPECT_GT(mDisplayconfigs.size(), 0); + + // set display power to on to make sure config can be changed + SurfaceComposerClient::setDisplayPowerMode(mDisplayToken, HWC_POWER_MODE_NORMAL); + } + + sp<IBinder> mDisplayToken; + Vector<DisplayInfo> mDisplayconfigs; +}; + +TEST_F(DisplayActiveConfigTest, allConfigsAllowed) { + std::vector<int32_t> allowedConfigs; + + // Add all configs to the allowed configs + for (int i = 0; i < mDisplayconfigs.size(); i++) { + allowedConfigs.push_back(i); + } + + status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs); + EXPECT_EQ(res, NO_ERROR); + + std::vector<int32_t> outConfigs; + res = SurfaceComposerClient::getAllowedDisplayConfigs(mDisplayToken, &outConfigs); + EXPECT_EQ(res, NO_ERROR); + EXPECT_EQ(allowedConfigs, outConfigs); +} + +TEST_F(DisplayActiveConfigTest, changeAllowedConfig) { + // we need at least 2 configs available for this test + if (mDisplayconfigs.size() <= 1) return; + + int activeConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken); + + // We want to set the allowed config to everything but the active config + std::vector<int32_t> allowedConfigs; + for (int i = 0; i < mDisplayconfigs.size(); i++) { + if (i != activeConfig) { + allowedConfigs.push_back(i); + } + } + + status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs); + EXPECT_EQ(res, NO_ERROR); + + // Allow some time for the config change + std::this_thread::sleep_for(200ms); + + int newActiveConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken); + EXPECT_NE(activeConfig, newActiveConfig); + + // Make sure the new config is part of allowed config + EXPECT_TRUE(std::find(allowedConfigs.begin(), allowedConfigs.end(), newActiveConfig) != + allowedConfigs.end()); +} +} // namespace android diff --git a/services/surfaceflinger/tests/IPC_test.cpp b/services/surfaceflinger/tests/IPC_test.cpp new file mode 100644 index 0000000000..8a756a668c --- /dev/null +++ b/services/surfaceflinger/tests/IPC_test.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <binder/IInterface.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> + +#include <gui/ISurfaceComposer.h> +#include <gui/LayerState.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> + +#include <limits> + +#include <ui/DisplayInfo.h> + +#include <utils/String8.h> + +#include "BufferGenerator.h" +#include "utils/CallbackUtils.h" +#include "utils/ColorUtils.h" +#include "utils/TransactionUtils.h" + +namespace android { + +namespace test { + +using Transaction = SurfaceComposerClient::Transaction; +using CallbackInfo = SurfaceComposerClient::CallbackInfo; +using TCLHash = SurfaceComposerClient::TCLHash; +using android::hardware::graphics::common::V1_1::BufferUsage; + +class TransactionHelper : public Transaction { +public: + size_t getNumListeners() { return mListenerCallbacks.size(); } + + std::unordered_map<sp<ITransactionCompletedListener>, CallbackInfo, TCLHash> + getListenerCallbacks() { + return mListenerCallbacks; + } +}; + +class IPCTestUtils { +public: + static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult, + bool finalState = false); + static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence); +}; + +class IIPCTest : public IInterface { +public: + DECLARE_META_INTERFACE(IPCTest) + enum class Tag : uint32_t { + SetDeathToken = IBinder::FIRST_CALL_TRANSACTION, + InitClient, + CreateTransaction, + MergeAndApply, + VerifyCallbacks, + CleanUp, + Last, + }; + + virtual status_t setDeathToken(sp<IBinder>& token) = 0; + + virtual status_t initClient() = 0; + + virtual status_t createTransaction(TransactionHelper* outTransaction, uint32_t width, + uint32_t height) = 0; + + virtual status_t mergeAndApply(TransactionHelper transaction) = 0; + + virtual status_t verifyCallbacks() = 0; + + virtual status_t cleanUp() = 0; +}; + +class BpIPCTest : public SafeBpInterface<IIPCTest> { +public: + explicit BpIPCTest(const sp<IBinder>& impl) : SafeBpInterface<IIPCTest>(impl, "BpIPCTest") {} + + status_t setDeathToken(sp<IBinder>& token) { + return callRemote<decltype(&IIPCTest::setDeathToken)>(Tag::SetDeathToken, token); + } + + status_t initClient() { return callRemote<decltype(&IIPCTest::initClient)>(Tag::InitClient); } + + status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) { + return callRemote<decltype(&IIPCTest::createTransaction)>(Tag::CreateTransaction, + transaction, width, height); + } + + status_t mergeAndApply(TransactionHelper transaction) { + return callRemote<decltype(&IIPCTest::mergeAndApply)>(Tag::MergeAndApply, transaction); + } + + status_t verifyCallbacks() { + return callRemote<decltype(&IIPCTest::verifyCallbacks)>(Tag::VerifyCallbacks); + } + + status_t cleanUp() { return callRemote<decltype(&IIPCTest::cleanUp)>(Tag::CleanUp); } +}; + +IMPLEMENT_META_INTERFACE(IPCTest, "android.gfx.tests.IIPCTest") + +class onTestDeath : public IBinder::DeathRecipient { +public: + void binderDied(const wp<IBinder>& /*who*/) override { + ALOGE("onTestDeath::binderDied, exiting"); + exit(0); + } +}; + +sp<onTestDeath> getDeathToken() { + static sp<onTestDeath> token = new onTestDeath; + return token; +} + +class BnIPCTest : public SafeBnInterface<IIPCTest> { +public: + BnIPCTest() : SafeBnInterface("BnIPCTest") {} + + status_t setDeathToken(sp<IBinder>& token) override { + return token->linkToDeath(getDeathToken()); + } + + status_t initClient() override { + mClient = new SurfaceComposerClient; + auto err = mClient->initCheck(); + return err; + } + + status_t createTransaction(TransactionHelper* transaction, uint32_t width, uint32_t height) { + if (transaction == nullptr) { + ALOGE("Error in createTransaction: transaction is nullptr"); + return BAD_VALUE; + } + mSurfaceControl = mClient->createSurface(String8("parentProcessSurface"), 0, 0, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState, + /*parent*/ nullptr); + sp<GraphicBuffer> gb; + sp<Fence> fence; + int err = IPCTestUtils::getBuffer(&gb, &fence); + if (err != NO_ERROR) return err; + + TransactionUtils::fillGraphicBufferColor(gb, + {0, 0, static_cast<int32_t>(width), + static_cast<int32_t>(height)}, + Color::RED); + transaction->setLayerStack(mSurfaceControl, 0) + .setLayer(mSurfaceControl, std::numeric_limits<int32_t>::max()) + .setFrame(mSurfaceControl, Rect(0, 0, width, height)) + .setBuffer(mSurfaceControl, gb) + .setAcquireFence(mSurfaceControl, fence) + .show(mSurfaceControl) + .addTransactionCompletedCallback(mCallbackHelper.function, + mCallbackHelper.getContext()); + return NO_ERROR; + } + + status_t mergeAndApply(TransactionHelper /*transaction*/) { + // transaction.apply(); + return NO_ERROR; + } + + status_t verifyCallbacks() { + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, mSurfaceControl); + EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(mCallbackHelper, expected, true)); + return NO_ERROR; + } + + status_t cleanUp() { + if (mClient) mClient->dispose(); + mSurfaceControl = nullptr; + IPCThreadState::self()->stopProcess(); + return NO_ERROR; + } + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t /*flags*/) override { + EXPECT_GE(code, IBinder::FIRST_CALL_TRANSACTION); + EXPECT_LT(code, static_cast<uint32_t>(IIPCTest::Tag::Last)); + switch (static_cast<IIPCTest::Tag>(code)) { + case IIPCTest::Tag::SetDeathToken: + return callLocal(data, reply, &IIPCTest::setDeathToken); + case IIPCTest::Tag::InitClient: + return callLocal(data, reply, &IIPCTest::initClient); + case IIPCTest::Tag::CreateTransaction: + return callLocal(data, reply, &IIPCTest::createTransaction); + case IIPCTest::Tag::MergeAndApply: + return callLocal(data, reply, &IIPCTest::mergeAndApply); + case IIPCTest::Tag::VerifyCallbacks: + return callLocal(data, reply, &IIPCTest::verifyCallbacks); + case IIPCTest::Tag::CleanUp: + return callLocal(data, reply, &IIPCTest::cleanUp); + default: + return UNKNOWN_ERROR; + } + } + +private: + sp<SurfaceComposerClient> mClient; + sp<SurfaceControl> mSurfaceControl; + CallbackHelper mCallbackHelper; +}; + +class IPCTest : public ::testing::Test { +public: + IPCTest() : mDeathRecipient(new BBinder), mRemote(initRemoteService()) { + ProcessState::self()->startThreadPool(); + } + void SetUp() { + mClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mClient->initCheck()); + + mPrimaryDisplay = mClient->getInternalDisplayToken(); + DisplayInfo info; + mClient->getDisplayInfo(mPrimaryDisplay, &info); + mDisplayWidth = info.w; + mDisplayHeight = info.h; + + Transaction setupTransaction; + setupTransaction.setDisplayLayerStack(mPrimaryDisplay, 0); + setupTransaction.apply(); + } + +protected: + sp<IIPCTest> initRemoteService(); + + sp<IBinder> mDeathRecipient; + sp<IIPCTest> mRemote; + sp<SurfaceComposerClient> mClient; + sp<IBinder> mPrimaryDisplay; + uint32_t mDisplayWidth; + uint32_t mDisplayHeight; + sp<SurfaceControl> sc; +}; + +status_t IPCTestUtils::getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { + static BufferGenerator bufferGenerator; + return bufferGenerator.get(outBuffer, outFence); +} + +void IPCTestUtils::waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult, + bool finalState) { + CallbackData callbackData; + ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData)); + EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData)); + + if (finalState) { + ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState()); + } +} + +sp<IIPCTest> IPCTest::initRemoteService() { + static std::mutex mMutex; + static sp<IIPCTest> remote; + const String16 serviceName("IPCTest"); + + std::unique_lock<decltype(mMutex)> lock; + if (remote == nullptr) { + pid_t forkPid = fork(); + EXPECT_NE(forkPid, -1); + + if (forkPid == 0) { + sp<IIPCTest> nativeService = new BnIPCTest; + if (!nativeService) { + ALOGE("null service..."); + } + status_t err = defaultServiceManager()->addService(serviceName, + IInterface::asBinder(nativeService)); + if (err != NO_ERROR) { + ALOGE("failed to add service: %d", err); + } + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); + [&]() { exit(0); }(); + } + sp<IBinder> binder = defaultServiceManager()->getService(serviceName); + remote = interface_cast<IIPCTest>(binder); + remote->setDeathToken(mDeathRecipient); + } + return remote; +} + +TEST_F(IPCTest, MergeBasic) { + CallbackHelper helper1; + sc = mClient->createSurface(String8("parentProcessSurface"), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState, + /*parent*/ nullptr); + sp<GraphicBuffer> gb; + sp<Fence> fence; + int err = IPCTestUtils::getBuffer(&gb, &fence); + ASSERT_EQ(NO_ERROR, err); + TransactionUtils::fillGraphicBufferColor(gb, + {0, 0, static_cast<int32_t>(mDisplayWidth), + static_cast<int32_t>(mDisplayHeight)}, + Color::RED); + + Transaction transaction; + transaction.setLayerStack(sc, 0) + .setLayer(sc, std::numeric_limits<int32_t>::max() - 1) + .setBuffer(sc, gb) + .setAcquireFence(sc, fence) + .show(sc) + .addTransactionCompletedCallback(helper1.function, helper1.getContext()); + + TransactionHelper remote; + mRemote->initClient(); + mRemote->createTransaction(&remote, mDisplayWidth / 2, mDisplayHeight / 2); + ASSERT_EQ(1, remote.getNumListeners()); + auto remoteListenerCallbacks = remote.getListenerCallbacks(); + auto remoteCallback = remoteListenerCallbacks.begin(); + auto remoteCallbackInfo = remoteCallback->second; + auto remoteListenerScs = remoteCallbackInfo.surfaceControls; + ASSERT_EQ(1, remoteCallbackInfo.callbackIds.size()); + ASSERT_EQ(1, remoteListenerScs.size()); + + sp<SurfaceControl> remoteSc = *(remoteListenerScs.begin()); + transaction.merge(std::move(remote)); + transaction.apply(); + + sleep(1); + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, sc); + expected.addSurface(ExpectedResult::Transaction::PRESENTED, remoteSc); + EXPECT_NO_FATAL_FAILURE(IPCTestUtils::waitForCallback(helper1, expected, true)); + + mRemote->verifyCallbacks(); + mRemote->cleanUp(); +} + +} // namespace test +} // namespace android diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp new file mode 100644 index 0000000000..7a5ed484d1 --- /dev/null +++ b/services/surfaceflinger/tests/LayerCallback_test.cpp @@ -0,0 +1,872 @@ +/* + * Copyright (C) 2019 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 "LayerTransactionTest.h" +#include "utils/CallbackUtils.h" + +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +::testing::Environment* const binderEnv = + ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); + +class LayerCallbackTest : public LayerTransactionTest { +public: + virtual sp<SurfaceControl> createBufferStateLayer() { + return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState); + } + + static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper, + const sp<SurfaceControl>& layer = nullptr, bool setBuffer = true, + bool setBackgroundColor = false) { + if (layer) { + sp<GraphicBuffer> buffer; + sp<Fence> fence; + if (setBuffer) { + int err = getBuffer(&buffer, &fence); + if (err != NO_ERROR) { + return err; + } + + transaction.setBuffer(layer, buffer); + transaction.setAcquireFence(layer, fence); + } + + if (setBackgroundColor) { + transaction.setBackgroundColor(layer, /*color*/ half3(1.0f, 0, 0), /*alpha*/ 1.0f, + ui::Dataspace::UNKNOWN); + } + } + + transaction.addTransactionCompletedCallback(callbackHelper->function, + callbackHelper->getContext()); + return NO_ERROR; + } + + static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult, + bool finalState = false) { + CallbackData callbackData; + ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData)); + EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData)); + + if (finalState) { + ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState()); + } + } + + static void waitForCallbacks(CallbackHelper& helper, + const std::vector<ExpectedResult>& expectedResults, + bool finalState = false) { + for (const auto& expectedResult : expectedResults) { + waitForCallback(helper, expectedResult); + } + if (finalState) { + ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState()); + } + } +}; + +TEST_F(LayerCallbackTest, BufferColor) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback, layer, true, true); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} + +TEST_F(LayerCallbackTest, NoBufferNoColor) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback, layer, false, false); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer, + ExpectedResult::Buffer::NOT_ACQUIRED); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} + +TEST_F(LayerCallbackTest, BufferNoColor) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback, layer, true, false); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} + +TEST_F(LayerCallbackTest, NoBufferColor) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback, layer, false, true); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::NOT_ACQUIRED); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} + +TEST_F(LayerCallbackTest, NoStateChange) { + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.apply(); + + ExpectedResult expected; + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} + +TEST_F(LayerCallbackTest, OffScreen) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} + +TEST_F(LayerCallbackTest, MergeBufferNoColor) { + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + int err = fillTransaction(transaction1, &callback1, layer1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2, layer2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); + transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); + + ExpectedResult expected; + expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); +} + +TEST_F(LayerCallbackTest, MergeNoBufferColor) { + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + int err = fillTransaction(transaction1, &callback1, layer1, false, true); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2, layer2, false, true); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); + transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); + + ExpectedResult expected; + expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}, + ExpectedResult::Buffer::NOT_ACQUIRED); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); +} + +TEST_F(LayerCallbackTest, MergeOneBufferOneColor) { + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + int err = fillTransaction(transaction1, &callback1, layer1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2, layer2, false, true); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); + transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1); + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer2, + ExpectedResult::Buffer::NOT_ACQUIRED); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); +} +TEST_F(LayerCallbackTest, Merge_SameCallback) { + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + + Transaction transaction1, transaction2; + CallbackHelper callback; + int err = fillTransaction(transaction1, &callback, layer1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback, layer2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction2.merge(std::move(transaction1)).apply(); + + ExpectedResult expected; + expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} + +TEST_F(LayerCallbackTest, Merge_SameLayer) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + int err = fillTransaction(transaction1, &callback1, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction2.merge(std::move(transaction1)).apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); +} + +TEST_F(LayerCallbackTest, Merge_DifferentClients) { + sp<SurfaceComposerClient> client1(new SurfaceComposerClient), + client2(new SurfaceComposerClient); + + ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; + ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; + + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0, + ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0, + ISurfaceComposerClient::eFXSurfaceBufferState)); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + int err = fillTransaction(transaction1, &callback1, layer1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2, layer2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); + transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); + + ExpectedResult expected; + expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); +} + +TEST_F(LayerCallbackTest, MultipleTransactions) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + for (size_t i = 0; i < 10; i++) { + int err = fillTransaction(transaction, &callback, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::ACQUIRED, + (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED + : ExpectedResult::PreviousBuffer::RELEASED); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected)); + } + ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState()); +} + +TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + for (size_t i = 0; i < 10; i++) { + ExpectedResult expected; + + if (i == 0) { + int err = fillTransaction(transaction, &callback, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + } else { + int err = fillTransaction(transaction, &callback); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + } + + transaction.apply(); + + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected)); + } + ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState()); +} + +TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + for (size_t i = 0; i < 10; i++) { + if (i == 0) { + int err = fillTransaction(transaction, &callback, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + } else { + int err = fillTransaction(transaction, &callback); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + } + + transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); + + ExpectedResult expected; + expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED + : ExpectedResult::Transaction::NOT_PRESENTED, + layer, + (i == 0) ? ExpectedResult::Buffer::ACQUIRED + : ExpectedResult::Buffer::NOT_ACQUIRED); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0)); + } + ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState()); +} + +TEST_F(LayerCallbackTest, MultipleTransactions_Merge) { + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); + ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + for (size_t i = 0; i < 10; i++) { + int err = fillTransaction(transaction1, &callback1, layer1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2, layer2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); + transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); + + ExpectedResult expected; + expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}, + ExpectedResult::Buffer::ACQUIRED, + (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED + : ExpectedResult::PreviousBuffer::RELEASED); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected)); + } + ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState()); + ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState()); +} + +TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) { + sp<SurfaceComposerClient> client1(new SurfaceComposerClient), + client2(new SurfaceComposerClient); + ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; + ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; + + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0, + ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0, + ISurfaceComposerClient::eFXSurfaceBufferState)); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + for (size_t i = 0; i < 10; i++) { + int err = fillTransaction(transaction1, &callback1, layer1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2, layer2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); + transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); + + ExpectedResult expected; + expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}, + ExpectedResult::Buffer::ACQUIRED, + (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED + : ExpectedResult::PreviousBuffer::RELEASED); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected)); + } + ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState()); + ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState()); +} + +TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) { + sp<SurfaceComposerClient> client1(new SurfaceComposerClient), + client2(new SurfaceComposerClient); + ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; + ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; + + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0, + ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0, + ISurfaceComposerClient::eFXSurfaceBufferState)); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + + // Normal call to set up test + int err = fillTransaction(transaction1, &callback1, layer1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2, layer2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); + transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); + + ExpectedResult expected; + expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); + expected.reset(); + + // Test + err = fillTransaction(transaction1, &callback1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction2.merge(std::move(transaction1)).apply(); + + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); +} + +TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) { + sp<SurfaceComposerClient> client1(new SurfaceComposerClient), + client2(new SurfaceComposerClient); + + ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; + ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; + + sp<SurfaceControl> layer1, layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0, + ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0, + ISurfaceComposerClient::eFXSurfaceBufferState)); + + Transaction transaction1, transaction2; + CallbackHelper callback1, callback2; + + // Normal call to set up test + int err = fillTransaction(transaction1, &callback1, layer1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2, layer2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); + transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); + + ExpectedResult expected; + expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); + expected.reset(); + + // Test + err = fillTransaction(transaction1, &callback1); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + err = fillTransaction(transaction2, &callback2); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); + + expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2, + ExpectedResult::Buffer::NOT_ACQUIRED); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); +} + +TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + std::vector<ExpectedResult> expectedResults(50); + for (auto& expected : expectedResults) { + expected.reset(); + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::ACQUIRED, + ExpectedResult::PreviousBuffer::UNKNOWN); + + int err = fillTransaction(transaction, &callback, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.apply(); + } + EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true)); +} + +TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + // Normal call to set up test + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); + + // Test + std::vector<ExpectedResult> expectedResults(50); + for (auto& expected : expectedResults) { + expected.reset(); + + err = fillTransaction(transaction, &callback); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.apply(); + } + EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true)); +} + +TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + // Normal call to set up test + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); + + ExpectedResult expectedResult; + expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expectedResult, true)); + + // Test + std::vector<ExpectedResult> expectedResults(50); + for (auto& expected : expectedResults) { + expected.reset(); + expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer, + ExpectedResult::Buffer::NOT_ACQUIRED); + + err = fillTransaction(transaction, &callback); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); + } + EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true)); +} + +TEST_F(LayerCallbackTest, DesiredPresentTime) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + // Try to present 100ms in the future + nsecs_t time = systemTime() + (100 * 1e6); + + transaction.setDesiredPresentTime(time); + transaction.apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + expected.addExpectedPresentTime(time); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} + +TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback1; + int err = fillTransaction(transaction, &callback1, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + // Try to present 100ms in the future + nsecs_t time = systemTime() + (100 * 1e6); + + transaction.setDesiredPresentTime(time); + transaction.apply(); + + ExpectedResult expected1; + expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + expected1.addExpectedPresentTime(time); + + CallbackHelper callback2; + err = fillTransaction(transaction, &callback2, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + // Try to present 33ms after the first frame + time += (33.3 * 1e6); + + transaction.setDesiredPresentTime(time); + transaction.apply(); + + ExpectedResult expected2; + expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::ACQUIRED, + ExpectedResult::PreviousBuffer::RELEASED); + expected2.addExpectedPresentTime(time); + + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true)); +} + +TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback1; + int err = fillTransaction(transaction, &callback1, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + // Try to present 100ms in the future + nsecs_t time = systemTime() + (100 * 1e6); + + transaction.setDesiredPresentTime(time); + transaction.apply(); + + ExpectedResult expected1; + expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + expected1.addExpectedPresentTime(time); + + CallbackHelper callback2; + err = fillTransaction(transaction, &callback2, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + // Try to present 33ms before the previous frame + time -= (33.3 * 1e6); + + transaction.setDesiredPresentTime(time); + transaction.apply(); + + ExpectedResult expected2; + expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::ACQUIRED, + ExpectedResult::PreviousBuffer::RELEASED); + + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true)); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true)); +} + +TEST_F(LayerCallbackTest, DesiredPresentTime_Past) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); + + Transaction transaction; + CallbackHelper callback; + int err = fillTransaction(transaction, &callback, layer); + if (err) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + // Try to present 100ms in the past + nsecs_t time = systemTime() - (100 * 1e6); + + transaction.setDesiredPresentTime(time); + transaction.apply(); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); + expected.addExpectedPresentTime(systemTime()); + EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); +} +} // namespace android diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp new file mode 100644 index 0000000000..a48f553d74 --- /dev/null +++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp @@ -0,0 +1,1834 @@ +/* + * Copyright (C) 2019 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 <gui/BufferItemConsumer.h> +#include <thread> +#include "TransactionTestHarnesses.h" + +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +::testing::Environment* const binderEnv = + ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); + +class LayerRenderTypeTransactionTest : public LayerTransactionTest, + public ::testing::WithParamInterface<RenderPath> { +public: + LayerRenderTypeTransactionTest() : mHarness(LayerRenderPathTestHarness(this, GetParam())) {} + + std::unique_ptr<ScreenCapture> getScreenCapture() { return mHarness.getScreenCapture(); } + void setRelativeZBasicHelper(uint32_t layerType); + void setRelativeZGroupHelper(uint32_t layerType); + void setAlphaBasicHelper(uint32_t layerType); + void setBackgroundColorHelper(uint32_t layerType, bool priorColor, bool bufferFill, float alpha, + Color finalColor); + +protected: + LayerRenderPathTestHarness mHarness; +}; + +INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest, + ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)); + +TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + { + SCOPED_TRACE("default position"); + const Rect rect(0, 0, 32, 32); + auto shot = getScreenCapture(); + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); + } + + Transaction().setPosition(layer, 5, 10).apply(); + { + SCOPED_TRACE("new position"); + const Rect rect(5, 10, 37, 42); + auto shot = getScreenCapture(); + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + // GLES requires only 4 bits of subpixel precision during rasterization + // XXX GLES composition does not match HWC composition due to precision + // loss (b/69315223) + const float epsilon = 1.0f / 16.0f; + Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply(); + { + SCOPED_TRACE("rounding down"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); + } + + Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply(); + { + SCOPED_TRACE("rounding up"); + getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + Transaction().setPosition(layer, -32, -32).apply(); + { + SCOPED_TRACE("negative coordinates"); + getScreenCapture()->expectColor(mDisplayRect, Color::BLACK); + } + + Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply(); + { + SCOPED_TRACE("positive coordinates"); + getScreenCapture()->expectColor(mDisplayRect, Color::BLACK); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + // partially out of bounds + Transaction().setPosition(layer, -30, -30).apply(); + { + SCOPED_TRACE("negative coordinates"); + getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED); + } + + Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply(); + { + SCOPED_TRACE("positive coordinates"); + getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth, + mDisplayHeight), + Color::RED); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + // setPosition is applied immediately by default, with or without resize + // pending + Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply(); + { + SCOPED_TRACE("resize pending"); + auto shot = getScreenCapture(); + const Rect rect(5, 10, 37, 42); + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64)); + { + SCOPED_TRACE("resize applied"); + getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + Transaction().setSize(layer, 64, 64).apply(); + { + SCOPED_TRACE("resize pending"); + auto shot = getScreenCapture(); + const Rect rect(0, 0, 32, 32); + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64)); + { + SCOPED_TRACE("resize applied"); + auto shot = getScreenCapture(); + const Rect rect(0, 0, 64, 64); + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition + Transaction() + .setSize(layer, 64, 64) + .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) + .apply(); + getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED); +} + +void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) { + sp<SurfaceControl> layerR; + sp<SurfaceControl> layerG; + ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32, layerType)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32, layerType)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32)); + + switch (layerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + Transaction() + .setPosition(layerG, 16, 16) + .setRelativeLayer(layerG, layerR->getHandle(), 1) + .apply(); + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + Transaction() + .setFrame(layerR, Rect(0, 0, 32, 32)) + .setFrame(layerG, Rect(16, 16, 48, 48)) + .setRelativeLayer(layerG, layerR->getHandle(), 1) + .apply(); + break; + default: + ASSERT_FALSE(true) << "Unsupported layer type"; + } + { + SCOPED_TRACE("layerG above"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 16, 16), Color::RED); + shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN); + } + + Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply(); + { + SCOPED_TRACE("layerG below"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) { + ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); +} + +TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) { + ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); +} + +void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) { + sp<SurfaceControl> layerR; + sp<SurfaceControl> layerG; + sp<SurfaceControl> layerB; + ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test", 32, 32, layerType)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test", 32, 32, layerType)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32)); + ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test", 32, 32, layerType)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerB, Color::BLUE, 32, 32)); + + // layerR = 0, layerG = layerR + 3, layerB = 2 + switch (layerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + Transaction() + .setPosition(layerG, 8, 8) + .setRelativeLayer(layerG, layerR->getHandle(), 3) + .setPosition(layerB, 16, 16) + .setLayer(layerB, mLayerZBase + 2) + .apply(); + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + Transaction() + .setFrame(layerR, Rect(0, 0, 32, 32)) + .setFrame(layerG, Rect(8, 8, 40, 40)) + .setRelativeLayer(layerG, layerR->getHandle(), 3) + .setFrame(layerB, Rect(16, 16, 48, 48)) + .setLayer(layerB, mLayerZBase + 2) + .apply(); + break; + default: + ASSERT_FALSE(true) << "Unsupported layer type"; + } + + { + SCOPED_TRACE("(layerR < layerG) < layerB"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 8, 8), Color::RED); + shot->expectColor(Rect(8, 8, 16, 16), Color::GREEN); + shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE); + } + + // layerR = 4, layerG = layerR + 3, layerB = 2 + Transaction().setLayer(layerR, mLayerZBase + 4).apply(); + { + SCOPED_TRACE("layerB < (layerR < layerG)"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 8, 8), Color::RED); + shot->expectColor(Rect(8, 8, 40, 40), Color::GREEN); + shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE); + } + + // layerR = 4, layerG = layerR - 3, layerB = 2 + Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply(); + { + SCOPED_TRACE("layerB < (layerG < layerR)"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectColor(Rect(32, 32, 40, 40), Color::GREEN); + shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE); + } + + // restore to absolute z + // layerR = 4, layerG = 0, layerB = 2 + Transaction().setLayer(layerG, mLayerZBase).apply(); + { + SCOPED_TRACE("layerG < layerB < layerR"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE); + } + + // layerR should not affect layerG anymore + // layerR = 1, layerG = 0, layerB = 2 + Transaction().setLayer(layerR, mLayerZBase + 1).apply(); + { + SCOPED_TRACE("layerG < layerR < layerB"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 16, 16), Color::RED); + shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferQueue) { + ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); +} + +TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferState) { + ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); +} + +TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) { + const Rect top(0, 0, 32, 16); + const Rect bottom(0, 16, 32, 32); + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + + ANativeWindow_Buffer buffer; + ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); + ASSERT_NO_FATAL_FAILURE( + TransactionUtils::fillANativeWindowBufferColor(buffer, top, Color::TRANSPARENT)); + ASSERT_NO_FATAL_FAILURE( + TransactionUtils::fillANativeWindowBufferColor(buffer, bottom, Color::RED)); + // setTransparentRegionHint always applies to the following buffer + Transaction().setTransparentRegionHint(layer, Region(top)).apply(); + ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer)); + { + SCOPED_TRACE("top transparent"); + auto shot = getScreenCapture(); + shot->expectColor(top, Color::BLACK); + shot->expectColor(bottom, Color::RED); + } + + Transaction().setTransparentRegionHint(layer, Region(bottom)).apply(); + { + SCOPED_TRACE("transparent region hint pending"); + auto shot = getScreenCapture(); + shot->expectColor(top, Color::BLACK); + shot->expectColor(bottom, Color::RED); + } + + ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); + ASSERT_NO_FATAL_FAILURE( + TransactionUtils::fillANativeWindowBufferColor(buffer, top, Color::RED)); + ASSERT_NO_FATAL_FAILURE( + TransactionUtils::fillANativeWindowBufferColor(buffer, bottom, Color::TRANSPARENT)); + ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer)); + { + SCOPED_TRACE("bottom transparent"); + auto shot = getScreenCapture(); + shot->expectColor(top, Color::RED); + shot->expectColor(bottom, Color::BLACK); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState) { + const Rect top(0, 0, 32, 16); + const Rect bottom(0, 16, 32, 32); + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + + ASSERT_NO_FATAL_FAILURE( + TransactionUtils::fillGraphicBufferColor(buffer, top, Color::TRANSPARENT)); + ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, bottom, Color::RED)); + Transaction() + .setTransparentRegionHint(layer, Region(top)) + .setBuffer(layer, buffer) + .setFrame(layer, Rect(0, 0, 32, 32)) + .apply(); + { + SCOPED_TRACE("top transparent"); + auto shot = getScreenCapture(); + shot->expectColor(top, Color::BLACK); + shot->expectColor(bottom, Color::RED); + } + + Transaction().setTransparentRegionHint(layer, Region(bottom)).apply(); + { + SCOPED_TRACE("transparent region hint intermediate"); + auto shot = getScreenCapture(); + shot->expectColor(top, Color::BLACK); + shot->expectColor(bottom, Color::BLACK); + } + + buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + + ASSERT_NO_FATAL_FAILURE(TransactionUtils::fillGraphicBufferColor(buffer, top, Color::RED)); + ASSERT_NO_FATAL_FAILURE( + TransactionUtils::fillGraphicBufferColor(buffer, bottom, Color::TRANSPARENT)); + Transaction().setBuffer(layer, buffer).apply(); + { + SCOPED_TRACE("bottom transparent"); + auto shot = getScreenCapture(); + shot->expectColor(top, Color::RED); + shot->expectColor(bottom, Color::BLACK); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) { + sp<SurfaceControl> layerTransparent; + sp<SurfaceControl> layerR; + ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32)); + ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); + + // check that transparent region hint is bound by the layer size + Transaction() + .setTransparentRegionHint(layerTransparent, Region(mDisplayRect)) + .setPosition(layerR, 16, 16) + .setLayer(layerR, mLayerZBase + 1) + .apply(); + ASSERT_NO_FATAL_FAILURE( + fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32)); + getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED); +} + +TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) { + sp<SurfaceControl> layerTransparent; + sp<SurfaceControl> layerR; + ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32)); + ASSERT_NO_FATAL_FAILURE( + layerR = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + // check that transparent region hint is bound by the layer size + Transaction() + .setTransparentRegionHint(layerTransparent, Region(mDisplayRect)) + .setFrame(layerR, Rect(16, 16, 48, 48)) + .setLayer(layerR, mLayerZBase + 1) + .apply(); + ASSERT_NO_FATAL_FAILURE( + fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32)); + getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED); +} + +void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) { + sp<SurfaceControl> layer1; + sp<SurfaceControl> layer2; + ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32, layerType)); + ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32, layerType)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer1, {64, 0, 0, 255}, 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer2, {0, 64, 0, 255}, 32, 32)); + + switch (layerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + Transaction() + .setAlpha(layer1, 0.25f) + .setAlpha(layer2, 0.75f) + .setPosition(layer2, 16, 0) + .setLayer(layer2, mLayerZBase + 1) + .apply(); + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + Transaction() + .setAlpha(layer1, 0.25f) + .setAlpha(layer2, 0.75f) + .setFrame(layer1, Rect(0, 0, 32, 32)) + .setFrame(layer2, Rect(16, 0, 48, 32)) + .setLayer(layer2, mLayerZBase + 1) + .apply(); + break; + default: + ASSERT_FALSE(true) << "Unsupported layer type"; + } + { + auto shot = getScreenCapture(); + uint8_t r = 16; // 64 * 0.25f + uint8_t g = 48; // 64 * 0.75f + shot->expectColor(Rect(0, 0, 16, 32), {r, 0, 0, 255}); + shot->expectColor(Rect(32, 0, 48, 32), {0, g, 0, 255}); + + r /= 4; // r * (1.0f - 0.75f) + shot->expectColor(Rect(16, 0, 32, 32), {r, g, 0, 255}); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferQueue) { + ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); +} + +TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferState) { + ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); +} + +TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) { + sp<SurfaceControl> bufferLayer; + sp<SurfaceControl> colorLayer; + ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(colorLayer = + createLayer("test", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor)); + + Transaction() + .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) + .setLayer(colorLayer, mLayerZBase + 1) + .apply(); + + { + SCOPED_TRACE("default color"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + } + + const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f); + const Color expected = {15, 51, 85, 255}; + // this is handwavy, but the precison loss scaled by 255 (8-bit per + // channel) should be less than one + const uint8_t tolerance = 1; + Transaction().setColor(colorLayer, color).apply(); + { + SCOPED_TRACE("new color"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance); + } +} + +// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill +// BLUE: prior background color +// GREEN: final background color +// BLACK: no color or fill +void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType, bool priorColor, + bool bufferFill, float alpha, + Color finalColor) { + sp<SurfaceControl> layer; + int32_t width = 500; + int32_t height = 500; + + Color fillColor = Color::RED; + Color priorBgColor = Color::BLUE; + Color expectedColor = Color::BLACK; + switch (layerType) { + case ISurfaceComposerClient::eFXSurfaceColor: + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType)); + Transaction() + .setCrop_legacy(layer, Rect(0, 0, width, height)) + .setColor(layer, half3(1.0f, 0, 0)) + .apply(); + expectedColor = fillColor; + break; + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height)); + if (bufferFill) { + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height)); + expectedColor = fillColor; + } + Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply(); + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType)); + if (bufferFill) { + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height)); + expectedColor = fillColor; + } + Transaction().setFrame(layer, Rect(0, 0, width, height)).apply(); + break; + default: + GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper"; + return; + } + + if (priorColor && layerType != ISurfaceComposerClient::eFXSurfaceColor) { + Transaction() + .setBackgroundColor(layer, half3(0, 0, 1.0f), 1.0f, ui::Dataspace::UNKNOWN) + .apply(); + if (!bufferFill) { + expectedColor = priorBgColor; + } + } + + { + SCOPED_TRACE("default before setting background color layer"); + screenshot()->expectColor(Rect(0, 0, width, height), expectedColor); + } + Transaction() + .setBackgroundColor(layer, half3(0, 1.0f, 0), alpha, ui::Dataspace::UNKNOWN) + .apply(); + + { + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, width, height), finalColor); + shot->expectBorder(Rect(0, 0, width, height), Color::BLACK); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_Color_NoEffect) { + bool priorColor = false; + bool bufferFill = false; + float alpha = 1.0f; + Color finalColor = Color::RED; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceColor, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, + SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) { + bool priorColor = false; + bool bufferFill = true; + float alpha = 1.0f; + Color finalColor = Color::RED; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, + SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) { + bool priorColor = false; + bool bufferFill = false; + float alpha = 1.0f; + Color finalColor = Color::GREEN; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) { + bool priorColor = true; + bool bufferFill = true; + float alpha = 1.0f; + Color finalColor = Color::RED; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, + SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) { + bool priorColor = true; + bool bufferFill = false; + float alpha = 1.0f; + Color finalColor = Color::GREEN; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, + priorColor, bufferFill, alpha, finalColor)); +} +TEST_P(LayerRenderTypeTransactionTest, + SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) { + bool priorColor = false; + bool bufferFill = false; + float alpha = 0; + Color finalColor = Color::BLACK; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, + SetBackgroundColor_BufferQueue_PriorColor_ZeroAlpha_DeleteBackground) { + bool priorColor = true; + bool bufferFill = false; + float alpha = 0; + Color finalColor = Color::BLACK; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, + SetBackgroundColor_BufferState_BufferFill_NoPriorColor_Basic) { + bool priorColor = false; + bool bufferFill = true; + float alpha = 1.0f; + Color finalColor = Color::RED; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, + SetBackgroundColor_BufferState_NoBufferFill_NoPriorColor_Basic) { + bool priorColor = false; + bool bufferFill = false; + float alpha = 1.0f; + Color finalColor = Color::GREEN; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, + SetBackgroundColor_BufferState_NoBufferFill_PriorColor_Basic) { + bool priorColor = true; + bool bufferFill = false; + float alpha = 1.0f; + Color finalColor = Color::GREEN; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, + SetBackgroundColor_BufferState_NoPriorColor_ZeroAlpha_NoEffect) { + bool priorColor = false; + bool bufferFill = false; + float alpha = 0; + Color finalColor = Color::BLACK; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, + SetBackgroundColor_BufferState_PriorColor_ZeroAlpha_DeleteBackground) { + bool priorColor = true; + bool bufferFill = false; + float alpha = 0; + Color finalColor = Color::BLACK; + ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState, + priorColor, bufferFill, alpha, finalColor)); +} + +TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) { + sp<SurfaceControl> colorLayer; + ASSERT_NO_FATAL_FAILURE(colorLayer = + createLayer("test", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor)); + Transaction() + .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) + .setColor(colorLayer, half3(2.0f, -1.0f, 0.0f)) + .apply(); + + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); +} + +TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) { + sp<SurfaceControl> bufferLayer; + sp<SurfaceControl> colorLayer; + ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(colorLayer = + createLayer("test", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor)); + Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply(); + + const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f); + const float alpha = 0.25f; + const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f); + // this is handwavy, but the precison loss scaled by 255 (8-bit per + // channel) should be less than one + const uint8_t tolerance = 1; + Transaction() + .setColor(colorLayer, color) + .setAlpha(colorLayer, alpha) + .setLayer(colorLayer, mLayerZBase + 1) + .apply(); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255}, + tolerance); +} + +TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) { + sp<SurfaceControl> bufferLayer; + sp<SurfaceControl> parentLayer; + sp<SurfaceControl> colorLayer; + ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32)); + ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */, + 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor)); + Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply(); + const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f); + const float alpha = 0.25f; + const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f); + // this is handwavy, but the precision loss scaled by 255 (8-bit per + // channel) should be less than one + const uint8_t tolerance = 1; + Transaction() + .reparent(colorLayer, parentLayer->getHandle()) + .setColor(colorLayer, color) + .setAlpha(parentLayer, alpha) + .setLayer(parentLayer, mLayerZBase + 1) + .apply(); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255}, + tolerance); +} + +TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); + + Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply(); + { + SCOPED_TRACE("IDENTITY"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE); + } + + Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 32, 0).apply(); + { + SCOPED_TRACE("FLIP_H"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, + Color::WHITE, Color::BLUE); + } + + Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).setPosition(layer, 0, 32).apply(); + { + SCOPED_TRACE("FLIP_V"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, + Color::RED, Color::GREEN); + } + + Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).setPosition(layer, 32, 0).apply(); + { + SCOPED_TRACE("ROT_90"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, + Color::WHITE, Color::GREEN); + } + + Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setPosition(layer, 0, 0).apply(); + { + SCOPED_TRACE("SCALE"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE, true /* filtered */); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); + + Transaction() + .setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f) + .setFrame(layer, Rect(0, 0, 32, 32)) + .apply(); + { + SCOPED_TRACE("IDENTITY"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE); + } + + Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply(); + { + SCOPED_TRACE("FLIP_H"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE); + } + + Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply(); + { + SCOPED_TRACE("FLIP_V"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE); + } + + Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply(); + { + SCOPED_TRACE("ROT_90"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE); + } + + Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply(); + { + SCOPED_TRACE("SCALE"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetMatrixRot45_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); + + const float rot = M_SQRT1_2; // 45 degrees + const float trans = M_SQRT2 * 16.0f; + Transaction().setMatrix(layer, rot, rot, -rot, rot).setPosition(layer, trans, 0).apply(); + + auto shot = getScreenCapture(); + // check a 8x8 region inside each color + auto get8x8Rect = [](int32_t centerX, int32_t centerY) { + const int32_t halfL = 4; + return Rect(centerX - halfL, centerY - halfL, centerX + halfL, centerY + halfL); + }; + const int32_t unit = int32_t(trans / 2); + shot->expectColor(get8x8Rect(2 * unit, 1 * unit), Color::RED); + shot->expectColor(get8x8Rect(3 * unit, 2 * unit), Color::GREEN); + shot->expectColor(get8x8Rect(1 * unit, 2 * unit), Color::BLUE); + shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE); +} + +TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + // setMatrix is applied after any pending resize, unlike setPosition + Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply(); + { + SCOPED_TRACE("resize pending"); + auto shot = getScreenCapture(); + const Rect rect(0, 0, 32, 32); + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64)); + { + SCOPED_TRACE("resize applied"); + const Rect rect(0, 0, 128, 128); + getScreenCapture()->expectColor(rect, Color::RED); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition + Transaction() + .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f) + .setSize(layer, 64, 64) + .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) + .apply(); + getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED); +} + +TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); + + // XXX SCALE_CROP is not respected; calling setSize and + // setOverrideScalingMode in separate transactions does not work + // (b/69315456) + Transaction() + .setSize(layer, 64, 16) + .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) + .apply(); + { + SCOPED_TRACE("SCALE_TO_WINDOW"); + getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE, true /* filtered */); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + const Rect crop(8, 8, 24, 24); + + Transaction().setCrop_legacy(layer, crop).apply(); + auto shot = getScreenCapture(); + shot->expectColor(crop, Color::RED); + shot->expectBorder(crop, Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + const Rect crop(8, 8, 24, 24); + + Transaction().setCrop(layer, crop).apply(); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + { + SCOPED_TRACE("empty rect"); + Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply(); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); + } + + { + SCOPED_TRACE("negative rect"); + Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply(); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + { + SCOPED_TRACE("empty rect"); + Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply(); + getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + } + + { + SCOPED_TRACE("negative rect"); + Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply(); + getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply(); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState)); + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED); + + Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply(); + + Transaction().setBuffer(layer, buffer).apply(); + + // Partially out of bounds in the negative (upper left) direction + Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply(); + { + SCOPED_TRACE("out of bounds, negative (upper left) direction"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE); + shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); + } + + // Partially out of bounds in the positive (lower right) direction + Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply(); + { + SCOPED_TRACE("out of bounds, positive (lower right) direction"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 64, 64), Color::RED); + shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); + } + + // Fully out of buffer space bounds + Transaction().setCrop(layer, Rect(-128, -128, -1, -1)).apply(); + { + SCOPED_TRACE("Fully out of bounds"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE); + shot->expectColor(Rect(0, 16, 64, 64), Color::RED); + shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + const Point position(32, 32); + const Rect crop(8, 8, 24, 24); + Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply(); + auto shot = getScreenCapture(); + shot->expectColor(crop + position, Color::RED); + shot->expectBorder(crop + position, Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + const Rect frame(32, 32, 64, 64); + const Rect crop(8, 8, 24, 24); + Transaction().setFrame(layer, frame).setCrop(layer, crop).apply(); + auto shot = getScreenCapture(); + shot->expectColor(frame, Color::RED); + shot->expectBorder(frame, Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + // crop_legacy is affected by matrix + Transaction() + .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f) + .setCrop_legacy(layer, Rect(8, 8, 24, 24)) + .apply(); + auto shot = getScreenCapture(); + shot->expectColor(Rect(16, 16, 48, 48), Color::RED); + shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + // setCrop_legacy is applied immediately by default, with or without resize pending + Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply(); + { + SCOPED_TRACE("resize pending"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(8, 8, 24, 24), Color::RED); + shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16)); + { + SCOPED_TRACE("resize applied"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(8, 8, 16, 16), Color::RED); + shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + const Rect frame(8, 8, 24, 24); + + Transaction().setFrame(layer, frame).apply(); + auto shot = getScreenCapture(); + shot->expectColor(frame, Color::RED); + shot->expectBorder(frame, Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + { + SCOPED_TRACE("empty rect"); + Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply(); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + } + + { + SCOPED_TRACE("negative rect"); + Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply(); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10)); + + // A parentless layer will default to a frame with the same size as the buffer + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) { + sp<SurfaceControl> parent, child; + ASSERT_NO_FATAL_FAILURE( + parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32)); + Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply(); + + ASSERT_NO_FATAL_FAILURE( + child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); + + Transaction().reparent(child, parent->getHandle()).apply(); + + // A layer will default to the frame of its parent + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) { + sp<SurfaceControl> parent, child; + ASSERT_NO_FATAL_FAILURE(parent = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32)); + + ASSERT_NO_FATAL_FAILURE( + child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); + + Transaction().reparent(child, parent->getHandle()).apply(); + + // A layer will default to the frame of its parent + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + Transaction().setFrame(layer, Rect(0, 0, 32, 32)).apply(); + + std::this_thread::sleep_for(500ms); + + Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply(); + + auto shot = getScreenCapture(); + shot->expectColor(Rect(16, 16, 48, 48), Color::RED); + shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) { + sp<SurfaceControl> parent, child; + ASSERT_NO_FATAL_FAILURE( + parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE( + child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + Transaction().reparent(child, parent->getHandle()).apply(); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32)); + Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply(); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); + Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply(); + + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 16), Color::RED); + shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE); + shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + { + SCOPED_TRACE("set buffer 1"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32)); + + { + SCOPED_TRACE("set buffer 2"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + + { + SCOPED_TRACE("set buffer 3"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) { + sp<SurfaceControl> layer1; + ASSERT_NO_FATAL_FAILURE( + layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<SurfaceControl> layer2; + ASSERT_NO_FATAL_FAILURE( + layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64)); + + Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply(); + { + SCOPED_TRACE("set layer 1 buffer red"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 64, 64), Color::RED); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32)); + + Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply(); + { + SCOPED_TRACE("set layer 2 buffer blue"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); + shot->expectColor(Rect(0, 32, 64, 64), Color::RED); + shot->expectColor(Rect(0, 32, 32, 64), Color::RED); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64)); + { + SCOPED_TRACE("set layer 1 buffer green"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); + shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN); + shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN); + } + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32)); + + { + SCOPED_TRACE("set layer 2 buffer white"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE); + shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN); + shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN}; + + std::array<sp<GraphicBuffer>, 10> buffers; + + size_t idx = 0; + for (auto& buffer : buffers) { + buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + Color color = colors[idx % colors.size()]; + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color); + idx++; + } + + // Set each buffer twice. The first time adds it to the cache, the second time tests that the + // cache is working. + idx = 0; + for (auto& buffer : buffers) { + for (int i = 0; i < 2; i++) { + Transaction().setBuffer(layer, buffer).apply(); + + Color color = colors[idx % colors.size()]; + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); + } + idx++; + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN}; + + std::array<sp<GraphicBuffer>, 70> buffers; + + size_t idx = 0; + for (auto& buffer : buffers) { + buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + Color color = colors[idx % colors.size()]; + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color); + idx++; + } + + // Set each buffer twice. The first time adds it to the cache, the second time tests that the + // cache is working. + idx = 0; + for (auto& buffer : buffers) { + for (int i = 0; i < 2; i++) { + Transaction().setBuffer(layer, buffer).apply(); + + Color color = colors[idx % colors.size()]; + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); + } + idx++; + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN}; + + std::array<sp<GraphicBuffer>, 65> buffers; + + size_t idx = 0; + for (auto& buffer : buffers) { + buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + Color color = colors[idx % colors.size()]; + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color); + idx++; + } + + // Set each buffer twice. The first time adds it to the cache, the second time tests that the + // cache is working. + idx = 0; + for (auto& buffer : buffers) { + for (int i = 0; i < 2; i++) { + Transaction().setBuffer(layer, buffer).apply(); + + Color color = colors[idx % colors.size()]; + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); + } + if (idx == 0) { + buffers[0].clear(); + } + idx++; + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetTransformRotate90_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); + + Transaction() + .setFrame(layer, Rect(0, 0, 32, 32)) + .setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90) + .apply(); + + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE, + Color::GREEN, true /* filtered */); +} + +TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipH_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); + + Transaction() + .setFrame(layer, Rect(0, 0, 32, 32)) + .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H) + .apply(); + + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE, + Color::BLUE, true /* filtered */); +} + +TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, + Color::BLUE, Color::WHITE)); + + Transaction() + .setFrame(layer, Rect(0, 0, 32, 32)) + .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V) + .apply(); + + getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED, + Color::GREEN, true /* filtered */); +} + +TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) { + sp<SurfaceControl> layer; + Transaction transaction; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); + + sp<Fence> fence; + if (getBuffer(nullptr, &fence) != NO_ERROR) { + GTEST_SUCCEED() << "test not supported"; + return; + } + + Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply(); + + status_t status = fence->wait(1000); + ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status); + std::this_thread::sleep_for(200ms); + + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); + + sp<Fence> fence = Fence::NO_FENCE; + + Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply(); + + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); + + Transaction().setBuffer(layer, buffer).setDataspace(layer, ui::Dataspace::UNKNOWN).apply(); + + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); + + HdrMetadata hdrMetadata; + hdrMetadata.validTypes = 0; + Transaction().setBuffer(layer, buffer).setHdrMetadata(layer, hdrMetadata).apply(); + + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); + + Region region; + region.set(32, 32); + Transaction().setBuffer(layer, buffer).setSurfaceDamageRegion(layer, region).apply(); + + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + sp<GraphicBuffer> buffer = + new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); + + Transaction().setBuffer(layer, buffer).setApi(layer, NATIVE_WINDOW_API_CPU).apply(); + + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); +} + +TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) { + sp<SurfaceControl> colorLayer; + ASSERT_NO_FATAL_FAILURE(colorLayer = + createLayer("test", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor)); + Transaction() + .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) + .setLayer(colorLayer, mLayerZBase + 1) + .apply(); + { + SCOPED_TRACE("default color"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + } + + const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f); + half3 expected = color; + mat3 matrix; + matrix[0][0] = 0.3; + matrix[1][0] = 0.59; + matrix[2][0] = 0.11; + matrix[0][1] = 0.3; + matrix[1][1] = 0.59; + matrix[2][1] = 0.11; + matrix[0][2] = 0.3; + matrix[1][2] = 0.59; + matrix[2][2] = 0.11; + + // degamma before applying the matrix + if (mColorManagementUsed) { + ColorTransformHelper::DegammaColor(expected); + } + + ColorTransformHelper::applyMatrix(expected, matrix); + + if (mColorManagementUsed) { + ColorTransformHelper::GammaColor(expected); + } + + const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255), + uint8_t(expected.b * 255), 255}; + + // this is handwavy, but the precison loss scaled by 255 (8-bit per + // channel) should be less than one + const uint8_t tolerance = 1; + + Transaction().setColor(colorLayer, color).setColorTransform(colorLayer, matrix, vec3()).apply(); + { + SCOPED_TRACE("new color"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) { + sp<SurfaceControl> parentLayer; + sp<SurfaceControl> colorLayer; + ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */, + 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceContainer)); + ASSERT_NO_FATAL_FAILURE( + colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get())); + + Transaction() + .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100)) + .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) + .setLayer(parentLayer, mLayerZBase + 1) + .apply(); + { + SCOPED_TRACE("default color"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + } + + const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f); + half3 expected = color; + mat3 matrix; + matrix[0][0] = 0.3; + matrix[1][0] = 0.59; + matrix[2][0] = 0.11; + matrix[0][1] = 0.3; + matrix[1][1] = 0.59; + matrix[2][1] = 0.11; + matrix[0][2] = 0.3; + matrix[1][2] = 0.59; + matrix[2][2] = 0.11; + + // degamma before applying the matrix + if (mColorManagementUsed) { + ColorTransformHelper::DegammaColor(expected); + } + + ColorTransformHelper::applyMatrix(expected, matrix); + + if (mColorManagementUsed) { + ColorTransformHelper::GammaColor(expected); + } + + const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255), + uint8_t(expected.b * 255), 255}; + + // this is handwavy, but the precison loss scaled by 255 (8-bit per + // channel) should be less than one + const uint8_t tolerance = 1; + + Transaction() + .setColor(colorLayer, color) + .setColorTransform(parentLayer, matrix, vec3()) + .apply(); + { + SCOPED_TRACE("new color"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance); + } +} + +TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) { + sp<SurfaceControl> parentLayer; + sp<SurfaceControl> colorLayer; + ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */, + 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceContainer)); + ASSERT_NO_FATAL_FAILURE( + colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get())); + + Transaction() + .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100)) + .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) + .setLayer(parentLayer, mLayerZBase + 1) + .apply(); + { + SCOPED_TRACE("default color"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + } + + const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f); + half3 expected = color; + mat3 matrixChild; + matrixChild[0][0] = 0.3; + matrixChild[1][0] = 0.59; + matrixChild[2][0] = 0.11; + matrixChild[0][1] = 0.3; + matrixChild[1][1] = 0.59; + matrixChild[2][1] = 0.11; + matrixChild[0][2] = 0.3; + matrixChild[1][2] = 0.59; + matrixChild[2][2] = 0.11; + mat3 matrixParent; + matrixParent[0][0] = 0.2; + matrixParent[1][0] = 0.4; + matrixParent[2][0] = 0.10; + matrixParent[0][1] = 0.2; + matrixParent[1][1] = 0.4; + matrixParent[2][1] = 0.10; + matrixParent[0][2] = 0.2; + matrixParent[1][2] = 0.4; + matrixParent[2][2] = 0.10; + + // degamma before applying the matrix + if (mColorManagementUsed) { + ColorTransformHelper::DegammaColor(expected); + } + + ColorTransformHelper::applyMatrix(expected, matrixChild); + ColorTransformHelper::applyMatrix(expected, matrixParent); + + if (mColorManagementUsed) { + ColorTransformHelper::GammaColor(expected); + } + + const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255), + uint8_t(expected.b * 255), 255}; + + // this is handwavy, but the precison loss scaled by 255 (8-bit per + // channel) should be less than one + const uint8_t tolerance = 1; + + Transaction() + .setColor(colorLayer, color) + .setColorTransform(parentLayer, matrixParent, vec3()) + .setColorTransform(colorLayer, matrixChild, vec3()) + .apply(); + { + SCOPED_TRACE("new color"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance); + } +} +} // namespace android diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h new file mode 100644 index 0000000000..7edddb6ea3 --- /dev/null +++ b/services/surfaceflinger/tests/LayerTransactionTest.h @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ANDROID_LAYER_TRANSACTION_TEST_H +#define ANDROID_LAYER_TRANSACTION_TEST_H + +#include <gtest/gtest.h> + +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <hardware/hwcomposer_defs.h> +#include <private/gui/ComposerService.h> + +#include <ui/DisplayInfo.h> + +#include "BufferGenerator.h" +#include "utils/ScreenshotUtils.h" +#include "utils/TransactionUtils.h" + +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +class LayerTransactionTest : public ::testing::Test { +protected: + void SetUp() override { + mClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient"; + + ASSERT_NO_FATAL_FAILURE(SetUpDisplay()); + + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed)); + } + + virtual void TearDown() { + mBlackBgSurface = 0; + mClient->dispose(); + mClient = 0; + } + + virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client, + const char* name, uint32_t width, uint32_t height, + uint32_t flags = 0, SurfaceControl* parent = nullptr) { + auto layer = + createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, parent); + + Transaction t; + t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase); + + status_t error = t.apply(); + if (error != NO_ERROR) { + ADD_FAILURE() << "failed to initialize SurfaceControl"; + layer.clear(); + } + + return layer; + } + + virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client, + const char* name, uint32_t width, uint32_t height, + PixelFormat format, uint32_t flags, + SurfaceControl* parent = nullptr) { + auto layer = client->createSurface(String8(name), width, height, format, flags, parent); + EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl"; + return layer; + } + + virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, + uint32_t flags = 0, SurfaceControl* parent = nullptr) { + return createLayer(mClient, name, width, height, flags, parent); + } + + sp<SurfaceControl> createColorLayer(const char* name, const Color& color, + SurfaceControl* parent = nullptr) { + auto colorLayer = createSurface(mClient, name, 0 /* buffer width */, 0 /* buffer height */, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor, parent); + asTransaction([&](Transaction& t) { + t.setColor(colorLayer, half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f}); + t.setAlpha(colorLayer, color.a / 255.0f); + }); + return colorLayer; + } + + ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) { + // wait for previous transactions (such as setSize) to complete + Transaction().apply(true); + + ANativeWindow_Buffer buffer = {}; + EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr)); + + return buffer; + } + + void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) { + ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost()); + + // wait for the newly posted buffer to be latched + waitForLayerBuffers(); + } + + virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color, + int32_t bufferWidth, int32_t bufferHeight) { + ANativeWindow_Buffer buffer; + ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); + TransactionUtils::fillANativeWindowBufferColor(buffer, + Rect(0, 0, bufferWidth, bufferHeight), + color); + postBufferQueueLayerBuffer(layer); + } + + virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color, + int32_t bufferWidth, int32_t bufferHeight) { + sp<GraphicBuffer> buffer = + new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), + color); + Transaction().setBuffer(layer, buffer).apply(); + } + + void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color, + int32_t bufferWidth, int32_t bufferHeight) { + switch (mLayerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight); + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight); + break; + default: + ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType; + } + } + + void fillLayerQuadrant(uint32_t mLayerType, const sp<SurfaceControl>& layer, + int32_t bufferWidth, int32_t bufferHeight, const Color& topLeft, + const Color& topRight, const Color& bottomLeft, + const Color& bottomRight) { + switch (mLayerType) { + case ISurfaceComposerClient::eFXSurfaceBufferQueue: + fillBufferQueueLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight, + bottomLeft, bottomRight); + break; + case ISurfaceComposerClient::eFXSurfaceBufferState: + fillBufferStateLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight, + bottomLeft, bottomRight); + break; + default: + ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType; + } + } + + virtual void fillBufferQueueLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth, + int32_t bufferHeight, const Color& topLeft, + const Color& topRight, const Color& bottomLeft, + const Color& bottomRight) { + ANativeWindow_Buffer buffer; + ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); + ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0); + + const int32_t halfW = bufferWidth / 2; + const int32_t halfH = bufferHeight / 2; + TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft); + TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), + topRight); + TransactionUtils::fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), + bottomLeft); + TransactionUtils::fillANativeWindowBufferColor(buffer, + Rect(halfW, halfH, bufferWidth, + bufferHeight), + bottomRight); + + postBufferQueueLayerBuffer(layer); + } + + virtual void fillBufferStateLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth, + int32_t bufferHeight, const Color& topLeft, + const Color& topRight, const Color& bottomLeft, + const Color& bottomRight) { + sp<GraphicBuffer> buffer = + new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1, + BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, + "test"); + + ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0); + + const int32_t halfW = bufferWidth / 2; + const int32_t halfH = bufferHeight / 2; + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), + topRight); + TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), + bottomLeft); + TransactionUtils::fillGraphicBufferColor(buffer, + Rect(halfW, halfH, bufferWidth, bufferHeight), + bottomRight); + + Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply(); + } + + std::unique_ptr<ScreenCapture> screenshot() { + std::unique_ptr<ScreenCapture> screenshot; + ScreenCapture::captureScreen(&screenshot); + return screenshot; + } + + void asTransaction(const std::function<void(Transaction&)>& exec) { + Transaction t; + exec(t); + t.apply(true); + } + + static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { + static BufferGenerator bufferGenerator; + return bufferGenerator.get(outBuffer, outFence); + } + + sp<SurfaceComposerClient> mClient; + + sp<IBinder> mDisplay; + uint32_t mDisplayWidth; + uint32_t mDisplayHeight; + uint32_t mDisplayLayerStack; + Rect mDisplayRect = Rect::INVALID_RECT; + + // leave room for ~256 layers + const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256; + + sp<SurfaceControl> mBlackBgSurface; + bool mColorManagementUsed; + +private: + void SetUpDisplay() { + mDisplay = mClient->getInternalDisplayToken(); + ASSERT_FALSE(mDisplay == nullptr) << "failed to get display"; + + // get display width/height + DisplayInfo info; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info)); + mDisplayWidth = info.w; + mDisplayHeight = info.h; + mDisplayRect = + Rect(static_cast<int32_t>(mDisplayWidth), static_cast<int32_t>(mDisplayHeight)); + + // After a new buffer is queued, SurfaceFlinger is notified and will + // latch the new buffer on next vsync. Let's heuristically wait for 3 + // vsyncs. + mBufferPostDelay = int32_t(1e6 / info.fps) * 3; + + mDisplayLayerStack = 0; + + mBlackBgSurface = + createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor); + + // set layer stack (b/68888219) + Transaction t; + t.setDisplayLayerStack(mDisplay, mDisplayLayerStack); + t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight)); + t.setLayerStack(mBlackBgSurface, mDisplayLayerStack); + t.setColor(mBlackBgSurface, half3{0, 0, 0}); + t.setLayer(mBlackBgSurface, mLayerZBase); + t.apply(); + } + + void waitForLayerBuffers() { + // Request an empty transaction to get applied synchronously to ensure the buffer is + // latched. + Transaction().apply(true); + usleep(mBufferPostDelay); + } + + int32_t mBufferPostDelay; + + friend class LayerRenderPathTestHarness; +}; +} // namespace android + +#endif diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp new file mode 100644 index 0000000000..35c51e1915 --- /dev/null +++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2019 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 <private/android_filesystem_config.h> +#include <thread> +#include "LayerTransactionTest.h" + +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<GraphicBuffer> outBuffer; + Transaction() + .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure) + .apply(true); + ASSERT_EQ(PERMISSION_DENIED, + composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); + + UIDFaker f(AID_SYSTEM); + + // By default the system can capture screenshots with secure layers but they + // will be blacked out + ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); + + { + SCOPED_TRACE("as system"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + } + + // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able + // to receive them...we are expected to take care with the results. + bool outCapturedSecureLayers; + ASSERT_EQ(NO_ERROR, + composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers, + ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0, + 0, false, ISurfaceComposer::eRotateNone, true)); + ASSERT_EQ(true, outCapturedSecureLayers); + ScreenCapture sc(outBuffer); + sc.expectColor(Rect(0, 0, 32, 32), Color::RED); +} + +TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + Transaction().setTransformToDisplayInverse(layer, false).apply(); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32)); + + Transaction().setTransformToDisplayInverse(layer, true).apply(); +} + +TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); + + // verify this doesn't cause a crash + Transaction().setSidebandStream(layer, nullptr).apply(); +} + +TEST_F(LayerTransactionTest, ReparentToSelf) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + Transaction().reparent(layer, layer->getHandle()).apply(); + + { + // We expect the transaction to be silently dropped, but for SurfaceFlinger + // to still be functioning. + SCOPED_TRACE("after reparent to self"); + const Rect rect(0, 0, 32, 32); + auto shot = screenshot(); + shot->expectColor(rect, Color::RED); + shot->expectBorder(rect, Color::BLACK); + } +} + +// This test ensures that when we drop an app buffer in SurfaceFlinger, we merge +// the dropped buffer's damage region into the next buffer's damage region. If +// we don't do this, we'll report an incorrect damage region to hardware +// composer, resulting in broken rendering. This test checks the BufferQueue +// case. +// +// Unfortunately, we don't currently have a way to inspect the damage region +// SurfaceFlinger sends to hardware composer from a test, so this test requires +// the dev to manually watch the device's screen during the test to spot broken +// rendering. Because the results can't be automatically verified, this test is +// marked disabled. +TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDroppingBuffers) { + const int width = mDisplayWidth; + const int height = mDisplayHeight; + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height)); + const auto producer = layer->getIGraphicBufferProducer(); + const sp<IProducerListener> dummyListener(new DummyProducerListener); + IGraphicBufferProducer::QueueBufferOutput queueBufferOutput; + ASSERT_EQ(OK, + producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput)); + + std::map<int, sp<GraphicBuffer>> slotMap; + auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) { + ASSERT_NE(nullptr, buf); + const auto iter = slotMap.find(slot); + ASSERT_NE(slotMap.end(), iter); + *buf = iter->second; + }; + + auto dequeue = [&](int* outSlot) { + ASSERT_NE(nullptr, outSlot); + *outSlot = -1; + int slot; + sp<Fence> fence; + uint64_t age; + FrameEventHistoryDelta timestamps; + const status_t dequeueResult = + producer->dequeueBuffer(&slot, &fence, width, height, PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + &age, ×tamps); + if (dequeueResult == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { + sp<GraphicBuffer> newBuf; + ASSERT_EQ(OK, producer->requestBuffer(slot, &newBuf)); + ASSERT_NE(nullptr, newBuf.get()); + slotMap[slot] = newBuf; + } else { + ASSERT_EQ(OK, dequeueResult); + } + *outSlot = slot; + }; + + auto queue = [&](int slot, const Region& damage, nsecs_t displayTime) { + IGraphicBufferProducer::QueueBufferInput input( + /*timestamp=*/displayTime, /*isAutoTimestamp=*/false, HAL_DATASPACE_UNKNOWN, + /*crop=*/Rect::EMPTY_RECT, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, + /*transform=*/0, Fence::NO_FENCE); + input.setSurfaceDamage(damage); + IGraphicBufferProducer::QueueBufferOutput output; + ASSERT_EQ(OK, producer->queueBuffer(slot, input, &output)); + }; + + auto fillAndPostBuffers = [&](const Color& color) { + int slot1; + ASSERT_NO_FATAL_FAILURE(dequeue(&slot1)); + int slot2; + ASSERT_NO_FATAL_FAILURE(dequeue(&slot2)); + + sp<GraphicBuffer> buf1; + ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot1, &buf1)); + sp<GraphicBuffer> buf2; + ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot2, &buf2)); + TransactionUtils::fillGraphicBufferColor(buf1, Rect(width, height), color); + TransactionUtils::fillGraphicBufferColor(buf2, Rect(width, height), color); + + const auto displayTime = systemTime() + milliseconds_to_nanoseconds(100); + ASSERT_NO_FATAL_FAILURE(queue(slot1, Region::INVALID_REGION, displayTime)); + ASSERT_NO_FATAL_FAILURE( + queue(slot2, Region(Rect(width / 3, height / 3, 2 * width / 3, 2 * height / 3)), + displayTime)); + }; + + const auto startTime = systemTime(); + const std::array<Color, 3> colors = {Color::RED, Color::GREEN, Color::BLUE}; + int colorIndex = 0; + while (nanoseconds_to_seconds(systemTime() - startTime) < 10) { + ASSERT_NO_FATAL_FAILURE(fillAndPostBuffers(colors[colorIndex++ % colors.size()])); + std::this_thread::sleep_for(1s); + } + + ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU)); +} +} // namespace android diff --git a/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp new file mode 100644 index 0000000000..daeff17ea3 --- /dev/null +++ b/services/surfaceflinger/tests/LayerTypeAndRenderTypeTransaction_test.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2019 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 <gui/BufferItemConsumer.h> +#include "TransactionTestHarnesses.h" + +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +class LayerTypeAndRenderTypeTransactionTest + : public LayerTypeTransactionHarness, + public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath>> { +public: + LayerTypeAndRenderTypeTransactionTest() + : LayerTypeTransactionHarness(std::get<0>(GetParam())), + mRenderPathHarness(LayerRenderPathTestHarness(this, std::get<1>(GetParam()))) {} + + std::unique_ptr<ScreenCapture> getScreenCapture() { + return mRenderPathHarness.getScreenCapture(); + } + +protected: + LayerRenderPathTestHarness mRenderPathHarness; +}; + +::testing::Environment* const binderEnv = + ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); + +INSTANTIATE_TEST_CASE_P( + LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest, + ::testing::Combine( + ::testing::Values( + static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue), + static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)), + ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT))); + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) { + // cannot test robustness against invalid sizes (zero or really huge) +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) { + sp<SurfaceControl> layerR; + sp<SurfaceControl> layerG; + ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); + + Transaction().setLayer(layerR, mLayerZBase + 1).apply(); + { + SCOPED_TRACE("layerR"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); + } + + Transaction().setLayer(layerG, mLayerZBase + 2).apply(); + { + SCOPED_TRACE("layerG"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN); + } +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) { + sp<SurfaceControl> layerR; + sp<SurfaceControl> layerG; + + ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); + + Transaction() + .setPosition(layerG, 16, 16) + .setRelativeLayer(layerG, layerR->getHandle(), 1) + .apply(); + + layerG.clear(); + // layerG should have been removed + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); + + Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply(); + { + SCOPED_TRACE("layer hidden"); + getScreenCapture()->expectColor(mDisplayRect, Color::BLACK); + } + + Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply(); + { + SCOPED_TRACE("layer shown"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); + } +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsOpaque) { + const Color translucentRed = {100, 0, 0, 100}; + sp<SurfaceControl> layerR; + sp<SurfaceControl> layerG; + ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed, 32, 32)); + ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); + + Transaction() + .setLayer(layerR, mLayerZBase + 1) + .setFlags(layerR, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque) + .apply(); + { + SCOPED_TRACE("layerR opaque"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255}); + } + + Transaction().setFlags(layerR, 0, layer_state_t::eLayerOpaque).apply(); + { + SCOPED_TRACE("layerR translucent"); + const uint8_t g = uint8_t(255 - translucentRed.a); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255}); + } +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) { + sp<SurfaceControl> parent = + LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceContainer); + Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply(); + sp<SurfaceControl> layerR; + sp<SurfaceControl> layerG; + ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); + + Transaction() + .reparent(layerR, parent->getHandle()) + .reparent(layerG, parent->getHandle()) + .apply(); + Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply(); + { + SCOPED_TRACE("layerR"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::RED); + } + + Transaction().setLayer(layerR, -3).apply(); + { + SCOPED_TRACE("layerG"); + auto shot = getScreenCapture(); + shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN); + } +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetAlphaClamped) { + const Color color = {64, 0, 0, 255}; + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color, 32, 32)); + + Transaction().setAlpha(layer, 2.0f).apply(); + { + SCOPED_TRACE("clamped to 1.0f"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color); + } + + Transaction().setAlpha(layer, -1.0f).apply(); + { + SCOPED_TRACE("clamped to 0.0f"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + } +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) { + sp<SurfaceControl> layer; + const uint8_t size = 64; + const uint8_t testArea = 4; + const float cornerRadius = 20.0f; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size)); + + Transaction() + .setCornerRadius(layer, cornerRadius) + .setCrop_legacy(layer, Rect(0, 0, size, size)) + .apply(); + { + const uint8_t bottom = size - 1; + const uint8_t right = size - 1; + auto shot = getScreenCapture(); + // Transparent corners + shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK); + shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK); + shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK); + shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK); + } +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) { + sp<SurfaceControl> parent; + sp<SurfaceControl> child; + const uint8_t size = 64; + const uint8_t testArea = 4; + const float cornerRadius = 20.0f; + ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size)); + ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2)); + + Transaction() + .setCornerRadius(parent, cornerRadius) + .setCrop_legacy(parent, Rect(0, 0, size, size)) + .reparent(child, parent->getHandle()) + .setPosition(child, 0, size / 2) + .apply(); + { + const uint8_t bottom = size - 1; + const uint8_t right = size - 1; + auto shot = getScreenCapture(); + // Top edge of child should not have rounded corners because it's translated in the parent + shot->expectColor(Rect(0, size / 2, right, static_cast<int>(bottom - cornerRadius)), + Color::GREEN); + // But bottom edges should have been clipped according to parent bounds + shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK); + shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK); + } +} +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) { + sp<SurfaceControl> bufferLayer; + ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32)); + + // color is ignored + Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply(); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); +} + +TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); + + Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply(); + { + SCOPED_TRACE("non-existing layer stack"); + getScreenCapture()->expectColor(mDisplayRect, Color::BLACK); + } + + Transaction().setLayerStack(layer, mDisplayLayerStack).apply(); + { + SCOPED_TRACE("original layer stack"); + getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); + } +} +} // namespace android diff --git a/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp new file mode 100644 index 0000000000..42ec34a129 --- /dev/null +++ b/services/surfaceflinger/tests/LayerTypeTransaction_test.cpp @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2019 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 <gui/BufferItemConsumer.h> +#include "TransactionTestHarnesses.h" + +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +class LayerTypeTransactionTest : public LayerTypeTransactionHarness, + public ::testing::WithParamInterface<uint32_t> { +public: + LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {} +}; + +::testing::Environment* const binderEnv = + ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); + +INSTANTIATE_TEST_CASE_P( + LayerTypeTransactionTests, LayerTypeTransactionTest, + ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue), + static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState))); + +TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) { + sp<SurfaceControl> parent = + LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceContainer); + Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply(); + sp<SurfaceControl> layerR; + sp<SurfaceControl> layerG; + sp<SurfaceControl> layerB; + ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); + ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); + ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32)); + + Transaction().reparent(layerB, parent->getHandle()).apply(); + + // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2 + Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply(); + + std::unique_ptr<ScreenCapture> screenshot; + // only layerB is in this range + sp<IBinder> parentHandle = parent->getHandle(); + ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32)); + screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); +} + +TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) { + sp<SurfaceControl> parent = + LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor); + + sp<SurfaceControl> childLayer; + ASSERT_NO_FATAL_FAILURE( + childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */, + 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor, + parent.get())); + Transaction() + .setColor(childLayer, half3{1.0f, 0.0f, 0.0f}) + .setColor(parent, half3{0.0f, 0.0f, 0.0f}) + .show(childLayer) + .show(parent) + .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)) + .setCrop_legacy(childLayer, Rect(0, 0, 20, 30)) + .apply(); + + Transaction() + .setRelativeLayer(childLayer, parent->getHandle(), -1) + .setLayer(childLayer, 1) + .apply(); + + { + SCOPED_TRACE("setLayer above"); + // Set layer should get applied and place the child above. + std::unique_ptr<ScreenCapture> screenshot; + ScreenCapture::captureScreen(&screenshot); + screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED); + } + + Transaction() + .setLayer(childLayer, 1) + .setRelativeLayer(childLayer, parent->getHandle(), -1) + .apply(); + + { + SCOPED_TRACE("setRelative below"); + // Set relative layer should get applied and place the child below. + std::unique_ptr<ScreenCapture> screenshot; + ScreenCapture::captureScreen(&screenshot); + screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK); + } +} + +TEST_P(LayerTypeTransactionTest, HideRelativeParentHidesLayer) { + sp<SurfaceControl> parent = + LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor); + sp<SurfaceControl> relativeParent = + LayerTransactionTest::createLayer("RelativeParent", 0 /* buffer width */, + 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor); + + sp<SurfaceControl> childLayer; + ASSERT_NO_FATAL_FAILURE( + childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */, + 0 /* buffer height */, + ISurfaceComposerClient::eFXSurfaceColor, + parent.get())); + Transaction() + .setColor(childLayer, half3{1.0f, 0.0f, 0.0f}) + .setColor(parent, half3{0.0f, 0.0f, 0.0f}) + .setColor(relativeParent, half3{0.0f, 1.0f, 0.0f}) + .show(childLayer) + .show(parent) + .show(relativeParent) + .setLayer(parent, mLayerZBase - 1) + .setLayer(relativeParent, mLayerZBase) + .apply(); + + Transaction().setRelativeLayer(childLayer, relativeParent->getHandle(), 1).apply(); + + { + SCOPED_TRACE("setLayer above"); + // Set layer should get applied and place the child above. + std::unique_ptr<ScreenCapture> screenshot; + ScreenCapture::captureScreen(&screenshot); + screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED); + } + + Transaction().hide(relativeParent).apply(); + + { + SCOPED_TRACE("hide relative parent"); + // The relative should no longer be visible. + std::unique_ptr<ScreenCapture> screenshot; + ScreenCapture::captureScreen(&screenshot); + screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK); + } +} + +TEST_P(LayerTypeTransactionTest, SetFlagsSecure) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); + + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<GraphicBuffer> outBuffer; + Transaction() + .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure) + .apply(true); + ASSERT_EQ(PERMISSION_DENIED, + composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); + + Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true); + ASSERT_EQ(NO_ERROR, composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); +} +TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + + sp<IBinder> handle = layer->getHandle(); + ASSERT_TRUE(handle != nullptr); + + FrameStats frameStats; + mClient->getLayerFrameStats(handle, &frameStats); + + ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0)); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp new file mode 100644 index 0000000000..73f563dfc7 --- /dev/null +++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp @@ -0,0 +1,1655 @@ +/* + * Copyright (C) 2019 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 "LayerTransactionTest.h" + +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +::testing::Environment* const binderEnv = + ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); + +class LayerUpdateTest : public LayerTransactionTest { +protected: + virtual void SetUp() { + LayerTransactionTest::SetUp(); + ASSERT_EQ(NO_ERROR, mClient->initCheck()); + + const auto display = SurfaceComposerClient::getInternalDisplayToken(); + ASSERT_FALSE(display == nullptr); + + DisplayInfo info; + ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info)); + + ssize_t displayWidth = info.w; + ssize_t displayHeight = info.h; + + // Background surface + mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth, displayHeight, 0); + ASSERT_TRUE(mBGSurfaceControl != nullptr); + ASSERT_TRUE(mBGSurfaceControl->isValid()); + TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195); + + // Foreground surface + mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0); + + ASSERT_TRUE(mFGSurfaceControl != nullptr); + ASSERT_TRUE(mFGSurfaceControl->isValid()); + + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); + + // Synchronization surface + mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0); + ASSERT_TRUE(mSyncSurfaceControl != nullptr); + ASSERT_TRUE(mSyncSurfaceControl->isValid()); + + TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); + + asTransaction([&](Transaction& t) { + t.setDisplayLayerStack(display, 0); + + t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl); + + t.setLayer(mFGSurfaceControl, INT32_MAX - 1) + .setPosition(mFGSurfaceControl, 64, 64) + .show(mFGSurfaceControl); + + t.setLayer(mSyncSurfaceControl, INT32_MAX - 1) + .setPosition(mSyncSurfaceControl, displayWidth - 2, displayHeight - 2) + .show(mSyncSurfaceControl); + }); + } + + virtual void TearDown() { + LayerTransactionTest::TearDown(); + mBGSurfaceControl = 0; + mFGSurfaceControl = 0; + mSyncSurfaceControl = 0; + } + + void waitForPostedBuffers() { + // Since the sync surface is in synchronous mode (i.e. double buffered) + // posting three buffers to it should ensure that at least two + // SurfaceFlinger::handlePageFlip calls have been made, which should + // guaranteed that a buffer posted to another Surface has been retired. + TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); + TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); + TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); + } + + sp<SurfaceControl> mBGSurfaceControl; + sp<SurfaceControl> mFGSurfaceControl; + + // This surface is used to ensure that the buffers posted to + // mFGSurfaceControl have been picked up by SurfaceFlinger. + sp<SurfaceControl> mSyncSurfaceControl; +}; + +TEST_F(LayerUpdateTest, RelativesAreNotDetached) { + std::unique_ptr<ScreenCapture> sc; + + sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0); + TransactionUtils::fillSurfaceRGBA8(relative, 10, 10, 10); + waitForPostedBuffers(); + + Transaction{} + .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1) + .setPosition(relative, 64, 64) + .apply(); + + { + // The relative should be on top of the FG control. + ScreenCapture::captureScreen(&sc); + sc->checkPixel(64, 64, 10, 10, 10); + } + Transaction{}.detachChildren(mFGSurfaceControl).apply(); + + { + // Nothing should change at this point. + ScreenCapture::captureScreen(&sc); + sc->checkPixel(64, 64, 10, 10, 10); + } + + Transaction{}.hide(relative).apply(); + + { + // Ensure that the relative was actually hidden, rather than + // being left in the detached but visible state. + ScreenCapture::captureScreen(&sc); + sc->expectFGColor(64, 64); + } +} + +class GeometryLatchingTest : public LayerUpdateTest { +protected: + void EXPECT_INITIAL_STATE(const char* trace) { + SCOPED_TRACE(trace); + ScreenCapture::captureScreen(&sc); + // We find the leading edge of the FG surface. + sc->expectFGColor(127, 127); + sc->expectBGColor(128, 128); + } + + void lockAndFillFGBuffer() { + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63, false); + } + + void unlockFGBuffer() { + sp<Surface> s = mFGSurfaceControl->getSurface(); + ASSERT_EQ(NO_ERROR, s->unlockAndPost()); + waitForPostedBuffers(); + } + + void completeFGResize() { + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); + waitForPostedBuffers(); + } + void restoreInitialState() { + asTransaction([&](Transaction& t) { + t.setSize(mFGSurfaceControl, 64, 64); + t.setPosition(mFGSurfaceControl, 64, 64); + t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64)); + }); + + EXPECT_INITIAL_STATE("After restoring initial state"); + } + std::unique_ptr<ScreenCapture> sc; +}; + +class CropLatchingTest : public GeometryLatchingTest { +protected: + void EXPECT_CROPPED_STATE(const char* trace) { + SCOPED_TRACE(trace); + ScreenCapture::captureScreen(&sc); + // The edge should be moved back one pixel by our crop. + sc->expectFGColor(126, 126); + sc->expectBGColor(127, 127); + sc->expectBGColor(128, 128); + } + + void EXPECT_RESIZE_STATE(const char* trace) { + SCOPED_TRACE(trace); + ScreenCapture::captureScreen(&sc); + // The FG is now resized too 128,128 at 64,64 + sc->expectFGColor(64, 64); + sc->expectFGColor(191, 191); + sc->expectBGColor(192, 192); + } +}; + +TEST_F(LayerUpdateTest, DeferredTransactionTest) { + std::unique_ptr<ScreenCapture> sc; + { + SCOPED_TRACE("before anything"); + ScreenCapture::captureScreen(&sc); + sc->expectBGColor(32, 32); + sc->expectFGColor(96, 96); + sc->expectBGColor(160, 160); + } + + // set up two deferred transactions on different frames + asTransaction([&](Transaction& t) { + t.setAlpha(mFGSurfaceControl, 0.75); + t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), + mSyncSurfaceControl->getSurface()->getNextFrameNumber()); + }); + + asTransaction([&](Transaction& t) { + t.setPosition(mFGSurfaceControl, 128, 128); + t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), + mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1); + }); + + { + SCOPED_TRACE("before any trigger"); + ScreenCapture::captureScreen(&sc); + sc->expectBGColor(32, 32); + sc->expectFGColor(96, 96); + sc->expectBGColor(160, 160); + } + + // should trigger the first deferred transaction, but not the second one + TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); + { + SCOPED_TRACE("after first trigger"); + ScreenCapture::captureScreen(&sc); + sc->expectBGColor(32, 32); + sc->checkPixel(96, 96, 162, 63, 96); + sc->expectBGColor(160, 160); + } + + // should show up immediately since it's not deferred + asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 1.0); }); + + // trigger the second deferred transaction + TransactionUtils::fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); + { + SCOPED_TRACE("after second trigger"); + ScreenCapture::captureScreen(&sc); + sc->expectBGColor(32, 32); + sc->expectBGColor(96, 96); + sc->expectFGColor(160, 160); + } +} + +TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) { + std::unique_ptr<ScreenCapture> sc; + + sp<SurfaceControl> childNoBuffer = + createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20, + PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get()); + TransactionUtils::fillSurfaceRGBA8(childBuffer, 200, 200, 200); + SurfaceComposerClient::Transaction{} + .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10)) + .show(childNoBuffer) + .show(childBuffer) + .apply(true); + { + ScreenCapture::captureScreen(&sc); + sc->expectChildColor(73, 73); + sc->expectFGColor(74, 74); + } + SurfaceComposerClient::Transaction{} + .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20)) + .apply(true); + { + ScreenCapture::captureScreen(&sc); + sc->expectChildColor(73, 73); + sc->expectChildColor(74, 74); + } +} + +TEST_F(LayerUpdateTest, MergingTransactions) { + std::unique_ptr<ScreenCapture> sc; + { + SCOPED_TRACE("before move"); + ScreenCapture::captureScreen(&sc); + sc->expectBGColor(0, 12); + sc->expectFGColor(75, 75); + sc->expectBGColor(145, 145); + } + + Transaction t1, t2; + t1.setPosition(mFGSurfaceControl, 128, 128); + t2.setPosition(mFGSurfaceControl, 0, 0); + // We expect that the position update from t2 now + // overwrites the position update from t1. + t1.merge(std::move(t2)); + t1.apply(); + + { + ScreenCapture::captureScreen(&sc); + sc->expectFGColor(1, 1); + } +} + +TEST_F(LayerUpdateTest, MergingTransactionFlags) { + Transaction().hide(mFGSurfaceControl).apply(); + std::unique_ptr<ScreenCapture> sc; + { + SCOPED_TRACE("before merge"); + ScreenCapture::captureScreen(&sc); + sc->expectBGColor(0, 12); + sc->expectBGColor(75, 75); + sc->expectBGColor(145, 145); + } + + Transaction t1, t2; + t1.show(mFGSurfaceControl); + t2.setFlags(mFGSurfaceControl, 0 /* flags */, layer_state_t::eLayerSecure /* mask */); + t1.merge(std::move(t2)); + t1.apply(); + + { + SCOPED_TRACE("after merge"); + ScreenCapture::captureScreen(&sc); + sc->expectFGColor(75, 75); + } +} + +class ChildLayerTest : public LayerUpdateTest { +protected: + void SetUp() override { + LayerUpdateTest::SetUp(); + mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0, + mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200); + + { + SCOPED_TRACE("before anything"); + mCapture = screenshot(); + mCapture->expectChildColor(64, 64); + } + } + void TearDown() override { + LayerUpdateTest::TearDown(); + mChild = 0; + } + + sp<SurfaceControl> mChild; + std::unique_ptr<ScreenCapture> mCapture; +}; + +TEST_F(ChildLayerTest, ChildLayerPositioning) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 10, 10); + t.setPosition(mFGSurfaceControl, 64, 64); + }); + + { + mCapture = screenshot(); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(74, 74); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(84, 84); + } + + asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); }); + + { + mCapture = screenshot(); + // Top left of foreground should now be at 0, 0 + mCapture->expectFGColor(0, 0); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(10, 10); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(20, 20); + } +} + +TEST_F(ChildLayerTest, ChildLayerCropping) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 0, 0); + t.setPosition(mFGSurfaceControl, 0, 0); + t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5)); + }); + + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(4, 4); + mCapture->expectBGColor(5, 5); + } +} + +TEST_F(ChildLayerTest, ChildLayerConstraints) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mFGSurfaceControl, 0, 0); + t.setPosition(mChild, 63, 63); + }); + + { + mCapture = screenshot(); + mCapture->expectFGColor(0, 0); + // Last pixel in foreground should now be the child. + mCapture->expectChildColor(63, 63); + // But the child should be constrained and the next pixel + // must be the background + mCapture->expectBGColor(64, 64); + } +} + +TEST_F(ChildLayerTest, ChildLayerScaling) { + asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); }); + + // Find the boundary between the parent and child + { + mCapture = screenshot(); + mCapture->expectChildColor(9, 9); + mCapture->expectFGColor(10, 10); + } + + asTransaction([&](Transaction& t) { t.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0); }); + + // The boundary should be twice as far from the origin now. + // The pixels from the last test should all be child now + { + mCapture = screenshot(); + mCapture->expectChildColor(9, 9); + mCapture->expectChildColor(10, 10); + mCapture->expectChildColor(19, 19); + mCapture->expectFGColor(20, 20); + } +} + +// A child with a scale transform should be cropped by its parent bounds. +TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) { + asTransaction([&](Transaction& t) { + t.setPosition(mFGSurfaceControl, 0, 0); + t.setPosition(mChild, 0, 0); + }); + + // Find the boundary between the parent and child. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(9, 9); + mCapture->expectFGColor(10, 10); + } + + asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); }); + + // The child should fill its parent bounds and be cropped by it. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(63, 63); + mCapture->expectBGColor(64, 64); + } +} + +TEST_F(ChildLayerTest, ChildLayerAlpha) { + TransactionUtils::fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254); + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0); + TransactionUtils::fillSurfaceRGBA8(mChild, 0, 254, 0); + waitForPostedBuffers(); + + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 0, 0); + t.setPosition(mFGSurfaceControl, 0, 0); + }); + + { + mCapture = screenshot(); + // Unblended child color + mCapture->checkPixel(0, 0, 0, 254, 0); + } + + asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); }); + + { + mCapture = screenshot(); + // Child and BG blended. + mCapture->checkPixel(0, 0, 127, 127, 0); + } + + asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); }); + + { + mCapture = screenshot(); + // Child and BG blended. + mCapture->checkPixel(0, 0, 95, 64, 95); + } +} + +TEST_F(ChildLayerTest, ReparentChildren) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 10, 10); + t.setPosition(mFGSurfaceControl, 64, 64); + }); + + { + mCapture = screenshot(); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(74, 74); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(84, 84); + } + + asTransaction([&](Transaction& t) { + t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle()); + }); + + { + mCapture = screenshot(); + mCapture->expectFGColor(64, 64); + // In reparenting we should have exposed the entire foreground surface. + mCapture->expectFGColor(74, 74); + // And the child layer should now begin at 10, 10 (since the BG + // layer is at (0, 0)). + mCapture->expectBGColor(9, 9); + mCapture->expectChildColor(10, 10); + } +} + +TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) { + sp<SurfaceControl> mGrandChild = + createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get()); + TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111); + + { + SCOPED_TRACE("Grandchild visible"); + ScreenCapture::captureScreen(&mCapture); + mCapture->checkPixel(64, 64, 111, 111, 111); + } + + mChild.clear(); + + { + SCOPED_TRACE("After destroying child"); + ScreenCapture::captureScreen(&mCapture); + mCapture->expectFGColor(64, 64); + } + + asTransaction([&](Transaction& t) { t.reparent(mGrandChild, mFGSurfaceControl->getHandle()); }); + + { + SCOPED_TRACE("After reparenting grandchild"); + ScreenCapture::captureScreen(&mCapture); + mCapture->checkPixel(64, 64, 111, 111, 111); + } +} + +TEST_F(ChildLayerTest, ChildrenRelativeZSurvivesParentDestruction) { + sp<SurfaceControl> mGrandChild = + createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get()); + TransactionUtils::fillSurfaceRGBA8(mGrandChild, 111, 111, 111); + + // draw grand child behind the foreground surface + asTransaction([&](Transaction& t) { + t.setRelativeLayer(mGrandChild, mFGSurfaceControl->getHandle(), -1); + }); + + { + SCOPED_TRACE("Child visible"); + ScreenCapture::captureScreen(&mCapture); + mCapture->checkPixel(64, 64, 200, 200, 200); + } + + asTransaction([&](Transaction& t) { + t.reparent(mChild, nullptr); + t.reparentChildren(mChild, mFGSurfaceControl->getHandle()); + }); + + { + SCOPED_TRACE("foreground visible reparenting grandchild"); + ScreenCapture::captureScreen(&mCapture); + mCapture->checkPixel(64, 64, 195, 63, 63); + } +} + +TEST_F(ChildLayerTest, DetachChildrenSameClient) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 10, 10); + t.setPosition(mFGSurfaceControl, 64, 64); + }); + + { + mCapture = screenshot(); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(74, 74); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(84, 84); + } + + asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); }); + + asTransaction([&](Transaction& t) { t.hide(mChild); }); + + // Since the child has the same client as the parent, it will not get + // detached and will be hidden. + { + mCapture = screenshot(); + mCapture->expectFGColor(64, 64); + mCapture->expectFGColor(74, 74); + mCapture->expectFGColor(84, 84); + } +} + +TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { + sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> mChildNewClient = + createSurface(mNewComposerClient, "New Child Test Surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + + ASSERT_TRUE(mChildNewClient->isValid()); + + TransactionUtils::fillSurfaceRGBA8(mChildNewClient, 200, 200, 200); + + asTransaction([&](Transaction& t) { + t.hide(mChild); + t.show(mChildNewClient); + t.setPosition(mChildNewClient, 10, 10); + t.setPosition(mFGSurfaceControl, 64, 64); + }); + + { + mCapture = screenshot(); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(74, 74); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(84, 84); + } + + asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); }); + + asTransaction([&](Transaction& t) { t.hide(mChildNewClient); }); + + // Nothing should have changed. + { + mCapture = screenshot(); + mCapture->expectFGColor(64, 64); + mCapture->expectChildColor(74, 74); + mCapture->expectFGColor(84, 84); + } +} + +TEST_F(ChildLayerTest, DetachChildrenThenAttach) { + sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> childNewClient = + newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + + ASSERT_TRUE(childNewClient != nullptr); + ASSERT_TRUE(childNewClient->isValid()); + + TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200); + + Transaction() + .hide(mChild) + .show(childNewClient) + .setPosition(childNewClient, 10, 10) + .setPosition(mFGSurfaceControl, 64, 64) + .apply(); + + { + mCapture = screenshot(); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(74, 74); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(84, 84); + } + + Transaction().detachChildren(mFGSurfaceControl).apply(); + Transaction().hide(childNewClient).apply(); + + // Nothing should have changed. + { + mCapture = screenshot(); + mCapture->expectFGColor(64, 64); + mCapture->expectChildColor(74, 74); + mCapture->expectFGColor(84, 84); + } + + sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0); + fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32, + 32); + Transaction() + .setLayer(newParentSurface, INT32_MAX - 1) + .show(newParentSurface) + .setPosition(newParentSurface, 20, 20) + .reparent(childNewClient, newParentSurface->getHandle()) + .apply(); + { + mCapture = screenshot(); + // Child is now hidden. + mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED); + } +} +TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) { + sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> childNewClient = + newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + + ASSERT_TRUE(childNewClient != nullptr); + ASSERT_TRUE(childNewClient->isValid()); + + TransactionUtils::fillSurfaceRGBA8(childNewClient, 200, 200, 200); + + Transaction() + .hide(mChild) + .show(childNewClient) + .setPosition(childNewClient, 10, 10) + .setPosition(mFGSurfaceControl, 64, 64) + .apply(); + + { + mCapture = screenshot(); + Rect rect = Rect(74, 74, 84, 84); + mCapture->expectBorder(rect, Color{195, 63, 63, 255}); + mCapture->expectColor(rect, Color{200, 200, 200, 255}); + } + + Transaction() + .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(), + mFGSurfaceControl->getSurface()->getNextFrameNumber()) + .apply(); + Transaction().detachChildren(mFGSurfaceControl).apply(); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32)); + + // BufferLayer can still dequeue buffers even though there's a detached layer with a + // deferred transaction. + { + SCOPED_TRACE("new buffer"); + mCapture = screenshot(); + Rect rect = Rect(74, 74, 84, 84); + mCapture->expectBorder(rect, Color::RED); + mCapture->expectColor(rect, Color{200, 200, 200, 255}); + } +} + +TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 0, 0); + t.setPosition(mFGSurfaceControl, 0, 0); + }); + + { + mCapture = screenshot(); + // We've positioned the child in the top left. + mCapture->expectChildColor(0, 0); + // But it's only 10x15. + mCapture->expectFGColor(10, 15); + } + + asTransaction([&](Transaction& t) { + t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + // We cause scaling by 2. + t.setSize(mFGSurfaceControl, 128, 128); + }); + + { + mCapture = screenshot(); + // We've positioned the child in the top left. + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(10, 10); + mCapture->expectChildColor(19, 29); + // And now it should be scaled all the way to 20x30 + mCapture->expectFGColor(20, 30); + } +} + +// Regression test for b/37673612 +TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 0, 0); + t.setPosition(mFGSurfaceControl, 0, 0); + }); + + { + mCapture = screenshot(); + // We've positioned the child in the top left. + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(9, 14); + // But it's only 10x15. + mCapture->expectFGColor(10, 15); + } + // We set things up as in b/37673612 so that there is a mismatch between the buffer size and + // the WM specified state size. + asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); }); + sp<Surface> s = mFGSurfaceControl->getSurface(); + auto anw = static_cast<ANativeWindow*>(s.get()); + native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); + native_window_set_buffers_dimensions(anw, 64, 128); + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); + waitForPostedBuffers(); + + { + // The child should still be in the same place and not have any strange scaling as in + // b/37673612. + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectFGColor(10, 10); + } +} + +// A child with a buffer transform from its parents should be cropped by its parent bounds. +TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 0, 0); + t.setPosition(mFGSurfaceControl, 0, 0); + t.setSize(mChild, 100, 100); + }); + TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200); + + { + mCapture = screenshot(); + + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(63, 63); + mCapture->expectBGColor(64, 64); + } + + asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); }); + sp<Surface> s = mFGSurfaceControl->getSurface(); + auto anw = static_cast<ANativeWindow*>(s.get()); + // Apply a 90 transform on the buffer. + native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); + native_window_set_buffers_dimensions(anw, 64, 128); + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); + waitForPostedBuffers(); + + // The child should be cropped by the new parent bounds. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(99, 63); + mCapture->expectFGColor(100, 63); + mCapture->expectBGColor(128, 64); + } +} + +// A child with a scale transform from its parents should be cropped by its parent bounds. +TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 0, 0); + t.setPosition(mFGSurfaceControl, 0, 0); + t.setSize(mChild, 200, 200); + }); + TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200); + + { + mCapture = screenshot(); + + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(63, 63); + mCapture->expectBGColor(64, 64); + } + + asTransaction([&](Transaction& t) { + t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + // Set a scaling by 2. + t.setSize(mFGSurfaceControl, 128, 128); + }); + + // Child should inherit its parents scale but should be cropped by its parent bounds. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(127, 127); + mCapture->expectBGColor(128, 128); + } +} + +// Regression test for b/127368943 +// Child should ignore the buffer transform but apply parent scale transform. +TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 0, 0); + t.setPosition(mFGSurfaceControl, 0, 0); + }); + + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(9, 14); + mCapture->expectFGColor(10, 15); + } + + // Change the size of the foreground to 128 * 64 so we can test rotation as well. + asTransaction([&](Transaction& t) { + t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + t.setSize(mFGSurfaceControl, 128, 64); + }); + sp<Surface> s = mFGSurfaceControl->getSurface(); + auto anw = static_cast<ANativeWindow*>(s.get()); + // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we + // have an effective scale of 2.0 applied to the buffer along with a rotation transform. + native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); + native_window_set_buffers_dimensions(anw, 32, 64); + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); + waitForPostedBuffers(); + + // The child should ignore the buffer transform but apply the 2.0 scale from parent. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(19, 29); + mCapture->expectFGColor(20, 30); + } +} + +TEST_F(ChildLayerTest, Bug36858924) { + // Destroy the child layer + mChild.clear(); + + // Now recreate it as hidden + mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eHidden, mFGSurfaceControl.get()); + + // Show the child layer in a deferred transaction + asTransaction([&](Transaction& t) { + t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(), + mFGSurfaceControl->getSurface()->getNextFrameNumber()); + t.show(mChild); + }); + + // Render the foreground surface a few times + // + // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third + // frame because SurfaceFlinger would never process the deferred transaction and would therefore + // never acquire/release the first buffer + ALOGI("Filling 1"); + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0); + ALOGI("Filling 2"); + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 0, 255); + ALOGI("Filling 3"); + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 255, 0, 0); + ALOGI("Filling 4"); + TransactionUtils::fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0); +} + +TEST_F(ChildLayerTest, Reparent) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 10, 10); + t.setPosition(mFGSurfaceControl, 64, 64); + }); + + { + mCapture = screenshot(); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(74, 74); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(84, 84); + } + + asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); }); + + { + mCapture = screenshot(); + mCapture->expectFGColor(64, 64); + // In reparenting we should have exposed the entire foreground surface. + mCapture->expectFGColor(74, 74); + // And the child layer should now begin at 10, 10 (since the BG + // layer is at (0, 0)). + mCapture->expectBGColor(9, 9); + mCapture->expectChildColor(10, 10); + } +} + +TEST_F(ChildLayerTest, ReparentToNoParent) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 10, 10); + t.setPosition(mFGSurfaceControl, 64, 64); + }); + + { + mCapture = screenshot(); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(74, 74); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(84, 84); + } + asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); }); + { + mCapture = screenshot(); + // The surface should now be offscreen. + mCapture->expectFGColor(64, 64); + mCapture->expectFGColor(74, 74); + mCapture->expectFGColor(84, 84); + } +} + +TEST_F(ChildLayerTest, ReparentFromNoParent) { + sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0); + ASSERT_TRUE(newSurface != nullptr); + ASSERT_TRUE(newSurface->isValid()); + + TransactionUtils::fillSurfaceRGBA8(newSurface, 63, 195, 63); + asTransaction([&](Transaction& t) { + t.hide(mChild); + t.show(newSurface); + t.setPosition(newSurface, 10, 10); + t.setLayer(newSurface, INT32_MAX - 2); + t.setPosition(mFGSurfaceControl, 64, 64); + }); + + { + mCapture = screenshot(); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // At 10, 10 we should see the new surface + mCapture->checkPixel(10, 10, 63, 195, 63); + } + + asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); }); + + { + mCapture = screenshot(); + // newSurface will now be a child of mFGSurface so it will be 10, 10 offset from + // mFGSurface, putting it at 74, 74. + mCapture->expectFGColor(64, 64); + mCapture->checkPixel(74, 74, 63, 195, 63); + mCapture->expectFGColor(84, 84); + } +} + +TEST_F(ChildLayerTest, NestedChildren) { + sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mChild.get()); + TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50); + + { + mCapture = screenshot(); + // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer + // which begins at 64, 64 + mCapture->checkPixel(64, 64, 50, 50, 50); + } +} + +TEST_F(ChildLayerTest, ChildLayerRelativeLayer) { + sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0); + TransactionUtils::fillSurfaceRGBA8(relative, 255, 255, 255); + + Transaction t; + t.setLayer(relative, INT32_MAX) + .setRelativeLayer(mChild, relative->getHandle(), 1) + .setPosition(mFGSurfaceControl, 0, 0) + .apply(true); + + // We expect that the child should have been elevated above our + // INT_MAX layer even though it's not a child of it. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(9, 9); + mCapture->checkPixel(10, 10, 255, 255, 255); + } +} + +class BoundlessLayerTest : public LayerUpdateTest { +protected: + std::unique_ptr<ScreenCapture> mCapture; +}; + +// Verify setting a size on a buffer layer has no effect. +TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) { + sp<SurfaceControl> bufferLayer = + createSurface(mClient, "BufferLayer", 45, 45, PIXEL_FORMAT_RGBA_8888, 0, + mFGSurfaceControl.get()); + ASSERT_TRUE(bufferLayer->isValid()); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30)); + asTransaction([&](Transaction& t) { t.show(bufferLayer); }); + { + mCapture = screenshot(); + // Top left of background must now be visible + mCapture->expectBGColor(0, 0); + // Foreground Surface bounds must be color layer + mCapture->expectColor(Rect(64, 64, 94, 94), Color::BLACK); + // Buffer layer should not extend past buffer bounds + mCapture->expectFGColor(95, 95); + } +} + +// Verify a boundless color layer will fill its parent bounds. The parent has a buffer size +// which will crop the color layer. +TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) { + sp<SurfaceControl> colorLayer = + createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get()); + ASSERT_TRUE(colorLayer->isValid()); + asTransaction([&](Transaction& t) { + t.setColor(colorLayer, half3{0, 0, 0}); + t.show(colorLayer); + }); + { + mCapture = screenshot(); + // Top left of background must now be visible + mCapture->expectBGColor(0, 0); + // Foreground Surface bounds must be color layer + mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK); + // Color layer should not extend past foreground bounds + mCapture->expectBGColor(129, 129); + } +} + +// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has +// a crop which will be used to crop the color layer. +TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) { + sp<SurfaceControl> cropLayer = createSurface(mClient, "CropLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, + 0 /* flags */, mFGSurfaceControl.get()); + ASSERT_TRUE(cropLayer->isValid()); + sp<SurfaceControl> colorLayer = + createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get()); + ASSERT_TRUE(colorLayer->isValid()); + asTransaction([&](Transaction& t) { + t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10)); + t.setColor(colorLayer, half3{0, 0, 0}); + t.show(cropLayer); + t.show(colorLayer); + }); + { + mCapture = screenshot(); + // Top left of background must now be visible + mCapture->expectBGColor(0, 0); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // 5 pixels from the foreground we should see the child surface + mCapture->expectColor(Rect(69, 69, 74, 74), Color::BLACK); + // 10 pixels from the foreground we should be back to the foreground surface + mCapture->expectFGColor(74, 74); + } +} + +// Verify for boundless layer with no children, their transforms have no effect. +TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) { + sp<SurfaceControl> colorLayer = + createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get()); + ASSERT_TRUE(colorLayer->isValid()); + asTransaction([&](Transaction& t) { + t.setPosition(colorLayer, 320, 320); + t.setMatrix(colorLayer, 2, 0, 0, 2); + t.setColor(colorLayer, half3{0, 0, 0}); + t.show(colorLayer); + }); + { + mCapture = screenshot(); + // Top left of background must now be visible + mCapture->expectBGColor(0, 0); + // Foreground Surface bounds must be color layer + mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK); + // Color layer should not extend past foreground bounds + mCapture->expectBGColor(129, 129); + } +} + +// Verify for boundless layer with children, their transforms have an effect. +TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) { + sp<SurfaceControl> boundlessLayerRightShift = + createSurface(mClient, "BoundlessLayerRightShift", 0, 0, PIXEL_FORMAT_RGBA_8888, + 0 /* flags */, mFGSurfaceControl.get()); + ASSERT_TRUE(boundlessLayerRightShift->isValid()); + sp<SurfaceControl> boundlessLayerDownShift = + createSurface(mClient, "BoundlessLayerLeftShift", 0, 0, PIXEL_FORMAT_RGBA_8888, + 0 /* flags */, boundlessLayerRightShift.get()); + ASSERT_TRUE(boundlessLayerDownShift->isValid()); + sp<SurfaceControl> colorLayer = + createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor, boundlessLayerDownShift.get()); + ASSERT_TRUE(colorLayer->isValid()); + asTransaction([&](Transaction& t) { + t.setPosition(boundlessLayerRightShift, 32, 0); + t.show(boundlessLayerRightShift); + t.setPosition(boundlessLayerDownShift, 0, 32); + t.show(boundlessLayerDownShift); + t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64)); + t.setColor(colorLayer, half3{0, 0, 0}); + t.show(colorLayer); + }); + { + mCapture = screenshot(); + // Top left of background must now be visible + mCapture->expectBGColor(0, 0); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // Foreground Surface bounds must be color layer + mCapture->expectColor(Rect(96, 96, 128, 128), Color::BLACK); + // Color layer should not extend past foreground bounds + mCapture->expectBGColor(129, 129); + } +} + +// Verify child layers do not get clipped if they temporarily move into the negative +// coordinate space as the result of an intermediate transformation. +TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) { + sp<SurfaceControl> boundlessLayer = + mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888, + 0 /* flags */, mFGSurfaceControl.get()); + ASSERT_TRUE(boundlessLayer != nullptr); + ASSERT_TRUE(boundlessLayer->isValid()); + sp<SurfaceControl> colorLayer = + mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor, boundlessLayer.get()); + ASSERT_TRUE(colorLayer != nullptr); + ASSERT_TRUE(colorLayer->isValid()); + asTransaction([&](Transaction& t) { + // shift child layer off bounds. If this layer was not boundless, we will + // expect the child layer to be cropped. + t.setPosition(boundlessLayer, 32, 32); + t.show(boundlessLayer); + t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64)); + // undo shift by parent + t.setPosition(colorLayer, -32, -32); + t.setColor(colorLayer, half3{0, 0, 0}); + t.show(colorLayer); + }); + { + mCapture = screenshot(); + // Top left of background must now be visible + mCapture->expectBGColor(0, 0); + // Foreground Surface bounds must be color layer + mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK); + // Color layer should not extend past foreground bounds + mCapture->expectBGColor(129, 129); + } +} + +// Verify for boundless root layers with children, their transforms have an effect. +TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) { + sp<SurfaceControl> rootBoundlessLayer = createSurface(mClient, "RootBoundlessLayer", 0, 0, + PIXEL_FORMAT_RGBA_8888, 0 /* flags */); + ASSERT_TRUE(rootBoundlessLayer->isValid()); + sp<SurfaceControl> colorLayer = + createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor, rootBoundlessLayer.get()); + + ASSERT_TRUE(colorLayer->isValid()); + asTransaction([&](Transaction& t) { + t.setLayer(rootBoundlessLayer, INT32_MAX - 1); + t.setPosition(rootBoundlessLayer, 32, 32); + t.show(rootBoundlessLayer); + t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64)); + t.setColor(colorLayer, half3{0, 0, 0}); + t.show(colorLayer); + t.hide(mFGSurfaceControl); + }); + { + mCapture = screenshot(); + // Top left of background must now be visible + mCapture->expectBGColor(0, 0); + // Top left of foreground must now be visible + mCapture->expectBGColor(31, 31); + // Foreground Surface bounds must be color layer + mCapture->expectColor(Rect(32, 32, 96, 96), Color::BLACK); + // Color layer should not extend past foreground bounds + mCapture->expectBGColor(97, 97); + } +} + +class ScreenCaptureTest : public LayerUpdateTest { +protected: + std::unique_ptr<ScreenCapture> mCapture; +}; + +TEST_F(ScreenCaptureTest, CaptureSingleLayer) { + auto bgHandle = mBGSurfaceControl->getHandle(); + ScreenCapture::captureLayers(&mCapture, bgHandle); + mCapture->expectBGColor(0, 0); + // Doesn't capture FG layer which is at 64, 64 + mCapture->expectBGColor(64, 64); +} + +TEST_F(ScreenCaptureTest, CaptureLayerWithChild) { + auto fgHandle = mFGSurfaceControl->getHandle(); + + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + + SurfaceComposerClient::Transaction().show(child).apply(true); + + // Captures mFGSurfaceControl layer and its child. + ScreenCapture::captureLayers(&mCapture, fgHandle); + mCapture->expectFGColor(10, 10); + mCapture->expectChildColor(0, 0); +} + +TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) { + auto fgHandle = mFGSurfaceControl->getHandle(); + + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + + SurfaceComposerClient::Transaction().show(child).apply(true); + + // Captures mFGSurfaceControl's child + ScreenCapture::captureChildLayers(&mCapture, fgHandle); + mCapture->checkPixel(10, 10, 0, 0, 0); + mCapture->expectChildColor(0, 0); +} + +TEST_F(ScreenCaptureTest, CaptureLayerExclude) { + auto fgHandle = mFGSurfaceControl->getHandle(); + + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200); + + SurfaceComposerClient::Transaction() + .show(child) + .show(child2) + .setLayer(child, 1) + .setLayer(child2, 2) + .apply(true); + + // Child2 would be visible but its excluded, so we should see child1 color instead. + ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()}); + mCapture->checkPixel(10, 10, 0, 0, 0); + mCapture->checkPixel(0, 0, 200, 200, 200); +} + +// Like the last test but verifies that children are also exclude. +TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) { + auto fgHandle = mFGSurfaceControl->getHandle(); + + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200); + sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, child2.get()); + TransactionUtils::fillSurfaceRGBA8(child2, 200, 0, 200); + + SurfaceComposerClient::Transaction() + .show(child) + .show(child2) + .show(child3) + .setLayer(child, 1) + .setLayer(child2, 2) + .apply(true); + + // Child2 would be visible but its excluded, so we should see child1 color instead. + ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()}); + mCapture->checkPixel(10, 10, 0, 0, 0); + mCapture->checkPixel(0, 0, 200, 200, 200); +} + +TEST_F(ScreenCaptureTest, CaptureTransparent) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + + SurfaceComposerClient::Transaction().show(child).apply(true); + + auto childHandle = child->getHandle(); + + // Captures child + ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20}); + mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255}); + // Area outside of child's bounds is transparent. + mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0}); +} + +TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) { + auto fgHandle = mFGSurfaceControl->getHandle(); + + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + ASSERT_NE(nullptr, child.get()) << "failed to create surface"; + sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100); + + SurfaceComposerClient::Transaction() + .show(child) + // Set relative layer above fg layer so should be shown above when computing all layers. + .setRelativeLayer(relative, fgHandle, 1) + .show(relative) + .apply(true); + + // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured. + ScreenCapture::captureLayers(&mCapture, fgHandle); + mCapture->expectFGColor(10, 10); + mCapture->expectChildColor(0, 0); +} + +TEST_F(ScreenCaptureTest, CaptureRelativeInTree) { + auto fgHandle = mFGSurfaceControl->getHandle(); + + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + TransactionUtils::fillSurfaceRGBA8(relative, 100, 100, 100); + + SurfaceComposerClient::Transaction() + .show(child) + // Set relative layer below fg layer but relative to child layer so it should be shown + // above child layer. + .setLayer(relative, -1) + .setRelativeLayer(relative, child->getHandle(), 1) + .show(relative) + .apply(true); + + // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its + // relative value should be taken into account, placing it above child layer. + ScreenCapture::captureLayers(&mCapture, fgHandle); + mCapture->expectFGColor(10, 10); + // Relative layer is showing on top of child layer + mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255}); +} + +// In the following tests we verify successful skipping of a parent layer, +// so we use the same verification logic and only change how we mutate +// the parent layer to verify that various properties are ignored. +class ScreenCaptureChildOnlyTest : public LayerUpdateTest { +public: + void SetUp() override { + LayerUpdateTest::SetUp(); + + mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, + mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(mChild, 200, 200, 200); + + SurfaceComposerClient::Transaction().show(mChild).apply(true); + } + + void verify(std::function<void()> verifyStartingState) { + // Verify starting state before a screenshot is taken. + verifyStartingState(); + + // Verify child layer does not inherit any of the properties of its + // parent when its screenshot is captured. + auto fgHandle = mFGSurfaceControl->getHandle(); + ScreenCapture::captureChildLayers(&mCapture, fgHandle); + mCapture->checkPixel(10, 10, 0, 0, 0); + mCapture->expectChildColor(0, 0); + + // Verify all assumptions are still true after the screenshot is taken. + verifyStartingState(); + } + + std::unique_ptr<ScreenCapture> mCapture; + sp<SurfaceControl> mChild; +}; + +// Regression test b/76099859 +TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) { + SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true); + + // Even though the parent is hidden we should still capture the child. + + // Before and after reparenting, verify child is properly hidden + // when rendering full-screen. + verify([&] { screenshot()->expectBGColor(64, 64); }); +} + +TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) { + SurfaceComposerClient::Transaction() + .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1)) + .apply(true); + + // Even though the parent is cropped out we should still capture the child. + + // Before and after reparenting, verify child is cropped by parent. + verify([&] { screenshot()->expectBGColor(65, 65); }); +} + +// Regression test b/124372894 +TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) { + SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true); + + // We should not inherit the parent scaling. + + // Before and after reparenting, verify child is properly scaled. + verify([&] { screenshot()->expectChildColor(80, 80); }); +} + +TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) { + auto fgHandle = mFGSurfaceControl->getHandle(); + + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + + sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5, + PIXEL_FORMAT_RGBA_8888, 0, child.get()); + + TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50); + SurfaceComposerClient::Transaction() + .show(child) + .setPosition(grandchild, 5, 5) + .show(grandchild) + .apply(true); + + // Captures mFGSurfaceControl, its child, and the grandchild. + ScreenCapture::captureLayers(&mCapture, fgHandle); + mCapture->expectFGColor(10, 10); + mCapture->expectChildColor(0, 0); + mCapture->checkPixel(5, 5, 50, 50, 50); +} + +TEST_F(ScreenCaptureTest, CaptureChildOnly) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + auto childHandle = child->getHandle(); + + SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true); + + // Captures only the child layer, and not the parent. + ScreenCapture::captureLayers(&mCapture, childHandle); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(9, 9); +} + +TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) { + sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, + PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); + TransactionUtils::fillSurfaceRGBA8(child, 200, 200, 200); + auto childHandle = child->getHandle(); + + sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5, + PIXEL_FORMAT_RGBA_8888, 0, child.get()); + TransactionUtils::fillSurfaceRGBA8(grandchild, 50, 50, 50); + + SurfaceComposerClient::Transaction() + .show(child) + .setPosition(grandchild, 5, 5) + .show(grandchild) + .apply(true); + + auto grandchildHandle = grandchild->getHandle(); + + // Captures only the grandchild. + ScreenCapture::captureLayers(&mCapture, grandchildHandle); + mCapture->checkPixel(0, 0, 50, 50, 50); + mCapture->checkPixel(4, 4, 50, 50, 50); +} + +TEST_F(ScreenCaptureTest, CaptureCrop) { + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); + sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30, + PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); + + SurfaceComposerClient::Transaction() + .setLayer(redLayer, INT32_MAX - 1) + .show(redLayer) + .show(blueLayer) + .apply(true); + + auto redLayerHandle = redLayer->getHandle(); + + // Capturing full screen should have both red and blue are visible. + ScreenCapture::captureLayers(&mCapture, redLayerHandle); + mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); + // red area below the blue area + mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED); + // red area to the right of the blue area + mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); + + const Rect crop = Rect(0, 0, 30, 30); + ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop); + // Capturing the cropped screen, cropping out the shown red area, should leave only the blue + // area visible. + mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); + mCapture->checkPixel(30, 30, 0, 0, 0); +} + +TEST_F(ScreenCaptureTest, CaptureSize) { + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); + sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30, + PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); + + SurfaceComposerClient::Transaction() + .setLayer(redLayer, INT32_MAX - 1) + .show(redLayer) + .show(blueLayer) + .apply(true); + + auto redLayerHandle = redLayer->getHandle(); + + // Capturing full screen should have both red and blue are visible. + ScreenCapture::captureLayers(&mCapture, redLayerHandle); + mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); + // red area below the blue area + mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED); + // red area to the right of the blue area + mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); + + ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5); + // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area. + mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE); + // red area below the blue area + mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED); + // red area to the right of the blue area + mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED); + mCapture->checkPixel(30, 30, 0, 0, 0); +} + +TEST_F(ScreenCaptureTest, CaptureInvalidLayer) { + sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); + + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); + + auto redLayerHandle = redLayer->getHandle(); + redLayer.clear(); + SurfaceComposerClient::Transaction().apply(true); + + sp<GraphicBuffer> outBuffer; + + // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0)); +} +} // namespace android diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp new file mode 100644 index 0000000000..0bcac1a880 --- /dev/null +++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2019 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 "LayerTransactionTest.h" + +namespace android { + +class MirrorLayerTest : public LayerTransactionTest { +protected: + virtual void SetUp() { + LayerTransactionTest::SetUp(); + ASSERT_EQ(NO_ERROR, mClient->initCheck()); + + const auto display = SurfaceComposerClient::getInternalDisplayToken(); + ASSERT_FALSE(display == nullptr); + + mParentLayer = createColorLayer("Parent layer", Color::RED); + mChildLayer = createColorLayer("Child layer", Color::GREEN, mParentLayer.get()); + asTransaction([&](Transaction& t) { + t.setDisplayLayerStack(display, 0); + t.setLayer(mParentLayer, INT32_MAX - 2).show(mParentLayer); + t.setCrop_legacy(mChildLayer, Rect(0, 0, 400, 400)).show(mChildLayer); + t.setPosition(mChildLayer, 50, 50); + t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); + t.setFlags(mChildLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque); + }); + } + + virtual void TearDown() { + LayerTransactionTest::TearDown(); + mParentLayer = 0; + mChildLayer = 0; + } + + sp<SurfaceControl> mParentLayer; + sp<SurfaceControl> mChildLayer; +}; + +TEST_F(MirrorLayerTest, MirrorColorLayer) { + sp<SurfaceControl> grandchild = + createColorLayer("Grandchild layer", Color::BLUE, mChildLayer.get()); + Transaction() + .setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque) + .setCrop_legacy(grandchild, Rect(0, 0, 200, 200)) + .show(grandchild) + .apply(); + + // Mirror mChildLayer + sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get()); + ASSERT_NE(mirrorLayer, nullptr); + + // Add mirrorLayer as child of mParentLayer so it's shown on the display + Transaction() + .reparent(mirrorLayer, mParentLayer->getHandle()) + .setPosition(mirrorLayer, 500, 500) + .show(mirrorLayer) + .apply(); + + { + SCOPED_TRACE("Initial Mirror"); + auto shot = screenshot(); + // Grandchild mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); + } + + // Set color to white on grandchild layer. + Transaction().setColor(grandchild, half3{1, 1, 1}).apply(); + { + SCOPED_TRACE("Updated Grandchild Layer Color"); + auto shot = screenshot(); + // Grandchild mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); + } + + // Set color to black on child layer. + Transaction().setColor(mChildLayer, half3{0, 0, 0}).apply(); + { + SCOPED_TRACE("Updated Child Layer Color"); + auto shot = screenshot(); + // Grandchild mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); + } + + // Remove grandchild layer + Transaction().reparent(grandchild, nullptr).apply(); + { + SCOPED_TRACE("Removed Grandchild Layer"); + auto shot = screenshot(); + // Grandchild mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::BLACK); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); + } + + // Remove child layer + Transaction().reparent(mChildLayer, nullptr).apply(); + { + SCOPED_TRACE("Removed Child Layer"); + auto shot = screenshot(); + // Grandchild mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::RED); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::RED); + } + + // Add grandchild layer to offscreen layer + Transaction().reparent(grandchild, mChildLayer->getHandle()).apply(); + { + SCOPED_TRACE("Added Grandchild Layer"); + auto shot = screenshot(); + // Grandchild mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::RED); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::RED); + } + + // Add child layer + Transaction().reparent(mChildLayer, mParentLayer->getHandle()).apply(); + { + SCOPED_TRACE("Added Child Layer"); + auto shot = screenshot(); + // Grandchild mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); + } +} + +TEST_F(MirrorLayerTest, MirrorBufferLayer) { + sp<SurfaceControl> bufferQueueLayer = + createLayer("BufferQueueLayer", 200, 200, 0, mChildLayer.get()); + fillBufferQueueLayerColor(bufferQueueLayer, Color::BLUE, 200, 200); + Transaction().show(bufferQueueLayer).apply(); + + sp<SurfaceControl> mirrorLayer = mClient->mirrorSurface(mChildLayer.get()); + Transaction() + .reparent(mirrorLayer, mParentLayer->getHandle()) + .setPosition(mirrorLayer, 500, 500) + .show(mirrorLayer) + .apply(); + + { + SCOPED_TRACE("Initial Mirror BufferQueueLayer"); + auto shot = screenshot(); + // Buffer mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); + } + + fillBufferQueueLayerColor(bufferQueueLayer, Color::WHITE, 200, 200); + { + SCOPED_TRACE("Update BufferQueueLayer"); + auto shot = screenshot(); + // Buffer mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); + } + + Transaction().reparent(bufferQueueLayer, nullptr).apply(); + { + SCOPED_TRACE("Removed BufferQueueLayer"); + auto shot = screenshot(); + // Buffer mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); + } + + sp<SurfaceControl> bufferStateLayer = + createLayer("BufferStateLayer", 200, 200, ISurfaceComposerClient::eFXSurfaceBufferState, + mChildLayer.get()); + fillBufferStateLayerColor(bufferStateLayer, Color::BLUE, 200, 200); + Transaction().setFrame(bufferStateLayer, Rect(0, 0, 200, 200)).show(bufferStateLayer).apply(); + + { + SCOPED_TRACE("Initial Mirror BufferStateLayer"); + auto shot = screenshot(); + // Buffer mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::BLUE); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); + } + + fillBufferStateLayerColor(bufferStateLayer, Color::WHITE, 200, 200); + { + SCOPED_TRACE("Update BufferStateLayer"); + auto shot = screenshot(); + // Buffer mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); + } + + Transaction().reparent(bufferStateLayer, nullptr).apply(); + { + SCOPED_TRACE("Removed BufferStateLayer"); + auto shot = screenshot(); + // Buffer mirror + shot->expectColor(Rect(550, 550, 750, 750), Color::GREEN); + // Child mirror + shot->expectColor(Rect(750, 750, 950, 950), Color::GREEN); + } +} + +} // namespace android diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp new file mode 100644 index 0000000000..066c9aa3ce --- /dev/null +++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 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 "LayerTransactionTest.h" + +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +::testing::Environment* const binderEnv = + ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); + +class MultiDisplayLayerBoundsTest : public LayerTransactionTest { +protected: + virtual void SetUp() { + LayerTransactionTest::SetUp(); + ASSERT_EQ(NO_ERROR, mClient->initCheck()); + + mMainDisplay = SurfaceComposerClient::getInternalDisplayToken(); + SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo); + + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&mProducer, &consumer); + consumer->setConsumerName(String8("Virtual disp consumer")); + consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h); + } + + virtual void TearDown() { + SurfaceComposerClient::destroyDisplay(mVirtualDisplay); + LayerTransactionTest::TearDown(); + mColorLayer = 0; + } + + void createDisplay(const Rect& layerStackRect, uint32_t layerStack) { + mVirtualDisplay = + SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/); + asTransaction([&](Transaction& t) { + t.setDisplaySurface(mVirtualDisplay, mProducer); + t.setDisplayLayerStack(mVirtualDisplay, layerStack); + t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect, + Rect(mMainDisplayInfo.w, mMainDisplayInfo.h)); + }); + } + + void createColorLayer(uint32_t layerStack) { + mColorLayer = + createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */, + PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor); + ASSERT_TRUE(mColorLayer != nullptr); + ASSERT_TRUE(mColorLayer->isValid()); + asTransaction([&](Transaction& t) { + t.setLayerStack(mColorLayer, layerStack); + t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40)); + t.setLayer(mColorLayer, INT32_MAX - 2); + t.setColor(mColorLayer, + half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f, + mExpectedColor.b / 255.0f}); + t.show(mColorLayer); + }); + } + + DisplayInfo mMainDisplayInfo; + sp<IBinder> mMainDisplay; + sp<IBinder> mVirtualDisplay; + sp<IGraphicBufferProducer> mProducer; + sp<SurfaceControl> mColorLayer; + Color mExpectedColor = {63, 63, 195, 255}; +}; + +TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) { + createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */); + createColorLayer(1 /* layerStack */); + + asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); }); + + // Verify color layer does not render on main display. + std::unique_ptr<ScreenCapture> sc; + ScreenCapture::captureScreen(&sc, mMainDisplay); + sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255}); + sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); + + // Verify color layer renders correctly on virtual display. + ScreenCapture::captureScreen(&sc, mVirtualDisplay); + sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor); + sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0}); +} + +TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) { + // Create a display and set its layer stack to the main display's layer stack so + // the contents of the main display are mirrored on to the virtual display. + + // Assumption here is that the new mirrored display has the same viewport as the + // primary display that it is mirroring. + createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */); + createColorLayer(0 /* layerStack */); + + asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); }); + + // Verify color layer renders correctly on main display and it is mirrored on the + // virtual display. + std::unique_ptr<ScreenCapture> sc; + ScreenCapture::captureScreen(&sc, mMainDisplay); + sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor); + sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); + + ScreenCapture::captureScreen(&sc, mVirtualDisplay); + sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor); + sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/RelativeZ_test.cpp b/services/surfaceflinger/tests/RelativeZ_test.cpp new file mode 100644 index 0000000000..8c56d27e8a --- /dev/null +++ b/services/surfaceflinger/tests/RelativeZ_test.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2019 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 "LayerTransactionTest.h" + +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +::testing::Environment* const binderEnv = + ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); + +class RelativeZTest : public LayerTransactionTest { +protected: + virtual void SetUp() { + LayerTransactionTest::SetUp(); + ASSERT_EQ(NO_ERROR, mClient->initCheck()); + + const auto display = SurfaceComposerClient::getInternalDisplayToken(); + ASSERT_FALSE(display == nullptr); + + // Back layer + mBackgroundLayer = createColorLayer("Background layer", Color::RED); + + // Front layer + mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN); + + asTransaction([&](Transaction& t) { + t.setDisplayLayerStack(display, 0); + t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer); + t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer); + }); + } + + virtual void TearDown() { + LayerTransactionTest::TearDown(); + mBackgroundLayer = 0; + mForegroundLayer = 0; + } + + sp<SurfaceControl> mBackgroundLayer; + sp<SurfaceControl> mForegroundLayer; +}; + +// When a layer is reparented offscreen, remove relative z order if the relative parent +// is still onscreen so that the layer is not drawn. +TEST_F(RelativeZTest, LayerRemoved) { + std::unique_ptr<ScreenCapture> sc; + + // Background layer (RED) + // Child layer (WHITE) (relative to foregroud layer) + // Foregroud layer (GREEN) + sp<SurfaceControl> childLayer = + createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get()); + + Transaction{} + .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1) + .show(childLayer) + .apply(); + + { + // The childLayer should be in front of the FG control. + ScreenCapture::captureScreen(&sc); + sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b); + } + + // Background layer (RED) + // Foregroud layer (GREEN) + Transaction{}.reparent(childLayer, nullptr).apply(); + + // Background layer (RED) + // Child layer (WHITE) + // Foregroud layer (GREEN) + Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply(); + + { + // The relative z info for child layer should be reset, leaving FG control on top. + ScreenCapture::captureScreen(&sc); + sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b); + } +} + +// When a layer is reparented offscreen, preseve relative z order if the relative parent +// is also offscreen. Regression test b/132613412 +TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) { + std::unique_ptr<ScreenCapture> sc; + + // Background layer (RED) + // Foregroud layer (GREEN) + // child level 1 (WHITE) + // child level 2a (BLUE) + // child level 3 (GREEN) (relative to child level 2b) + // child level 2b (BLACK) + sp<SurfaceControl> childLevel1 = + createColorLayer("child level 1", Color::WHITE, mForegroundLayer.get()); + sp<SurfaceControl> childLevel2a = + createColorLayer("child level 2a", Color::BLUE, childLevel1.get()); + sp<SurfaceControl> childLevel2b = + createColorLayer("child level 2b", Color::BLACK, childLevel1.get()); + sp<SurfaceControl> childLevel3 = + createColorLayer("child level 3", Color::GREEN, childLevel2a.get()); + + Transaction{} + .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1) + .show(childLevel2a) + .show(childLevel2b) + .show(childLevel3) + .apply(); + + { + // The childLevel3 should be in front of childLevel2b. + ScreenCapture::captureScreen(&sc); + sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b); + } + + // Background layer (RED) + // Foregroud layer (GREEN) + Transaction{}.reparent(childLevel1, nullptr).apply(); + + // Background layer (RED) + // Foregroud layer (GREEN) + // child level 1 (WHITE) + // child level 2 back (BLUE) + // child level 3 (GREEN) (relative to child level 2b) + // child level 2 front (BLACK) + Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply(); + + { + // Nothing should change at this point since relative z info was preserved. + ScreenCapture::captureScreen(&sc); + sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b); + } +} + +TEST_F(RelativeZTest, LayerAndRelativeRemoved) { + std::unique_ptr<ScreenCapture> sc; + + // Background layer (RED) + // Foregroud layer (GREEN) + // Child layer (BLUE) (relative to relativeToLayer layer) + // Relative layer (WHITE) + sp<SurfaceControl> childLayer = + createColorLayer("Child layer", Color::BLUE, mForegroundLayer.get()); + sp<SurfaceControl> relativeToLayer = + createColorLayer("Relative layer", Color::WHITE, mForegroundLayer.get()); + + Transaction{} + .setRelativeLayer(childLayer, relativeToLayer->getHandle(), 1) + .show(childLayer) + .show(relativeToLayer) + .apply(); + + { + // The childLayer should be in front of relativeToLayer. + ScreenCapture::captureScreen(&sc); + sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b); + } + + // Remove layer that childLayer is relative to + // Background layer (RED) + // Foregroud layer (GREEN) + // Child layer (BLUE) (relative to relativeToLayer layer) + Transaction{}.reparent(relativeToLayer, nullptr).apply(); + relativeToLayer = 0; + + { + // The child layer is relative to an deleted layer so it won't be drawn. + ScreenCapture::captureScreen(&sc); + sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b); + } + + // Background layer (RED) + // Foregroud layer (GREEN) + Transaction{}.reparent(childLayer, nullptr).apply(); + + { + // The child layer is offscreen, so it won't be drawn. + ScreenCapture::captureScreen(&sc); + sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b); + } + + // Background layer (RED) + // Foregroud layer (GREEN) + // Child layer (BLUE) + Transaction{}.reparent(childLayer, mForegroundLayer->getHandle()).apply(); + + { + // The relative z info for child layer should be reset, leaving the child layer on top. + ScreenCapture::captureScreen(&sc); + sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b); + } +} +} // namespace android diff --git a/services/surfaceflinger/tests/SetGeometry_test.cpp b/services/surfaceflinger/tests/SetGeometry_test.cpp new file mode 100644 index 0000000000..dca06ec8d4 --- /dev/null +++ b/services/surfaceflinger/tests/SetGeometry_test.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2019 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 "LayerTransactionTest.h" + +namespace android { + +class SetGeometryTest : public LayerTransactionTest { +protected: + void SetUp() { + LayerTransactionTest::SetUp(); + ASSERT_EQ(NO_ERROR, mClient->initCheck()); + + mLayer = createLayer("Layer", mLayerWidth, mLayerHeight); + fillBufferQueueLayerColor(mLayer, Color::RED, mLayerWidth, mLayerHeight); + asTransaction([&](Transaction& t) { t.setLayer(mLayer, INT32_MAX - 1).show(mLayer); }); + + { + SCOPED_TRACE("init"); + ScreenCapture::captureScreen(&sc); + sc->expectColor(Rect(0, 0, mLayerWidth, mLayerHeight), Color::RED); + sc->expectBorder(Rect(0, 0, mLayerWidth, mLayerHeight), Color::BLACK); + } + } + + void TearDown() { + LayerTransactionTest::TearDown(); + sc = 0; + mLayer = 0; + } + + std::unique_ptr<ScreenCapture> sc; + sp<SurfaceControl> mLayer; + const int mLayerWidth = 100; + const int mLayerHeight = 200; +}; + +TEST_F(SetGeometryTest, SourceAtZeroNoScale) { + Rect source = Rect(0, 0, 30, 30); + Rect dest = Rect(60, 60, 90, 90); + Transaction{}.setGeometry(mLayer, source, dest, 0).apply(); + + { + SCOPED_TRACE("geometry applied"); + ScreenCapture::captureScreen(&sc); + sc->expectColor(dest, Color::RED); + sc->expectBorder(dest, Color::BLACK); + } +} + +TEST_F(SetGeometryTest, SourceNotAtZero) { + Rect source = Rect(40, 40, 70, 70); + Rect dest = Rect(60, 60, 90, 90); + Transaction{}.setGeometry(mLayer, source, dest, 0).apply(); + + { + SCOPED_TRACE("geometry applied"); + ScreenCapture::captureScreen(&sc); + sc->expectColor(dest, Color::RED); + sc->expectBorder(dest, Color::BLACK); + } +} + +TEST_F(SetGeometryTest, Scale) { + Rect source = Rect(0, 0, 100, 200); + Rect dest = Rect(0, 0, 200, 400); + Transaction{}.setGeometry(mLayer, source, dest, 0).apply(); + + { + SCOPED_TRACE("Scaled by 2"); + ScreenCapture::captureScreen(&sc); + sc->expectColor(dest, Color::RED); + sc->expectBorder(dest, Color::BLACK); + } + + dest = Rect(0, 0, 50, 100); + Transaction{}.setGeometry(mLayer, source, dest, 0).apply(); + { + SCOPED_TRACE("Scaled by .5"); + ScreenCapture::captureScreen(&sc); + sc->expectColor(dest, Color::RED); + sc->expectBorder(dest, Color::BLACK); + } +} + +} // namespace android diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter index 6b4634ae76..b19668422b 100644 --- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter +++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter @@ -1,5 +1,5 @@ { "presubmit": { - "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*:InvalidHandleTest.*:VirtualDisplayTest.*:RelativeZTest.*" + "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*:InvalidHandleTest.*:VirtualDisplayTest.*:RelativeZTest.*:SetGeometryTest.*" } } diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp index 5cc946aa79..59e9c00ae7 100644 --- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp +++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp @@ -36,6 +36,9 @@ namespace android { using Transaction = SurfaceComposerClient::Transaction; +using SurfaceChange = surfaceflinger::SurfaceChange; +using Trace = surfaceflinger::Trace; +using Increment = surfaceflinger::Increment; constexpr int32_t SCALING_UPDATE = 1; constexpr uint32_t BUFFER_UPDATES = 18; @@ -43,18 +46,21 @@ constexpr uint32_t LAYER_UPDATE = INT_MAX - 2; constexpr uint32_t SIZE_UPDATE = 134; constexpr uint32_t STACK_UPDATE = 1; constexpr uint64_t DEFERRED_UPDATE = 0; +constexpr int32_t RELATIVE_Z = 42; constexpr float ALPHA_UPDATE = 0.29f; constexpr float CORNER_RADIUS_UPDATE = 0.2f; constexpr float POSITION_UPDATE = 121; const Rect CROP_UPDATE(16, 16, 32, 32); const String8 DISPLAY_NAME("SurfaceInterceptor Display Test"); -constexpr auto TEST_SURFACE_NAME = "BG Interceptor Test Surface"; -constexpr auto UNIQUE_TEST_SURFACE_NAME = "BG Interceptor Test Surface#0"; +constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface"; +constexpr auto TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface"; +constexpr auto UNIQUE_TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface#0"; +constexpr auto UNIQUE_TEST_FG_SURFACE_NAME = "FG Interceptor Test Surface#0"; constexpr auto LAYER_NAME = "Layer Create and Delete Test"; constexpr auto UNIQUE_LAYER_NAME = "Layer Create and Delete Test#0"; -constexpr auto DEFAULT_FILENAME = "/data/SurfaceTrace.dat"; +constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/transaction_trace.pb"; // Fill an RGBA_8888 formatted surface with a single color. static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b) { @@ -136,12 +142,15 @@ protected: void TearDown() override { mComposerClient->dispose(); mBGSurfaceControl.clear(); + mFGSurfaceControl.clear(); mComposerClient.clear(); } sp<SurfaceComposerClient> mComposerClient; sp<SurfaceControl> mBGSurfaceControl; + sp<SurfaceControl> mFGSurfaceControl; int32_t mBGLayerId; + int32_t mFGLayerId; public: using TestTransactionAction = void (SurfaceInterceptorTest::*)(Transaction&); @@ -177,6 +186,10 @@ public: bool opaqueFlagUpdateFound(const SurfaceChange& change, bool foundOpaqueFlag); bool secureFlagUpdateFound(const SurfaceChange& change, bool foundSecureFlag); bool deferredTransactionUpdateFound(const SurfaceChange& change, bool foundDeferred); + bool reparentUpdateFound(const SurfaceChange& change, bool found); + bool relativeParentUpdateFound(const SurfaceChange& change, bool found); + bool detachChildrenUpdateFound(const SurfaceChange& change, bool found); + bool reparentChildrenUpdateFound(const SurfaceChange& change, bool found); bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase); // Find all of the updates in the single trace @@ -209,6 +222,10 @@ public: void opaqueFlagUpdate(Transaction&); void secureFlagUpdate(Transaction&); void deferredTransactionUpdate(Transaction&); + void reparentUpdate(Transaction&); + void relativeParentUpdate(Transaction&); + void detachChildrenUpdate(Transaction&); + void reparentChildrenUpdate(Transaction&); void surfaceCreation(Transaction&); void displayCreation(Transaction&); void displayDeletion(Transaction&); @@ -250,21 +267,30 @@ void SurfaceInterceptorTest::setupBackgroundSurface() { ssize_t displayHeight = info.h; // Background surface - mBGSurfaceControl = mComposerClient->createSurface( - String8(TEST_SURFACE_NAME), displayWidth, displayHeight, - PIXEL_FORMAT_RGBA_8888, 0); + mBGSurfaceControl = mComposerClient->createSurface(String8(TEST_BG_SURFACE_NAME), displayWidth, + displayHeight, PIXEL_FORMAT_RGBA_8888, 0); ASSERT_TRUE(mBGSurfaceControl != nullptr); ASSERT_TRUE(mBGSurfaceControl->isValid()); + // Foreground surface + mFGSurfaceControl = mComposerClient->createSurface(String8(TEST_FG_SURFACE_NAME), displayWidth, + displayHeight, PIXEL_FORMAT_RGBA_8888, 0); + ASSERT_TRUE(mFGSurfaceControl != nullptr); + ASSERT_TRUE(mFGSurfaceControl->isValid()); + Transaction t; t.setDisplayLayerStack(display, 0); - ASSERT_EQ(NO_ERROR, t.setLayer(mBGSurfaceControl, INT_MAX-3) - .show(mBGSurfaceControl) - .apply()); + ASSERT_EQ(NO_ERROR, + t.setLayer(mBGSurfaceControl, INT_MAX - 3) + .show(mBGSurfaceControl) + .setLayer(mFGSurfaceControl, INT_MAX - 3) + .show(mFGSurfaceControl) + .apply()); } void SurfaceInterceptorTest::preProcessTrace(const Trace& trace) { - mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_SURFACE_NAME); + mBGLayerId = getSurfaceId(trace, UNIQUE_TEST_BG_SURFACE_NAME); + mFGLayerId = getSurfaceId(trace, UNIQUE_TEST_FG_SURFACE_NAME); } void SurfaceInterceptorTest::captureTest(TestTransactionAction action, @@ -364,6 +390,22 @@ void SurfaceInterceptorTest::deferredTransactionUpdate(Transaction& t) { DEFERRED_UPDATE); } +void SurfaceInterceptorTest::reparentUpdate(Transaction& t) { + t.reparent(mBGSurfaceControl, mFGSurfaceControl->getHandle()); +} + +void SurfaceInterceptorTest::relativeParentUpdate(Transaction& t) { + t.setRelativeLayer(mBGSurfaceControl, mFGSurfaceControl->getHandle(), RELATIVE_Z); +} + +void SurfaceInterceptorTest::detachChildrenUpdate(Transaction& t) { + t.detachChildren(mBGSurfaceControl); +} + +void SurfaceInterceptorTest::reparentChildrenUpdate(Transaction& t) { + t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl->getHandle()); +} + void SurfaceInterceptorTest::displayCreation(Transaction&) { sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true); SurfaceComposerClient::destroyDisplay(testDisplay); @@ -389,6 +431,10 @@ void SurfaceInterceptorTest::runAllUpdates() { runInTransaction(&SurfaceInterceptorTest::opaqueFlagUpdate); runInTransaction(&SurfaceInterceptorTest::secureFlagUpdate); runInTransaction(&SurfaceInterceptorTest::deferredTransactionUpdate); + runInTransaction(&SurfaceInterceptorTest::reparentUpdate); + runInTransaction(&SurfaceInterceptorTest::reparentChildrenUpdate); + runInTransaction(&SurfaceInterceptorTest::detachChildrenUpdate); + runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate); } void SurfaceInterceptorTest::surfaceCreation(Transaction&) { @@ -569,6 +615,46 @@ bool SurfaceInterceptorTest::deferredTransactionUpdateFound(const SurfaceChange& return foundDeferred; } +bool SurfaceInterceptorTest::reparentUpdateFound(const SurfaceChange& change, bool found) { + bool hasId(change.reparent().parent_id() == mFGLayerId); + if (hasId && !found) { + found = true; + } else if (hasId && found) { + []() { FAIL(); }(); + } + return found; +} + +bool SurfaceInterceptorTest::relativeParentUpdateFound(const SurfaceChange& change, bool found) { + bool hasId(change.relative_parent().relative_parent_id() == mFGLayerId); + if (hasId && !found) { + found = true; + } else if (hasId && found) { + []() { FAIL(); }(); + } + return found; +} + +bool SurfaceInterceptorTest::detachChildrenUpdateFound(const SurfaceChange& change, bool found) { + bool detachChildren(change.detach_children().detach_children()); + if (detachChildren && !found) { + found = true; + } else if (detachChildren && found) { + []() { FAIL(); }(); + } + return found; +} + +bool SurfaceInterceptorTest::reparentChildrenUpdateFound(const SurfaceChange& change, bool found) { + bool hasId(change.reparent_children().parent_id() == mFGLayerId); + if (hasId && !found) { + found = true; + } else if (hasId && found) { + []() { FAIL(); }(); + } + return found; +} + bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase) { bool foundUpdate = false; @@ -620,6 +706,18 @@ bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace, case SurfaceChange::SurfaceChangeCase::kDeferredTransaction: foundUpdate = deferredTransactionUpdateFound(change, foundUpdate); break; + case SurfaceChange::SurfaceChangeCase::kReparent: + foundUpdate = reparentUpdateFound(change, foundUpdate); + break; + case SurfaceChange::SurfaceChangeCase::kReparentChildren: + foundUpdate = reparentChildrenUpdateFound(change, foundUpdate); + break; + case SurfaceChange::SurfaceChangeCase::kRelativeParent: + foundUpdate = relativeParentUpdateFound(change, foundUpdate); + break; + case SurfaceChange::SurfaceChangeCase::kDetachChildren: + foundUpdate = detachChildrenUpdateFound(change, foundUpdate); + break; case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET: break; } @@ -644,6 +742,10 @@ void SurfaceInterceptorTest::assertAllUpdatesFound(const Trace& trace) { ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kOpaqueFlag)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kSecureFlag)); ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDeferredTransaction)); + ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparent)); + ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kReparentChildren)); + ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kRelativeParent)); + ASSERT_TRUE(surfaceUpdateFound(trace, SurfaceChange::SurfaceChangeCase::kDetachChildren)); } bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) { @@ -798,6 +900,26 @@ TEST_F(SurfaceInterceptorTest, InterceptDeferredTransactionUpdateWorks) { SurfaceChange::SurfaceChangeCase::kDeferredTransaction); } +TEST_F(SurfaceInterceptorTest, InterceptReparentUpdateWorks) { + captureTest(&SurfaceInterceptorTest::reparentUpdate, + SurfaceChange::SurfaceChangeCase::kReparent); +} + +TEST_F(SurfaceInterceptorTest, InterceptReparentChildrenUpdateWorks) { + captureTest(&SurfaceInterceptorTest::reparentChildrenUpdate, + SurfaceChange::SurfaceChangeCase::kReparentChildren); +} + +TEST_F(SurfaceInterceptorTest, InterceptRelativeParentUpdateWorks) { + captureTest(&SurfaceInterceptorTest::relativeParentUpdate, + SurfaceChange::SurfaceChangeCase::kRelativeParent); +} + +TEST_F(SurfaceInterceptorTest, InterceptDetachChildrenUpdateWorks) { + captureTest(&SurfaceInterceptorTest::detachChildrenUpdate, + SurfaceChange::SurfaceChangeCase::kDetachChildren); +} + TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) { captureTest(&SurfaceInterceptorTest::runAllUpdates, &SurfaceInterceptorTest::assertAllUpdatesFound); @@ -861,5 +983,4 @@ TEST_F(SurfaceInterceptorTest, InterceptSimultaneousUpdatesWorks) { ASSERT_TRUE(bufferUpdatesFound(capturedTrace)); ASSERT_TRUE(singleIncrementFound(capturedTrace, Increment::IncrementCase::kSurfaceCreation)); } - } diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h new file mode 100644 index 0000000000..8fdcde40b6 --- /dev/null +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2019 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_TRANSACTION_TEST_HARNESSES +#define ANDROID_TRANSACTION_TEST_HARNESSES + +/*#include <algorithm> +#include <chrono> +#include <cinttypes> +#include <functional> +#include <limits> +#include <ostream> + +#include <android/native_window.h> + +#include <binder/ProcessState.h> +#include <gui/BufferItemConsumer.h> +#include <gui/IProducerListener.h> +#include <gui/ISurfaceComposer.h> +#include <gui/LayerState.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <hardware/hwcomposer_defs.h> +#include <private/android_filesystem_config.h> +#include <private/gui/ComposerService.h> + +#include <ui/DisplayInfo.h> + +#include <math.h> +#include <math/vec3.h> +#include <sys/types.h> +#include <unistd.h> + +#include "BufferGenerator.h" +*/ +#include "LayerTransactionTest.h" +/*#include "utils/CallbackUtils.h" +#include "utils/ColorUtils.h" +#include "utils/ScreenshotUtils.h" +#include "utils/TransactionUtils.h" +*/ +namespace android { + +using android::hardware::graphics::common::V1_1::BufferUsage; + +class LayerRenderPathTestHarness { +public: + LayerRenderPathTestHarness(LayerTransactionTest* delegate, RenderPath renderPath) + : mDelegate(delegate), mRenderPath(renderPath) {} + + std::unique_ptr<ScreenCapture> getScreenCapture() { + switch (mRenderPath) { + case RenderPath::SCREENSHOT: + return mDelegate->screenshot(); + case RenderPath::VIRTUAL_DISPLAY: + + const auto mainDisplay = SurfaceComposerClient::getInternalDisplayToken(); + DisplayInfo mainDisplayInfo; + SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo); + + sp<IBinder> vDisplay; + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + sp<BufferItemConsumer> itemConsumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + consumer->setConsumerName(String8("Virtual disp consumer")); + consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h); + + itemConsumer = new BufferItemConsumer(consumer, + // Sample usage bits from screenrecord + GRALLOC_USAGE_HW_VIDEO_ENCODER | + GRALLOC_USAGE_SW_READ_OFTEN); + + vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), + false /*secure*/); + + SurfaceComposerClient::Transaction t; + t.setDisplaySurface(vDisplay, producer); + t.setDisplayLayerStack(vDisplay, 0); + t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation, + Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH), + Rect(mainDisplayInfo.w, mainDisplayInfo.h)); + t.apply(); + SurfaceComposerClient::Transaction().apply(true); + BufferItem item; + itemConsumer->acquireBuffer(&item, 0, true); + auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer); + itemConsumer->releaseBuffer(item); + SurfaceComposerClient::destroyDisplay(vDisplay); + return sc; + } + } + +protected: + LayerTransactionTest* mDelegate; + RenderPath mRenderPath; +}; + +class LayerTypeTransactionHarness : public LayerTransactionTest { +public: + LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {} + + sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, + uint32_t flags = 0, SurfaceControl* parent = nullptr) { + // if the flags already have a layer type specified, return an error + if (flags & ISurfaceComposerClient::eFXSurfaceMask) { + return nullptr; + } + return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent); + } + + void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth, + int32_t bufferHeight) { + ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color, + bufferWidth, bufferHeight)); + } + + void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth, + int32_t bufferHeight, const Color& topLeft, const Color& topRight, + const Color& bottomLeft, const Color& bottomRight) { + ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer, + bufferWidth, bufferHeight, + topLeft, topRight, + bottomLeft, bottomRight)); + } + +protected: + uint32_t mLayerType; +}; +} // namespace android +#endif diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp deleted file mode 100644 index c93e15ef96..0000000000 --- a/services/surfaceflinger/tests/Transaction_test.cpp +++ /dev/null @@ -1,6156 +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. - */ - -#include <algorithm> -#include <chrono> -#include <cinttypes> -#include <functional> -#include <limits> -#include <ostream> -#include <thread> - -#include <gtest/gtest.h> - -#include <android/native_window.h> - -#include <binder/ProcessState.h> -#include <gui/BufferItemConsumer.h> -#include <gui/IProducerListener.h> -#include <gui/ISurfaceComposer.h> -#include <gui/LayerState.h> -#include <gui/Surface.h> -#include <gui/SurfaceComposerClient.h> -#include <hardware/hwcomposer_defs.h> -#include <private/android_filesystem_config.h> -#include <private/gui/ComposerService.h> - -#include <ui/ColorSpace.h> -#include <ui/DisplayInfo.h> -#include <ui/Rect.h> -#include <utils/String8.h> - -#include <math.h> -#include <math/vec3.h> -#include <sys/types.h> -#include <unistd.h> - -#include "BufferGenerator.h" - -namespace android { - -namespace { - -struct Color { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; - - static const Color RED; - static const Color GREEN; - static const Color BLUE; - static const Color WHITE; - static const Color BLACK; - static const Color TRANSPARENT; -}; - -const Color Color::RED{255, 0, 0, 255}; -const Color Color::GREEN{0, 255, 0, 255}; -const Color Color::BLUE{0, 0, 255, 255}; -const Color Color::WHITE{255, 255, 255, 255}; -const Color Color::BLACK{0, 0, 0, 255}; -const Color Color::TRANSPARENT{0, 0, 0, 0}; - -using android::hardware::graphics::common::V1_1::BufferUsage; -using namespace std::chrono_literals; - -std::ostream& operator<<(std::ostream& os, const Color& color) { - os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a); - return os; -} - -// Fill a region with the specified color. -void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, - const Color& color) { - Rect r(0, 0, buffer.width, buffer.height); - if (!r.intersect(rect, &r)) { - return; - } - - int32_t width = r.right - r.left; - int32_t height = r.bottom - r.top; - - for (int32_t row = 0; row < height; row++) { - uint8_t* dst = - static_cast<uint8_t*>(buffer.bits) + (buffer.stride * (r.top + row) + r.left) * 4; - for (int32_t column = 0; column < width; column++) { - dst[0] = color.r; - dst[1] = color.g; - dst[2] = color.b; - dst[3] = color.a; - dst += 4; - } - } -} - -// Fill a region with the specified color. -void fillGraphicBufferColor(const sp<GraphicBuffer>& buffer, const Rect& rect, const Color& color) { - Rect r(0, 0, buffer->width, buffer->height); - if (!r.intersect(rect, &r)) { - return; - } - - int32_t width = r.right - r.left; - int32_t height = r.bottom - r.top; - - uint8_t* pixels; - buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, - reinterpret_cast<void**>(&pixels)); - - for (int32_t row = 0; row < height; row++) { - uint8_t* dst = pixels + (buffer->getStride() * (r.top + row) + r.left) * 4; - for (int32_t column = 0; column < width; column++) { - dst[0] = color.r; - dst[1] = color.g; - dst[2] = color.b; - dst[3] = color.a; - dst += 4; - } - } - buffer->unlock(); -} - -// Check if a region has the specified color. -void expectBufferColor(const sp<GraphicBuffer>& outBuffer, uint8_t* pixels, const Rect& rect, - const Color& color, uint8_t tolerance) { - int32_t x = rect.left; - int32_t y = rect.top; - int32_t width = rect.right - rect.left; - int32_t height = rect.bottom - rect.top; - - int32_t bufferWidth = int32_t(outBuffer->getWidth()); - int32_t bufferHeight = int32_t(outBuffer->getHeight()); - if (x + width > bufferWidth) { - x = std::min(x, bufferWidth); - width = bufferWidth - x; - } - if (y + height > bufferHeight) { - y = std::min(y, bufferHeight); - height = bufferHeight - y; - } - - auto colorCompare = [tolerance](uint8_t a, uint8_t b) { - uint8_t tmp = a >= b ? a - b : b - a; - return tmp <= tolerance; - }; - for (int32_t j = 0; j < height; j++) { - const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4; - for (int32_t i = 0; i < width; i++) { - const uint8_t expected[4] = {color.r, color.g, color.b, color.a}; - EXPECT_TRUE(std::equal(src, src + 4, expected, colorCompare)) - << "pixel @ (" << x + i << ", " << y + j << "): " - << "expected (" << color << "), " - << "got (" << Color{src[0], src[1], src[2], src[3]} << ")"; - src += 4; - } - } -} - -} // anonymous namespace - -using Transaction = SurfaceComposerClient::Transaction; - -// Fill an RGBA_8888 formatted surface with a single color. -static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b, - bool unlock = true) { - ANativeWindow_Buffer outBuffer; - sp<Surface> s = sc->getSurface(); - ASSERT_TRUE(s != nullptr); - ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr)); - uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits); - for (int y = 0; y < outBuffer.height; y++) { - for (int x = 0; x < outBuffer.width; x++) { - uint8_t* pixel = img + (4 * (y * outBuffer.stride + x)); - pixel[0] = r; - pixel[1] = g; - pixel[2] = b; - pixel[3] = 255; - } - } - if (unlock) { - ASSERT_EQ(NO_ERROR, s->unlockAndPost()); - } -} - -// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check -// individual pixel values for testing purposes. -class ScreenCapture : public RefBase { -public: - static void captureScreen(std::unique_ptr<ScreenCapture>* sc) { - captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken()); - } - - static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) { - const auto sf = ComposerService::getComposerService(); - SurfaceComposerClient::Transaction().apply(true); - - sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false)); - *sc = std::make_unique<ScreenCapture>(outBuffer); - } - - static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, - Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - SurfaceComposerClient::Transaction().apply(true); - - sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale)); - *sc = std::make_unique<ScreenCapture>(outBuffer); - } - - static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, - Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - SurfaceComposerClient::Transaction().apply(true); - - sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true)); - *sc = std::make_unique<ScreenCapture>(outBuffer); - } - - static void captureChildLayersExcluding( - std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, - std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) { - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - SurfaceComposerClient::Transaction().apply(true); - - sp<GraphicBuffer> outBuffer; - ASSERT_EQ(NO_ERROR, - sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers, - 1.0f, true)); - *sc = std::make_unique<ScreenCapture>(outBuffer); - } - - void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) { - ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat()); - expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance); - } - - void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) { - ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat()); - const bool leftBorder = rect.left > 0; - const bool topBorder = rect.top > 0; - const bool rightBorder = rect.right < int32_t(mOutBuffer->getWidth()); - const bool bottomBorder = rect.bottom < int32_t(mOutBuffer->getHeight()); - - if (topBorder) { - Rect top(rect.left, rect.top - 1, rect.right, rect.top); - if (leftBorder) { - top.left -= 1; - } - if (rightBorder) { - top.right += 1; - } - expectColor(top, color, tolerance); - } - if (leftBorder) { - Rect left(rect.left - 1, rect.top, rect.left, rect.bottom); - expectColor(left, color, tolerance); - } - if (rightBorder) { - Rect right(rect.right, rect.top, rect.right + 1, rect.bottom); - expectColor(right, color, tolerance); - } - if (bottomBorder) { - Rect bottom(rect.left, rect.bottom, rect.right, rect.bottom + 1); - if (leftBorder) { - bottom.left -= 1; - } - if (rightBorder) { - bottom.right += 1; - } - expectColor(bottom, color, tolerance); - } - } - - void expectQuadrant(const Rect& rect, const Color& topLeft, const Color& topRight, - const Color& bottomLeft, const Color& bottomRight, bool filtered = false, - uint8_t tolerance = 0) { - ASSERT_TRUE((rect.right - rect.left) % 2 == 0 && (rect.bottom - rect.top) % 2 == 0); - - const int32_t centerX = rect.left + (rect.right - rect.left) / 2; - const int32_t centerY = rect.top + (rect.bottom - rect.top) / 2; - // avoid checking borders due to unspecified filtering behavior - const int32_t offsetX = filtered ? 2 : 0; - const int32_t offsetY = filtered ? 2 : 0; - expectColor(Rect(rect.left, rect.top, centerX - offsetX, centerY - offsetY), topLeft, - tolerance); - expectColor(Rect(centerX + offsetX, rect.top, rect.right, centerY - offsetY), topRight, - tolerance); - expectColor(Rect(rect.left, centerY + offsetY, centerX - offsetX, rect.bottom), bottomLeft, - tolerance); - expectColor(Rect(centerX + offsetX, centerY + offsetY, rect.right, rect.bottom), - bottomRight, tolerance); - } - - void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) { - ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat()); - const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x)); - if (r != pixel[0] || g != pixel[1] || b != pixel[2]) { - String8 err(String8::format("pixel @ (%3d, %3d): " - "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]", - x, y, r, g, b, pixel[0], pixel[1], pixel[2])); - EXPECT_EQ(String8(), err) << err.string(); - } - } - - void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); } - - void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); } - - void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); } - - explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) { - mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels)); - } - - ~ScreenCapture() { mOutBuffer->unlock(); } - -private: - sp<GraphicBuffer> mOutBuffer; - uint8_t* mPixels = nullptr; -}; - -class LayerTransactionTest : public ::testing::Test { -protected: - void SetUp() override { - mClient = new SurfaceComposerClient; - ASSERT_EQ(NO_ERROR, mClient->initCheck()) << "failed to create SurfaceComposerClient"; - - ASSERT_NO_FATAL_FAILURE(SetUpDisplay()); - - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - ASSERT_NO_FATAL_FAILURE(sf->getColorManagement(&mColorManagementUsed)); - } - - virtual void TearDown() { - mBlackBgSurface = 0; - mClient->dispose(); - mClient = 0; - } - - virtual sp<SurfaceControl> createLayer(const sp<SurfaceComposerClient>& client, - const char* name, uint32_t width, uint32_t height, - uint32_t flags = 0, SurfaceControl* parent = nullptr) { - auto layer = - createSurface(client, name, width, height, PIXEL_FORMAT_RGBA_8888, flags, parent); - - Transaction t; - t.setLayerStack(layer, mDisplayLayerStack).setLayer(layer, mLayerZBase); - - status_t error = t.apply(); - if (error != NO_ERROR) { - ADD_FAILURE() << "failed to initialize SurfaceControl"; - layer.clear(); - } - - return layer; - } - - virtual sp<SurfaceControl> createSurface(const sp<SurfaceComposerClient>& client, - const char* name, uint32_t width, uint32_t height, - PixelFormat format, uint32_t flags, - SurfaceControl* parent = nullptr) { - auto layer = client->createSurface(String8(name), width, height, format, flags, parent); - EXPECT_NE(nullptr, layer.get()) << "failed to create SurfaceControl"; - return layer; - } - - virtual sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, - uint32_t flags = 0, SurfaceControl* parent = nullptr) { - return createLayer(mClient, name, width, height, flags, parent); - } - - sp<SurfaceControl> createColorLayer(const char* name, const Color& color, - SurfaceControl* parent = nullptr) { - auto colorLayer = createSurface(mClient, name, 0 /* buffer width */, 0 /* buffer height */, - PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceColor, parent); - asTransaction([&](Transaction& t) { - t.setColor(colorLayer, half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f}); - t.setAlpha(colorLayer, color.a / 255.0f); - }); - return colorLayer; - } - - ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) { - // wait for previous transactions (such as setSize) to complete - Transaction().apply(true); - - ANativeWindow_Buffer buffer = {}; - EXPECT_EQ(NO_ERROR, layer->getSurface()->lock(&buffer, nullptr)); - - return buffer; - } - - void postBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) { - ASSERT_EQ(NO_ERROR, layer->getSurface()->unlockAndPost()); - - // wait for the newly posted buffer to be latched - waitForLayerBuffers(); - } - - virtual void fillBufferQueueLayerColor(const sp<SurfaceControl>& layer, const Color& color, - int32_t bufferWidth, int32_t bufferHeight) { - ANativeWindow_Buffer buffer; - ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); - fillANativeWindowBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color); - postBufferQueueLayerBuffer(layer); - } - - virtual void fillBufferStateLayerColor(const sp<SurfaceControl>& layer, const Color& color, - int32_t bufferWidth, int32_t bufferHeight) { - sp<GraphicBuffer> buffer = - new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - fillGraphicBufferColor(buffer, Rect(0, 0, bufferWidth, bufferHeight), color); - Transaction().setBuffer(layer, buffer).apply(); - } - - void fillLayerColor(uint32_t mLayerType, const sp<SurfaceControl>& layer, const Color& color, - int32_t bufferWidth, int32_t bufferHeight) { - switch (mLayerType) { - case ISurfaceComposerClient::eFXSurfaceBufferQueue: - fillBufferQueueLayerColor(layer, color, bufferWidth, bufferHeight); - break; - case ISurfaceComposerClient::eFXSurfaceBufferState: - fillBufferStateLayerColor(layer, color, bufferWidth, bufferHeight); - break; - default: - ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType; - } - } - - void fillLayerQuadrant(uint32_t mLayerType, const sp<SurfaceControl>& layer, - int32_t bufferWidth, int32_t bufferHeight, const Color& topLeft, - const Color& topRight, const Color& bottomLeft, - const Color& bottomRight) { - switch (mLayerType) { - case ISurfaceComposerClient::eFXSurfaceBufferQueue: - fillBufferQueueLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight, - bottomLeft, bottomRight); - break; - case ISurfaceComposerClient::eFXSurfaceBufferState: - fillBufferStateLayerQuadrant(layer, bufferWidth, bufferHeight, topLeft, topRight, - bottomLeft, bottomRight); - break; - default: - ASSERT_TRUE(false) << "unsupported layer type: " << mLayerType; - } - } - - virtual void fillBufferQueueLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth, - int32_t bufferHeight, const Color& topLeft, - const Color& topRight, const Color& bottomLeft, - const Color& bottomRight) { - ANativeWindow_Buffer buffer; - ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); - ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0); - - const int32_t halfW = bufferWidth / 2; - const int32_t halfH = bufferHeight / 2; - fillANativeWindowBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft); - fillANativeWindowBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), topRight); - fillANativeWindowBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), bottomLeft); - fillANativeWindowBufferColor(buffer, Rect(halfW, halfH, bufferWidth, bufferHeight), - bottomRight); - - postBufferQueueLayerBuffer(layer); - } - - virtual void fillBufferStateLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth, - int32_t bufferHeight, const Color& topLeft, - const Color& topRight, const Color& bottomLeft, - const Color& bottomRight) { - sp<GraphicBuffer> buffer = - new GraphicBuffer(bufferWidth, bufferHeight, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - - ASSERT_TRUE(bufferWidth % 2 == 0 && bufferHeight % 2 == 0); - - const int32_t halfW = bufferWidth / 2; - const int32_t halfH = bufferHeight / 2; - fillGraphicBufferColor(buffer, Rect(0, 0, halfW, halfH), topLeft); - fillGraphicBufferColor(buffer, Rect(halfW, 0, bufferWidth, halfH), topRight); - fillGraphicBufferColor(buffer, Rect(0, halfH, halfW, bufferHeight), bottomLeft); - fillGraphicBufferColor(buffer, Rect(halfW, halfH, bufferWidth, bufferHeight), bottomRight); - - Transaction().setBuffer(layer, buffer).setSize(layer, bufferWidth, bufferHeight).apply(); - } - - std::unique_ptr<ScreenCapture> screenshot() { - std::unique_ptr<ScreenCapture> screenshot; - ScreenCapture::captureScreen(&screenshot); - return screenshot; - } - - void asTransaction(const std::function<void(Transaction&)>& exec) { - Transaction t; - exec(t); - t.apply(true); - } - - static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { - static BufferGenerator bufferGenerator; - return bufferGenerator.get(outBuffer, outFence); - } - - sp<SurfaceComposerClient> mClient; - - sp<IBinder> mDisplay; - uint32_t mDisplayWidth; - uint32_t mDisplayHeight; - uint32_t mDisplayLayerStack; - Rect mDisplayRect = Rect::INVALID_RECT; - - // leave room for ~256 layers - const int32_t mLayerZBase = std::numeric_limits<int32_t>::max() - 256; - - sp<SurfaceControl> mBlackBgSurface; - bool mColorManagementUsed; - -private: - void SetUpDisplay() { - mDisplay = mClient->getInternalDisplayToken(); - ASSERT_FALSE(mDisplay == nullptr) << "failed to get display"; - - // get display width/height - DisplayInfo info; - ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(mDisplay, &info)); - mDisplayWidth = info.w; - mDisplayHeight = info.h; - mDisplayRect = - Rect(static_cast<int32_t>(mDisplayWidth), static_cast<int32_t>(mDisplayHeight)); - - // After a new buffer is queued, SurfaceFlinger is notified and will - // latch the new buffer on next vsync. Let's heuristically wait for 3 - // vsyncs. - mBufferPostDelay = int32_t(1e6 / info.fps) * 3; - - mDisplayLayerStack = 0; - - mBlackBgSurface = - createSurface(mClient, "BaseSurface", 0 /* buffer width */, 0 /* buffer height */, - PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor); - - // set layer stack (b/68888219) - Transaction t; - t.setDisplayLayerStack(mDisplay, mDisplayLayerStack); - t.setCrop_legacy(mBlackBgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight)); - t.setLayerStack(mBlackBgSurface, mDisplayLayerStack); - t.setColor(mBlackBgSurface, half3{0, 0, 0}); - t.setLayer(mBlackBgSurface, mLayerZBase); - t.apply(); - } - - void waitForLayerBuffers() { - // Request an empty transaction to get applied synchronously to ensure the buffer is - // latched. - Transaction().apply(true); - usleep(mBufferPostDelay); - } - - int32_t mBufferPostDelay; - - friend class LayerRenderPathTestHarness; -}; -enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY }; - -class LayerRenderPathTestHarness { -public: - LayerRenderPathTestHarness(LayerTransactionTest* delegate, RenderPath renderPath) - : mDelegate(delegate), mRenderPath(renderPath) {} - - std::unique_ptr<ScreenCapture> getScreenCapture() { - switch (mRenderPath) { - case RenderPath::SCREENSHOT: - return mDelegate->screenshot(); - case RenderPath::VIRTUAL_DISPLAY: - - const auto mainDisplay = SurfaceComposerClient::getInternalDisplayToken(); - DisplayInfo mainDisplayInfo; - SurfaceComposerClient::getDisplayInfo(mainDisplay, &mainDisplayInfo); - - sp<IBinder> vDisplay; - sp<IGraphicBufferProducer> producer; - sp<IGraphicBufferConsumer> consumer; - sp<BufferItemConsumer> itemConsumer; - BufferQueue::createBufferQueue(&producer, &consumer); - - consumer->setConsumerName(String8("Virtual disp consumer")); - consumer->setDefaultBufferSize(mainDisplayInfo.w, mainDisplayInfo.h); - - itemConsumer = new BufferItemConsumer(consumer, - // Sample usage bits from screenrecord - GRALLOC_USAGE_HW_VIDEO_ENCODER | - GRALLOC_USAGE_SW_READ_OFTEN); - - vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), - false /*secure*/); - - SurfaceComposerClient::Transaction t; - t.setDisplaySurface(vDisplay, producer); - t.setDisplayLayerStack(vDisplay, 0); - t.setDisplayProjection(vDisplay, mainDisplayInfo.orientation, - Rect(mainDisplayInfo.viewportW, mainDisplayInfo.viewportH), - Rect(mainDisplayInfo.w, mainDisplayInfo.h)); - t.apply(); - SurfaceComposerClient::Transaction().apply(true); - BufferItem item; - itemConsumer->acquireBuffer(&item, 0, true); - auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer); - itemConsumer->releaseBuffer(item); - SurfaceComposerClient::destroyDisplay(vDisplay); - return sc; - } - } - -protected: - LayerTransactionTest* mDelegate; - RenderPath mRenderPath; -}; - -class LayerTypeTransactionHarness : public LayerTransactionTest { -public: - LayerTypeTransactionHarness(uint32_t layerType) : mLayerType(layerType) {} - - sp<SurfaceControl> createLayer(const char* name, uint32_t width, uint32_t height, - uint32_t flags = 0, SurfaceControl* parent = nullptr) { - // if the flags already have a layer type specified, return an error - if (flags & ISurfaceComposerClient::eFXSurfaceMask) { - return nullptr; - } - return LayerTransactionTest::createLayer(name, width, height, flags | mLayerType, parent); - } - - void fillLayerColor(const sp<SurfaceControl>& layer, const Color& color, int32_t bufferWidth, - int32_t bufferHeight) { - ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerColor(mLayerType, layer, color, - bufferWidth, bufferHeight)); - } - - void fillLayerQuadrant(const sp<SurfaceControl>& layer, int32_t bufferWidth, - int32_t bufferHeight, const Color& topLeft, const Color& topRight, - const Color& bottomLeft, const Color& bottomRight) { - ASSERT_NO_FATAL_FAILURE(LayerTransactionTest::fillLayerQuadrant(mLayerType, layer, - bufferWidth, bufferHeight, - topLeft, topRight, - bottomLeft, bottomRight)); - } - -protected: - uint32_t mLayerType; -}; - -class LayerTypeTransactionTest : public LayerTypeTransactionHarness, - public ::testing::WithParamInterface<uint32_t> { -public: - LayerTypeTransactionTest() : LayerTypeTransactionHarness(GetParam()) {} -}; - -class LayerTypeAndRenderTypeTransactionTest - : public LayerTypeTransactionHarness, - public ::testing::WithParamInterface<std::tuple<uint32_t, RenderPath>> { -public: - LayerTypeAndRenderTypeTransactionTest() - : LayerTypeTransactionHarness(std::get<0>(GetParam())), - mRenderPathHarness(LayerRenderPathTestHarness(this, std::get<1>(GetParam()))) {} - - std::unique_ptr<ScreenCapture> getScreenCapture() { - return mRenderPathHarness.getScreenCapture(); - } - -protected: - LayerRenderPathTestHarness mRenderPathHarness; -}; - -// Environment for starting up binder threads. This is required for testing -// virtual displays, as BufferQueue parameters may be queried over binder. -class BinderEnvironment : public ::testing::Environment { -public: - void SetUp() override { ProcessState::self()->startThreadPool(); } -}; - -::testing::Environment* const binderEnv = - ::testing::AddGlobalTestEnvironment(new BinderEnvironment()); - -class LayerRenderTypeTransactionTest : public LayerTransactionTest, - public ::testing::WithParamInterface<RenderPath> { -public: - LayerRenderTypeTransactionTest() : mHarness(LayerRenderPathTestHarness(this, GetParam())) {} - - std::unique_ptr<ScreenCapture> getScreenCapture() { return mHarness.getScreenCapture(); } - void setRelativeZBasicHelper(uint32_t layerType); - void setRelativeZGroupHelper(uint32_t layerType); - void setAlphaBasicHelper(uint32_t layerType); - void setBackgroundColorHelper(uint32_t layerType, bool priorColor, bool bufferFill, float alpha, - Color finalColor); - -protected: - LayerRenderPathTestHarness mHarness; -}; - -INSTANTIATE_TEST_CASE_P( - LayerTypeAndRenderTypeTransactionTests, LayerTypeAndRenderTypeTransactionTest, - ::testing::Combine( - ::testing::Values( - static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue), - static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState)), - ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT))); - -INSTANTIATE_TEST_CASE_P(LayerRenderTypeTransactionTests, LayerRenderTypeTransactionTest, - ::testing::Values(RenderPath::VIRTUAL_DISPLAY, RenderPath::SCREENSHOT)); - -INSTANTIATE_TEST_CASE_P( - LayerTypeTransactionTests, LayerTypeTransactionTest, - ::testing::Values(static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferQueue), - static_cast<uint32_t>(ISurfaceComposerClient::eFXSurfaceBufferState))); - -TEST_P(LayerRenderTypeTransactionTest, SetPositionBasic_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - { - SCOPED_TRACE("default position"); - const Rect rect(0, 0, 32, 32); - auto shot = getScreenCapture(); - shot->expectColor(rect, Color::RED); - shot->expectBorder(rect, Color::BLACK); - } - - Transaction().setPosition(layer, 5, 10).apply(); - { - SCOPED_TRACE("new position"); - const Rect rect(5, 10, 37, 42); - auto shot = getScreenCapture(); - shot->expectColor(rect, Color::RED); - shot->expectBorder(rect, Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetPositionRounding_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // GLES requires only 4 bits of subpixel precision during rasterization - // XXX GLES composition does not match HWC composition due to precision - // loss (b/69315223) - const float epsilon = 1.0f / 16.0f; - Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply(); - { - SCOPED_TRACE("rounding down"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } - - Transaction().setPosition(layer, 0.5f + epsilon, 0.5f + epsilon).apply(); - { - SCOPED_TRACE("rounding up"); - getScreenCapture()->expectColor(Rect(1, 1, 33, 33), Color::RED); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetPositionOutOfBounds_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - Transaction().setPosition(layer, -32, -32).apply(); - { - SCOPED_TRACE("negative coordinates"); - getScreenCapture()->expectColor(mDisplayRect, Color::BLACK); - } - - Transaction().setPosition(layer, mDisplayWidth, mDisplayHeight).apply(); - { - SCOPED_TRACE("positive coordinates"); - getScreenCapture()->expectColor(mDisplayRect, Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetPositionPartiallyOutOfBounds_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // partially out of bounds - Transaction().setPosition(layer, -30, -30).apply(); - { - SCOPED_TRACE("negative coordinates"); - getScreenCapture()->expectColor(Rect(0, 0, 2, 2), Color::RED); - } - - Transaction().setPosition(layer, mDisplayWidth - 2, mDisplayHeight - 2).apply(); - { - SCOPED_TRACE("positive coordinates"); - getScreenCapture()->expectColor(Rect(mDisplayWidth - 2, mDisplayHeight - 2, mDisplayWidth, - mDisplayHeight), - Color::RED); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetPositionWithResize_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // setPosition is applied immediately by default, with or without resize - // pending - Transaction().setPosition(layer, 5, 10).setSize(layer, 64, 64).apply(); - { - SCOPED_TRACE("resize pending"); - auto shot = getScreenCapture(); - const Rect rect(5, 10, 37, 42); - shot->expectColor(rect, Color::RED); - shot->expectBorder(rect, Color::BLACK); - } - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64)); - { - SCOPED_TRACE("resize applied"); - getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResize_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // request setPosition to be applied with the next resize - Transaction().setPosition(layer, 5, 10).setGeometryAppliesWithResize(layer).apply(); - { - SCOPED_TRACE("new position pending"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } - - Transaction().setPosition(layer, 15, 20).apply(); - { - SCOPED_TRACE("pending new position modified"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } - - Transaction().setSize(layer, 64, 64).apply(); - { - SCOPED_TRACE("resize pending"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } - - // finally resize and latch the buffer - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64)); - { - SCOPED_TRACE("new position applied"); - getScreenCapture()->expectColor(Rect(15, 20, 79, 84), Color::RED); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetPositionWithNextResizeScaleToWindow_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // setPosition is not immediate even with SCALE_TO_WINDOW override - Transaction() - .setPosition(layer, 5, 10) - .setSize(layer, 64, 64) - .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) - .setGeometryAppliesWithResize(layer) - .apply(); - { - SCOPED_TRACE("new position pending"); - getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED); - } - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64)); - { - SCOPED_TRACE("new position applied"); - getScreenCapture()->expectColor(Rect(5, 10, 69, 74), Color::RED); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetSizeBasic_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - Transaction().setSize(layer, 64, 64).apply(); - { - SCOPED_TRACE("resize pending"); - auto shot = getScreenCapture(); - const Rect rect(0, 0, 32, 32); - shot->expectColor(rect, Color::RED); - shot->expectBorder(rect, Color::BLACK); - } - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64)); - { - SCOPED_TRACE("resize applied"); - auto shot = getScreenCapture(); - const Rect rect(0, 0, 64, 64); - shot->expectColor(rect, Color::RED); - shot->expectBorder(rect, Color::BLACK); - } -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetSizeInvalid) { - // cannot test robustness against invalid sizes (zero or really huge) -} - -TEST_P(LayerRenderTypeTransactionTest, SetSizeWithScaleToWindow_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // setSize is immediate with SCALE_TO_WINDOW, unlike setPosition - Transaction() - .setSize(layer, 64, 64) - .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) - .apply(); - getScreenCapture()->expectColor(Rect(0, 0, 64, 64), Color::RED); -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZBasic) { - sp<SurfaceControl> layerR; - sp<SurfaceControl> layerG; - ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); - ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); - - Transaction().setLayer(layerR, mLayerZBase + 1).apply(); - { - SCOPED_TRACE("layerR"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } - - Transaction().setLayer(layerG, mLayerZBase + 2).apply(); - { - SCOPED_TRACE("layerG"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::GREEN); - } -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetZNegative) { - sp<SurfaceControl> parent = - LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceContainer); - Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply(); - sp<SurfaceControl> layerR; - sp<SurfaceControl> layerG; - ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); - ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); - - Transaction() - .reparent(layerR, parent->getHandle()) - .reparent(layerG, parent->getHandle()) - .apply(); - Transaction().setLayer(layerR, -1).setLayer(layerG, -2).apply(); - { - SCOPED_TRACE("layerR"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - } - - Transaction().setLayer(layerR, -3).apply(); - { - SCOPED_TRACE("layerG"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::GREEN); - } -} - -void LayerRenderTypeTransactionTest::setRelativeZBasicHelper(uint32_t layerType) { - sp<SurfaceControl> layerR; - sp<SurfaceControl> layerG; - ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32, layerType)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32)); - ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32, layerType)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32)); - - switch (layerType) { - case ISurfaceComposerClient::eFXSurfaceBufferQueue: - Transaction() - .setPosition(layerG, 16, 16) - .setRelativeLayer(layerG, layerR->getHandle(), 1) - .apply(); - break; - case ISurfaceComposerClient::eFXSurfaceBufferState: - Transaction() - .setFrame(layerR, Rect(0, 0, 32, 32)) - .setFrame(layerG, Rect(16, 16, 48, 48)) - .setRelativeLayer(layerG, layerR->getHandle(), 1) - .apply(); - break; - default: - ASSERT_FALSE(true) << "Unsupported layer type"; - } - { - SCOPED_TRACE("layerG above"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 16, 16), Color::RED); - shot->expectColor(Rect(16, 16, 48, 48), Color::GREEN); - } - - Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).apply(); - { - SCOPED_TRACE("layerG below"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectColor(Rect(32, 32, 48, 48), Color::GREEN); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferQueue) { - ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); -} - -TEST_P(LayerRenderTypeTransactionTest, SetRelativeZBasic_BufferState) { - ASSERT_NO_FATAL_FAILURE(setRelativeZBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); -} - -TEST_P(LayerTypeTransactionTest, SetRelativeZNegative) { - sp<SurfaceControl> parent = - LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceContainer); - Transaction().setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)).apply(); - sp<SurfaceControl> layerR; - sp<SurfaceControl> layerG; - sp<SurfaceControl> layerB; - ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); - ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); - ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test B", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerB, Color::BLUE, 32, 32)); - - Transaction() - .reparent(layerB, parent->getHandle()) - .apply(); - - // layerR = mLayerZBase, layerG = layerR - 1, layerB = -2 - Transaction().setRelativeLayer(layerG, layerR->getHandle(), -1).setLayer(layerB, -2).apply(); - - std::unique_ptr<ScreenCapture> screenshot; - // only layerB is in this range - sp<IBinder> parentHandle = parent->getHandle(); - ScreenCapture::captureLayers(&screenshot, parentHandle, Rect(0, 0, 32, 32)); - screenshot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); -} - -TEST_P(LayerTypeTransactionTest, SetLayerAndRelative) { - sp<SurfaceControl> parent = - LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor); - - sp<SurfaceControl> childLayer; - ASSERT_NO_FATAL_FAILURE( - childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */, - 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor, - parent.get())); - Transaction() - .setColor(childLayer, half3{1.0f, 0.0f, 0.0f}) - .setColor(parent, half3{0.0f, 0.0f, 0.0f}) - .show(childLayer) - .show(parent) - .setCrop_legacy(parent, Rect(0, 0, mDisplayWidth, mDisplayHeight)) - .setCrop_legacy(childLayer, Rect(0, 0, 20, 30)) - .apply(); - - Transaction() - .setRelativeLayer(childLayer, parent->getHandle(), -1) - .setLayer(childLayer, 1) - .apply(); - - { - SCOPED_TRACE("setLayer above"); - // Set layer should get applied and place the child above. - std::unique_ptr<ScreenCapture> screenshot; - ScreenCapture::captureScreen(&screenshot); - screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED); - } - - Transaction() - .setLayer(childLayer, 1) - .setRelativeLayer(childLayer, parent->getHandle(), -1) - .apply(); - - { - SCOPED_TRACE("setRelative below"); - // Set relative layer should get applied and place the child below. - std::unique_ptr<ScreenCapture> screenshot; - ScreenCapture::captureScreen(&screenshot); - screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK); - } -} - -TEST_P(LayerTypeTransactionTest, HideRelativeParentHidesLayer) { - sp<SurfaceControl> parent = - LayerTransactionTest::createLayer("Parent", 0 /* buffer width */, 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor); - sp<SurfaceControl> relativeParent = - LayerTransactionTest::createLayer("RelativeParent", 0 /* buffer width */, - 0 /* buffer height */, ISurfaceComposerClient::eFXSurfaceColor); - - sp<SurfaceControl> childLayer; - ASSERT_NO_FATAL_FAILURE( - childLayer = LayerTransactionTest::createLayer("childLayer", 0 /* buffer width */, - 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor, - parent.get())); - Transaction() - .setColor(childLayer, half3{1.0f, 0.0f, 0.0f}) - .setColor(parent, half3{0.0f, 0.0f, 0.0f}) - .setColor(relativeParent, half3{0.0f, 1.0f, 0.0f}) - .show(childLayer) - .show(parent) - .show(relativeParent) - .setLayer(parent, mLayerZBase - 1) - .setLayer(relativeParent, mLayerZBase) - .apply(); - - Transaction() - .setRelativeLayer(childLayer, relativeParent->getHandle(), 1) - .apply(); - - { - SCOPED_TRACE("setLayer above"); - // Set layer should get applied and place the child above. - std::unique_ptr<ScreenCapture> screenshot; - ScreenCapture::captureScreen(&screenshot); - screenshot->expectColor(Rect(0, 0, 20, 30), Color::RED); - } - - Transaction() - .hide(relativeParent) - .apply(); - - { - SCOPED_TRACE("hide relative parent"); - // The relative should no longer be visible. - std::unique_ptr<ScreenCapture> screenshot; - ScreenCapture::captureScreen(&screenshot); - screenshot->expectColor(Rect(0, 0, 20, 30), Color::BLACK); - } -} - -void LayerRenderTypeTransactionTest::setRelativeZGroupHelper(uint32_t layerType) { - sp<SurfaceControl> layerR; - sp<SurfaceControl> layerG; - sp<SurfaceControl> layerB; - ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test", 32, 32, layerType)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerR, Color::RED, 32, 32)); - ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test", 32, 32, layerType)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerG, Color::GREEN, 32, 32)); - ASSERT_NO_FATAL_FAILURE(layerB = createLayer("test", 32, 32, layerType)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layerB, Color::BLUE, 32, 32)); - - // layerR = 0, layerG = layerR + 3, layerB = 2 - switch (layerType) { - case ISurfaceComposerClient::eFXSurfaceBufferQueue: - Transaction() - .setPosition(layerG, 8, 8) - .setRelativeLayer(layerG, layerR->getHandle(), 3) - .setPosition(layerB, 16, 16) - .setLayer(layerB, mLayerZBase + 2) - .apply(); - break; - case ISurfaceComposerClient::eFXSurfaceBufferState: - Transaction() - .setFrame(layerR, Rect(0, 0, 32, 32)) - .setFrame(layerG, Rect(8, 8, 40, 40)) - .setRelativeLayer(layerG, layerR->getHandle(), 3) - .setFrame(layerB, Rect(16, 16, 48, 48)) - .setLayer(layerB, mLayerZBase + 2) - .apply(); - break; - default: - ASSERT_FALSE(true) << "Unsupported layer type"; - } - - { - SCOPED_TRACE("(layerR < layerG) < layerB"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 8, 8), Color::RED); - shot->expectColor(Rect(8, 8, 16, 16), Color::GREEN); - shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE); - } - - // layerR = 4, layerG = layerR + 3, layerB = 2 - Transaction().setLayer(layerR, mLayerZBase + 4).apply(); - { - SCOPED_TRACE("layerB < (layerR < layerG)"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 8, 8), Color::RED); - shot->expectColor(Rect(8, 8, 40, 40), Color::GREEN); - shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE); - } - - // layerR = 4, layerG = layerR - 3, layerB = 2 - Transaction().setRelativeLayer(layerG, layerR->getHandle(), -3).apply(); - { - SCOPED_TRACE("layerB < (layerG < layerR)"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectColor(Rect(32, 32, 40, 40), Color::GREEN); - shot->expectColor(Rect(40, 40, 48, 48), Color::BLUE); - } - - // restore to absolute z - // layerR = 4, layerG = 0, layerB = 2 - Transaction().setLayer(layerG, mLayerZBase).apply(); - { - SCOPED_TRACE("layerG < layerB < layerR"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectColor(Rect(32, 32, 48, 48), Color::BLUE); - } - - // layerR should not affect layerG anymore - // layerR = 1, layerG = 0, layerB = 2 - Transaction().setLayer(layerR, mLayerZBase + 1).apply(); - { - SCOPED_TRACE("layerG < layerR < layerB"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 16, 16), Color::RED); - shot->expectColor(Rect(16, 16, 48, 48), Color::BLUE); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferQueue) { - ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); -} - -TEST_P(LayerRenderTypeTransactionTest, SetRelativeZGroup_BufferState) { - ASSERT_NO_FATAL_FAILURE(setRelativeZGroupHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetRelativeZBug64572777) { - sp<SurfaceControl> layerR; - sp<SurfaceControl> layerG; - - ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, Color::RED, 32, 32)); - ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); - - Transaction() - .setPosition(layerG, 16, 16) - .setRelativeLayer(layerG, layerR->getHandle(), 1) - .apply(); - - layerG.clear(); - // layerG should have been removed - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsHidden) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); - - Transaction().setFlags(layer, layer_state_t::eLayerHidden, layer_state_t::eLayerHidden).apply(); - { - SCOPED_TRACE("layer hidden"); - getScreenCapture()->expectColor(mDisplayRect, Color::BLACK); - } - - Transaction().setFlags(layer, 0, layer_state_t::eLayerHidden).apply(); - { - SCOPED_TRACE("layer shown"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetFlagsOpaque) { - const Color translucentRed = {100, 0, 0, 100}; - sp<SurfaceControl> layerR; - sp<SurfaceControl> layerG; - ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerR, translucentRed, 32, 32)); - ASSERT_NO_FATAL_FAILURE(layerG = createLayer("test G", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerG, Color::GREEN, 32, 32)); - - Transaction() - .setLayer(layerR, mLayerZBase + 1) - .setFlags(layerR, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque) - .apply(); - { - SCOPED_TRACE("layerR opaque"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, 0, 0, 255}); - } - - Transaction().setFlags(layerR, 0, layer_state_t::eLayerOpaque).apply(); - { - SCOPED_TRACE("layerR translucent"); - const uint8_t g = uint8_t(255 - translucentRed.a); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {100, g, 0, 255}); - } -} - -TEST_P(LayerTypeTransactionTest, SetFlagsSecure) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); - - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); - sp<GraphicBuffer> outBuffer; - Transaction() - .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure) - .apply(true); - ASSERT_EQ(PERMISSION_DENIED, - composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); - - Transaction().setFlags(layer, 0, layer_state_t::eLayerSecure).apply(true); - ASSERT_EQ(NO_ERROR, - composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); -} - -/** RAII Wrapper around get/seteuid */ -class UIDFaker { - uid_t oldId; -public: - UIDFaker(uid_t uid) { - oldId = geteuid(); - seteuid(uid); - } - ~UIDFaker() { - seteuid(oldId); - } -}; - -TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - sp<ISurfaceComposer> composer = ComposerService::getComposerService(); - sp<GraphicBuffer> outBuffer; - Transaction() - .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure) - .apply(true); - ASSERT_EQ(PERMISSION_DENIED, - composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); - - UIDFaker f(AID_SYSTEM); - - // By default the system can capture screenshots with secure layers but they - // will be blacked out - ASSERT_EQ(NO_ERROR, - composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); - - { - SCOPED_TRACE("as system"); - auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK); - } - - // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able - // to receive them...we are expected to take care with the results. - bool outCapturedSecureLayers; - ASSERT_EQ(NO_ERROR, - composer->captureScreen(mDisplay, &outBuffer, outCapturedSecureLayers, - ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, Rect(), 0, - 0, false, ISurfaceComposer::eRotateNone, true)); - ASSERT_EQ(true, outCapturedSecureLayers); - ScreenCapture sc(outBuffer); - sc.expectColor(Rect(0, 0, 32, 32), Color::RED); -} - -TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) { - const Rect top(0, 0, 32, 16); - const Rect bottom(0, 16, 32, 32); - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - - ANativeWindow_Buffer buffer; - ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); - ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, top, Color::TRANSPARENT)); - ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, bottom, Color::RED)); - // setTransparentRegionHint always applies to the following buffer - Transaction().setTransparentRegionHint(layer, Region(top)).apply(); - ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer)); - { - SCOPED_TRACE("top transparent"); - auto shot = getScreenCapture(); - shot->expectColor(top, Color::BLACK); - shot->expectColor(bottom, Color::RED); - } - - Transaction().setTransparentRegionHint(layer, Region(bottom)).apply(); - { - SCOPED_TRACE("transparent region hint pending"); - auto shot = getScreenCapture(); - shot->expectColor(top, Color::BLACK); - shot->expectColor(bottom, Color::RED); - } - - ASSERT_NO_FATAL_FAILURE(buffer = getBufferQueueLayerBuffer(layer)); - ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, top, Color::RED)); - ASSERT_NO_FATAL_FAILURE(fillANativeWindowBufferColor(buffer, bottom, Color::TRANSPARENT)); - ASSERT_NO_FATAL_FAILURE(postBufferQueueLayerBuffer(layer)); - { - SCOPED_TRACE("bottom transparent"); - auto shot = getScreenCapture(); - shot->expectColor(top, Color::RED); - shot->expectColor(bottom, Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferState) { - const Rect top(0, 0, 32, 16); - const Rect bottom(0, 16, 32, 32); - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - - ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, top, Color::TRANSPARENT)); - ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, bottom, Color::RED)); - Transaction() - .setTransparentRegionHint(layer, Region(top)) - .setBuffer(layer, buffer) - .setFrame(layer, Rect(0, 0, 32, 32)) - .apply(); - { - SCOPED_TRACE("top transparent"); - auto shot = getScreenCapture(); - shot->expectColor(top, Color::BLACK); - shot->expectColor(bottom, Color::RED); - } - - Transaction().setTransparentRegionHint(layer, Region(bottom)).apply(); - { - SCOPED_TRACE("transparent region hint intermediate"); - auto shot = getScreenCapture(); - shot->expectColor(top, Color::BLACK); - shot->expectColor(bottom, Color::BLACK); - } - - buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - - ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, top, Color::RED)); - ASSERT_NO_FATAL_FAILURE(fillGraphicBufferColor(buffer, bottom, Color::TRANSPARENT)); - Transaction().setBuffer(layer, buffer).apply(); - { - SCOPED_TRACE("bottom transparent"); - auto shot = getScreenCapture(); - shot->expectColor(top, Color::RED); - shot->expectColor(bottom, Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferQueue) { - sp<SurfaceControl> layerTransparent; - sp<SurfaceControl> layerR; - ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32)); - ASSERT_NO_FATAL_FAILURE(layerR = createLayer("test R", 32, 32)); - - // check that transparent region hint is bound by the layer size - Transaction() - .setTransparentRegionHint(layerTransparent, Region(mDisplayRect)) - .setPosition(layerR, 16, 16) - .setLayer(layerR, mLayerZBase + 1) - .apply(); - ASSERT_NO_FATAL_FAILURE( - fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layerR, Color::RED, 32, 32)); - getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED); -} - -TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintOutOfBounds_BufferState) { - sp<SurfaceControl> layerTransparent; - sp<SurfaceControl> layerR; - ASSERT_NO_FATAL_FAILURE(layerTransparent = createLayer("test transparent", 32, 32)); - ASSERT_NO_FATAL_FAILURE( - layerR = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - // check that transparent region hint is bound by the layer size - Transaction() - .setTransparentRegionHint(layerTransparent, Region(mDisplayRect)) - .setFrame(layerR, Rect(16, 16, 48, 48)) - .setLayer(layerR, mLayerZBase + 1) - .apply(); - ASSERT_NO_FATAL_FAILURE( - fillBufferQueueLayerColor(layerTransparent, Color::TRANSPARENT, 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layerR, Color::RED, 32, 32)); - getScreenCapture()->expectColor(Rect(16, 16, 48, 48), Color::RED); -} - -void LayerRenderTypeTransactionTest::setAlphaBasicHelper(uint32_t layerType) { - sp<SurfaceControl> layer1; - sp<SurfaceControl> layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createLayer("test 1", 32, 32, layerType)); - ASSERT_NO_FATAL_FAILURE(layer2 = createLayer("test 2", 32, 32, layerType)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer1, {64, 0, 0, 255}, 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layerType, layer2, {0, 64, 0, 255}, 32, 32)); - - switch (layerType) { - case ISurfaceComposerClient::eFXSurfaceBufferQueue: - Transaction() - .setAlpha(layer1, 0.25f) - .setAlpha(layer2, 0.75f) - .setPosition(layer2, 16, 0) - .setLayer(layer2, mLayerZBase + 1) - .apply(); - break; - case ISurfaceComposerClient::eFXSurfaceBufferState: - Transaction() - .setAlpha(layer1, 0.25f) - .setAlpha(layer2, 0.75f) - .setFrame(layer1, Rect(0, 0, 32, 32)) - .setFrame(layer2, Rect(16, 0, 48, 32)) - .setLayer(layer2, mLayerZBase + 1) - .apply(); - break; - default: - ASSERT_FALSE(true) << "Unsupported layer type"; - } - { - auto shot = getScreenCapture(); - uint8_t r = 16; // 64 * 0.25f - uint8_t g = 48; // 64 * 0.75f - shot->expectColor(Rect(0, 0, 16, 32), {r, 0, 0, 255}); - shot->expectColor(Rect(32, 0, 48, 32), {0, g, 0, 255}); - - r /= 4; // r * (1.0f - 0.75f) - shot->expectColor(Rect(16, 0, 32, 32), {r, g, 0, 255}); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferQueue) { - ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue)); -} - -TEST_P(LayerRenderTypeTransactionTest, SetAlphaBasic_BufferState) { - ASSERT_NO_FATAL_FAILURE(setAlphaBasicHelper(ISurfaceComposerClient::eFXSurfaceBufferState)); -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetAlphaClamped) { - const Color color = {64, 0, 0, 255}; - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, color, 32, 32)); - - Transaction().setAlpha(layer, 2.0f).apply(); - { - SCOPED_TRACE("clamped to 1.0f"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), color); - } - - Transaction().setAlpha(layer, -1.0f).apply(); - { - SCOPED_TRACE("clamped to 0.0f"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); - } -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadius) { - sp<SurfaceControl> layer; - const uint8_t size = 64; - const uint8_t testArea = 4; - const float cornerRadius = 20.0f; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", size, size)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, size, size)); - - Transaction() - .setCornerRadius(layer, cornerRadius) - .apply(); - { - const uint8_t bottom = size - 1; - const uint8_t right = size - 1; - auto shot = getScreenCapture(); - // Transparent corners - shot->expectColor(Rect(0, 0, testArea, testArea), Color::BLACK); - shot->expectColor(Rect(size - testArea, 0, right, testArea), Color::BLACK); - shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK); - shot->expectColor(Rect(size - testArea, bottom - testArea, right, bottom), Color::BLACK); - } -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetCornerRadiusChildCrop) { - sp<SurfaceControl> parent; - sp<SurfaceControl> child; - const uint8_t size = 64; - const uint8_t testArea = 4; - const float cornerRadius = 20.0f; - ASSERT_NO_FATAL_FAILURE(parent = createLayer("parent", size, size)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(parent, Color::RED, size, size)); - ASSERT_NO_FATAL_FAILURE(child = createLayer("child", size, size / 2)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(child, Color::GREEN, size, size / 2)); - - Transaction() - .setCornerRadius(parent, cornerRadius) - .reparent(child, parent->getHandle()) - .setPosition(child, 0, size / 2) - .apply(); - { - const uint8_t bottom = size - 1; - const uint8_t right = size - 1; - auto shot = getScreenCapture(); - // Top edge of child should not have rounded corners because it's translated in the parent - shot->expectColor(Rect(0, size / 2, right, static_cast<int>(bottom - cornerRadius)), - Color::GREEN); - // But bottom edges should have been clipped according to parent bounds - shot->expectColor(Rect(0, bottom - testArea, testArea, bottom), Color::BLACK); - shot->expectColor(Rect(right - testArea, bottom - testArea, right, bottom), Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetColorBasic) { - sp<SurfaceControl> bufferLayer; - sp<SurfaceControl> colorLayer; - ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32)); - ASSERT_NO_FATAL_FAILURE(colorLayer = - createLayer("test", 0 /* buffer width */, 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor)); - - Transaction() - .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) - .setLayer(colorLayer, mLayerZBase + 1) - .apply(); - - { - SCOPED_TRACE("default color"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); - } - - const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f); - const Color expected = {15, 51, 85, 255}; - // this is handwavy, but the precison loss scaled by 255 (8-bit per - // channel) should be less than one - const uint8_t tolerance = 1; - Transaction().setColor(colorLayer, color).apply(); - { - SCOPED_TRACE("new color"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expected, tolerance); - } -} - -// RED: Color layer base color and BufferQueueLayer/BufferStateLayer fill -// BLUE: prior background color -// GREEN: final background color -// BLACK: no color or fill -void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType, bool priorColor, - bool bufferFill, float alpha, - Color finalColor) { - sp<SurfaceControl> layer; - int32_t width = 500; - int32_t height = 500; - - Color fillColor = Color::RED; - Color priorBgColor = Color::BLUE; - Color expectedColor = Color::BLACK; - switch (layerType) { - case ISurfaceComposerClient::eFXSurfaceColor: - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 0, 0, layerType)); - Transaction() - .setCrop_legacy(layer, Rect(0, 0, width, height)) - .setColor(layer, half3(1.0f, 0, 0)) - .apply(); - expectedColor = fillColor; - break; - case ISurfaceComposerClient::eFXSurfaceBufferQueue: - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height)); - if (bufferFill) { - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, fillColor, width, height)); - expectedColor = fillColor; - } - Transaction().setCrop_legacy(layer, Rect(0, 0, width, height)).apply(); - break; - case ISurfaceComposerClient::eFXSurfaceBufferState: - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height, layerType)); - if (bufferFill) { - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, fillColor, width, height)); - expectedColor = fillColor; - } - Transaction().setFrame(layer, Rect(0, 0, width, height)).apply(); - break; - default: - GTEST_FAIL() << "Unknown layer type in setBackgroundColorHelper"; - return; - } - - if (priorColor && layerType != ISurfaceComposerClient::eFXSurfaceColor) { - Transaction() - .setBackgroundColor(layer, half3(0, 0, 1.0f), 1.0f, ui::Dataspace::UNKNOWN) - .apply(); - if (!bufferFill) { - expectedColor = priorBgColor; - } - } - - { - SCOPED_TRACE("default before setting background color layer"); - screenshot()->expectColor(Rect(0, 0, width, height), expectedColor); - } - Transaction() - .setBackgroundColor(layer, half3(0, 1.0f, 0), alpha, ui::Dataspace::UNKNOWN) - .apply(); - - { - auto shot = screenshot(); - shot->expectColor(Rect(0, 0, width, height), finalColor); - shot->expectBorder(Rect(0, 0, width, height), Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_Color_NoEffect) { - bool priorColor = false; - bool bufferFill = false; - float alpha = 1.0f; - Color finalColor = Color::RED; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceColor, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, - SetBackgroundColor_BufferQueue_BufferFill_NoPriorColor_Basic) { - bool priorColor = false; - bool bufferFill = true; - float alpha = 1.0f; - Color finalColor = Color::RED; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, - SetBackgroundColor_BufferQueue_NoBufferFill_NoPriorColor_Basic) { - bool priorColor = false; - bool bufferFill = false; - float alpha = 1.0f; - Color finalColor = Color::GREEN; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, SetBackgroundColor_BufferQueue_BufferFill_PriorColor_Basic) { - bool priorColor = true; - bool bufferFill = true; - float alpha = 1.0f; - Color finalColor = Color::RED; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, - SetBackgroundColor_BufferQueue_NoBufferFill_PriorColor_Basic) { - bool priorColor = true; - bool bufferFill = false; - float alpha = 1.0f; - Color finalColor = Color::GREEN; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, - priorColor, bufferFill, alpha, finalColor)); -} -TEST_P(LayerRenderTypeTransactionTest, - SetBackgroundColor_BufferQueue_NoPriorColor_ZeroAlpha_NoEffect) { - bool priorColor = false; - bool bufferFill = false; - float alpha = 0; - Color finalColor = Color::BLACK; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, - SetBackgroundColor_BufferQueue_PriorColor_ZeroAlpha_DeleteBackground) { - bool priorColor = true; - bool bufferFill = false; - float alpha = 0; - Color finalColor = Color::BLACK; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferQueue, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, - SetBackgroundColor_BufferState_BufferFill_NoPriorColor_Basic) { - bool priorColor = false; - bool bufferFill = true; - float alpha = 1.0f; - Color finalColor = Color::RED; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, - SetBackgroundColor_BufferState_NoBufferFill_NoPriorColor_Basic) { - bool priorColor = false; - bool bufferFill = false; - float alpha = 1.0f; - Color finalColor = Color::GREEN; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, - SetBackgroundColor_BufferState_NoBufferFill_PriorColor_Basic) { - bool priorColor = true; - bool bufferFill = false; - float alpha = 1.0f; - Color finalColor = Color::GREEN; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, - SetBackgroundColor_BufferState_NoPriorColor_ZeroAlpha_NoEffect) { - bool priorColor = false; - bool bufferFill = false; - float alpha = 0; - Color finalColor = Color::BLACK; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, - SetBackgroundColor_BufferState_PriorColor_ZeroAlpha_DeleteBackground) { - bool priorColor = true; - bool bufferFill = false; - float alpha = 0; - Color finalColor = Color::BLACK; - ASSERT_NO_FATAL_FAILURE(setBackgroundColorHelper(ISurfaceComposerClient::eFXSurfaceBufferState, - priorColor, bufferFill, alpha, finalColor)); -} - -TEST_P(LayerRenderTypeTransactionTest, SetColorClamped) { - sp<SurfaceControl> colorLayer; - ASSERT_NO_FATAL_FAILURE(colorLayer = - createLayer("test", 0 /* buffer width */, 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor)); - Transaction() - .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) - .setColor(colorLayer, half3(2.0f, -1.0f, 0.0f)) - .apply(); - - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); -} - -TEST_P(LayerRenderTypeTransactionTest, SetColorWithAlpha) { - sp<SurfaceControl> bufferLayer; - sp<SurfaceControl> colorLayer; - ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32)); - ASSERT_NO_FATAL_FAILURE(colorLayer = - createLayer("test", 0 /* buffer width */, 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor)); - Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply(); - - const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f); - const float alpha = 0.25f; - const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f); - // this is handwavy, but the precison loss scaled by 255 (8-bit per - // channel) should be less than one - const uint8_t tolerance = 1; - Transaction() - .setColor(colorLayer, color) - .setAlpha(colorLayer, alpha) - .setLayer(colorLayer, mLayerZBase + 1) - .apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255}, - tolerance); -} - -TEST_P(LayerRenderTypeTransactionTest, SetColorWithParentAlpha_Bug74220420) { - sp<SurfaceControl> bufferLayer; - sp<SurfaceControl> parentLayer; - sp<SurfaceControl> colorLayer; - ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test bg", 32, 32)); - ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parentWithAlpha", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::RED, 32, 32)); - ASSERT_NO_FATAL_FAILURE(colorLayer = createLayer("childWithColor", 0 /* buffer width */, - 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor)); - Transaction().setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)).apply(); - const half3 color(15.0f / 255.0f, 51.0f / 255.0f, 85.0f / 255.0f); - const float alpha = 0.25f; - const ubyte3 expected((vec3(color) * alpha + vec3(1.0f, 0.0f, 0.0f) * (1.0f - alpha)) * 255.0f); - // this is handwavy, but the precision loss scaled by 255 (8-bit per - // channel) should be less than one - const uint8_t tolerance = 1; - Transaction() - .reparent(colorLayer, parentLayer->getHandle()) - .setColor(colorLayer, color) - .setAlpha(parentLayer, alpha) - .setLayer(parentLayer, mLayerZBase + 1) - .apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), {expected.r, expected.g, expected.b, 255}, - tolerance); -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetColorWithBuffer) { - sp<SurfaceControl> bufferLayer; - ASSERT_NO_FATAL_FAILURE(bufferLayer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(bufferLayer, Color::RED, 32, 32)); - - // color is ignored - Transaction().setColor(bufferLayer, half3(0.0f, 1.0f, 0.0f)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); -} - -TEST_P(LayerTypeAndRenderTypeTransactionTest, SetLayerStackBasic) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillLayerColor(layer, Color::RED, 32, 32)); - - Transaction().setLayerStack(layer, mDisplayLayerStack + 1).apply(); - { - SCOPED_TRACE("non-existing layer stack"); - getScreenCapture()->expectColor(mDisplayRect, Color::BLACK); - } - - Transaction().setLayerStack(layer, mDisplayLayerStack).apply(); - { - SCOPED_TRACE("original layer stack"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE)); - - Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 0, 0).apply(); - { - SCOPED_TRACE("IDENTITY"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE); - } - - Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).setPosition(layer, 32, 0).apply(); - { - SCOPED_TRACE("FLIP_H"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, - Color::WHITE, Color::BLUE); - } - - Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).setPosition(layer, 0, 32).apply(); - { - SCOPED_TRACE("FLIP_V"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, - Color::RED, Color::GREEN); - } - - Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).setPosition(layer, 32, 0).apply(); - { - SCOPED_TRACE("ROT_90"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, - Color::WHITE, Color::GREEN); - } - - Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setPosition(layer, 0, 0).apply(); - { - SCOPED_TRACE("SCALE"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 64), Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE, true /* filtered */); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetMatrixBasic_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE)); - - Transaction() - .setMatrix(layer, 1.0f, 0.0f, 0.0f, 1.0f) - .setFrame(layer, Rect(0, 0, 32, 32)) - .apply(); - { - SCOPED_TRACE("IDENTITY"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE); - } - - Transaction().setMatrix(layer, -1.0f, 0.0f, 0.0f, 1.0f).apply(); - { - SCOPED_TRACE("FLIP_H"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE); - } - - Transaction().setMatrix(layer, 1.0f, 0.0f, 0.0f, -1.0f).apply(); - { - SCOPED_TRACE("FLIP_V"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE); - } - - Transaction().setMatrix(layer, 0.0f, 1.0f, -1.0f, 0.0f).apply(); - { - SCOPED_TRACE("ROT_90"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE); - } - - Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).apply(); - { - SCOPED_TRACE("SCALE"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetMatrixRot45_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE)); - - const float rot = M_SQRT1_2; // 45 degrees - const float trans = M_SQRT2 * 16.0f; - Transaction().setMatrix(layer, rot, rot, -rot, rot).setPosition(layer, trans, 0).apply(); - - auto shot = getScreenCapture(); - // check a 8x8 region inside each color - auto get8x8Rect = [](int32_t centerX, int32_t centerY) { - const int32_t halfL = 4; - return Rect(centerX - halfL, centerY - halfL, centerX + halfL, centerY + halfL); - }; - const int32_t unit = int32_t(trans / 2); - shot->expectColor(get8x8Rect(2 * unit, 1 * unit), Color::RED); - shot->expectColor(get8x8Rect(3 * unit, 2 * unit), Color::GREEN); - shot->expectColor(get8x8Rect(1 * unit, 2 * unit), Color::BLUE); - shot->expectColor(get8x8Rect(2 * unit, 3 * unit), Color::WHITE); -} - -TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithResize_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // setMatrix is applied after any pending resize, unlike setPosition - Transaction().setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f).setSize(layer, 64, 64).apply(); - { - SCOPED_TRACE("resize pending"); - auto shot = getScreenCapture(); - const Rect rect(0, 0, 32, 32); - shot->expectColor(rect, Color::RED); - shot->expectBorder(rect, Color::BLACK); - } - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 64, 64)); - { - SCOPED_TRACE("resize applied"); - const Rect rect(0, 0, 128, 128); - getScreenCapture()->expectColor(rect, Color::RED); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetMatrixWithScaleToWindow_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // setMatrix is immediate with SCALE_TO_WINDOW, unlike setPosition - Transaction() - .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f) - .setSize(layer, 64, 64) - .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) - .apply(); - getScreenCapture()->expectColor(Rect(0, 0, 128, 128), Color::RED); -} - -TEST_P(LayerRenderTypeTransactionTest, SetOverrideScalingModeBasic_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE)); - - // XXX SCALE_CROP is not respected; calling setSize and - // setOverrideScalingMode in separate transactions does not work - // (b/69315456) - Transaction() - .setSize(layer, 64, 16) - .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) - .apply(); - { - SCOPED_TRACE("SCALE_TO_WINDOW"); - getScreenCapture()->expectQuadrant(Rect(0, 0, 64, 16), Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE, true /* filtered */); - } -} - -TEST_P(LayerTypeTransactionTest, RefreshRateIsInitialized) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - - sp<IBinder> handle = layer->getHandle(); - ASSERT_TRUE(handle != nullptr); - - FrameStats frameStats; - mClient->getLayerFrameStats(handle, &frameStats); - - ASSERT_GT(frameStats.refreshPeriodNano, static_cast<nsecs_t>(0)); -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - const Rect crop(8, 8, 24, 24); - - Transaction().setCrop_legacy(layer, crop).apply(); - auto shot = getScreenCapture(); - shot->expectColor(crop, Color::RED); - shot->expectBorder(crop, Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - const Rect crop(8, 8, 24, 24); - - Transaction().setCrop(layer, crop).apply(); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - { - SCOPED_TRACE("empty rect"); - Transaction().setCrop_legacy(layer, Rect(8, 8, 8, 8)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } - - { - SCOPED_TRACE("negative rect"); - Transaction().setCrop_legacy(layer, Rect(8, 8, 0, 0)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - - { - SCOPED_TRACE("empty rect"); - Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - } - - { - SCOPED_TRACE("negative rect"); - Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - Transaction().setCrop_legacy(layer, Rect(-128, -64, 128, 64)).apply(); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState)); - sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE); - fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED); - - Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply(); - - Transaction().setBuffer(layer, buffer).apply(); - - // Partially out of bounds in the negative (upper left) direction - Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply(); - { - SCOPED_TRACE("out of bounds, negative (upper left) direction"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE); - shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); - } - - // Partially out of bounds in the positive (lower right) direction - Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply(); - { - SCOPED_TRACE("out of bounds, positive (lower right) direction"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 64, 64), Color::RED); - shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); - } - - // Fully out of buffer space bounds - Transaction().setCrop(layer, Rect(-128, -128, -1, -1)).apply(); - { - SCOPED_TRACE("Fully out of bounds"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE); - shot->expectColor(Rect(0, 16, 64, 64), Color::RED); - shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - const Point position(32, 32); - const Rect crop(8, 8, 24, 24); - Transaction().setPosition(layer, position.x, position.y).setCrop_legacy(layer, crop).apply(); - auto shot = getScreenCapture(); - shot->expectColor(crop + position, Color::RED); - shot->expectBorder(crop + position, Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropWithTranslation_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - - const Rect frame(32, 32, 64, 64); - const Rect crop(8, 8, 24, 24); - Transaction().setFrame(layer, frame).setCrop(layer, crop).apply(); - auto shot = getScreenCapture(); - shot->expectColor(frame, Color::RED); - shot->expectBorder(frame, Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropWithScale_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // crop_legacy is affected by matrix - Transaction() - .setMatrix(layer, 2.0f, 0.0f, 0.0f, 2.0f) - .setCrop_legacy(layer, Rect(8, 8, 24, 24)) - .apply(); - auto shot = getScreenCapture(); - shot->expectColor(Rect(16, 16, 48, 48), Color::RED); - shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropWithResize_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // setCrop_legacy is applied immediately by default, with or without resize pending - Transaction().setCrop_legacy(layer, Rect(8, 8, 24, 24)).setSize(layer, 16, 16).apply(); - { - SCOPED_TRACE("resize pending"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(8, 8, 24, 24), Color::RED); - shot->expectBorder(Rect(8, 8, 24, 24), Color::BLACK); - } - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16)); - { - SCOPED_TRACE("resize applied"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(8, 8, 16, 16), Color::RED); - shot->expectBorder(Rect(8, 8, 16, 16), Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResize_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // request setCrop_legacy to be applied with the next resize - Transaction() - .setCrop_legacy(layer, Rect(8, 8, 24, 24)) - .setGeometryAppliesWithResize(layer) - .apply(); - { - SCOPED_TRACE("waiting for next resize"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } - - Transaction().setCrop_legacy(layer, Rect(4, 4, 12, 12)).apply(); - { - SCOPED_TRACE("pending crop modified"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } - - Transaction().setSize(layer, 16, 16).apply(); - { - SCOPED_TRACE("resize pending"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); - } - - // finally resize - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16)); - { - SCOPED_TRACE("new crop applied"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(4, 4, 12, 12), Color::RED); - shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetCropWithNextResizeScaleToWindow_BufferQueue) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - - // setCrop_legacy is not immediate even with SCALE_TO_WINDOW override - Transaction() - .setCrop_legacy(layer, Rect(4, 4, 12, 12)) - .setSize(layer, 16, 16) - .setOverrideScalingMode(layer, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW) - .setGeometryAppliesWithResize(layer) - .apply(); - { - SCOPED_TRACE("new crop pending"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 16, 16), Color::RED); - shot->expectBorder(Rect(0, 0, 16, 16), Color::BLACK); - } - - // XXX crop is never latched without other geometry change (b/69315677) - Transaction().setPosition(layer, 1, 0).setGeometryAppliesWithResize(layer).apply(); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 16, 16)); - Transaction().setPosition(layer, 0, 0).apply(); - { - SCOPED_TRACE("new crop applied"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(4, 4, 12, 12), Color::RED); - shot->expectBorder(Rect(4, 4, 12, 12), Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetFrameBasic_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - const Rect frame(8, 8, 24, 24); - - Transaction().setFrame(layer, frame).apply(); - auto shot = getScreenCapture(); - shot->expectColor(frame, Color::RED); - shot->expectBorder(frame, Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetFrameEmpty_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - - { - SCOPED_TRACE("empty rect"); - Transaction().setFrame(layer, Rect(8, 8, 8, 8)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); - } - - { - SCOPED_TRACE("negative rect"); - Transaction().setFrame(layer, Rect(8, 8, 0, 0)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 10, 10)); - - // A parentless layer will default to a frame with the same size as the buffer - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) { - sp<SurfaceControl> parent, child; - ASSERT_NO_FATAL_FAILURE( - parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32)); - Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply(); - - ASSERT_NO_FATAL_FAILURE( - child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); - - Transaction().reparent(child, parent->getHandle()).apply(); - - // A layer will default to the frame of its parent - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBQParent_BufferState) { - sp<SurfaceControl> parent, child; - ASSERT_NO_FATAL_FAILURE(parent = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(parent, Color::RED, 32, 32)); - - ASSERT_NO_FATAL_FAILURE( - child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); - - Transaction().reparent(child, parent->getHandle()).apply(); - - // A layer will default to the frame of its parent - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetFrameUpdate_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - Transaction().setFrame(layer, Rect(0, 0, 32, 32)).apply(); - - std::this_thread::sleep_for(500ms); - - Transaction().setFrame(layer, Rect(16, 16, 48, 48)).apply(); - - auto shot = getScreenCapture(); - shot->expectColor(Rect(16, 16, 48, 48), Color::RED); - shot->expectBorder(Rect(16, 16, 48, 48), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetFrameOutsideBounds_BufferState) { - sp<SurfaceControl> parent, child; - ASSERT_NO_FATAL_FAILURE( - parent = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE( - child = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - Transaction().reparent(child, parent->getHandle()).apply(); - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(parent, Color::RED, 32, 32)); - Transaction().setFrame(parent, Rect(0, 0, 32, 32)).apply(); - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(child, Color::BLUE, 10, 10)); - Transaction().setFrame(child, Rect(0, 16, 32, 32)).apply(); - - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 16), Color::RED); - shot->expectColor(Rect(0, 16, 32, 32), Color::BLUE); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - - { - SCOPED_TRACE("set buffer 1"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); - } - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32)); - - { - SCOPED_TRACE("set buffer 2"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); - } - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); - - { - SCOPED_TRACE("set buffer 3"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleLayers_BufferState) { - sp<SurfaceControl> layer1; - ASSERT_NO_FATAL_FAILURE( - layer1 = createLayer("test", 64, 64, ISurfaceComposerClient::eFXSurfaceBufferState)); - - sp<SurfaceControl> layer2; - ASSERT_NO_FATAL_FAILURE( - layer2 = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::RED, 64, 64)); - - Transaction().setFrame(layer1, Rect(0, 0, 64, 64)).apply(); - { - SCOPED_TRACE("set layer 1 buffer red"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 64, 64), Color::RED); - } - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::BLUE, 32, 32)); - - Transaction().setFrame(layer2, Rect(0, 0, 32, 32)).apply(); - { - SCOPED_TRACE("set layer 2 buffer blue"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); - shot->expectColor(Rect(0, 32, 64, 64), Color::RED); - shot->expectColor(Rect(0, 32, 32, 64), Color::RED); - } - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer1, Color::GREEN, 64, 64)); - { - SCOPED_TRACE("set layer 1 buffer green"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); - shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN); - shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN); - } - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer2, Color::WHITE, 32, 32)); - - { - SCOPED_TRACE("set layer 2 buffer white"); - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::WHITE); - shot->expectColor(Rect(0, 32, 64, 64), Color::GREEN); - shot->expectColor(Rect(0, 32, 32, 64), Color::GREEN); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN}; - - std::array<sp<GraphicBuffer>, 10> buffers; - - size_t idx = 0; - for (auto& buffer : buffers) { - buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - Color color = colors[idx % colors.size()]; - fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color); - idx++; - } - - // Set each buffer twice. The first time adds it to the cache, the second time tests that the - // cache is working. - idx = 0; - for (auto& buffer : buffers) { - for (int i = 0; i < 2; i++) { - Transaction().setBuffer(layer, buffer).apply(); - - Color color = colors[idx % colors.size()]; - auto shot = screenshot(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); - } - idx++; - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN}; - - std::array<sp<GraphicBuffer>, 70> buffers; - - size_t idx = 0; - for (auto& buffer : buffers) { - buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - Color color = colors[idx % colors.size()]; - fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color); - idx++; - } - - // Set each buffer twice. The first time adds it to the cache, the second time tests that the - // cache is working. - idx = 0; - for (auto& buffer : buffers) { - for (int i = 0; i < 2; i++) { - Transaction().setBuffer(layer, buffer).apply(); - - Color color = colors[idx % colors.size()]; - auto shot = screenshot(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); - } - idx++; - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - std::array<Color, 4> colors = {Color::RED, Color::BLUE, Color::WHITE, Color::GREEN}; - - std::array<sp<GraphicBuffer>, 65> buffers; - - size_t idx = 0; - for (auto& buffer : buffers) { - buffer = new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - Color color = colors[idx % colors.size()]; - fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), color); - idx++; - } - - // Set each buffer twice. The first time adds it to the cache, the second time tests that the - // cache is working. - idx = 0; - for (auto& buffer : buffers) { - for (int i = 0; i < 2; i++) { - Transaction().setBuffer(layer, buffer).apply(); - - Color color = colors[idx % colors.size()]; - auto shot = screenshot(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); - } - if (idx == 0) { - buffers[0].clear(); - } - idx++; - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetTransformRotate90_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE)); - - Transaction() - .setFrame(layer, Rect(0, 0, 32, 32)) - .setTransform(layer, NATIVE_WINDOW_TRANSFORM_ROT_90) - .apply(); - - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::RED, Color::WHITE, - Color::GREEN, true /* filtered */); -} - -TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipH_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE)); - - Transaction() - .setFrame(layer, Rect(0, 0, 32, 32)) - .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_H) - .apply(); - - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::GREEN, Color::RED, Color::WHITE, - Color::BLUE, true /* filtered */); -} - -TEST_P(LayerRenderTypeTransactionTest, SetTransformFlipV_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerQuadrant(layer, 32, 32, Color::RED, Color::GREEN, - Color::BLUE, Color::WHITE)); - - Transaction() - .setFrame(layer, Rect(0, 0, 32, 32)) - .setTransform(layer, NATIVE_WINDOW_TRANSFORM_FLIP_V) - .apply(); - - getScreenCapture()->expectQuadrant(Rect(0, 0, 32, 32), Color::BLUE, Color::WHITE, Color::RED, - Color::GREEN, true /* filtered */); -} - -TEST_F(LayerTransactionTest, SetTransformToDisplayInverse_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - Transaction().setTransformToDisplayInverse(layer, false).apply(); - - ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::GREEN, 32, 32)); - - Transaction().setTransformToDisplayInverse(layer, true).apply(); -} - -TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) { - sp<SurfaceControl> layer; - Transaction transaction; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); - - sp<Fence> fence; - if (getBuffer(nullptr, &fence) != NO_ERROR) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - Transaction().setBuffer(layer, buffer).setAcquireFence(layer, fence).apply(); - - status_t status = fence->wait(1000); - ASSERT_NE(static_cast<status_t>(Fence::Status::Unsignaled), status); - std::this_thread::sleep_for(200ms); - - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); - - sp<Fence> fence = Fence::NO_FENCE; - - Transaction() - .setBuffer(layer, buffer) - .setAcquireFence(layer, fence) - .apply(); - - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); - - Transaction() - .setBuffer(layer, buffer) - .setDataspace(layer, ui::Dataspace::UNKNOWN) - .apply(); - - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); - - HdrMetadata hdrMetadata; - hdrMetadata.validTypes = 0; - Transaction() - .setBuffer(layer, buffer) - .setHdrMetadata(layer, hdrMetadata) - .apply(); - - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); - - Region region; - region.set(32, 32); - Transaction() - .setBuffer(layer, buffer) - .setSurfaceDamageRegion(layer, region) - .apply(); - - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); -} - -TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - sp<GraphicBuffer> buffer = - new GraphicBuffer(32, 32, PIXEL_FORMAT_RGBA_8888, 1, - BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | - BufferUsage::COMPOSER_OVERLAY, - "test"); - fillGraphicBufferColor(buffer, Rect(0, 0, 32, 32), Color::RED); - - Transaction() - .setBuffer(layer, buffer) - .setApi(layer, NATIVE_WINDOW_API_CPU) - .apply(); - - auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); -} - -TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE( - layer = createLayer("test", 32, 32, ISurfaceComposerClient::eFXSurfaceBufferState)); - - // verify this doesn't cause a crash - Transaction().setSidebandStream(layer, nullptr).apply(); -} - -TEST_F(LayerTransactionTest, ReparentToSelf) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); - Transaction().reparent(layer, layer->getHandle()).apply(); - - { - // We expect the transaction to be silently dropped, but for SurfaceFlinger - // to still be functioning. - SCOPED_TRACE("after reparent to self"); - const Rect rect(0, 0, 32, 32); - auto shot = screenshot(); - shot->expectColor(rect, Color::RED); - shot->expectBorder(rect, Color::BLACK); - } -} - -class ColorTransformHelper { -public: - static void DegammaColorSingle(half& s) { - if (s <= 0.03928f) - s = s / 12.92f; - else - s = pow((s + 0.055f) / 1.055f, 2.4f); - } - - static void DegammaColor(half3& color) { - DegammaColorSingle(color.r); - DegammaColorSingle(color.g); - DegammaColorSingle(color.b); - } - - static void GammaColorSingle(half& s) { - if (s <= 0.0031308f) { - s = s * 12.92f; - } else { - s = 1.055f * pow(s, (1.0f / 2.4f)) - 0.055f; - } - } - - static void GammaColor(half3& color) { - GammaColorSingle(color.r); - GammaColorSingle(color.g); - GammaColorSingle(color.b); - } - - static void applyMatrix(half3& color, const mat3& mat) { - half3 ret = half3(0); - - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - ret[i] = ret[i] + color[j] * mat[j][i]; - } - } - color = ret; - } -}; - -TEST_P(LayerRenderTypeTransactionTest, SetColorTransformBasic) { - sp<SurfaceControl> colorLayer; - ASSERT_NO_FATAL_FAILURE(colorLayer = - createLayer("test", 0 /* buffer width */, 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor)); - Transaction() - .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) - .setLayer(colorLayer, mLayerZBase + 1) - .apply(); - { - SCOPED_TRACE("default color"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); - } - - const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f); - half3 expected = color; - mat3 matrix; - matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11; - matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11; - matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11; - - // degamma before applying the matrix - if (mColorManagementUsed) { - ColorTransformHelper::DegammaColor(expected); - } - - ColorTransformHelper::applyMatrix(expected, matrix); - - if (mColorManagementUsed) { - ColorTransformHelper::GammaColor(expected); - } - - const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255), - uint8_t(expected.b * 255), 255}; - - // this is handwavy, but the precison loss scaled by 255 (8-bit per - // channel) should be less than one - const uint8_t tolerance = 1; - - Transaction().setColor(colorLayer, color) - .setColorTransform(colorLayer, matrix, vec3()).apply(); - { - SCOPED_TRACE("new color"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnParent) { - sp<SurfaceControl> parentLayer; - sp<SurfaceControl> colorLayer; - ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */, - 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceContainer)); - ASSERT_NO_FATAL_FAILURE( - colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get())); - - Transaction() - .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100)) - .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) - .setLayer(parentLayer, mLayerZBase + 1) - .apply(); - { - SCOPED_TRACE("default color"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); - } - - const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f); - half3 expected = color; - mat3 matrix; - matrix[0][0] = 0.3; matrix[1][0] = 0.59; matrix[2][0] = 0.11; - matrix[0][1] = 0.3; matrix[1][1] = 0.59; matrix[2][1] = 0.11; - matrix[0][2] = 0.3; matrix[1][2] = 0.59; matrix[2][2] = 0.11; - - // degamma before applying the matrix - if (mColorManagementUsed) { - ColorTransformHelper::DegammaColor(expected); - } - - ColorTransformHelper::applyMatrix(expected, matrix); - - if (mColorManagementUsed) { - ColorTransformHelper::GammaColor(expected); - } - - const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255), - uint8_t(expected.b * 255), 255}; - - // this is handwavy, but the precison loss scaled by 255 (8-bit per - // channel) should be less than one - const uint8_t tolerance = 1; - - Transaction() - .setColor(colorLayer, color) - .setColorTransform(parentLayer, matrix, vec3()) - .apply(); - { - SCOPED_TRACE("new color"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance); - } -} - -TEST_P(LayerRenderTypeTransactionTest, SetColorTransformOnChildAndParent) { - sp<SurfaceControl> parentLayer; - sp<SurfaceControl> colorLayer; - ASSERT_NO_FATAL_FAILURE(parentLayer = createLayer("parent", 0 /* buffer width */, - 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceContainer)); - ASSERT_NO_FATAL_FAILURE( - colorLayer = createLayer("test", 0 /* buffer width */, 0 /* buffer height */, - ISurfaceComposerClient::eFXSurfaceColor, parentLayer.get())); - - Transaction() - .setCrop_legacy(parentLayer, Rect(0, 0, 100, 100)) - .setCrop_legacy(colorLayer, Rect(0, 0, 32, 32)) - .setLayer(parentLayer, mLayerZBase + 1) - .apply(); - { - SCOPED_TRACE("default color"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::BLACK); - } - - const half3 color(50.0f / 255.0f, 100.0f / 255.0f, 150.0f / 255.0f); - half3 expected = color; - mat3 matrixChild; - matrixChild[0][0] = 0.3; matrixChild[1][0] = 0.59; matrixChild[2][0] = 0.11; - matrixChild[0][1] = 0.3; matrixChild[1][1] = 0.59; matrixChild[2][1] = 0.11; - matrixChild[0][2] = 0.3; matrixChild[1][2] = 0.59; matrixChild[2][2] = 0.11; - mat3 matrixParent; - matrixParent[0][0] = 0.2; matrixParent[1][0] = 0.4; matrixParent[2][0] = 0.10; - matrixParent[0][1] = 0.2; matrixParent[1][1] = 0.4; matrixParent[2][1] = 0.10; - matrixParent[0][2] = 0.2; matrixParent[1][2] = 0.4; matrixParent[2][2] = 0.10; - - // degamma before applying the matrix - if (mColorManagementUsed) { - ColorTransformHelper::DegammaColor(expected); - } - - ColorTransformHelper::applyMatrix(expected, matrixChild); - ColorTransformHelper::applyMatrix(expected, matrixParent); - - if (mColorManagementUsed) { - ColorTransformHelper::GammaColor(expected); - } - - const Color expectedColor = {uint8_t(expected.r * 255), uint8_t(expected.g * 255), - uint8_t(expected.b * 255), 255}; - - // this is handwavy, but the precison loss scaled by 255 (8-bit per - // channel) should be less than one - const uint8_t tolerance = 1; - - Transaction() - .setColor(colorLayer, color) - .setColorTransform(parentLayer, matrixParent, vec3()) - .setColorTransform(colorLayer, matrixChild, vec3()) - .apply(); - { - SCOPED_TRACE("new color"); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), expectedColor, tolerance); - } -} - -struct CallbackData { - CallbackData() = default; - CallbackData(nsecs_t time, const sp<Fence>& fence, - const std::vector<SurfaceControlStats>& stats) - : latchTime(time), presentFence(fence), surfaceControlStats(stats) {} - - nsecs_t latchTime; - sp<Fence> presentFence; - std::vector<SurfaceControlStats> surfaceControlStats; -}; - -class ExpectedResult { -public: - enum Transaction { - NOT_PRESENTED = 0, - PRESENTED, - }; - - enum Buffer { - NOT_ACQUIRED = 0, - ACQUIRED, - }; - - enum PreviousBuffer { - NOT_RELEASED = 0, - RELEASED, - UNKNOWN, - }; - - void reset() { - mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; - mExpectedSurfaceResults.clear(); - } - - void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer, - ExpectedResult::Buffer bufferResult = ACQUIRED, - ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { - mTransactionResult = transactionResult; - mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer), - std::forward_as_tuple(bufferResult, previousBufferResult)); - } - - void addSurfaces(ExpectedResult::Transaction transactionResult, - const std::vector<sp<SurfaceControl>>& layers, - ExpectedResult::Buffer bufferResult = ACQUIRED, - ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { - for (const auto& layer : layers) { - addSurface(transactionResult, layer, bufferResult, previousBufferResult); - } - } - - void addExpectedPresentTime(nsecs_t expectedPresentTime) { - mExpectedPresentTime = expectedPresentTime; - } - - void verifyCallbackData(const CallbackData& callbackData) const { - const auto& [latchTime, presentFence, surfaceControlStats] = callbackData; - if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) { - ASSERT_GE(latchTime, 0) << "bad latch time"; - ASSERT_NE(presentFence, nullptr); - if (mExpectedPresentTime >= 0) { - ASSERT_EQ(presentFence->wait(3000), NO_ERROR); - ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6)); - // if the panel is running at 30 hz, at the worst case, our expected time just - // misses vsync and we have to wait another 33.3ms - ASSERT_LE(presentFence->getSignalTime(), - mExpectedPresentTime + nsecs_t(66.666666 * 1e6)); - } - } else { - ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented"; - ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched"; - } - - ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size()) - << "wrong number of surfaces"; - - for (const auto& stats : surfaceControlStats) { - ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control"; - - const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl); - ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end()) - << "unexpected surface control"; - expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime); - } - } - -private: - class ExpectedSurfaceResult { - public: - ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult, - ExpectedResult::PreviousBuffer previousBufferResult) - : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {} - - void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats, - nsecs_t latchTime) const { - const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceControlStats; - - ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED) - << "bad acquire time"; - ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time"; - - if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) { - ASSERT_NE(previousReleaseFence, nullptr) - << "failed to set release prev buffer fence"; - } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) { - ASSERT_EQ(previousReleaseFence, nullptr) - << "should not have set released prev buffer fence"; - } - } - - private: - ExpectedResult::Buffer mBufferResult; - ExpectedResult::PreviousBuffer mPreviousBufferResult; - }; - - struct SCHash { - std::size_t operator()(const sp<SurfaceControl>& sc) const { - return std::hash<IBinder*>{}(sc->getHandle().get()); - } - }; - ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; - nsecs_t mExpectedPresentTime = -1; - std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults; -}; - -class CallbackHelper { -public: - static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence, - const std::vector<SurfaceControlStats>& stats) { - if (!callbackContext) { - ALOGE("failed to get callback context"); - } - CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext); - std::lock_guard lock(helper->mMutex); - helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats); - helper->mConditionVariable.notify_all(); - } - - void getCallbackData(CallbackData* outData) { - std::unique_lock lock(mMutex); - - if (mCallbackDataQueue.empty()) { - ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)), - std::cv_status::timeout) - << "did not receive callback"; - } - - *outData = std::move(mCallbackDataQueue.front()); - mCallbackDataQueue.pop(); - } - - void verifyFinalState() { - // Wait to see if there are extra callbacks - std::this_thread::sleep_for(500ms); - - std::lock_guard lock(mMutex); - EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received"; - mCallbackDataQueue = {}; - } - - void* getContext() { return static_cast<void*>(this); } - - std::mutex mMutex; - std::condition_variable mConditionVariable; - std::queue<CallbackData> mCallbackDataQueue; -}; - -class LayerCallbackTest : public LayerTransactionTest { -public: - virtual sp<SurfaceControl> createBufferStateLayer() { - return createLayer(mClient, "test", 0, 0, ISurfaceComposerClient::eFXSurfaceBufferState); - } - - static int fillTransaction(Transaction& transaction, CallbackHelper* callbackHelper, - const sp<SurfaceControl>& layer = nullptr, bool setBuffer = true, - bool setBackgroundColor = false) { - if (layer) { - sp<GraphicBuffer> buffer; - sp<Fence> fence; - if (setBuffer) { - int err = getBuffer(&buffer, &fence); - if (err != NO_ERROR) { - return err; - } - - transaction.setBuffer(layer, buffer); - transaction.setAcquireFence(layer, fence); - } - - if (setBackgroundColor) { - transaction.setBackgroundColor(layer, /*color*/ half3(1.0f, 0, 0), /*alpha*/ 1.0f, - ui::Dataspace::UNKNOWN); - } - } - - transaction.addTransactionCompletedCallback(callbackHelper->function, - callbackHelper->getContext()); - return NO_ERROR; - } - - static void waitForCallback(CallbackHelper& helper, const ExpectedResult& expectedResult, - bool finalState = false) { - CallbackData callbackData; - ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData)); - EXPECT_NO_FATAL_FAILURE(expectedResult.verifyCallbackData(callbackData)); - - if (finalState) { - ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState()); - } - } - - static void waitForCallbacks(CallbackHelper& helper, - const std::vector<ExpectedResult>& expectedResults, - bool finalState = false) { - for (const auto& expectedResult : expectedResults) { - waitForCallback(helper, expectedResult); - } - if (finalState) { - ASSERT_NO_FATAL_FAILURE(helper.verifyFinalState()); - } - } -}; - -TEST_F(LayerCallbackTest, BufferColor) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - int err = fillTransaction(transaction, &callback, layer, true, true); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); -} - -TEST_F(LayerCallbackTest, NoBufferNoColor) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - int err = fillTransaction(transaction, &callback, layer, false, false); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer, - ExpectedResult::Buffer::NOT_ACQUIRED); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); -} - -TEST_F(LayerCallbackTest, BufferNoColor) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - int err = fillTransaction(transaction, &callback, layer, true, false); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); -} - -TEST_F(LayerCallbackTest, NoBufferColor) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - int err = fillTransaction(transaction, &callback, layer, false, true); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, - ExpectedResult::Buffer::NOT_ACQUIRED); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); -} - -TEST_F(LayerCallbackTest, NoStateChange) { - Transaction transaction; - CallbackHelper callback; - int err = fillTransaction(transaction, &callback); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.apply(); - - ExpectedResult expected; - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); -} - -TEST_F(LayerCallbackTest, OffScreen) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - int err = fillTransaction(transaction, &callback, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.setFrame(layer, Rect(-100, -100, 100, 100)).apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); -} - -TEST_F(LayerCallbackTest, MergeBufferNoColor) { - sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); - - Transaction transaction1, transaction2; - CallbackHelper callback1, callback2; - int err = fillTransaction(transaction1, &callback1, layer1); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2, layer2); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); - transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); - - ExpectedResult expected; - expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); -} - -TEST_F(LayerCallbackTest, MergeNoBufferColor) { - sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); - - Transaction transaction1, transaction2; - CallbackHelper callback1, callback2; - int err = fillTransaction(transaction1, &callback1, layer1, false, true); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2, layer2, false, true); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); - transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); - - ExpectedResult expected; - expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}, - ExpectedResult::Buffer::NOT_ACQUIRED); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); -} - -TEST_F(LayerCallbackTest, MergeOneBufferOneColor) { - sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); - - Transaction transaction1, transaction2; - CallbackHelper callback1, callback2; - int err = fillTransaction(transaction1, &callback1, layer1); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2, layer2, false, true); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); - transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer1); - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer2, - ExpectedResult::Buffer::NOT_ACQUIRED); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); -} -TEST_F(LayerCallbackTest, Merge_SameCallback) { - sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); - - Transaction transaction1, transaction2; - CallbackHelper callback; - int err = fillTransaction(transaction1, &callback, layer1); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback, layer2); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction2.merge(std::move(transaction1)).apply(); - - ExpectedResult expected; - expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); -} - -TEST_F(LayerCallbackTest, Merge_SameLayer) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction1, transaction2; - CallbackHelper callback1, callback2; - int err = fillTransaction(transaction1, &callback1, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction2.merge(std::move(transaction1)).apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); -} - -TEST_F(LayerCallbackTest, Merge_DifferentClients) { - sp<SurfaceComposerClient> client1(new SurfaceComposerClient), - client2(new SurfaceComposerClient); - - ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; - ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; - - sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0, - ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0, - ISurfaceComposerClient::eFXSurfaceBufferState)); - - Transaction transaction1, transaction2; - CallbackHelper callback1, callback2; - int err = fillTransaction(transaction1, &callback1, layer1); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2, layer2); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); - transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); - - ExpectedResult expected; - expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); -} - -TEST_F(LayerCallbackTest, MultipleTransactions) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - for (size_t i = 0; i < 10; i++) { - int err = fillTransaction(transaction, &callback, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, - ExpectedResult::Buffer::ACQUIRED, - (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED - : ExpectedResult::PreviousBuffer::RELEASED); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected)); - } - ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState()); -} - -TEST_F(LayerCallbackTest, MultipleTransactions_NoStateChange) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - for (size_t i = 0; i < 10; i++) { - ExpectedResult expected; - - if (i == 0) { - int err = fillTransaction(transaction, &callback, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - } else { - int err = fillTransaction(transaction, &callback); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - } - - transaction.apply(); - - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected)); - } - ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState()); -} - -TEST_F(LayerCallbackTest, MultipleTransactions_SameStateChange) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - for (size_t i = 0; i < 10; i++) { - if (i == 0) { - int err = fillTransaction(transaction, &callback, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - } else { - int err = fillTransaction(transaction, &callback); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - } - - transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); - - ExpectedResult expected; - expected.addSurface((i == 0) ? ExpectedResult::Transaction::PRESENTED - : ExpectedResult::Transaction::NOT_PRESENTED, - layer, - (i == 0) ? ExpectedResult::Buffer::ACQUIRED - : ExpectedResult::Buffer::NOT_ACQUIRED); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, i == 0)); - } - ASSERT_NO_FATAL_FAILURE(callback.verifyFinalState()); -} - -TEST_F(LayerCallbackTest, MultipleTransactions_Merge) { - sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createBufferStateLayer()); - ASSERT_NO_FATAL_FAILURE(layer2 = createBufferStateLayer()); - - Transaction transaction1, transaction2; - CallbackHelper callback1, callback2; - for (size_t i = 0; i < 10; i++) { - int err = fillTransaction(transaction1, &callback1, layer1); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2, layer2); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); - transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); - - ExpectedResult expected; - expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}, - ExpectedResult::Buffer::ACQUIRED, - (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED - : ExpectedResult::PreviousBuffer::RELEASED); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected)); - } - ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState()); - ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState()); -} - -TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients) { - sp<SurfaceComposerClient> client1(new SurfaceComposerClient), - client2(new SurfaceComposerClient); - ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; - ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; - - sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0, - ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0, - ISurfaceComposerClient::eFXSurfaceBufferState)); - - Transaction transaction1, transaction2; - CallbackHelper callback1, callback2; - for (size_t i = 0; i < 10; i++) { - int err = fillTransaction(transaction1, &callback1, layer1); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2, layer2); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); - transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); - - ExpectedResult expected; - expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}, - ExpectedResult::Buffer::ACQUIRED, - (i == 0) ? ExpectedResult::PreviousBuffer::NOT_RELEASED - : ExpectedResult::PreviousBuffer::RELEASED); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected)); - } - ASSERT_NO_FATAL_FAILURE(callback1.verifyFinalState()); - ASSERT_NO_FATAL_FAILURE(callback2.verifyFinalState()); -} - -TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_NoStateChange) { - sp<SurfaceComposerClient> client1(new SurfaceComposerClient), - client2(new SurfaceComposerClient); - ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; - ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; - - sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0, - ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0, - ISurfaceComposerClient::eFXSurfaceBufferState)); - - Transaction transaction1, transaction2; - CallbackHelper callback1, callback2; - - // Normal call to set up test - int err = fillTransaction(transaction1, &callback1, layer1); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2, layer2); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); - transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); - - ExpectedResult expected; - expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); - expected.reset(); - - // Test - err = fillTransaction(transaction1, &callback1); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction2.merge(std::move(transaction1)).apply(); - - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); -} - -TEST_F(LayerCallbackTest, MultipleTransactions_Merge_DifferentClients_SameStateChange) { - sp<SurfaceComposerClient> client1(new SurfaceComposerClient), - client2(new SurfaceComposerClient); - - ASSERT_EQ(NO_ERROR, client1->initCheck()) << "failed to create SurfaceComposerClient"; - ASSERT_EQ(NO_ERROR, client2->initCheck()) << "failed to create SurfaceComposerClient"; - - sp<SurfaceControl> layer1, layer2; - ASSERT_NO_FATAL_FAILURE(layer1 = createLayer(client1, "test", 0, 0, - ISurfaceComposerClient::eFXSurfaceBufferState)); - ASSERT_NO_FATAL_FAILURE(layer2 = createLayer(client2, "test", 0, 0, - ISurfaceComposerClient::eFXSurfaceBufferState)); - - Transaction transaction1, transaction2; - CallbackHelper callback1, callback2; - - // Normal call to set up test - int err = fillTransaction(transaction1, &callback1, layer1); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2, layer2); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction1.setFrame(layer1, Rect(0, 0, 32, 32)); - transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); - - ExpectedResult expected; - expected.addSurfaces(ExpectedResult::Transaction::PRESENTED, {layer1, layer2}); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); - expected.reset(); - - // Test - err = fillTransaction(transaction1, &callback1); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - err = fillTransaction(transaction2, &callback2); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction2.setFrame(layer2, Rect(32, 32, 64, 64)).merge(std::move(transaction1)).apply(); - - expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer2, - ExpectedResult::Buffer::NOT_ACQUIRED); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected, true)); -} - -TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - std::vector<ExpectedResult> expectedResults(50); - for (auto& expected : expectedResults) { - expected.reset(); - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, - ExpectedResult::Buffer::ACQUIRED, - ExpectedResult::PreviousBuffer::UNKNOWN); - - int err = fillTransaction(transaction, &callback, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.apply(); - } - EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true)); -} - -TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_NoStateChange) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - // Normal call to set up test - Transaction transaction; - CallbackHelper callback; - int err = fillTransaction(transaction, &callback, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); - - // Test - std::vector<ExpectedResult> expectedResults(50); - for (auto& expected : expectedResults) { - expected.reset(); - - err = fillTransaction(transaction, &callback); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.apply(); - } - EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true)); -} - -TEST_F(LayerCallbackTest, MultipleTransactions_SingleFrame_SameStateChange) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - // Normal call to set up test - Transaction transaction; - CallbackHelper callback; - int err = fillTransaction(transaction, &callback, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); - - ExpectedResult expectedResult; - expectedResult.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expectedResult, true)); - - // Test - std::vector<ExpectedResult> expectedResults(50); - for (auto& expected : expectedResults) { - expected.reset(); - expected.addSurface(ExpectedResult::Transaction::NOT_PRESENTED, layer, - ExpectedResult::Buffer::NOT_ACQUIRED); - - err = fillTransaction(transaction, &callback); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - transaction.setFrame(layer, Rect(0, 0, 32, 32)).apply(); - } - EXPECT_NO_FATAL_FAILURE(waitForCallbacks(callback, expectedResults, true)); -} - -TEST_F(LayerCallbackTest, DesiredPresentTime) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - int err = fillTransaction(transaction, &callback, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - // Try to present 100ms in the future - nsecs_t time = systemTime() + (100 * 1e6); - - transaction.setDesiredPresentTime(time); - transaction.apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - expected.addExpectedPresentTime(time); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); -} - -TEST_F(LayerCallbackTest, DesiredPresentTime_Multiple) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback1; - int err = fillTransaction(transaction, &callback1, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - // Try to present 100ms in the future - nsecs_t time = systemTime() + (100 * 1e6); - - transaction.setDesiredPresentTime(time); - transaction.apply(); - - ExpectedResult expected1; - expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - expected1.addExpectedPresentTime(time); - - CallbackHelper callback2; - err = fillTransaction(transaction, &callback2, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - // Try to present 33ms after the first frame - time += (33.3 * 1e6); - - transaction.setDesiredPresentTime(time); - transaction.apply(); - - ExpectedResult expected2; - expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer, - ExpectedResult::Buffer::ACQUIRED, - ExpectedResult::PreviousBuffer::RELEASED); - expected2.addExpectedPresentTime(time); - - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true)); -} - -TEST_F(LayerCallbackTest, DesiredPresentTime_OutOfOrder) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback1; - int err = fillTransaction(transaction, &callback1, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - // Try to present 100ms in the future - nsecs_t time = systemTime() + (100 * 1e6); - - transaction.setDesiredPresentTime(time); - transaction.apply(); - - ExpectedResult expected1; - expected1.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - expected1.addExpectedPresentTime(time); - - CallbackHelper callback2; - err = fillTransaction(transaction, &callback2, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - // Try to present 33ms before the previous frame - time -= (33.3 * 1e6); - - transaction.setDesiredPresentTime(time); - transaction.apply(); - - ExpectedResult expected2; - expected2.addSurface(ExpectedResult::Transaction::PRESENTED, layer, - ExpectedResult::Buffer::ACQUIRED, - ExpectedResult::PreviousBuffer::RELEASED); - - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1, expected1, true)); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2, expected2, true)); -} - -TEST_F(LayerCallbackTest, DesiredPresentTime_Past) { - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer()); - - Transaction transaction; - CallbackHelper callback; - int err = fillTransaction(transaction, &callback, layer); - if (err) { - GTEST_SUCCEED() << "test not supported"; - return; - } - - // Try to present 100ms in the past - nsecs_t time = systemTime() - (100 * 1e6); - - transaction.setDesiredPresentTime(time); - transaction.apply(); - - ExpectedResult expected; - expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer); - expected.addExpectedPresentTime(systemTime()); - EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true)); -} - -class LayerUpdateTest : public LayerTransactionTest { -protected: - virtual void SetUp() { - LayerTransactionTest::SetUp(); - ASSERT_EQ(NO_ERROR, mClient->initCheck()); - - const auto display = SurfaceComposerClient::getInternalDisplayToken(); - ASSERT_FALSE(display == nullptr); - - DisplayInfo info; - ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info)); - - ssize_t displayWidth = info.w; - ssize_t displayHeight = info.h; - - // Background surface - mBGSurfaceControl = createLayer(String8("BG Test Surface"), displayWidth, - displayHeight, 0); - ASSERT_TRUE(mBGSurfaceControl != nullptr); - ASSERT_TRUE(mBGSurfaceControl->isValid()); - fillSurfaceRGBA8(mBGSurfaceControl, 63, 63, 195); - - // Foreground surface - mFGSurfaceControl = createLayer(String8("FG Test Surface"), 64, 64, 0); - - ASSERT_TRUE(mFGSurfaceControl != nullptr); - ASSERT_TRUE(mFGSurfaceControl->isValid()); - - fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); - - // Synchronization surface - mSyncSurfaceControl = createLayer(String8("Sync Test Surface"), 1, 1, 0); - ASSERT_TRUE(mSyncSurfaceControl != nullptr); - ASSERT_TRUE(mSyncSurfaceControl->isValid()); - - fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); - - asTransaction([&](Transaction& t) { - t.setDisplayLayerStack(display, 0); - - t.setLayer(mBGSurfaceControl, INT32_MAX - 2).show(mBGSurfaceControl); - - t.setLayer(mFGSurfaceControl, INT32_MAX - 1) - .setPosition(mFGSurfaceControl, 64, 64) - .show(mFGSurfaceControl); - - t.setLayer(mSyncSurfaceControl, INT32_MAX - 1) - .setPosition(mSyncSurfaceControl, displayWidth - 2, displayHeight - 2) - .show(mSyncSurfaceControl); - }); - } - - virtual void TearDown() { - LayerTransactionTest::TearDown(); - mBGSurfaceControl = 0; - mFGSurfaceControl = 0; - mSyncSurfaceControl = 0; - } - - void waitForPostedBuffers() { - // Since the sync surface is in synchronous mode (i.e. double buffered) - // posting three buffers to it should ensure that at least two - // SurfaceFlinger::handlePageFlip calls have been made, which should - // guaranteed that a buffer posted to another Surface has been retired. - fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); - fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); - fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); - } - - - sp<SurfaceControl> mBGSurfaceControl; - sp<SurfaceControl> mFGSurfaceControl; - - // This surface is used to ensure that the buffers posted to - // mFGSurfaceControl have been picked up by SurfaceFlinger. - sp<SurfaceControl> mSyncSurfaceControl; -}; - -TEST_F(LayerUpdateTest, RelativesAreNotDetached) { - - std::unique_ptr<ScreenCapture> sc; - - sp<SurfaceControl> relative = createLayer(String8("relativeTestSurface"), 10, 10, 0); - fillSurfaceRGBA8(relative, 10, 10, 10); - waitForPostedBuffers(); - - Transaction{} - .setRelativeLayer(relative, mFGSurfaceControl->getHandle(), 1) - .setPosition(relative, 64, 64) - .apply(); - - { - // The relative should be on top of the FG control. - ScreenCapture::captureScreen(&sc); - sc->checkPixel(64, 64, 10, 10, 10); - } - Transaction{}.detachChildren(mFGSurfaceControl).apply(); - - { - // Nothing should change at this point. - ScreenCapture::captureScreen(&sc); - sc->checkPixel(64, 64, 10, 10, 10); - } - - Transaction{}.hide(relative).apply(); - - { - // Ensure that the relative was actually hidden, rather than - // being left in the detached but visible state. - ScreenCapture::captureScreen(&sc); - sc->expectFGColor(64, 64); - } -} - -class GeometryLatchingTest : public LayerUpdateTest { -protected: - void EXPECT_INITIAL_STATE(const char* trace) { - SCOPED_TRACE(trace); - ScreenCapture::captureScreen(&sc); - // We find the leading edge of the FG surface. - sc->expectFGColor(127, 127); - sc->expectBGColor(128, 128); - } - - void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63, false); } - - void unlockFGBuffer() { - sp<Surface> s = mFGSurfaceControl->getSurface(); - ASSERT_EQ(NO_ERROR, s->unlockAndPost()); - waitForPostedBuffers(); - } - - void completeFGResize() { - fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); - waitForPostedBuffers(); - } - void restoreInitialState() { - asTransaction([&](Transaction& t) { - t.setSize(mFGSurfaceControl, 64, 64); - t.setPosition(mFGSurfaceControl, 64, 64); - t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64)); - }); - - EXPECT_INITIAL_STATE("After restoring initial state"); - } - std::unique_ptr<ScreenCapture> sc; -}; - -class CropLatchingTest : public GeometryLatchingTest { -protected: - void EXPECT_CROPPED_STATE(const char* trace) { - SCOPED_TRACE(trace); - ScreenCapture::captureScreen(&sc); - // The edge should be moved back one pixel by our crop. - sc->expectFGColor(126, 126); - sc->expectBGColor(127, 127); - sc->expectBGColor(128, 128); - } - - void EXPECT_RESIZE_STATE(const char* trace) { - SCOPED_TRACE(trace); - ScreenCapture::captureScreen(&sc); - // The FG is now resized too 128,128 at 64,64 - sc->expectFGColor(64, 64); - sc->expectFGColor(191, 191); - sc->expectBGColor(192, 192); - } -}; - -TEST_F(LayerUpdateTest, DeferredTransactionTest) { - std::unique_ptr<ScreenCapture> sc; - { - SCOPED_TRACE("before anything"); - ScreenCapture::captureScreen(&sc); - sc->expectBGColor(32, 32); - sc->expectFGColor(96, 96); - sc->expectBGColor(160, 160); - } - - // set up two deferred transactions on different frames - asTransaction([&](Transaction& t) { - t.setAlpha(mFGSurfaceControl, 0.75); - t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), - mSyncSurfaceControl->getSurface()->getNextFrameNumber()); - }); - - asTransaction([&](Transaction& t) { - t.setPosition(mFGSurfaceControl, 128, 128); - t.deferTransactionUntil_legacy(mFGSurfaceControl, mSyncSurfaceControl->getHandle(), - mSyncSurfaceControl->getSurface()->getNextFrameNumber() + 1); - }); - - { - SCOPED_TRACE("before any trigger"); - ScreenCapture::captureScreen(&sc); - sc->expectBGColor(32, 32); - sc->expectFGColor(96, 96); - sc->expectBGColor(160, 160); - } - - // should trigger the first deferred transaction, but not the second one - fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); - { - SCOPED_TRACE("after first trigger"); - ScreenCapture::captureScreen(&sc); - sc->expectBGColor(32, 32); - sc->checkPixel(96, 96, 162, 63, 96); - sc->expectBGColor(160, 160); - } - - // should show up immediately since it's not deferred - asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 1.0); }); - - // trigger the second deferred transaction - fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31); - { - SCOPED_TRACE("after second trigger"); - ScreenCapture::captureScreen(&sc); - sc->expectBGColor(32, 32); - sc->expectBGColor(96, 96); - sc->expectFGColor(160, 160); - } -} - -TEST_F(LayerUpdateTest, LayerWithNoBuffersResizesImmediately) { - std::unique_ptr<ScreenCapture> sc; - - sp<SurfaceControl> childNoBuffer = - createSurface(mClient, "Bufferless child", 0 /* buffer width */, 0 /* buffer height */, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - sp<SurfaceControl> childBuffer = createSurface(mClient, "Buffered child", 20, 20, - PIXEL_FORMAT_RGBA_8888, 0, childNoBuffer.get()); - fillSurfaceRGBA8(childBuffer, 200, 200, 200); - SurfaceComposerClient::Transaction{} - .setCrop_legacy(childNoBuffer, Rect(0, 0, 10, 10)) - .show(childNoBuffer) - .show(childBuffer) - .apply(true); - { - ScreenCapture::captureScreen(&sc); - sc->expectChildColor(73, 73); - sc->expectFGColor(74, 74); - } - SurfaceComposerClient::Transaction{} - .setCrop_legacy(childNoBuffer, Rect(0, 0, 20, 20)) - .apply(true); - { - ScreenCapture::captureScreen(&sc); - sc->expectChildColor(73, 73); - sc->expectChildColor(74, 74); - } -} - -TEST_F(LayerUpdateTest, MergingTransactions) { - std::unique_ptr<ScreenCapture> sc; - { - SCOPED_TRACE("before move"); - ScreenCapture::captureScreen(&sc); - sc->expectBGColor(0, 12); - sc->expectFGColor(75, 75); - sc->expectBGColor(145, 145); - } - - Transaction t1, t2; - t1.setPosition(mFGSurfaceControl, 128, 128); - t2.setPosition(mFGSurfaceControl, 0, 0); - // We expect that the position update from t2 now - // overwrites the position update from t1. - t1.merge(std::move(t2)); - t1.apply(); - - { - ScreenCapture::captureScreen(&sc); - sc->expectFGColor(1, 1); - } -} - -class ChildLayerTest : public LayerUpdateTest { -protected: - void SetUp() override { - LayerUpdateTest::SetUp(); - mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0, - mFGSurfaceControl.get()); - fillSurfaceRGBA8(mChild, 200, 200, 200); - - { - SCOPED_TRACE("before anything"); - mCapture = screenshot(); - mCapture->expectChildColor(64, 64); - } - } - void TearDown() override { - LayerUpdateTest::TearDown(); - mChild = 0; - } - - sp<SurfaceControl> mChild; - std::unique_ptr<ScreenCapture> mCapture; -}; - -TEST_F(ChildLayerTest, ChildLayerPositioning) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 10, 10); - t.setPosition(mFGSurfaceControl, 64, 64); - }); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(74, 74); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(84, 84); - } - - asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); }); - - { - mCapture = screenshot(); - // Top left of foreground should now be at 0, 0 - mCapture->expectFGColor(0, 0); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(10, 10); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(20, 20); - } -} - -TEST_F(ChildLayerTest, ChildLayerCropping) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 0, 0); - t.setPosition(mFGSurfaceControl, 0, 0); - t.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5)); - }); - - { - mCapture = screenshot(); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(4, 4); - mCapture->expectBGColor(5, 5); - } -} - -TEST_F(ChildLayerTest, ChildLayerConstraints) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mFGSurfaceControl, 0, 0); - t.setPosition(mChild, 63, 63); - }); - - { - mCapture = screenshot(); - mCapture->expectFGColor(0, 0); - // Last pixel in foreground should now be the child. - mCapture->expectChildColor(63, 63); - // But the child should be constrained and the next pixel - // must be the background - mCapture->expectBGColor(64, 64); - } -} - -TEST_F(ChildLayerTest, ChildLayerScaling) { - asTransaction([&](Transaction& t) { t.setPosition(mFGSurfaceControl, 0, 0); }); - - // Find the boundary between the parent and child - { - mCapture = screenshot(); - mCapture->expectChildColor(9, 9); - mCapture->expectFGColor(10, 10); - } - - asTransaction([&](Transaction& t) { t.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0); }); - - // The boundary should be twice as far from the origin now. - // The pixels from the last test should all be child now - { - mCapture = screenshot(); - mCapture->expectChildColor(9, 9); - mCapture->expectChildColor(10, 10); - mCapture->expectChildColor(19, 19); - mCapture->expectFGColor(20, 20); - } -} - -// A child with a scale transform should be cropped by its parent bounds. -TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) { - asTransaction([&](Transaction& t) { - t.setPosition(mFGSurfaceControl, 0, 0); - t.setPosition(mChild, 0, 0); - }); - - // Find the boundary between the parent and child. - { - mCapture = screenshot(); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(9, 9); - mCapture->expectFGColor(10, 10); - } - - asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); }); - - // The child should fill its parent bounds and be cropped by it. - { - mCapture = screenshot(); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(63, 63); - mCapture->expectBGColor(64, 64); - } -} - -TEST_F(ChildLayerTest, ChildLayerAlpha) { - fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254); - fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0); - fillSurfaceRGBA8(mChild, 0, 254, 0); - waitForPostedBuffers(); - - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 0, 0); - t.setPosition(mFGSurfaceControl, 0, 0); - }); - - { - mCapture = screenshot(); - // Unblended child color - mCapture->checkPixel(0, 0, 0, 254, 0); - } - - asTransaction([&](Transaction& t) { t.setAlpha(mChild, 0.5); }); - - { - mCapture = screenshot(); - // Child and BG blended. - mCapture->checkPixel(0, 0, 127, 127, 0); - } - - asTransaction([&](Transaction& t) { t.setAlpha(mFGSurfaceControl, 0.5); }); - - { - mCapture = screenshot(); - // Child and BG blended. - mCapture->checkPixel(0, 0, 95, 64, 95); - } -} - -TEST_F(ChildLayerTest, ReparentChildren) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 10, 10); - t.setPosition(mFGSurfaceControl, 64, 64); - }); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(74, 74); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(84, 84); - } - - asTransaction([&](Transaction& t) { - t.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle()); - }); - - { - mCapture = screenshot(); - mCapture->expectFGColor(64, 64); - // In reparenting we should have exposed the entire foreground surface. - mCapture->expectFGColor(74, 74); - // And the child layer should now begin at 10, 10 (since the BG - // layer is at (0, 0)). - mCapture->expectBGColor(9, 9); - mCapture->expectChildColor(10, 10); - } -} - -TEST_F(ChildLayerTest, ChildrenSurviveParentDestruction) { - sp<SurfaceControl> mGrandChild = - createSurface(mClient, "Grand Child", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, mChild.get()); - fillSurfaceRGBA8(mGrandChild, 111, 111, 111); - - { - SCOPED_TRACE("Grandchild visible"); - ScreenCapture::captureScreen(&mCapture); - mCapture->checkPixel(64, 64, 111, 111, 111); - } - - mChild.clear(); - - { - SCOPED_TRACE("After destroying child"); - ScreenCapture::captureScreen(&mCapture); - mCapture->expectFGColor(64, 64); - } - - asTransaction([&](Transaction& t) { - t.reparent(mGrandChild, mFGSurfaceControl->getHandle()); - }); - - { - SCOPED_TRACE("After reparenting grandchild"); - ScreenCapture::captureScreen(&mCapture); - mCapture->checkPixel(64, 64, 111, 111, 111); - } -} - -TEST_F(ChildLayerTest, DetachChildrenSameClient) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 10, 10); - t.setPosition(mFGSurfaceControl, 64, 64); - }); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(74, 74); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(84, 84); - } - - - asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); }); - - asTransaction([&](Transaction& t) { t.hide(mChild); }); - - // Since the child has the same client as the parent, it will not get - // detached and will be hidden. - { - mCapture = screenshot(); - mCapture->expectFGColor(64, 64); - mCapture->expectFGColor(74, 74); - mCapture->expectFGColor(84, 84); - } -} - -TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { - sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient; - sp<SurfaceControl> mChildNewClient = - createSurface(mNewComposerClient, "New Child Test Surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - - ASSERT_TRUE(mChildNewClient->isValid()); - - fillSurfaceRGBA8(mChildNewClient, 200, 200, 200); - - asTransaction([&](Transaction& t) { - t.hide(mChild); - t.show(mChildNewClient); - t.setPosition(mChildNewClient, 10, 10); - t.setPosition(mFGSurfaceControl, 64, 64); - }); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(74, 74); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(84, 84); - } - - asTransaction([&](Transaction& t) { t.detachChildren(mFGSurfaceControl); }); - - asTransaction([&](Transaction& t) { t.hide(mChildNewClient); }); - - // Nothing should have changed. - { - mCapture = screenshot(); - mCapture->expectFGColor(64, 64); - mCapture->expectChildColor(74, 74); - mCapture->expectFGColor(84, 84); - } -} - -TEST_F(ChildLayerTest, DetachChildrenThenAttach) { - sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; - sp<SurfaceControl> childNewClient = - newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - - ASSERT_TRUE(childNewClient != nullptr); - ASSERT_TRUE(childNewClient->isValid()); - - fillSurfaceRGBA8(childNewClient, 200, 200, 200); - - Transaction() - .hide(mChild) - .show(childNewClient) - .setPosition(childNewClient, 10, 10) - .setPosition(mFGSurfaceControl, 64, 64) - .apply(); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(74, 74); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(84, 84); - } - - Transaction().detachChildren(mFGSurfaceControl).apply(); - Transaction().hide(childNewClient).apply(); - - // Nothing should have changed. - { - mCapture = screenshot(); - mCapture->expectFGColor(64, 64); - mCapture->expectChildColor(74, 74); - mCapture->expectFGColor(84, 84); - } - - sp<SurfaceControl> newParentSurface = createLayer(String8("New Parent Surface"), 32, 32, 0); - fillLayerColor(ISurfaceComposerClient::eFXSurfaceBufferQueue, newParentSurface, Color::RED, 32, - 32); - Transaction() - .setLayer(newParentSurface, INT32_MAX - 1) - .show(newParentSurface) - .setPosition(newParentSurface, 20, 20) - .reparent(childNewClient, newParentSurface->getHandle()) - .apply(); - { - mCapture = screenshot(); - // Child is now hidden. - mCapture->expectColor(Rect(20, 20, 52, 52), Color::RED); - } -} -TEST_F(ChildLayerTest, DetachChildrenWithDeferredTransaction) { - sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient; - sp<SurfaceControl> childNewClient = - newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - - ASSERT_TRUE(childNewClient != nullptr); - ASSERT_TRUE(childNewClient->isValid()); - - fillSurfaceRGBA8(childNewClient, 200, 200, 200); - - Transaction() - .hide(mChild) - .show(childNewClient) - .setPosition(childNewClient, 10, 10) - .setPosition(mFGSurfaceControl, 64, 64) - .apply(); - - { - mCapture = screenshot(); - Rect rect = Rect(74, 74, 84, 84); - mCapture->expectBorder(rect, Color{195, 63, 63, 255}); - mCapture->expectColor(rect, Color{200, 200, 200, 255}); - } - - Transaction() - .deferTransactionUntil_legacy(childNewClient, mFGSurfaceControl->getHandle(), - mFGSurfaceControl->getSurface()->getNextFrameNumber()) - .apply(); - Transaction().detachChildren(mFGSurfaceControl).apply(); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(mFGSurfaceControl, Color::RED, 32, 32)); - - // BufferLayer can still dequeue buffers even though there's a detached layer with a - // deferred transaction. - { - SCOPED_TRACE("new buffer"); - mCapture = screenshot(); - Rect rect = Rect(74, 74, 84, 84); - mCapture->expectBorder(rect, Color::RED); - mCapture->expectColor(rect, Color{200, 200, 200, 255}); - } -} - -TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 0, 0); - t.setPosition(mFGSurfaceControl, 0, 0); - }); - - { - mCapture = screenshot(); - // We've positioned the child in the top left. - mCapture->expectChildColor(0, 0); - // But it's only 10x15. - mCapture->expectFGColor(10, 15); - } - - asTransaction([&](Transaction& t) { - t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - // We cause scaling by 2. - t.setSize(mFGSurfaceControl, 128, 128); - }); - - { - mCapture = screenshot(); - // We've positioned the child in the top left. - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(10, 10); - mCapture->expectChildColor(19, 29); - // And now it should be scaled all the way to 20x30 - mCapture->expectFGColor(20, 30); - } -} - -// Regression test for b/37673612 -TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 0, 0); - t.setPosition(mFGSurfaceControl, 0, 0); - }); - - { - mCapture = screenshot(); - // We've positioned the child in the top left. - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(9, 14); - // But it's only 10x15. - mCapture->expectFGColor(10, 15); - } - // We set things up as in b/37673612 so that there is a mismatch between the buffer size and - // the WM specified state size. - asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); }); - sp<Surface> s = mFGSurfaceControl->getSurface(); - auto anw = static_cast<ANativeWindow*>(s.get()); - native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); - native_window_set_buffers_dimensions(anw, 64, 128); - fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); - waitForPostedBuffers(); - - { - // The child should still be in the same place and not have any strange scaling as in - // b/37673612. - mCapture = screenshot(); - mCapture->expectChildColor(0, 0); - mCapture->expectFGColor(10, 10); - } -} - -// A child with a buffer transform from its parents should be cropped by its parent bounds. -TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 0, 0); - t.setPosition(mFGSurfaceControl, 0, 0); - t.setSize(mChild, 100, 100); - }); - fillSurfaceRGBA8(mChild, 200, 200, 200); - - { - mCapture = screenshot(); - - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(63, 63); - mCapture->expectBGColor(64, 64); - } - - asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); }); - sp<Surface> s = mFGSurfaceControl->getSurface(); - auto anw = static_cast<ANativeWindow*>(s.get()); - // Apply a 90 transform on the buffer. - native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); - native_window_set_buffers_dimensions(anw, 64, 128); - fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); - waitForPostedBuffers(); - - // The child should be cropped by the new parent bounds. - { - mCapture = screenshot(); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(99, 63); - mCapture->expectFGColor(100, 63); - mCapture->expectBGColor(128, 64); - } -} - -// A child with a scale transform from its parents should be cropped by its parent bounds. -TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 0, 0); - t.setPosition(mFGSurfaceControl, 0, 0); - t.setSize(mChild, 200, 200); - }); - fillSurfaceRGBA8(mChild, 200, 200, 200); - - { - mCapture = screenshot(); - - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(63, 63); - mCapture->expectBGColor(64, 64); - } - - asTransaction([&](Transaction& t) { - t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - // Set a scaling by 2. - t.setSize(mFGSurfaceControl, 128, 128); - }); - - // Child should inherit its parents scale but should be cropped by its parent bounds. - { - mCapture = screenshot(); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(127, 127); - mCapture->expectBGColor(128, 128); - } -} - -// Regression test for b/127368943 -// Child should ignore the buffer transform but apply parent scale transform. -TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 0, 0); - t.setPosition(mFGSurfaceControl, 0, 0); - }); - - { - mCapture = screenshot(); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(9, 14); - mCapture->expectFGColor(10, 15); - } - - // Change the size of the foreground to 128 * 64 so we can test rotation as well. - asTransaction([&](Transaction& t) { - t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); - t.setSize(mFGSurfaceControl, 128, 64); - }); - sp<Surface> s = mFGSurfaceControl->getSurface(); - auto anw = static_cast<ANativeWindow*>(s.get()); - // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we - // have an effective scale of 2.0 applied to the buffer along with a rotation transform. - native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); - native_window_set_buffers_dimensions(anw, 32, 64); - fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); - waitForPostedBuffers(); - - // The child should ignore the buffer transform but apply the 2.0 scale from parent. - { - mCapture = screenshot(); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(19, 29); - mCapture->expectFGColor(20, 30); - } -} - -TEST_F(ChildLayerTest, Bug36858924) { - // Destroy the child layer - mChild.clear(); - - // Now recreate it as hidden - mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eHidden, mFGSurfaceControl.get()); - - // Show the child layer in a deferred transaction - asTransaction([&](Transaction& t) { - t.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(), - mFGSurfaceControl->getSurface()->getNextFrameNumber()); - t.show(mChild); - }); - - // Render the foreground surface a few times - // - // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third - // frame because SurfaceFlinger would never process the deferred transaction and would therefore - // never acquire/release the first buffer - ALOGI("Filling 1"); - fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0); - ALOGI("Filling 2"); - fillSurfaceRGBA8(mFGSurfaceControl, 0, 0, 255); - ALOGI("Filling 3"); - fillSurfaceRGBA8(mFGSurfaceControl, 255, 0, 0); - ALOGI("Filling 4"); - fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0); -} - -TEST_F(ChildLayerTest, Reparent) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 10, 10); - t.setPosition(mFGSurfaceControl, 64, 64); - }); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(74, 74); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(84, 84); - } - - asTransaction([&](Transaction& t) { t.reparent(mChild, mBGSurfaceControl->getHandle()); }); - - { - mCapture = screenshot(); - mCapture->expectFGColor(64, 64); - // In reparenting we should have exposed the entire foreground surface. - mCapture->expectFGColor(74, 74); - // And the child layer should now begin at 10, 10 (since the BG - // layer is at (0, 0)). - mCapture->expectBGColor(9, 9); - mCapture->expectChildColor(10, 10); - } -} - -TEST_F(ChildLayerTest, ReparentToNoParent) { - asTransaction([&](Transaction& t) { - t.show(mChild); - t.setPosition(mChild, 10, 10); - t.setPosition(mFGSurfaceControl, 64, 64); - }); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // But 10 pixels in we should see the child surface - mCapture->expectChildColor(74, 74); - // And 10 more pixels we should be back to the foreground surface - mCapture->expectFGColor(84, 84); - } - asTransaction([&](Transaction& t) { t.reparent(mChild, nullptr); }); - { - mCapture = screenshot(); - // The surface should now be offscreen. - mCapture->expectFGColor(64, 64); - mCapture->expectFGColor(74, 74); - mCapture->expectFGColor(84, 84); - } -} - -TEST_F(ChildLayerTest, ReparentFromNoParent) { - sp<SurfaceControl> newSurface = createLayer(String8("New Surface"), 10, 10, 0); - ASSERT_TRUE(newSurface != nullptr); - ASSERT_TRUE(newSurface->isValid()); - - fillSurfaceRGBA8(newSurface, 63, 195, 63); - asTransaction([&](Transaction& t) { - t.hide(mChild); - t.show(newSurface); - t.setPosition(newSurface, 10, 10); - t.setLayer(newSurface, INT32_MAX - 2); - t.setPosition(mFGSurfaceControl, 64, 64); - }); - - { - mCapture = screenshot(); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // At 10, 10 we should see the new surface - mCapture->checkPixel(10, 10, 63, 195, 63); - } - - asTransaction([&](Transaction& t) { t.reparent(newSurface, mFGSurfaceControl->getHandle()); }); - - { - mCapture = screenshot(); - // newSurface will now be a child of mFGSurface so it will be 10, 10 offset from - // mFGSurface, putting it at 74, 74. - mCapture->expectFGColor(64, 64); - mCapture->checkPixel(74, 74, 63, 195, 63); - mCapture->expectFGColor(84, 84); - } -} - -TEST_F(ChildLayerTest, NestedChildren) { - sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mChild.get()); - fillSurfaceRGBA8(grandchild, 50, 50, 50); - - { - mCapture = screenshot(); - // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer - // which begins at 64, 64 - mCapture->checkPixel(64, 64, 50, 50, 50); - } -} - -TEST_F(ChildLayerTest, ChildLayerRelativeLayer) { - sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 128, 128, 0); - fillSurfaceRGBA8(relative, 255, 255, 255); - - Transaction t; - t.setLayer(relative, INT32_MAX) - .setRelativeLayer(mChild, relative->getHandle(), 1) - .setPosition(mFGSurfaceControl, 0, 0) - .apply(true); - - // We expect that the child should have been elevated above our - // INT_MAX layer even though it's not a child of it. - { - mCapture = screenshot(); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(9, 9); - mCapture->checkPixel(10, 10, 255, 255, 255); - } -} - -class BoundlessLayerTest : public LayerUpdateTest { -protected: - std::unique_ptr<ScreenCapture> mCapture; -}; - -// Verify setting a size on a buffer layer has no effect. -TEST_F(BoundlessLayerTest, BufferLayerIgnoresSize) { - sp<SurfaceControl> bufferLayer = - createSurface(mClient, "BufferLayer", 45, 45, PIXEL_FORMAT_RGBA_8888, 0, - mFGSurfaceControl.get()); - ASSERT_TRUE(bufferLayer->isValid()); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(bufferLayer, Color::BLACK, 30, 30)); - asTransaction([&](Transaction& t) { t.show(bufferLayer); }); - { - mCapture = screenshot(); - // Top left of background must now be visible - mCapture->expectBGColor(0, 0); - // Foreground Surface bounds must be color layer - mCapture->expectColor(Rect(64, 64, 94, 94), Color::BLACK); - // Buffer layer should not extend past buffer bounds - mCapture->expectFGColor(95, 95); - } -} - -// Verify a boundless color layer will fill its parent bounds. The parent has a buffer size -// which will crop the color layer. -TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentBufferBounds) { - sp<SurfaceControl> colorLayer = - createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get()); - ASSERT_TRUE(colorLayer->isValid()); - asTransaction([&](Transaction& t) { - t.setColor(colorLayer, half3{0, 0, 0}); - t.show(colorLayer); - }); - { - mCapture = screenshot(); - // Top left of background must now be visible - mCapture->expectBGColor(0, 0); - // Foreground Surface bounds must be color layer - mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK); - // Color layer should not extend past foreground bounds - mCapture->expectBGColor(129, 129); - } -} - -// Verify a boundless color layer will fill its parent bounds. The parent has no buffer but has -// a crop which will be used to crop the color layer. -TEST_F(BoundlessLayerTest, BoundlessColorLayerFillsParentCropBounds) { - sp<SurfaceControl> cropLayer = createSurface(mClient, "CropLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, - 0 /* flags */, mFGSurfaceControl.get()); - ASSERT_TRUE(cropLayer->isValid()); - sp<SurfaceControl> colorLayer = - createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceColor, cropLayer.get()); - ASSERT_TRUE(colorLayer->isValid()); - asTransaction([&](Transaction& t) { - t.setCrop_legacy(cropLayer, Rect(5, 5, 10, 10)); - t.setColor(colorLayer, half3{0, 0, 0}); - t.show(cropLayer); - t.show(colorLayer); - }); - { - mCapture = screenshot(); - // Top left of background must now be visible - mCapture->expectBGColor(0, 0); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // 5 pixels from the foreground we should see the child surface - mCapture->expectColor(Rect(69, 69, 74, 74), Color::BLACK); - // 10 pixels from the foreground we should be back to the foreground surface - mCapture->expectFGColor(74, 74); - } -} - -// Verify for boundless layer with no children, their transforms have no effect. -TEST_F(BoundlessLayerTest, BoundlessColorLayerTransformHasNoEffect) { - sp<SurfaceControl> colorLayer = - createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceColor, mFGSurfaceControl.get()); - ASSERT_TRUE(colorLayer->isValid()); - asTransaction([&](Transaction& t) { - t.setPosition(colorLayer, 320, 320); - t.setMatrix(colorLayer, 2, 0, 0, 2); - t.setColor(colorLayer, half3{0, 0, 0}); - t.show(colorLayer); - }); - { - mCapture = screenshot(); - // Top left of background must now be visible - mCapture->expectBGColor(0, 0); - // Foreground Surface bounds must be color layer - mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK); - // Color layer should not extend past foreground bounds - mCapture->expectBGColor(129, 129); - } -} - -// Verify for boundless layer with children, their transforms have an effect. -TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerCanSetTransform) { - sp<SurfaceControl> boundlessLayerRightShift = - createSurface(mClient, "BoundlessLayerRightShift", 0, 0, PIXEL_FORMAT_RGBA_8888, - 0 /* flags */, mFGSurfaceControl.get()); - ASSERT_TRUE(boundlessLayerRightShift->isValid()); - sp<SurfaceControl> boundlessLayerDownShift = - createSurface(mClient, "BoundlessLayerLeftShift", 0, 0, PIXEL_FORMAT_RGBA_8888, - 0 /* flags */, boundlessLayerRightShift.get()); - ASSERT_TRUE(boundlessLayerDownShift->isValid()); - sp<SurfaceControl> colorLayer = - createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceColor, boundlessLayerDownShift.get()); - ASSERT_TRUE(colorLayer->isValid()); - asTransaction([&](Transaction& t) { - t.setPosition(boundlessLayerRightShift, 32, 0); - t.show(boundlessLayerRightShift); - t.setPosition(boundlessLayerDownShift, 0, 32); - t.show(boundlessLayerDownShift); - t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64)); - t.setColor(colorLayer, half3{0, 0, 0}); - t.show(colorLayer); - }); - { - mCapture = screenshot(); - // Top left of background must now be visible - mCapture->expectBGColor(0, 0); - // Top left of foreground must now be visible - mCapture->expectFGColor(64, 64); - // Foreground Surface bounds must be color layer - mCapture->expectColor(Rect(96, 96, 128, 128), Color::BLACK); - // Color layer should not extend past foreground bounds - mCapture->expectBGColor(129, 129); - } -} - -// Verify child layers do not get clipped if they temporarily move into the negative -// coordinate space as the result of an intermediate transformation. -TEST_F(BoundlessLayerTest, IntermediateBoundlessLayerDoNotCrop) { - sp<SurfaceControl> boundlessLayer = - mClient->createSurface(String8("BoundlessLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888, - 0 /* flags */, mFGSurfaceControl.get()); - ASSERT_TRUE(boundlessLayer != nullptr); - ASSERT_TRUE(boundlessLayer->isValid()); - sp<SurfaceControl> colorLayer = - mClient->createSurface(String8("ColorLayer"), 0, 0, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceColor, boundlessLayer.get()); - ASSERT_TRUE(colorLayer != nullptr); - ASSERT_TRUE(colorLayer->isValid()); - asTransaction([&](Transaction& t) { - // shift child layer off bounds. If this layer was not boundless, we will - // expect the child layer to be cropped. - t.setPosition(boundlessLayer, 32, 32); - t.show(boundlessLayer); - t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64)); - // undo shift by parent - t.setPosition(colorLayer, -32, -32); - t.setColor(colorLayer, half3{0, 0, 0}); - t.show(colorLayer); - }); - { - mCapture = screenshot(); - // Top left of background must now be visible - mCapture->expectBGColor(0, 0); - // Foreground Surface bounds must be color layer - mCapture->expectColor(Rect(64, 64, 128, 128), Color::BLACK); - // Color layer should not extend past foreground bounds - mCapture->expectBGColor(129, 129); - } -} - -// Verify for boundless root layers with children, their transforms have an effect. -TEST_F(BoundlessLayerTest, RootBoundlessLayerCanSetTransform) { - sp<SurfaceControl> rootBoundlessLayer = createSurface(mClient, "RootBoundlessLayer", 0, 0, - PIXEL_FORMAT_RGBA_8888, 0 /* flags */); - ASSERT_TRUE(rootBoundlessLayer->isValid()); - sp<SurfaceControl> colorLayer = - createSurface(mClient, "ColorLayer", 0, 0, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceColor, rootBoundlessLayer.get()); - - ASSERT_TRUE(colorLayer->isValid()); - asTransaction([&](Transaction& t) { - t.setLayer(rootBoundlessLayer, INT32_MAX - 1); - t.setPosition(rootBoundlessLayer, 32, 32); - t.show(rootBoundlessLayer); - t.setCrop_legacy(colorLayer, Rect(0, 0, 64, 64)); - t.setColor(colorLayer, half3{0, 0, 0}); - t.show(colorLayer); - t.hide(mFGSurfaceControl); - }); - { - mCapture = screenshot(); - // Top left of background must now be visible - mCapture->expectBGColor(0, 0); - // Top left of foreground must now be visible - mCapture->expectBGColor(31, 31); - // Foreground Surface bounds must be color layer - mCapture->expectColor(Rect(32, 32, 96, 96), Color::BLACK); - // Color layer should not extend past foreground bounds - mCapture->expectBGColor(97, 97); - } -} - -class ScreenCaptureTest : public LayerUpdateTest { -protected: - std::unique_ptr<ScreenCapture> mCapture; -}; - -TEST_F(ScreenCaptureTest, CaptureSingleLayer) { - auto bgHandle = mBGSurfaceControl->getHandle(); - ScreenCapture::captureLayers(&mCapture, bgHandle); - mCapture->expectBGColor(0, 0); - // Doesn't capture FG layer which is at 64, 64 - mCapture->expectBGColor(64, 64); -} - -TEST_F(ScreenCaptureTest, CaptureLayerWithChild) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - fillSurfaceRGBA8(child, 200, 200, 200); - - SurfaceComposerClient::Transaction().show(child).apply(true); - - // Captures mFGSurfaceControl layer and its child. - ScreenCapture::captureLayers(&mCapture, fgHandle); - mCapture->expectFGColor(10, 10); - mCapture->expectChildColor(0, 0); -} - -TEST_F(ScreenCaptureTest, CaptureLayerChildOnly) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - fillSurfaceRGBA8(child, 200, 200, 200); - - SurfaceComposerClient::Transaction().show(child).apply(true); - - // Captures mFGSurfaceControl's child - ScreenCapture::captureChildLayers(&mCapture, fgHandle); - mCapture->checkPixel(10, 10, 0, 0, 0); - mCapture->expectChildColor(0, 0); -} - -TEST_F(ScreenCaptureTest, CaptureLayerExclude) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - fillSurfaceRGBA8(child, 200, 200, 200); - sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - fillSurfaceRGBA8(child2, 200, 0, 200); - - SurfaceComposerClient::Transaction() - .show(child) - .show(child2) - .setLayer(child, 1) - .setLayer(child2, 2) - .apply(true); - - // Child2 would be visible but its excluded, so we should see child1 color instead. - ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()}); - mCapture->checkPixel(10, 10, 0, 0, 0); - mCapture->checkPixel(0, 0, 200, 200, 200); -} - -// Like the last test but verifies that children are also exclude. -TEST_F(ScreenCaptureTest, CaptureLayerExcludeTree) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - fillSurfaceRGBA8(child, 200, 200, 200); - sp<SurfaceControl> child2 = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - fillSurfaceRGBA8(child2, 200, 0, 200); - sp<SurfaceControl> child3 = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, child2.get()); - fillSurfaceRGBA8(child2, 200, 0, 200); - - SurfaceComposerClient::Transaction() - .show(child) - .show(child2) - .show(child3) - .setLayer(child, 1) - .setLayer(child2, 2) - .apply(true); - - // Child2 would be visible but its excluded, so we should see child1 color instead. - ScreenCapture::captureChildLayersExcluding(&mCapture, fgHandle, {child2->getHandle()}); - mCapture->checkPixel(10, 10, 0, 0, 0); - mCapture->checkPixel(0, 0, 200, 200, 200); -} - -TEST_F(ScreenCaptureTest, CaptureTransparent) { - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - - fillSurfaceRGBA8(child, 200, 200, 200); - - SurfaceComposerClient::Transaction().show(child).apply(true); - - auto childHandle = child->getHandle(); - - // Captures child - ScreenCapture::captureLayers(&mCapture, childHandle, {0, 0, 10, 20}); - mCapture->expectColor(Rect(0, 0, 9, 9), {200, 200, 200, 255}); - // Area outside of child's bounds is transparent. - mCapture->expectColor(Rect(0, 10, 9, 19), {0, 0, 0, 0}); -} - -TEST_F(ScreenCaptureTest, DontCaptureRelativeOutsideTree) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - ASSERT_NE(nullptr, child.get()) << "failed to create surface"; - sp<SurfaceControl> relative = createLayer(String8("Relative surface"), 10, 10, 0); - fillSurfaceRGBA8(child, 200, 200, 200); - fillSurfaceRGBA8(relative, 100, 100, 100); - - SurfaceComposerClient::Transaction() - .show(child) - // Set relative layer above fg layer so should be shown above when computing all layers. - .setRelativeLayer(relative, fgHandle, 1) - .show(relative) - .apply(true); - - // Captures mFGSurfaceControl layer and its child. Relative layer shouldn't be captured. - ScreenCapture::captureLayers(&mCapture, fgHandle); - mCapture->expectFGColor(10, 10); - mCapture->expectChildColor(0, 0); -} - -TEST_F(ScreenCaptureTest, CaptureRelativeInTree) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - sp<SurfaceControl> relative = createSurface(mClient, "Relative surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - fillSurfaceRGBA8(child, 200, 200, 200); - fillSurfaceRGBA8(relative, 100, 100, 100); - - SurfaceComposerClient::Transaction() - .show(child) - // Set relative layer below fg layer but relative to child layer so it should be shown - // above child layer. - .setLayer(relative, -1) - .setRelativeLayer(relative, child->getHandle(), 1) - .show(relative) - .apply(true); - - // Captures mFGSurfaceControl layer and its children. Relative layer is a child of fg so its - // relative value should be taken into account, placing it above child layer. - ScreenCapture::captureLayers(&mCapture, fgHandle); - mCapture->expectFGColor(10, 10); - // Relative layer is showing on top of child layer - mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255}); -} - -// In the following tests we verify successful skipping of a parent layer, -// so we use the same verification logic and only change how we mutate -// the parent layer to verify that various properties are ignored. -class ScreenCaptureChildOnlyTest : public LayerUpdateTest { -public: - void SetUp() override { - LayerUpdateTest::SetUp(); - - mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, - mFGSurfaceControl.get()); - fillSurfaceRGBA8(mChild, 200, 200, 200); - - SurfaceComposerClient::Transaction().show(mChild).apply(true); - } - - void verify(std::function<void()> verifyStartingState) { - // Verify starting state before a screenshot is taken. - verifyStartingState(); - - // Verify child layer does not inherit any of the properties of its - // parent when its screenshot is captured. - auto fgHandle = mFGSurfaceControl->getHandle(); - ScreenCapture::captureChildLayers(&mCapture, fgHandle); - mCapture->checkPixel(10, 10, 0, 0, 0); - mCapture->expectChildColor(0, 0); - - // Verify all assumptions are still true after the screenshot is taken. - verifyStartingState(); - } - - std::unique_ptr<ScreenCapture> mCapture; - sp<SurfaceControl> mChild; -}; - -// Regression test b/76099859 -TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) { - - SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true); - - // Even though the parent is hidden we should still capture the child. - - // Before and after reparenting, verify child is properly hidden - // when rendering full-screen. - verify([&] { screenshot()->expectBGColor(64, 64); }); -} - -TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) { - SurfaceComposerClient::Transaction() - .setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 1, 1)) - .apply(true); - - // Even though the parent is cropped out we should still capture the child. - - // Before and after reparenting, verify child is cropped by parent. - verify([&] { screenshot()->expectBGColor(65, 65); }); -} - -// Regression test b/124372894 -TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) { - SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true); - - // We should not inherit the parent scaling. - - // Before and after reparenting, verify child is properly scaled. - verify([&] { screenshot()->expectChildColor(80, 80); }); -} - - -TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) { - auto fgHandle = mFGSurfaceControl->getHandle(); - - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - fillSurfaceRGBA8(child, 200, 200, 200); - - sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5, - PIXEL_FORMAT_RGBA_8888, 0, child.get()); - - fillSurfaceRGBA8(grandchild, 50, 50, 50); - SurfaceComposerClient::Transaction() - .show(child) - .setPosition(grandchild, 5, 5) - .show(grandchild) - .apply(true); - - // Captures mFGSurfaceControl, its child, and the grandchild. - ScreenCapture::captureLayers(&mCapture, fgHandle); - mCapture->expectFGColor(10, 10); - mCapture->expectChildColor(0, 0); - mCapture->checkPixel(5, 5, 50, 50, 50); -} - -TEST_F(ScreenCaptureTest, CaptureChildOnly) { - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - fillSurfaceRGBA8(child, 200, 200, 200); - auto childHandle = child->getHandle(); - - SurfaceComposerClient::Transaction().setPosition(child, 5, 5).show(child).apply(true); - - // Captures only the child layer, and not the parent. - ScreenCapture::captureLayers(&mCapture, childHandle); - mCapture->expectChildColor(0, 0); - mCapture->expectChildColor(9, 9); -} - -TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) { - sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10, - PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); - fillSurfaceRGBA8(child, 200, 200, 200); - auto childHandle = child->getHandle(); - - sp<SurfaceControl> grandchild = createSurface(mClient, "Grandchild surface", 5, 5, - PIXEL_FORMAT_RGBA_8888, 0, child.get()); - fillSurfaceRGBA8(grandchild, 50, 50, 50); - - SurfaceComposerClient::Transaction() - .show(child) - .setPosition(grandchild, 5, 5) - .show(grandchild) - .apply(true); - - auto grandchildHandle = grandchild->getHandle(); - - // Captures only the grandchild. - ScreenCapture::captureLayers(&mCapture, grandchildHandle); - mCapture->checkPixel(0, 0, 50, 50, 50); - mCapture->checkPixel(4, 4, 50, 50, 50); -} - -TEST_F(ScreenCaptureTest, CaptureCrop) { - sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); - sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30, - PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); - - SurfaceComposerClient::Transaction() - .setLayer(redLayer, INT32_MAX - 1) - .show(redLayer) - .show(blueLayer) - .apply(true); - - auto redLayerHandle = redLayer->getHandle(); - - // Capturing full screen should have both red and blue are visible. - ScreenCapture::captureLayers(&mCapture, redLayerHandle); - mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); - // red area below the blue area - mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED); - // red area to the right of the blue area - mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); - - const Rect crop = Rect(0, 0, 30, 30); - ScreenCapture::captureLayers(&mCapture, redLayerHandle, crop); - // Capturing the cropped screen, cropping out the shown red area, should leave only the blue - // area visible. - mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); - mCapture->checkPixel(30, 30, 0, 0, 0); -} - -TEST_F(ScreenCaptureTest, CaptureSize) { - sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); - sp<SurfaceControl> blueLayer = createSurface(mClient, "Blue surface", 30, 30, - PIXEL_FORMAT_RGBA_8888, 0, redLayer.get()); - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(blueLayer, Color::BLUE, 30, 30)); - - SurfaceComposerClient::Transaction() - .setLayer(redLayer, INT32_MAX - 1) - .show(redLayer) - .show(blueLayer) - .apply(true); - - auto redLayerHandle = redLayer->getHandle(); - - // Capturing full screen should have both red and blue are visible. - ScreenCapture::captureLayers(&mCapture, redLayerHandle); - mCapture->expectColor(Rect(0, 0, 29, 29), Color::BLUE); - // red area below the blue area - mCapture->expectColor(Rect(0, 30, 59, 59), Color::RED); - // red area to the right of the blue area - mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); - - ScreenCapture::captureLayers(&mCapture, redLayerHandle, Rect::EMPTY_RECT, 0.5); - // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area. - mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE); - // red area below the blue area - mCapture->expectColor(Rect(0, 15, 29, 29), Color::RED); - // red area to the right of the blue area - mCapture->expectColor(Rect(15, 0, 29, 29), Color::RED); - mCapture->checkPixel(30, 30, 0, 0, 0); -} - -TEST_F(ScreenCaptureTest, CaptureInvalidLayer) { - sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60, 0); - - ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60)); - - auto redLayerHandle = redLayer->getHandle(); - redLayer.clear(); - SurfaceComposerClient::Transaction().apply(true); - - sp<GraphicBuffer> outBuffer; - - // Layer was deleted so captureLayers should fail with NAME_NOT_FOUND - sp<ISurfaceComposer> sf(ComposerService::getComposerService()); - ASSERT_EQ(NAME_NOT_FOUND, sf->captureLayers(redLayerHandle, &outBuffer, Rect::EMPTY_RECT, 1.0)); -} - - -class DereferenceSurfaceControlTest : public LayerTransactionTest { -protected: - void SetUp() override { - LayerTransactionTest::SetUp(); - bgLayer = createLayer("BG layer", 20, 20); - fillBufferQueueLayerColor(bgLayer, Color::RED, 20, 20); - fgLayer = createLayer("FG layer", 20, 20); - fillBufferQueueLayerColor(fgLayer, Color::BLUE, 20, 20); - Transaction().setLayer(fgLayer, mLayerZBase + 1).apply(); - { - SCOPED_TRACE("before anything"); - auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE); - } - } - void TearDown() override { - LayerTransactionTest::TearDown(); - bgLayer = 0; - fgLayer = 0; - } - - sp<SurfaceControl> bgLayer; - sp<SurfaceControl> fgLayer; -}; - -TEST_F(DereferenceSurfaceControlTest, LayerNotInTransaction) { - fgLayer = nullptr; - { - SCOPED_TRACE("after setting null"); - auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 20, 20), Color::RED); - } -} - -TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) { - auto transaction = Transaction().show(fgLayer); - fgLayer = nullptr; - { - SCOPED_TRACE("after setting null"); - auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 20, 20), Color::BLUE); - } -} - -class MultiDisplayLayerBoundsTest : public LayerTransactionTest { -protected: - virtual void SetUp() { - LayerTransactionTest::SetUp(); - ASSERT_EQ(NO_ERROR, mClient->initCheck()); - - mMainDisplay = SurfaceComposerClient::getInternalDisplayToken(); - SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo); - - sp<IGraphicBufferConsumer> consumer; - BufferQueue::createBufferQueue(&mProducer, &consumer); - consumer->setConsumerName(String8("Virtual disp consumer")); - consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h); - } - - virtual void TearDown() { - SurfaceComposerClient::destroyDisplay(mVirtualDisplay); - LayerTransactionTest::TearDown(); - mColorLayer = 0; - } - - void createDisplay(const Rect& layerStackRect, uint32_t layerStack) { - mVirtualDisplay = - SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/); - asTransaction([&](Transaction& t) { - t.setDisplaySurface(mVirtualDisplay, mProducer); - t.setDisplayLayerStack(mVirtualDisplay, layerStack); - t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect, - Rect(mMainDisplayInfo.w, mMainDisplayInfo.h)); - }); - } - - void createColorLayer(uint32_t layerStack) { - mColorLayer = - createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */, - PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor); - ASSERT_TRUE(mColorLayer != nullptr); - ASSERT_TRUE(mColorLayer->isValid()); - asTransaction([&](Transaction& t) { - t.setLayerStack(mColorLayer, layerStack); - t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40)); - t.setLayer(mColorLayer, INT32_MAX - 2); - t.setColor(mColorLayer, - half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f, - mExpectedColor.b / 255.0f}); - t.show(mColorLayer); - }); - } - - DisplayInfo mMainDisplayInfo; - sp<IBinder> mMainDisplay; - sp<IBinder> mVirtualDisplay; - sp<IGraphicBufferProducer> mProducer; - sp<SurfaceControl> mColorLayer; - Color mExpectedColor = {63, 63, 195, 255}; -}; - -TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) { - createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */); - createColorLayer(1 /* layerStack */); - - asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); }); - - // Verify color layer does not render on main display. - std::unique_ptr<ScreenCapture> sc; - ScreenCapture::captureScreen(&sc, mMainDisplay); - sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255}); - sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); - - // Verify color layer renders correctly on virtual display. - ScreenCapture::captureScreen(&sc, mVirtualDisplay); - sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor); - sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0}); -} - -TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) { - // Create a display and set its layer stack to the main display's layer stack so - // the contents of the main display are mirrored on to the virtual display. - - // Assumption here is that the new mirrored display has the same viewport as the - // primary display that it is mirroring. - createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */); - createColorLayer(0 /* layerStack */); - - asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); }); - - // Verify color layer renders correctly on main display and it is mirrored on the - // virtual display. - std::unique_ptr<ScreenCapture> sc; - ScreenCapture::captureScreen(&sc, mMainDisplay); - sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor); - sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); - - ScreenCapture::captureScreen(&sc, mVirtualDisplay); - sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor); - sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); -} - -class DisplayActiveConfigTest : public ::testing::Test { -protected: - void SetUp() override { - mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); - SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &mDisplayconfigs); - EXPECT_GT(mDisplayconfigs.size(), 0); - - // set display power to on to make sure config can be changed - SurfaceComposerClient::setDisplayPowerMode(mDisplayToken, HWC_POWER_MODE_NORMAL); - } - - sp<IBinder> mDisplayToken; - Vector<DisplayInfo> mDisplayconfigs; -}; - -TEST_F(DisplayActiveConfigTest, allConfigsAllowed) { - std::vector<int32_t> allowedConfigs; - - // Add all configs to the allowed configs - for (int i = 0; i < mDisplayconfigs.size(); i++) { - allowedConfigs.push_back(i); - } - - status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs); - EXPECT_EQ(res, NO_ERROR); - - std::vector<int32_t> outConfigs; - res = SurfaceComposerClient::getAllowedDisplayConfigs(mDisplayToken, &outConfigs); - EXPECT_EQ(res, NO_ERROR); - EXPECT_EQ(allowedConfigs, outConfigs); -} - -TEST_F(DisplayActiveConfigTest, changeAllowedConfig) { - // we need at least 2 configs available for this test - if (mDisplayconfigs.size() <= 1) return; - - int activeConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken); - - // We want to set the allowed config to everything but the active config - std::vector<int32_t> allowedConfigs; - for (int i = 0; i < mDisplayconfigs.size(); i++) { - if (i != activeConfig) { - allowedConfigs.push_back(i); - } - } - - status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs); - EXPECT_EQ(res, NO_ERROR); - - // Allow some time for the config change - std::this_thread::sleep_for(200ms); - - int newActiveConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken); - EXPECT_NE(activeConfig, newActiveConfig); - - // Make sure the new config is part of allowed config - EXPECT_TRUE(std::find(allowedConfigs.begin(), allowedConfigs.end(), newActiveConfig) != - allowedConfigs.end()); -} - -class RelativeZTest : public LayerTransactionTest { -protected: - virtual void SetUp() { - LayerTransactionTest::SetUp(); - ASSERT_EQ(NO_ERROR, mClient->initCheck()); - - const auto display = SurfaceComposerClient::getInternalDisplayToken(); - ASSERT_FALSE(display == nullptr); - - // Back layer - mBackgroundLayer = createColorLayer("Background layer", Color::RED); - - // Front layer - mForegroundLayer = createColorLayer("Foreground layer", Color::GREEN); - - asTransaction([&](Transaction& t) { - t.setDisplayLayerStack(display, 0); - t.setLayer(mBackgroundLayer, INT32_MAX - 2).show(mBackgroundLayer); - t.setLayer(mForegroundLayer, INT32_MAX - 1).show(mForegroundLayer); - }); - } - - virtual void TearDown() { - LayerTransactionTest::TearDown(); - mBackgroundLayer = 0; - mForegroundLayer = 0; - } - - sp<SurfaceControl> mBackgroundLayer; - sp<SurfaceControl> mForegroundLayer; -}; - -// When a layer is reparented offscreen, remove relative z order if the relative parent -// is still onscreen so that the layer is not drawn. -TEST_F(RelativeZTest, LayerRemoved) { - std::unique_ptr<ScreenCapture> sc; - - // Background layer (RED) - // Child layer (WHITE) (relative to foregroud layer) - // Foregroud layer (GREEN) - sp<SurfaceControl> childLayer = - createColorLayer("Child layer", Color::BLUE, mBackgroundLayer.get()); - - Transaction{} - .setRelativeLayer(childLayer, mForegroundLayer->getHandle(), 1) - .show(childLayer) - .apply(); - - { - // The childLayer should be in front of the FG control. - ScreenCapture::captureScreen(&sc); - sc->checkPixel(1, 1, Color::BLUE.r, Color::BLUE.g, Color::BLUE.b); - } - - // Background layer (RED) - // Foregroud layer (GREEN) - Transaction{}.reparent(childLayer, nullptr).apply(); - - // Background layer (RED) - // Child layer (WHITE) - // Foregroud layer (GREEN) - Transaction{}.reparent(childLayer, mBackgroundLayer->getHandle()).apply(); - - { - // The relative z info for child layer should be reset, leaving FG control on top. - ScreenCapture::captureScreen(&sc); - sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b); - } -} - -// When a layer is reparented offscreen, preseve relative z order if the relative parent -// is also offscreen. Regression test b/132613412 -TEST_F(RelativeZTest, LayerRemovedOffscreenRelativeParent) { - std::unique_ptr<ScreenCapture> sc; - - // Background layer (RED) - // Foregroud layer (GREEN) - // child level 1 (WHITE) - // child level 2a (BLUE) - // child level 3 (GREEN) (relative to child level 2b) - // child level 2b (BLACK) - sp<SurfaceControl> childLevel1 = - createColorLayer("child level 1", Color::WHITE, mForegroundLayer.get()); - sp<SurfaceControl> childLevel2a = - createColorLayer("child level 2a", Color::BLUE, childLevel1.get()); - sp<SurfaceControl> childLevel2b = - createColorLayer("child level 2b", Color::BLACK, childLevel1.get()); - sp<SurfaceControl> childLevel3 = - createColorLayer("child level 3", Color::GREEN, childLevel2a.get()); - - Transaction{} - .setRelativeLayer(childLevel3, childLevel2b->getHandle(), 1) - .show(childLevel2a) - .show(childLevel2b) - .show(childLevel3) - .apply(); - - { - // The childLevel3 should be in front of childLevel2b. - ScreenCapture::captureScreen(&sc); - sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b); - } - - // Background layer (RED) - // Foregroud layer (GREEN) - Transaction{}.reparent(childLevel1, nullptr).apply(); - - // Background layer (RED) - // Foregroud layer (GREEN) - // child level 1 (WHITE) - // child level 2 back (BLUE) - // child level 3 (GREEN) (relative to child level 2b) - // child level 2 front (BLACK) - Transaction{}.reparent(childLevel1, mForegroundLayer->getHandle()).apply(); - - { - // Nothing should change at this point since relative z info was preserved. - ScreenCapture::captureScreen(&sc); - sc->checkPixel(1, 1, Color::GREEN.r, Color::GREEN.g, Color::GREEN.b); - } -} - -// This test ensures that when we drop an app buffer in SurfaceFlinger, we merge -// the dropped buffer's damage region into the next buffer's damage region. If -// we don't do this, we'll report an incorrect damage region to hardware -// composer, resulting in broken rendering. This test checks the BufferQueue -// case. -// -// Unfortunately, we don't currently have a way to inspect the damage region -// SurfaceFlinger sends to hardware composer from a test, so this test requires -// the dev to manually watch the device's screen during the test to spot broken -// rendering. Because the results can't be automatically verified, this test is -// marked disabled. -TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDroppingBuffers) { - const int width = mDisplayWidth; - const int height = mDisplayHeight; - sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height)); - const auto producer = layer->getIGraphicBufferProducer(); - const sp<IProducerListener> dummyListener(new DummyProducerListener); - IGraphicBufferProducer::QueueBufferOutput queueBufferOutput; - ASSERT_EQ(OK, - producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput)); - - std::map<int, sp<GraphicBuffer>> slotMap; - auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) { - ASSERT_NE(nullptr, buf); - const auto iter = slotMap.find(slot); - ASSERT_NE(slotMap.end(), iter); - *buf = iter->second; - }; - - auto dequeue = [&](int* outSlot) { - ASSERT_NE(nullptr, outSlot); - *outSlot = -1; - int slot; - sp<Fence> fence; - uint64_t age; - FrameEventHistoryDelta timestamps; - const status_t dequeueResult = - producer->dequeueBuffer(&slot, &fence, width, height, PIXEL_FORMAT_RGBA_8888, - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, - &age, ×tamps); - if (dequeueResult == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { - sp<GraphicBuffer> newBuf; - ASSERT_EQ(OK, producer->requestBuffer(slot, &newBuf)); - ASSERT_NE(nullptr, newBuf.get()); - slotMap[slot] = newBuf; - } else { - ASSERT_EQ(OK, dequeueResult); - } - *outSlot = slot; - }; - - auto queue = [&](int slot, const Region& damage, nsecs_t displayTime) { - IGraphicBufferProducer::QueueBufferInput input( - /*timestamp=*/displayTime, /*isAutoTimestamp=*/false, HAL_DATASPACE_UNKNOWN, - /*crop=*/Rect::EMPTY_RECT, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW, - /*transform=*/0, Fence::NO_FENCE); - input.setSurfaceDamage(damage); - IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, producer->queueBuffer(slot, input, &output)); - }; - - auto fillAndPostBuffers = [&](const Color& color) { - int slot1; - ASSERT_NO_FATAL_FAILURE(dequeue(&slot1)); - int slot2; - ASSERT_NO_FATAL_FAILURE(dequeue(&slot2)); - - sp<GraphicBuffer> buf1; - ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot1, &buf1)); - sp<GraphicBuffer> buf2; - ASSERT_NO_FATAL_FAILURE(slotToBuffer(slot2, &buf2)); - fillGraphicBufferColor(buf1, Rect(width, height), color); - fillGraphicBufferColor(buf2, Rect(width, height), color); - - const auto displayTime = systemTime() + milliseconds_to_nanoseconds(100); - ASSERT_NO_FATAL_FAILURE(queue(slot1, Region::INVALID_REGION, displayTime)); - ASSERT_NO_FATAL_FAILURE( - queue(slot2, Region(Rect(width / 3, height / 3, 2 * width / 3, 2 * height / 3)), - displayTime)); - }; - - const auto startTime = systemTime(); - const std::array<Color, 3> colors = {Color::RED, Color::GREEN, Color::BLUE}; - int colorIndex = 0; - while (nanoseconds_to_seconds(systemTime() - startTime) < 10) { - ASSERT_NO_FATAL_FAILURE(fillAndPostBuffers(colors[colorIndex++ % colors.size()])); - std::this_thread::sleep_for(1s); - } - - ASSERT_EQ(OK, producer->disconnect(NATIVE_WINDOW_API_CPU)); -} - -} // namespace android diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp index 2feff4570e..9d74761679 100644 --- a/services/surfaceflinger/tests/fakehwc/Android.bp +++ b/services/surfaceflinger/tests/fakehwc/Android.bp @@ -10,8 +10,10 @@ cc_test { ], shared_libs: [ "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.composer@2.1-resources", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@3.0", + "android.hardware.graphics.mapper@4.0", "android.hardware.power@1.3", "libbase", "libbinder", @@ -29,8 +31,11 @@ cc_test { "libutils", ], static_libs: [ + "libcompositionengine", "libgmock", + "libperfetto_client_experimental", "librenderengine", + "libtimestats", "libtrace_proto", ], header_libs: [ diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp index 51956ec970..4d2146810b 100644 --- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp @@ -167,7 +167,9 @@ void FakeHwcEnvironment::SetUp() { } // TODO: Try registering the mock as the default service instead. property_set("debug.sf.hwc_service_name", "mock"); - // This allows the SurfaceFlinger to load a HIDL service not listed in manifest files. + + // This allows tests/SF to register/load a HIDL service not listed in manifest files. + setenv("TREBLE_TESTING_OVERRIDE", "true", true); property_set("debug.sf.treble_testing_override", "true"); } diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp index a892a2abd0..67faa57195 100644 --- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -127,7 +127,6 @@ class DisplayTest : public ::testing::Test { public: class MockComposerClient : public FakeComposerClient { public: - MOCK_METHOD2(getDisplayType, Error(Display display, ComposerClient::DisplayType* outType)); MOCK_METHOD4(getDisplayAttribute, Error(Display display, Config config, IComposerClient::Attribute attribute, int32_t* outValue)); @@ -171,14 +170,11 @@ void DisplayTest::SetUp() { mMockComposer = new MockComposerClient; sp<ComposerClient> client = new ComposerClient(mMockComposer); mFakeService = new FakeComposerService(client); - (void)mFakeService->registerAsService("mock"); + ASSERT_EQ(android::OK, mFakeService->registerAsService("mock")); android::hardware::ProcessState::self()->startThreadPool(); android::ProcessState::self()->startThreadPool(); - EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _)) - .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), - Return(Error::NONE))); // Primary display will be queried twice for all 5 attributes. One // set of queries comes from the SurfaceFlinger proper an the // other set from the VR composer. @@ -270,10 +266,6 @@ bool DisplayTest::waitForHotplugEvent(PhysicalDisplayId displayId, bool connecte TEST_F(DisplayTest, Hotplug) { ALOGD("DisplayTest::Hotplug"); - EXPECT_CALL(*mMockComposer, getDisplayType(EXTERNAL_DISPLAY, _)) - .Times(2) - .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), - Return(Error::NONE))); // The attribute queries will get done twice. This is for defaults EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _)) .Times(2 * 3) @@ -381,10 +373,6 @@ TEST_F(DisplayTest, HotplugPrimaryDisplay) { mMockComposer->clearFrames(); - EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _)) - .Times(2) - .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), - Return(Error::NONE))); // The attribute queries will get done twice. This is for defaults EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _)) .Times(2 * 3) @@ -1331,16 +1319,6 @@ TEST_F(LatchingTest, SurfacePositionLatching) { restoreInitialState(); - // Now we repeat with setGeometryAppliesWithResize - // and verify the position DOESN'T latch. - { - TransactionScope ts(*sFakeComposer); - ts.setGeometryAppliesWithResize(mFGSurfaceControl); - ts.setSize(mFGSurfaceControl, 32, 32); - ts.setPosition(mFGSurfaceControl, 100, 100); - } - EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); - completeFGResize(); auto referenceFrame2 = mBaseFrame; @@ -1365,14 +1343,6 @@ TEST_F(LatchingTest, CropLatching) { restoreInitialState(); - { - TransactionScope ts(*sFakeComposer); - ts.setSize(mFGSurfaceControl, 128, 128); - ts.setGeometryAppliesWithResize(mFGSurfaceControl); - ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63)); - } - EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame())); - completeFGResize(); auto referenceFrame2 = mBaseFrame; diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index f842d61c7f..2183d34866 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -36,13 +36,13 @@ cc_test { ":libsurfaceflinger_sources", "libsurfaceflinger_unittest_main.cpp", "CachingTest.cpp", - "CompositionTest.cpp", + "CompositionTest.cpp", "DispSyncSourceTest.cpp", "DisplayIdentificationTest.cpp", "DisplayTransactionTest.cpp", "EventControlThreadTest.cpp", "EventThreadTest.cpp", - "IdleTimerTest.cpp", + "OneShotTimerTest.cpp", "LayerHistoryTest.cpp", "LayerMetadataTest.cpp", "SchedulerTest.cpp", @@ -51,11 +51,11 @@ cc_test { "RefreshRateStatsTest.cpp", "RegionSamplingTest.cpp", "TimeStatsTest.cpp", + "FrameTracerTest.cpp", + "TransactionApplicationTest.cpp", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockDisplay.cpp", "mock/DisplayHardware/MockPowerAdvisor.cpp", - "mock/gui/MockGraphicBufferConsumer.cpp", - "mock/gui/MockGraphicBufferProducer.cpp", "mock/MockDispSync.cpp", "mock/MockEventControlThread.cpp", "mock/MockEventThread.cpp", @@ -63,13 +63,21 @@ cc_test { "mock/MockNativeWindowSurface.cpp", "mock/MockSurfaceInterceptor.cpp", "mock/MockTimeStats.cpp", + "mock/MockFrameTracer.cpp", "mock/system/window/MockNativeWindow.cpp", ], static_libs: [ "libgmock", "libcompositionengine", "libcompositionengine_mocks", + "libgui_mocks", + "libperfetto_client_experimental", "librenderengine_mocks", + "libtimestats", + "perfetto_trace_protos", + ], + shared_libs: [ + "libsurfaceflinger", ], header_libs: [ "libsurfaceflinger_headers", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 4f8ed1ae1c..8aff096d5e 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -34,7 +34,6 @@ #include "ColorLayer.h" #include "Layer.h" -#include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/MockDispSync.h" @@ -95,10 +94,6 @@ public: mFlinger.mutableEventQueue().reset(mMessageQueue); setupScheduler(); - EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0)); - EXPECT_CALL(*mPrimaryDispSync, getPeriod()) - .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE)); - EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0)); EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0))); EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) @@ -125,15 +120,31 @@ public: } void setupScheduler() { - mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs()); - mScheduler->mutableEventControlThread().reset(mEventControlThread); - mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync); - EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_)); - sp<Scheduler::ConnectionHandle> connectionHandle = - mScheduler->addConnection(std::move(mEventThread)); - mFlinger.mutableSfConnectionHandle() = std::move(connectionHandle); + auto eventThread = std::make_unique<mock::EventThread>(); + auto sfEventThread = std::make_unique<mock::EventThread>(); + + EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*eventThread, createEventConnection(_, _)) + .WillOnce(Return( + new EventThreadConnection(eventThread.get(), ResyncCallback(), + ISurfaceComposer::eConfigChangedSuppress))); + + EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) + .WillOnce(Return( + new EventThreadConnection(sfEventThread.get(), ResyncCallback(), + ISurfaceComposer::eConfigChangedSuppress))); + + auto primaryDispSync = std::make_unique<mock::DispSync>(); - mFlinger.mutableScheduler().reset(mScheduler); + EXPECT_CALL(*primaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0)); + EXPECT_CALL(*primaryDispSync, getPeriod()) + .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE)); + EXPECT_CALL(*primaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0)); + + mFlinger.setupScheduler(std::move(primaryDispSync), + std::make_unique<mock::EventControlThread>(), + std::move(eventThread), std::move(sfEventThread)); } void setupForceGeometryDirty() { @@ -157,7 +168,6 @@ public: std::unordered_set<HWC2::Capability> mDefaultCapabilities = {HWC2::Capability::SidebandStream}; - TestableScheduler* mScheduler; TestableSurfaceFlinger mFlinger; sp<DisplayDevice> mDisplay; sp<DisplayDevice> mExternalDisplay; @@ -168,13 +178,9 @@ public: sp<GraphicBuffer> mBuffer = new GraphicBuffer(); ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer(); - std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>(); - mock::EventControlThread* mEventControlThread = new mock::EventControlThread(); - Hwc2::mock::Composer* mComposer = nullptr; renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); - mock::DispSync* mPrimaryDispSync = new mock::DispSync(); sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE; @@ -296,18 +302,12 @@ struct BaseDisplayVariant { EXPECT_CALL(*test->mComposer, setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY)) .Times(1); - EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1); EXPECT_CALL(*test->mComposer, getDisplayRequests(HWC_DISPLAY, _, _, _)).Times(1); EXPECT_CALL(*test->mComposer, acceptDisplayChanges(HWC_DISPLAY)).Times(1); EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1); EXPECT_CALL(*test->mComposer, getReleaseFences(HWC_DISPLAY, _, _)).Times(1); EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); - // TODO: remove once we verify that we can just grab the fence from the - // FramebufferSurface. - EXPECT_CALL(*test->mRenderEngine, flush()).WillRepeatedly(Invoke([]() { - return base::unique_fd(); - })); EXPECT_CALL(*test->mDisplaySurface, onFrameCommitted()).Times(1); EXPECT_CALL(*test->mDisplaySurface, advanceFrame()).Times(1); @@ -341,11 +341,21 @@ struct BaseDisplayVariant { } static void setupHwcCompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1); + EXPECT_CALL(*test->mDisplaySurface, prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC)) .Times(1); } + static void setupHwcClientCompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1); + } + + static void setupHwcForcedClientCompositionCallExpectations(CompositionTest* test) { + EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _)).Times(1); + } + static void setupRECompositionCallExpectations(CompositionTest* test) { EXPECT_CALL(*test->mDisplaySurface, prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GLES)) @@ -419,6 +429,8 @@ struct PoweredOffDisplaySetupVariant : public BaseDisplayVariant<PoweredOffDispl } static void setupHwcCompositionCallExpectations(CompositionTest*) {} + static void setupHwcClientCompositionCallExpectations(CompositionTest*) {} + static void setupHwcForcedClientCompositionCallExpectations(CompositionTest*) {} static void setupRECompositionCallExpectations(CompositionTest* test) { EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); @@ -507,7 +519,7 @@ struct BaseLayerProperties { EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); bool ignoredRecomputeVisibleRegions; - layer->latchBuffer(ignoredRecomputeVisibleRegions, 0); + layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0); Mock::VerifyAndClear(test->mRenderEngine); } @@ -602,6 +614,12 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so gtet the back layer. + if (layerSettings.empty()) { + ADD_FAILURE() << "layerSettings was not expected to be empty in " + "setupREBufferCompositionCommonCallExpectations " + "verification lambda"; + return NO_ERROR; + } renderengine::LayerSettings layer = layerSettings.back(); EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull())); EXPECT_THAT(layer.source.buffer.fence, Not(IsNull())); @@ -645,6 +663,12 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. + if (layerSettings.empty()) { + ADD_FAILURE() + << "layerSettings was not expected to be empty in " + "setupREColorCompositionCallExpectations verification lambda"; + return NO_ERROR; + } renderengine::LayerSettings layer = layerSettings.back(); EXPECT_THAT(layer.source.buffer.buffer, IsNull()); EXPECT_EQ(half3(LayerProperties::COLOR[0], LayerProperties::COLOR[1], @@ -715,6 +739,12 @@ struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. + if (layerSettings.empty()) { + ADD_FAILURE() << "layerSettings was not expected to be empty in " + "setupInsecureREBufferCompositionCommonCallExpectations " + "verification lambda"; + return NO_ERROR; + } renderengine::LayerSettings layer = layerSettings.back(); EXPECT_THAT(layer.source.buffer.buffer, IsNull()); EXPECT_EQ(half3(0.0f, 0.0f, 0.0f), layer.source.solidColor); @@ -774,7 +804,6 @@ struct BaseLayerVariant { layerDrawingState.color = half4(LayerProperties::COLOR[0], LayerProperties::COLOR[1], LayerProperties::COLOR[2], LayerProperties::COLOR[3]); layer->computeBounds(FloatRect(0, 0, 100, 100), ui::Transform()); - layer->setVisibleRegion(Region(Rect(0, 0, 100, 100))); return layer; } @@ -783,19 +812,13 @@ struct BaseLayerVariant { EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _)) .WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE))); - std::vector<std::unique_ptr<compositionengine::OutputLayer>> outputLayers; - outputLayers.emplace_back(test->mDisplay->getCompositionDisplay() - ->getOrCreateOutputLayer(DEFAULT_DISPLAY_ID, - layer->getCompositionLayer(), - layer)); - - test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(std::move(outputLayers)); + auto outputLayer = test->mDisplay->getCompositionDisplay() + ->injectOutputLayerForTest(layer->getCompositionLayer(), layer); + outputLayer->editState().visibleRegion = Region(Rect(0, 0, 100, 100)); + outputLayer->editState().outputSpaceVisibleRegion = Region(Rect(0, 0, 100, 100)); Mock::VerifyAndClear(test->mComposer); - Vector<sp<Layer>> layers; - layers.add(layer); - test->mDisplay->setVisibleLayersSortedByZ(layers); test->mFlinger.mutableDrawingState().layersSortedByZ.add(layer); } @@ -803,8 +826,7 @@ struct BaseLayerVariant { EXPECT_CALL(*test->mComposer, destroyLayer(HWC_DISPLAY, HWC_LAYER)) .WillOnce(Return(Error::NONE)); - test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ( - std::vector<std::unique_ptr<compositionengine::OutputLayer>>()); + test->mDisplay->getCompositionDisplay()->clearOutputLayers(); test->mFlinger.mutableDrawingState().layersSortedByZ.clear(); } }; @@ -856,11 +878,14 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { FlingerLayerType layer = Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() { - return new BufferQueueLayer( - LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), - String8("test-layer"), LayerProperties::WIDTH, - LayerProperties::HEIGHT, - LayerProperties::LAYER_FLAGS, LayerMetadata())); + sp<Client> client; + String8 name("test-layer"); + LayerCreationArgs args = + LayerCreationArgs(test->mFlinger.mFlinger.get(), client, name, + LayerProperties::WIDTH, LayerProperties::HEIGHT, + LayerProperties::LAYER_FLAGS, LayerMetadata()); + args.textureName = test->mFlinger.mutableTexturePool().back(); + return new BufferQueueLayer(args); }); LayerProperties::setupLayerState(test, layer); @@ -986,14 +1011,45 @@ struct RECompositionResultVariant : public CompositionResultBaseVariant { template <typename Case> static void setupCallExpectations(CompositionTest* test) { Case::Display::setupNonEmptyFrameCompositionCallExpectations(test); + Case::Display::setupHwcClientCompositionCallExpectations(test); Case::Display::setupRECompositionCallExpectations(test); Case::Display::template setupRELayerCompositionCallExpectations<Case>(test); } }; -struct ForcedClientCompositionResultVariant : public RECompositionResultVariant { +struct ForcedClientCompositionResultVariant : public CompositionResultBaseVariant { static void setupLayerState(CompositionTest* test, sp<Layer> layer) { - layer->forceClientComposition(test->mDisplay); + const auto outputLayer = layer->findOutputLayerForDisplay(test->mDisplay); + LOG_FATAL_IF(!outputLayer); + outputLayer->editState().forceClientComposition = true; + } + + template <typename Case> + static void setupCallExpectations(CompositionTest* test) { + Case::Display::setupNonEmptyFrameCompositionCallExpectations(test); + Case::Display::setupHwcForcedClientCompositionCallExpectations(test); + Case::Display::setupRECompositionCallExpectations(test); + Case::Display::template setupRELayerCompositionCallExpectations<Case>(test); + } + + template <typename Case> + static void setupCallExpectationsForDirtyGeometry(CompositionTest*) {} + + template <typename Case> + static void setupCallExpectationsForDirtyFrame(CompositionTest*) {} +}; + +struct ForcedClientCompositionViaDebugOptionResultVariant : public CompositionResultBaseVariant { + static void setupLayerState(CompositionTest* test, sp<Layer>) { + test->mFlinger.mutableDebugDisableHWC() = true; + } + + template <typename Case> + static void setupCallExpectations(CompositionTest* test) { + Case::Display::setupNonEmptyFrameCompositionCallExpectations(test); + Case::Display::setupHwcForcedClientCompositionCallExpectations(test); + Case::Display::setupRECompositionCallExpectations(test); + Case::Display::template setupRELayerCompositionCallExpectations<Case>(test); } template <typename Case> @@ -1073,8 +1129,6 @@ struct CompositionCase { for (auto& hwcDisplay : test->mFlinger.mFakeHwcDisplays) { hwcDisplay->mutableLayers().clear(); } - - test->mDisplay->setVisibleLayersSortedByZ(Vector<sp<android::Layer>>()); } }; @@ -1316,5 +1370,23 @@ TEST_F(CompositionTest, captureScreenNormalBufferLayerOnPoweredOffDisplay) { NoCompositionTypeVariant, REScreenshotResultVariant>>(); } +/* ------------------------------------------------------------------------ + * Client composition forced through debug/developer settings + */ + +TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyGeometry) { + displayRefreshCompositionDirtyGeometry< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>, + ForcedClientCompositionViaDebugOptionResultVariant>>(); +} + +TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyFrame) { + displayRefreshCompositionDirtyFrame< + CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>, + KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>, + ForcedClientCompositionViaDebugOptionResultVariant>>(); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 5f58e7dce9..b1a4951451 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -24,6 +24,8 @@ #include <compositionengine/mock/DisplaySurface.h> #include <gmock/gmock.h> #include <gtest/gtest.h> +#include <gui/mock/GraphicBufferConsumer.h> +#include <gui/mock/GraphicBufferProducer.h> #include <log/log.h> #include <renderengine/mock/RenderEngine.h> #include <ui/DebugUtils.h> @@ -38,8 +40,6 @@ #include "mock/MockMessageQueue.h" #include "mock/MockNativeWindowSurface.h" #include "mock/MockSurfaceInterceptor.h" -#include "mock/gui/MockGraphicBufferConsumer.h" -#include "mock/gui/MockGraphicBufferProducer.h" #include "mock/system/window/MockNativeWindow.h" namespace android { @@ -95,11 +95,10 @@ public: DisplayTransactionTest(); ~DisplayTransactionTest() override; - void setupScheduler(); - // -------------------------------------------------------------------- // Mock/Fake injection + void injectMockScheduler(); void injectMockComposer(int virtualDisplayCount); void injectFakeBufferQueueFactory(); void injectFakeNativeWindowSurfaceFactory(); @@ -119,11 +118,7 @@ public: // -------------------------------------------------------------------- // Test instances - TestableScheduler* mScheduler; TestableSurfaceFlinger mFlinger; - mock::EventThread* mEventThread = new mock::EventThread(); - mock::EventThread* mSFEventThread = new mock::EventThread(); - mock::EventControlThread* mEventControlThread = new mock::EventControlThread(); sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow(); sp<GraphicBuffer> mBuffer = new GraphicBuffer(); @@ -134,7 +129,11 @@ public: Hwc2::mock::Composer* mComposer = nullptr; mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); mock::SurfaceInterceptor* mSurfaceInterceptor = new mock::SurfaceInterceptor(); - mock::DispSync* mPrimaryDispSync = new mock::DispSync(); + + mock::DispSync* mPrimaryDispSync = new mock::DispSync; + mock::EventControlThread* mEventControlThread = new mock::EventControlThread; + mock::EventThread* mEventThread = new mock::EventThread; + mock::EventThread* mSFEventThread = new mock::EventThread; // These mocks are created only when expected to be created via a factory. sp<mock::GraphicBufferConsumer> mConsumer; @@ -150,7 +149,7 @@ DisplayTransactionTest::DisplayTransactionTest() { // Default to no wide color display support configured mFlinger.mutableHasWideColorDisplay() = false; mFlinger.mutableUseColorManagement() = false; - mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED; + mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; // Default to using HWC virtual displays mFlinger.mutableUseHwcVirtualDisplays() = true; @@ -164,7 +163,7 @@ DisplayTransactionTest::DisplayTransactionTest() { return nullptr; }); - setupScheduler(); + injectMockScheduler(); mFlinger.mutableEventQueue().reset(mMessageQueue); mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.mutableInterceptor().reset(mSurfaceInterceptor); @@ -178,20 +177,21 @@ DisplayTransactionTest::~DisplayTransactionTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } -void DisplayTransactionTest::setupScheduler() { - mScheduler = new TestableScheduler(mFlinger.mutableRefreshRateConfigs()); - mScheduler->mutableEventControlThread().reset(mEventControlThread); - mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync); +void DisplayTransactionTest::injectMockScheduler() { EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*mEventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(mEventThread, ResyncCallback(), + ISurfaceComposer::eConfigChangedSuppress))); + EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*mSFEventThread, createEventConnection(_, _)) + .WillOnce(Return(new EventThreadConnection(mSFEventThread, ResyncCallback(), + ISurfaceComposer::eConfigChangedSuppress))); - sp<Scheduler::ConnectionHandle> sfConnectionHandle = - mScheduler->addConnection(std::unique_ptr<EventThread>(mSFEventThread)); - mFlinger.mutableSfConnectionHandle() = std::move(sfConnectionHandle); - sp<Scheduler::ConnectionHandle> appConnectionHandle = - mScheduler->addConnection(std::unique_ptr<EventThread>(mEventThread)); - mFlinger.mutableAppConnectionHandle() = std::move(appConnectionHandle); - mFlinger.mutableScheduler().reset(mScheduler); + mFlinger.setupScheduler(std::unique_ptr<DispSync>(mPrimaryDispSync), + std::unique_ptr<EventControlThread>(mEventControlThread), + std::unique_ptr<EventThread>(mEventThread), + std::unique_ptr<EventThread>(mSFEventThread)); } void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) { @@ -445,10 +445,6 @@ struct HwcDisplayVariant { } static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mComposer, getDisplayType(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(static_cast<IComposerClient::DisplayType>( - HWC_DISPLAY_TYPE)), - Return(Error::NONE))); EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)).WillOnce(Return(Error::NONE)); EXPECT_CALL(*test->mComposer, getDisplayConfigs(HWC_DISPLAY_ID, _)) .WillOnce(DoAll(SetArgPointee<1>(std::vector<unsigned>{HWC_ACTIVE_CONFIG_ID}), @@ -598,7 +594,7 @@ struct WideColorSupportNotConfiguredVariant { static void injectConfigChange(DisplayTransactionTest* test) { test->mFlinger.mutableHasWideColorDisplay() = false; test->mFlinger.mutableUseColorManagement() = false; - test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED; + test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; } static void setupComposerCallExpectations(DisplayTransactionTest* test) { @@ -618,7 +614,7 @@ struct WideColorP3ColorimetricSupportedVariant { static void injectConfigChange(DisplayTransactionTest* test) { test->mFlinger.mutableUseColorManagement() = true; test->mFlinger.mutableHasWideColorDisplay() = true; - test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::UNMANAGED; + test->mFlinger.mutableDisplayColorSetting() = DisplayColorSetting::kUnmanaged; } static void setupComposerCallExpectations(DisplayTransactionTest* test) { @@ -1131,8 +1127,8 @@ TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) { // Preconditions // vsync is enabled and available - mScheduler->mutablePrimaryHWVsyncEnabled() = true; - mScheduler->mutableHWVsyncAvailable() = true; + mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = true; + mFlinger.scheduler()->mutableHWVsyncAvailable() = true; // A display exists auto existing = Case::Display::makeFakeExistingDisplayInjector(this); @@ -1156,8 +1152,8 @@ TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) { // Postconditions // vsyncs should be off and not available. - EXPECT_FALSE(mScheduler->mutablePrimaryHWVsyncEnabled()); - EXPECT_FALSE(mScheduler->mutableHWVsyncAvailable()); + EXPECT_FALSE(mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled()); + EXPECT_FALSE(mFlinger.scheduler()->mutableHWVsyncAvailable()); // The display should have been removed from the display map. EXPECT_FALSE(hasDisplayDevice(existing.token())); @@ -3008,7 +3004,7 @@ struct DisplayPowerCase { } static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) { - test->mScheduler->mutablePrimaryHWVsyncEnabled() = enabled; + test->mFlinger.scheduler()->mutablePrimaryHWVsyncEnabled() = enabled; } static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) { diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index dbd9b84039..2662f52581 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -42,6 +42,8 @@ constexpr PhysicalDisplayId DISPLAY_ID_64BIT = 0xabcd12349876fedcULL; class MockVSyncSource : public VSyncSource { public: + const char* getName() const override { return "test"; } + MOCK_METHOD1(setVSyncEnabled, void(bool)); MOCK_METHOD1(setCallback, void(VSyncSource::Callback*)); MOCK_METHOD1(setPhaseOffset, void(nsecs_t)); @@ -54,8 +56,7 @@ class EventThreadTest : public testing::Test { protected: class MockEventThreadConnection : public EventThreadConnection { public: - MockEventThreadConnection(android::impl::EventThread* eventThread, - ResyncCallback&& resyncCallback, + MockEventThreadConnection(impl::EventThread* eventThread, ResyncCallback&& resyncCallback, ISurfaceComposer::ConfigChanged configChanged) : EventThreadConnection(eventThread, std::move(resyncCallback), configChanged) {} MOCK_METHOD1(postEvent, status_t(const DisplayEventReceiver::Event& event)); @@ -67,7 +68,7 @@ protected: EventThreadTest(); ~EventThreadTest() override; - void createThread(); + void createThread(std::unique_ptr<VSyncSource>); sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder, ISurfaceComposer::ConfigChanged configChanged); @@ -91,9 +92,9 @@ protected: AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder; ConnectionEventRecorder mConnectionEventCallRecorder{0}; - MockVSyncSource mVSyncSource; + MockVSyncSource* mVSyncSource; VSyncSource::Callback* mCallback = nullptr; - std::unique_ptr<android::impl::EventThread> mThread; + std::unique_ptr<impl::EventThread> mThread; sp<MockEventThreadConnection> mConnection; }; @@ -102,16 +103,19 @@ EventThreadTest::EventThreadTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - EXPECT_CALL(mVSyncSource, setVSyncEnabled(_)) + auto vsyncSource = std::make_unique<MockVSyncSource>(); + mVSyncSource = vsyncSource.get(); + + EXPECT_CALL(*mVSyncSource, setVSyncEnabled(_)) .WillRepeatedly(Invoke(mVSyncSetEnabledCallRecorder.getInvocable())); - EXPECT_CALL(mVSyncSource, setCallback(_)) + EXPECT_CALL(*mVSyncSource, setCallback(_)) .WillRepeatedly(Invoke(mVSyncSetCallbackCallRecorder.getInvocable())); - EXPECT_CALL(mVSyncSource, setPhaseOffset(_)) + EXPECT_CALL(*mVSyncSource, setPhaseOffset(_)) .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable())); - createThread(); + createThread(std::move(vsyncSource)); mConnection = createConnection(mConnectionEventCallRecorder, ISurfaceComposer::eConfigChangedDispatch); @@ -129,11 +133,9 @@ EventThreadTest::~EventThreadTest() { EXPECT_TRUE(!mVSyncSetCallbackCallRecorder.waitForUnexpectedCall().has_value()); } -void EventThreadTest::createThread() { - mThread = - std::make_unique<android::impl::EventThread>(&mVSyncSource, - mInterceptVSyncCallRecorder.getInvocable(), - "unit-test-event-thread"); +void EventThreadTest::createThread(std::unique_ptr<VSyncSource> source) { + mThread = std::make_unique<impl::EventThread>(std::move(source), + mInterceptVSyncCallRecorder.getInvocable()); // EventThread should register itself as VSyncSource callback. mCallback = expectVSyncSetCallbackCallReceived(); diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h index 1d7501102e..66c7f6b81f 100644 --- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h +++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h @@ -20,42 +20,22 @@ #include "Scheduler/PhaseOffsets.h" -namespace android { -namespace scheduler { +namespace android::scheduler { -using RefreshRateType = RefreshRateConfigs::RefreshRateType; +struct FakePhaseOffsets : PhaseOffsets { + static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0; -class FakePhaseOffsets : public android::scheduler::PhaseOffsets { - nsecs_t FAKE_PHASE_OFFSET_NS = 0; + Offsets getOffsetsForRefreshRate(RefreshRateType) const override { return getCurrentOffsets(); } -public: - FakePhaseOffsets() = default; - ~FakePhaseOffsets() = default; - - nsecs_t getCurrentAppOffset() override { return FAKE_PHASE_OFFSET_NS; } - nsecs_t getCurrentSfOffset() override { return FAKE_PHASE_OFFSET_NS; } - - PhaseOffsets::Offsets getOffsetsForRefreshRate( - RefreshRateType /*refreshRateType*/) const override { - return getCurrentOffsets(); - } - - // Returns early, early GL, and late offsets for Apps and SF. - PhaseOffsets::Offsets getCurrentOffsets() const override { - return Offsets{{RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, - {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, - {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}}; + Offsets getCurrentOffsets() const override { + return {{RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, + {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, + {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}, + FAKE_PHASE_OFFSET_NS}; } - // This function should be called when the device is switching between different - // refresh rates, to properly update the offsets. - void setRefreshRateType(RefreshRateType /*refreshRateType*/) override {} - - nsecs_t getOffsetThresholdForNextVsync() const override { return FAKE_PHASE_OFFSET_NS; } - - // Returns current offsets in human friendly format. - void dump(std::string& /*result*/) const override {} + void setRefreshRateType(RefreshRateType) override {} + void dump(std::string&) const override {} }; -} // namespace scheduler -} // namespace android +} // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp new file mode 100644 index 0000000000..b5af591e25 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp @@ -0,0 +1,396 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include <FrameTracer/FrameTracer.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <log/log.h> +#include <perfetto/trace/trace.pb.h> + +#include "libsurfaceflinger_unittest_main.h" + +using namespace google::protobuf; + +namespace android { +namespace { + +class FrameTracerTest : public testing::Test { +public: + FrameTracerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + + // Need to initialize tracing in process for testing, and only once per test suite. + static bool wasInitialized = false; + if (!wasInitialized) { + perfetto::TracingInitArgs args; + args.backends = perfetto::kInProcessBackend; + perfetto::Tracing::Initialize(args); + wasInitialized = true; + } + } + + ~FrameTracerTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + void SetUp() override { + mFrameTracer = std::make_unique<FrameTracer>(); + mFrameTracer->registerDataSource(); + } + + void TearDown() override { mFrameTracer.reset(); } + + // Each tracing session can be used for a single block of Start -> Stop. + static std::unique_ptr<perfetto::TracingSession> getTracingSessionForTest() { + perfetto::TraceConfig cfg; + cfg.set_duration_ms(500); + cfg.add_buffers()->set_size_kb(1024); + auto* ds_cfg = cfg.add_data_sources()->mutable_config(); + ds_cfg->set_name(FrameTracer::kFrameTracerDataSource); + + auto tracingSession = perfetto::Tracing::NewTrace(perfetto::kInProcessBackend); + tracingSession->Setup(cfg); + return tracingSession; + } + + std::unique_ptr<FrameTracer> mFrameTracer; + FenceToFenceTimeMap fenceFactory; +}; + +TEST_F(FrameTracerTest, traceNewLayerStartsTrackingLayerWhenTracing) { + EXPECT_EQ(mFrameTracer->miniDump(), + "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n"); + + const std::string layerName = "co.layername#0"; + const int32_t layerID = 5; + mFrameTracer->traceNewLayer(layerID, layerName); + + EXPECT_EQ(mFrameTracer->miniDump(), + "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n"); + + auto tracingSession = getTracingSessionForTest(); + tracingSession->StartBlocking(); + EXPECT_EQ(mFrameTracer->miniDump(), + "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n"); + mFrameTracer->traceNewLayer(layerID, layerName); + EXPECT_EQ(mFrameTracer->miniDump(), + "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n"); + tracingSession->StopBlocking(); +} + +TEST_F(FrameTracerTest, onDestroyRemovesTheTrackedLayer) { + EXPECT_EQ(mFrameTracer->miniDump(), + "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n"); + + const std::string layerName = "co.layername#0"; + const int32_t layerID = 5; + const int32_t secondLayerID = 6; + + auto tracingSession = getTracingSessionForTest(); + tracingSession->StartBlocking(); + mFrameTracer->traceNewLayer(layerID, layerName); + mFrameTracer->traceNewLayer(secondLayerID, layerName); + EXPECT_EQ(mFrameTracer->miniDump(), + "FrameTracer miniDump:\nNumber of layers currently being traced is 2\n"); + tracingSession->StopBlocking(); + + mFrameTracer->onDestroy(layerID); + EXPECT_EQ(mFrameTracer->miniDump(), + "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n"); + mFrameTracer->onDestroy(layerID); + EXPECT_EQ(mFrameTracer->miniDump(), + "FrameTracer miniDump:\nNumber of layers currently being traced is 1\n"); + mFrameTracer->onDestroy(secondLayerID); + EXPECT_EQ(mFrameTracer->miniDump(), + "FrameTracer miniDump:\nNumber of layers currently being traced is 0\n"); +} + +TEST_F(FrameTracerTest, canTraceAfterAddingLayer) { + const std::string layerName = "co.layername#0"; + const int32_t layerID = 1; + const uint32_t bufferID = 2; + const uint64_t frameNumber = 3; + const nsecs_t timestamp = 4; + const nsecs_t duration = 5; + const auto type = FrameTracer::FrameEvent::POST; + + { + auto tracingSession = getTracingSessionForTest(); + + tracingSession->StartBlocking(); + // Clean up irrelevant traces. + tracingSession->ReadTraceBlocking(); + + mFrameTracer->traceTimestamp(layerID, bufferID, frameNumber, timestamp, type, duration); + // Create second trace packet to finalize the previous one. + mFrameTracer->traceTimestamp(layerID, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); + tracingSession->StopBlocking(); + + std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); + EXPECT_EQ(raw_trace.size(), 0); + } + + { + auto tracingSession = getTracingSessionForTest(); + + tracingSession->StartBlocking(); + // Clean up irrelevant traces. + tracingSession->ReadTraceBlocking(); + + mFrameTracer->traceNewLayer(layerID, layerName); + mFrameTracer->traceTimestamp(layerID, bufferID, frameNumber, timestamp, type, duration); + // Create second trace packet to finalize the previous one. + mFrameTracer->traceTimestamp(layerID, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); + tracingSession->StopBlocking(); + + std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); + ASSERT_GT(raw_trace.size(), 0); + + perfetto::protos::Trace trace; + ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()))); + ASSERT_FALSE(trace.packet().empty()); + EXPECT_EQ(trace.packet().size(), 1); + + const auto& packet = trace.packet().Get(0); + ASSERT_TRUE(packet.has_timestamp()); + EXPECT_EQ(packet.timestamp(), timestamp); + ASSERT_TRUE(packet.has_graphics_frame_event()); + const auto& frame_event = packet.graphics_frame_event(); + ASSERT_TRUE(frame_event.has_buffer_event()); + const auto& buffer_event = frame_event.buffer_event(); + ASSERT_TRUE(buffer_event.has_buffer_id()); + EXPECT_EQ(buffer_event.buffer_id(), bufferID); + ASSERT_TRUE(buffer_event.has_frame_number()); + EXPECT_EQ(buffer_event.frame_number(), frameNumber); + ASSERT_TRUE(buffer_event.has_type()); + EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type)); + ASSERT_TRUE(buffer_event.has_duration_ns()); + EXPECT_EQ(buffer_event.duration_ns(), duration); + } +} + +TEST_F(FrameTracerTest, traceFenceTriggersOnNextTraceAfterFenceFired) { + const std::string layerName = "co.layername#0"; + const int32_t layerID = 5; + const uint32_t bufferID = 4; + const uint64_t frameNumber = 3; + const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE; + + { + auto fenceTime = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + fenceFactory.signalAllForTest(Fence::NO_FENCE, Fence::SIGNAL_TIME_PENDING); + auto tracingSession = getTracingSessionForTest(); + tracingSession->StartBlocking(); + // Clean up irrelevant traces. + tracingSession->ReadTraceBlocking(); + // Trace. + mFrameTracer->traceNewLayer(layerID, layerName); + mFrameTracer->traceFence(layerID, bufferID, frameNumber, fenceTime, type); + // Create extra trace packet to (hopefully not) trigger and finalize the fence packet. + mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); + tracingSession->StopBlocking(); + std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); + EXPECT_EQ(raw_trace.size(), 0); + } + + { + auto fenceTime = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + auto tracingSession = getTracingSessionForTest(); + tracingSession->StartBlocking(); + // Clean up irrelevant traces. + tracingSession->ReadTraceBlocking(); + mFrameTracer->traceNewLayer(layerID, layerName); + mFrameTracer->traceFence(layerID, bufferID, frameNumber, fenceTime, type); + const nsecs_t timestamp = systemTime(); + fenceFactory.signalAllForTest(Fence::NO_FENCE, timestamp); + // Create extra trace packet to trigger and finalize fence trace packets. + mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); + tracingSession->StopBlocking(); + + std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); + ASSERT_GT(raw_trace.size(), 0); + + perfetto::protos::Trace trace; + ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()))); + ASSERT_FALSE(trace.packet().empty()); + EXPECT_EQ(trace.packet().size(), 2); // Two packets because of the extra trace made above. + + const auto& packet = trace.packet().Get(1); + ASSERT_TRUE(packet.has_timestamp()); + EXPECT_EQ(packet.timestamp(), timestamp); + ASSERT_TRUE(packet.has_graphics_frame_event()); + const auto& frame_event = packet.graphics_frame_event(); + ASSERT_TRUE(frame_event.has_buffer_event()); + const auto& buffer_event = frame_event.buffer_event(); + ASSERT_TRUE(buffer_event.has_buffer_id()); + EXPECT_EQ(buffer_event.buffer_id(), bufferID); + ASSERT_TRUE(buffer_event.has_frame_number()); + EXPECT_EQ(buffer_event.frame_number(), frameNumber); + ASSERT_TRUE(buffer_event.has_type()); + EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type)); + EXPECT_FALSE(buffer_event.has_duration_ns()); + } +} + +TEST_F(FrameTracerTest, traceFenceWithStartTimeAfterSignalTime_ShouldHaveNoDuration) { + const std::string layerName = "co.layername#0"; + const int32_t layerID = 5; + const uint32_t bufferID = 4; + const uint64_t frameNumber = 3; + const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE; + + auto tracingSession = getTracingSessionForTest(); + + tracingSession->StartBlocking(); + // Clean up irrelevant traces. + tracingSession->ReadTraceBlocking(); + mFrameTracer->traceNewLayer(layerID, layerName); + + // traceFence called after fence signalled. + const nsecs_t signalTime1 = systemTime(); + const nsecs_t startTime1 = signalTime1 + 100000; + auto fence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime1); + mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence1, type, startTime1); + + // traceFence called before fence signalled. + const nsecs_t signalTime2 = systemTime(); + const nsecs_t startTime2 = signalTime2 + 100000; + auto fence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence2, type, startTime2); + fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime2); + + // Create extra trace packet to trigger and finalize fence trace packets. + mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); + tracingSession->StopBlocking(); + + std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); + ASSERT_GT(raw_trace.size(), 0); + + perfetto::protos::Trace trace; + ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()))); + ASSERT_FALSE(trace.packet().empty()); + EXPECT_EQ(trace.packet().size(), 2); + + const auto& packet1 = trace.packet().Get(0); + ASSERT_TRUE(packet1.has_timestamp()); + EXPECT_EQ(packet1.timestamp(), signalTime1); + ASSERT_TRUE(packet1.has_graphics_frame_event()); + ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event()); + ASSERT_FALSE(packet1.graphics_frame_event().buffer_event().has_duration_ns()); + + const auto& packet2 = trace.packet().Get(1); + ASSERT_TRUE(packet2.has_timestamp()); + EXPECT_EQ(packet2.timestamp(), signalTime2); + ASSERT_TRUE(packet2.has_graphics_frame_event()); + ASSERT_TRUE(packet2.graphics_frame_event().has_buffer_event()); + ASSERT_FALSE(packet2.graphics_frame_event().buffer_event().has_duration_ns()); +} + +TEST_F(FrameTracerTest, traceFenceOlderThanDeadline_ShouldBeIgnored) { + const std::string layerName = "co.layername#0"; + const int32_t layerID = 5; + const uint32_t bufferID = 4; + const uint64_t frameNumber = 3; + const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE; + const nsecs_t signalTime = systemTime() - FrameTracer::kFenceSignallingDeadline; + + auto tracingSession = getTracingSessionForTest(); + auto fence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + + tracingSession->StartBlocking(); + // Clean up irrelevant traces. + tracingSession->ReadTraceBlocking(); + mFrameTracer->traceNewLayer(layerID, layerName); + mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence, type); + fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime); + // Create extra trace packet to trigger and finalize any previous fence packets. + mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); + tracingSession->StopBlocking(); + + std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); + EXPECT_EQ(raw_trace.size(), 0); +} + +TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) { + const std::string layerName = "co.layername#0"; + const int32_t layerID = 5; + const uint32_t bufferID = 4; + const uint64_t frameNumber = 3; + const auto type = FrameTracer::FrameEvent::ACQUIRE_FENCE; + const nsecs_t duration = 1234; + + auto tracingSession = getTracingSessionForTest(); + + tracingSession->StartBlocking(); + // Clean up irrelevant traces. + tracingSession->ReadTraceBlocking(); + mFrameTracer->traceNewLayer(layerID, layerName); + + // traceFence called after fence signalled. + const nsecs_t signalTime1 = systemTime(); + const nsecs_t startTime1 = signalTime1 - duration; + auto fence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime1); + mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence1, type, startTime1); + + // traceFence called before fence signalled. + const nsecs_t signalTime2 = systemTime(); + const nsecs_t startTime2 = signalTime2 - duration; + auto fence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); + mFrameTracer->traceFence(layerID, bufferID, frameNumber, fence2, type, startTime2); + fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime2); + + // Create extra trace packet to trigger and finalize fence trace packets. + mFrameTracer->traceTimestamp(layerID, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); + tracingSession->StopBlocking(); + + std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); + ASSERT_GT(raw_trace.size(), 0); + + perfetto::protos::Trace trace; + ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()))); + ASSERT_FALSE(trace.packet().empty()); + EXPECT_EQ(trace.packet().size(), 2); + + const auto& packet1 = trace.packet().Get(0); + ASSERT_TRUE(packet1.has_timestamp()); + EXPECT_EQ(packet1.timestamp(), startTime1); + ASSERT_TRUE(packet1.has_graphics_frame_event()); + ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event()); + ASSERT_TRUE(packet1.graphics_frame_event().buffer_event().has_duration_ns()); + const auto& buffer_event1 = packet1.graphics_frame_event().buffer_event(); + EXPECT_EQ(buffer_event1.duration_ns(), duration); + + const auto& packet2 = trace.packet().Get(1); + ASSERT_TRUE(packet2.has_timestamp()); + EXPECT_EQ(packet2.timestamp(), startTime2); + ASSERT_TRUE(packet2.has_graphics_frame_event()); + ASSERT_TRUE(packet2.graphics_frame_event().has_buffer_event()); + ASSERT_TRUE(packet2.graphics_frame_event().buffer_event().has_duration_ns()); + const auto& buffer_event2 = packet2.graphics_frame_event().buffer_event(); + EXPECT_EQ(buffer_event2.duration_ns(), duration); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp index eff22b6640..0208728026 100644 --- a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp +++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp @@ -21,17 +21,17 @@ #include <utils/Log.h> #include "AsyncCallRecorder.h" -#include "Scheduler/IdleTimer.h" +#include "Scheduler/OneShotTimer.h" using namespace std::chrono_literals; namespace android { namespace scheduler { -class IdleTimerTest : public testing::Test { +class OneShotTimerTest : public testing::Test { protected: - IdleTimerTest() = default; - ~IdleTimerTest() override = default; + OneShotTimerTest() = default; + ~OneShotTimerTest() override = default; // This timeout should be used when a 3ms callback is expected. // While the tests typically request a callback after 3ms, the scheduler @@ -46,7 +46,7 @@ protected: AsyncCallRecorder<void (*)()> mResetTimerCallback; AsyncCallRecorder<void (*)()> mExpiredTimerCallback; - std::unique_ptr<IdleTimer> mIdleTimer; + std::unique_ptr<OneShotTimer> mIdleTimer; void clearPendingCallbacks() { while (mExpiredTimerCallback.waitForCall(0us).has_value()) { @@ -55,13 +55,14 @@ protected: }; namespace { -TEST_F(IdleTimerTest, createAndDestroyTest) { - mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, [] {}, [] {}); +TEST_F(OneShotTimerTest, createAndDestroyTest) { + mIdleTimer = std::make_unique<scheduler::OneShotTimer>( + 3ms, [] {}, [] {}); } -TEST_F(IdleTimerTest, startStopTest) { - mIdleTimer = std::make_unique<scheduler::IdleTimer>(30ms, mResetTimerCallback.getInvocable(), - mExpiredTimerCallback.getInvocable()); +TEST_F(OneShotTimerTest, startStopTest) { + mIdleTimer = std::make_unique<scheduler::OneShotTimer>(30ms, mResetTimerCallback.getInvocable(), + mExpiredTimerCallback.getInvocable()); auto startTime = std::chrono::steady_clock::now(); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); @@ -70,7 +71,7 @@ TEST_F(IdleTimerTest, startStopTest) { bool callbackCalled = mExpiredTimerCallback.waitForCall(25ms).has_value(); // Under ideal conditions there should be no event. But occasionally // it is possible that the wait just prior takes more than 30ms, and - // a callback is observed. We check the elapsed time since before the IdleTimer + // a callback is observed. We check the elapsed time since before the OneShotTimer // thread was started as a sanity check to not have a flakey test. EXPECT_FALSE(callbackCalled && std::chrono::steady_clock::now() - startTime < 30ms); @@ -79,9 +80,9 @@ TEST_F(IdleTimerTest, startStopTest) { mIdleTimer->stop(); } -TEST_F(IdleTimerTest, resetTest) { - mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(), - mExpiredTimerCallback.getInvocable()); +TEST_F(OneShotTimerTest, resetTest) { + mIdleTimer = std::make_unique<scheduler::OneShotTimer>(20ms, mResetTimerCallback.getInvocable(), + mExpiredTimerCallback.getInvocable()); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); // Observe any event that happens in about 25ms. We don't care if one was @@ -104,9 +105,9 @@ TEST_F(IdleTimerTest, resetTest) { EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value()); } -TEST_F(IdleTimerTest, resetBackToBackTest) { - mIdleTimer = std::make_unique<scheduler::IdleTimer>(20ms, mResetTimerCallback.getInvocable(), - mExpiredTimerCallback.getInvocable()); +TEST_F(OneShotTimerTest, resetBackToBackTest) { + mIdleTimer = std::make_unique<scheduler::OneShotTimer>(20ms, mResetTimerCallback.getInvocable(), + mExpiredTimerCallback.getInvocable()); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); @@ -135,9 +136,9 @@ TEST_F(IdleTimerTest, resetBackToBackTest) { EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value()); } -TEST_F(IdleTimerTest, startNotCalledTest) { - mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(), - mExpiredTimerCallback.getInvocable()); +TEST_F(OneShotTimerTest, startNotCalledTest) { + mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(), + mExpiredTimerCallback.getInvocable()); // The start hasn't happened, so the callback does not happen. EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value()); EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value()); @@ -147,9 +148,9 @@ TEST_F(IdleTimerTest, startNotCalledTest) { EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value()); } -TEST_F(IdleTimerTest, idleTimerIdlesTest) { - mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(), - mExpiredTimerCallback.getInvocable()); +TEST_F(OneShotTimerTest, idleTimerIdlesTest) { + mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(), + mExpiredTimerCallback.getInvocable()); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); @@ -167,18 +168,18 @@ TEST_F(IdleTimerTest, idleTimerIdlesTest) { EXPECT_FALSE(mResetTimerCallback.waitForCall(0ms).has_value()); } -TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) { - mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(), - mExpiredTimerCallback.getInvocable()); +TEST_F(OneShotTimerTest, timeoutCallbackExecutionTest) { + mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(), + mExpiredTimerCallback.getInvocable()); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value()); mIdleTimer->stop(); } -TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) { - mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(), - mExpiredTimerCallback.getInvocable()); +TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) { + mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(), + mExpiredTimerCallback.getInvocable()); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value()); @@ -190,9 +191,9 @@ TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) { EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value()); } -TEST_F(IdleTimerTest, noCallbacksAfterStopTest) { - mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(), - mExpiredTimerCallback.getInvocable()); +TEST_F(OneShotTimerTest, noCallbacksAfterStopTest) { + mIdleTimer = std::make_unique<scheduler::OneShotTimer>(3ms, mResetTimerCallback.getInvocable(), + mExpiredTimerCallback.getInvocable()); mIdleTimer->start(); EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 5067fe890b..f315a8a86c 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -23,7 +23,6 @@ #include "DisplayHardware/HWC2.h" #include "Scheduler/RefreshRateConfigs.h" -#include "mock/DisplayHardware/MockDisplay.h" using namespace std::chrono_literals; using testing::_; @@ -50,9 +49,8 @@ protected: ASSERT_EQ(left.configId, right.configId); ASSERT_EQ(left.name, right.name); ASSERT_EQ(left.fps, right.fps); + ASSERT_EQ(left.vsyncPeriod, right.vsyncPeriod); } - - RefreshRateConfigs mConfigs; }; RefreshRateConfigsTest::RefreshRateConfigsTest() { @@ -71,101 +69,39 @@ namespace { /* ------------------------------------------------------------------------ * Test cases */ -TEST_F(RefreshRateConfigsTest, zeroDeviceConfigs_storesPowerSavingConfig) { - std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs; - mConfigs.populate(displayConfigs); - - // We always store a configuration for screen off. - const auto& rates = mConfigs.getRefreshRates(); - ASSERT_EQ(1, rates.size()); - const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING); - ASSERT_NE(rates.end(), powerSavingRate); - ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE)); - ASSERT_EQ(rates.end(), rates.find(RefreshRateType::DEFAULT)); - - RefreshRate expectedConfig = - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID}; - assertRatesEqual(expectedConfig, *powerSavingRate->second); - - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - assertRatesEqual(expectedConfig, *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); - ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - - // Sanity check that getRefreshRate() does not modify the underlying configs. - ASSERT_EQ(1, mConfigs.getRefreshRates().size()); +TEST_F(RefreshRateConfigsTest, oneDeviceConfig_isRejected) { + std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs, + /*currentConfig=*/0); + ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported()); } -TEST_F(RefreshRateConfigsTest, oneDeviceConfig_storesDefaultConfig) { - auto display = new Hwc2::mock::Display(); - std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs; - auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60); - config60.setVsyncPeriod(VSYNC_60); - displayConfigs.push_back(config60.build()); - mConfigs.populate(displayConfigs); +TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) { + std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}, + {HWC2_CONFIG_ID_90, VSYNC_90}}; + auto refreshRateConfigs = + std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs, + /*currentConfig=*/0); - const auto& rates = mConfigs.getRefreshRates(); + ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported()); + const auto& rates = refreshRateConfigs->getRefreshRateMap(); ASSERT_EQ(2, rates.size()); - const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING); - const auto& defaultRate = rates.find(RefreshRateType::DEFAULT); - ASSERT_NE(rates.end(), powerSavingRate); - ASSERT_NE(rates.end(), defaultRate); - ASSERT_EQ(rates.end(), rates.find(RefreshRateType::PERFORMANCE)); - - RefreshRate expectedPowerSavingConfig = - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID}; - assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second); - RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60}; - assertRatesEqual(expectedDefaultConfig, *defaultRate->second); - - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - assertRatesEqual(expectedPowerSavingConfig, - *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - ASSERT_FALSE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); - - // Sanity check that getRefreshRate() does not modify the underlying configs. - ASSERT_EQ(2, mConfigs.getRefreshRates().size()); -} - -TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesPerformanceConfig) { - auto display = new Hwc2::mock::Display(); - std::vector<std::shared_ptr<const HWC2::Display::Config>> displayConfigs; - auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60); - config60.setVsyncPeriod(VSYNC_60); - displayConfigs.push_back(config60.build()); - auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90); - config90.setVsyncPeriod(VSYNC_90); - displayConfigs.push_back(config90.build()); - mConfigs.populate(displayConfigs); - - const auto& rates = mConfigs.getRefreshRates(); - ASSERT_EQ(3, rates.size()); - const auto& powerSavingRate = rates.find(RefreshRateType::POWER_SAVING); const auto& defaultRate = rates.find(RefreshRateType::DEFAULT); const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE); - ASSERT_NE(rates.end(), powerSavingRate); ASSERT_NE(rates.end(), defaultRate); ASSERT_NE(rates.end(), performanceRate); - RefreshRate expectedPowerSavingConfig = - RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0, HWC2_SCREEN_OFF_CONFIG_ID}; - assertRatesEqual(expectedPowerSavingConfig, *powerSavingRate->second); - RefreshRate expectedDefaultConfig = RefreshRate{CONFIG_ID_60, "60fps", 60, HWC2_CONFIG_ID_60}; - assertRatesEqual(expectedDefaultConfig, *defaultRate->second); - RefreshRate expectedPerformanceConfig = - RefreshRate{CONFIG_ID_90, "90fps", 90, HWC2_CONFIG_ID_90}; - assertRatesEqual(expectedPerformanceConfig, *performanceRate->second); - - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - assertRatesEqual(expectedPowerSavingConfig, - *mConfigs.getRefreshRate(RefreshRateType::POWER_SAVING)); - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - assertRatesEqual(expectedDefaultConfig, *mConfigs.getRefreshRate(RefreshRateType::DEFAULT)); - ASSERT_TRUE(mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); + RefreshRate expectedDefaultConfig = {CONFIG_ID_60, "60fps", 60, VSYNC_60, HWC2_CONFIG_ID_60}; + assertRatesEqual(expectedDefaultConfig, defaultRate->second); + RefreshRate expectedPerformanceConfig = {CONFIG_ID_90, "90fps", 90, VSYNC_90, + HWC2_CONFIG_ID_90}; + assertRatesEqual(expectedPerformanceConfig, performanceRate->second); + + assertRatesEqual(expectedDefaultConfig, + refreshRateConfigs->getRefreshRateFromType(RefreshRateType::DEFAULT)); assertRatesEqual(expectedPerformanceConfig, - *mConfigs.getRefreshRate(RefreshRateType::PERFORMANCE)); + refreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE)); } } // namespace } // namespace scheduler diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp index 411ec61770..cec0b32a6b 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp @@ -22,7 +22,6 @@ #include <thread> #include "Scheduler/RefreshRateStats.h" -#include "mock/DisplayHardware/MockDisplay.h" #include "mock/MockTimeStats.h" using namespace std::chrono_literals; @@ -42,9 +41,18 @@ protected: RefreshRateStatsTest(); ~RefreshRateStatsTest(); + void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) { + mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>( + /*refreshRateSwitching=*/true, configs, /*currentConfig=*/0); + mRefreshRateStats = + std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats, + /*currentConfig=*/0, + /*currentPowerMode=*/HWC_POWER_MODE_OFF); + } + mock::TimeStats mTimeStats; - RefreshRateConfigs mRefreshRateConfigs; - RefreshRateStats mRefreshRateStats{mRefreshRateConfigs, mTimeStats}; + std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs; + std::unique_ptr<RefreshRateStats> mRefreshRateStats; }; RefreshRateStatsTest::RefreshRateStatsTest() { @@ -63,63 +71,46 @@ namespace { /* ------------------------------------------------------------------------ * Test cases */ -TEST_F(RefreshRateStatsTest, canCreateAndDestroyTest) { - std::vector<std::shared_ptr<const HWC2::Display::Config>> configs; - mRefreshRateConfigs.populate(configs); - - // There is one default config, so the refresh rates should have one item. - EXPECT_EQ(1, mRefreshRateStats.getTotalTimes().size()); -} - TEST_F(RefreshRateStatsTest, oneConfigTest) { - auto display = new Hwc2::mock::Display(); - - auto config = HWC2::Display::Config::Builder(*display, CONFIG_ID_90); - config.setVsyncPeriod(VSYNC_90); - std::vector<std::shared_ptr<const HWC2::Display::Config>> configs; - configs.push_back(config.build()); - - mRefreshRateConfigs.populate(configs); + init({{CONFIG_ID_90, VSYNC_90}}); EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1)); EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1)); - std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes(); - EXPECT_EQ(2, times.size()); + std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes(); + ASSERT_EQ(1, times.size()); EXPECT_NE(0u, times.count("ScreenOff")); - EXPECT_EQ(1u, times.count("90fps")); - EXPECT_EQ(0, times["90fps"]); // Setting up tests on mobile harness can be flaky with time passing, so testing for // exact time changes can result in flaxy numbers. To avoid that remember old // numbers to make sure the correct values are increasing in the next test. int screenOff = times["ScreenOff"]; - int ninety = times["90fps"]; // Screen is off by default. std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(0, times["90fps"]); + EXPECT_EQ(0u, times.count("90fps")); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - EXPECT_LT(ninety, times["90fps"]); + ASSERT_EQ(1u, times.count("90fps")); + EXPECT_LT(0, times["90fps"]); - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE); - ninety = mRefreshRateStats.getTotalTimes()["90fps"]; + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE); + int ninety = mRefreshRateStats->getTotalTimes()["90fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config // does not update refresh rates that come from the config. EXPECT_LT(screenOff, times["ScreenOff"]); @@ -127,93 +118,75 @@ TEST_F(RefreshRateStatsTest, oneConfigTest) { } TEST_F(RefreshRateStatsTest, twoConfigsTest) { - auto display = new Hwc2::mock::Display(); - - auto config90 = HWC2::Display::Config::Builder(*display, CONFIG_ID_90); - config90.setVsyncPeriod(VSYNC_90); - std::vector<std::shared_ptr<const HWC2::Display::Config>> configs; - configs.push_back(config90.build()); - - auto config60 = HWC2::Display::Config::Builder(*display, CONFIG_ID_60); - config60.setVsyncPeriod(VSYNC_60); - configs.push_back(config60.build()); - - mRefreshRateConfigs.populate(configs); + init({{CONFIG_ID_90, VSYNC_90}, {CONFIG_ID_60, VSYNC_60}}); EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1)); EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1)); EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1)); - std::unordered_map<std::string, int64_t> times = mRefreshRateStats.getTotalTimes(); - EXPECT_EQ(3, times.size()); + std::unordered_map<std::string, int64_t> times = mRefreshRateStats->getTotalTimes(); + ASSERT_EQ(1, times.size()); EXPECT_NE(0u, times.count("ScreenOff")); - EXPECT_EQ(1u, times.count("60fps")); - EXPECT_EQ(0, times["60fps"]); - EXPECT_EQ(1u, times.count("90fps")); - EXPECT_EQ(0, times["90fps"]); // Setting up tests on mobile harness can be flaky with time passing, so testing for // exact time changes can result in flaxy numbers. To avoid that remember old // numbers to make sure the correct values are increasing in the next test. int screenOff = times["ScreenOff"]; - int sixty = times["60fps"]; - int ninety = times["90fps"]; // Screen is off by default. std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); - EXPECT_EQ(sixty, times["60fps"]); - EXPECT_EQ(ninety, times["90fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_NORMAL); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); - EXPECT_EQ(sixty, times["60fps"]); - EXPECT_LT(ninety, times["90fps"]); + ASSERT_EQ(1u, times.count("90fps")); + EXPECT_LT(0, times["90fps"]); // When power mode is normal, time for configs updates. - mRefreshRateStats.setConfigMode(CONFIG_ID_60); - ninety = mRefreshRateStats.getTotalTimes()["90fps"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_60); + int ninety = mRefreshRateStats->getTotalTimes()["90fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); - EXPECT_LT(sixty, times["60fps"]); + ASSERT_EQ(1u, times.count("60fps")); + EXPECT_LT(0, times["60fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - sixty = mRefreshRateStats.getTotalTimes()["60fps"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + int sixty = mRefreshRateStats->getTotalTimes()["60fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); EXPECT_LT(ninety, times["90fps"]); EXPECT_EQ(sixty, times["60fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_60); - ninety = mRefreshRateStats.getTotalTimes()["90fps"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_60); + ninety = mRefreshRateStats->getTotalTimes()["90fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_EQ(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); EXPECT_LT(sixty, times["60fps"]); // Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config // does not update refresh rates that come from the config. - mRefreshRateStats.setPowerMode(HWC_POWER_MODE_DOZE); - mRefreshRateStats.setConfigMode(CONFIG_ID_90); - sixty = mRefreshRateStats.getTotalTimes()["60fps"]; + mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE); + mRefreshRateStats->setConfigMode(CONFIG_ID_90); + sixty = mRefreshRateStats->getTotalTimes()["60fps"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); EXPECT_EQ(sixty, times["60fps"]); - mRefreshRateStats.setConfigMode(CONFIG_ID_60); - screenOff = mRefreshRateStats.getTotalTimes()["ScreenOff"]; + mRefreshRateStats->setConfigMode(CONFIG_ID_60); + screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"]; std::this_thread::sleep_for(std::chrono::milliseconds(2)); - times = mRefreshRateStats.getTotalTimes(); + times = mRefreshRateStats->getTotalTimes(); EXPECT_LT(screenOff, times["ScreenOff"]); EXPECT_EQ(ninety, times["90fps"]); EXPECT_EQ(sixty, times["60fps"]); diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 740115ea32..b4cc1e15e8 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -3,14 +3,14 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> - #include <log/log.h> #include <mutex> #include "Scheduler/EventControlThread.h" #include "Scheduler/EventThread.h" -#include "Scheduler/Scheduler.h" +#include "Scheduler/RefreshRateConfigs.h" +#include "TestableScheduler.h" #include "mock/MockEventThread.h" using testing::_; @@ -34,37 +34,14 @@ protected: MOCK_METHOD0(requestNextVsync, void()); }; - scheduler::RefreshRateConfigs mRefreshRateConfigs; - - /** - * This mock Scheduler class uses implementation of mock::EventThread but keeps everything else - * the same. - */ - class MockScheduler : public android::Scheduler { - public: - MockScheduler(const scheduler::RefreshRateConfigs& refreshRateConfigs, - std::unique_ptr<EventThread> eventThread) - : Scheduler([](bool) {}, refreshRateConfigs), mEventThread(std::move(eventThread)) {} - - std::unique_ptr<EventThread> makeEventThread( - const char* /* connectionName */, DispSync* /* dispSync */, - nsecs_t /* phaseOffsetNs */, nsecs_t /* offsetThresholdForNextVsync */, - impl::EventThread::InterceptVSyncsCallback /* interceptCallback */) override { - return std::move(mEventThread); - } - - MockScheduler() = default; - ~MockScheduler() override = default; - - std::unique_ptr<EventThread> mEventThread; - }; - SchedulerTest(); ~SchedulerTest() override; - sp<Scheduler::ConnectionHandle> mConnectionHandle; + std::unique_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs; + std::unique_ptr<TestableScheduler> mScheduler; + + Scheduler::ConnectionHandle mConnectionHandle; mock::EventThread* mEventThread; - std::unique_ptr<MockScheduler> mScheduler; sp<MockEventThreadConnection> mEventThreadConnection; }; @@ -73,9 +50,15 @@ SchedulerTest::SchedulerTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - std::unique_ptr<mock::EventThread> eventThread = std::make_unique<mock::EventThread>(); + std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}}; + mRefreshRateConfigs = + std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs, + /*currentConfig=*/0); + + mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs); + + auto eventThread = std::make_unique<mock::EventThread>(); mEventThread = eventThread.get(); - mScheduler = std::make_unique<MockScheduler>(mRefreshRateConfigs, std::move(eventThread)); EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0)); mEventThreadConnection = new MockEventThreadConnection(mEventThread); @@ -85,9 +68,8 @@ SchedulerTest::SchedulerTest() { EXPECT_CALL(*mEventThread, createEventConnection(_, _)) .WillRepeatedly(Return(mEventThreadConnection)); - mConnectionHandle = mScheduler->createConnection("appConnection", 16, 16, ResyncCallback(), - impl::EventThread::InterceptVSyncsCallback()); - EXPECT_TRUE(mConnectionHandle != nullptr); + mConnectionHandle = mScheduler->createConnection(std::move(eventThread)); + EXPECT_TRUE(mConnectionHandle); } SchedulerTest::~SchedulerTest() { @@ -101,78 +83,48 @@ namespace { * Test cases */ -TEST_F(SchedulerTest, testNullPtr) { - // Passing a null pointer for ConnectionHandle is a valid argument. The code doesn't throw any - // exceptions, just gracefully continues. - sp<IDisplayEventConnection> returnedValue; - ASSERT_NO_FATAL_FAILURE( - returnedValue = - mScheduler->createDisplayEventConnection(nullptr, ResyncCallback(), - ISurfaceComposer:: - eConfigChangedSuppress)); - EXPECT_TRUE(returnedValue == nullptr); - EXPECT_TRUE(mScheduler->getEventThread(nullptr) == nullptr); - EXPECT_TRUE(mScheduler->getEventConnection(nullptr) == nullptr); - ASSERT_NO_FATAL_FAILURE(mScheduler->hotplugReceived(nullptr, PHYSICAL_DISPLAY_ID, false)); - ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(nullptr)); - ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(nullptr)); - std::string testString; - ASSERT_NO_FATAL_FAILURE(mScheduler->dump(nullptr, testString)); - EXPECT_TRUE(testString == ""); - ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(nullptr, 10)); -} - TEST_F(SchedulerTest, invalidConnectionHandle) { - // Passing an invalid ConnectionHandle is a valid argument. The code doesn't throw any - // exceptions, just gracefully continues. - sp<Scheduler::ConnectionHandle> connectionHandle = new Scheduler::ConnectionHandle(20); + Scheduler::ConnectionHandle handle; - sp<IDisplayEventConnection> returnedValue; + sp<IDisplayEventConnection> connection; ASSERT_NO_FATAL_FAILURE( - returnedValue = - mScheduler->createDisplayEventConnection(connectionHandle, ResyncCallback(), - ISurfaceComposer:: - eConfigChangedSuppress)); - EXPECT_TRUE(returnedValue == nullptr); - EXPECT_TRUE(mScheduler->getEventThread(connectionHandle) == nullptr); - EXPECT_TRUE(mScheduler->getEventConnection(connectionHandle) == nullptr); + connection = mScheduler->createDisplayEventConnection(handle, + ISurfaceComposer:: + eConfigChangedSuppress)); + EXPECT_FALSE(connection); + EXPECT_FALSE(mScheduler->getEventConnection(handle)); // The EXPECT_CALLS make sure we don't call the functions on the subsequent event threads. EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0); - ASSERT_NO_FATAL_FAILURE( - mScheduler->hotplugReceived(connectionHandle, PHYSICAL_DISPLAY_ID, false)); + ASSERT_NO_FATAL_FAILURE(mScheduler->onHotplugReceived(handle, PHYSICAL_DISPLAY_ID, false)); EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0); - ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(connectionHandle)); + ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(handle)); EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0); - ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(connectionHandle)); + ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(handle)); - std::string testString; + std::string output; EXPECT_CALL(*mEventThread, dump(_)).Times(0); - ASSERT_NO_FATAL_FAILURE(mScheduler->dump(connectionHandle, testString)); - EXPECT_TRUE(testString == ""); + ASSERT_NO_FATAL_FAILURE(mScheduler->dump(handle, output)); + EXPECT_TRUE(output.empty()); EXPECT_CALL(*mEventThread, setPhaseOffset(_)).Times(0); - ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(connectionHandle, 10)); + ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(handle, 10)); } TEST_F(SchedulerTest, validConnectionHandle) { - sp<IDisplayEventConnection> returnedValue; + sp<IDisplayEventConnection> connection; ASSERT_NO_FATAL_FAILURE( - returnedValue = - mScheduler->createDisplayEventConnection(mConnectionHandle, ResyncCallback(), - ISurfaceComposer:: - eConfigChangedSuppress)); - EXPECT_TRUE(returnedValue != nullptr); - ASSERT_EQ(returnedValue, mEventThreadConnection); - - EXPECT_TRUE(mScheduler->getEventThread(mConnectionHandle) != nullptr); - EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle) != nullptr); + connection = mScheduler->createDisplayEventConnection(mConnectionHandle, + ISurfaceComposer:: + eConfigChangedSuppress)); + ASSERT_EQ(mEventThreadConnection, connection); + EXPECT_TRUE(mScheduler->getEventConnection(mConnectionHandle)); EXPECT_CALL(*mEventThread, onHotplugReceived(PHYSICAL_DISPLAY_ID, false)).Times(1); ASSERT_NO_FATAL_FAILURE( - mScheduler->hotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false)); + mScheduler->onHotplugReceived(mConnectionHandle, PHYSICAL_DISPLAY_ID, false)); EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1); ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenAcquired(mConnectionHandle)); @@ -180,13 +132,14 @@ TEST_F(SchedulerTest, validConnectionHandle) { EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1); ASSERT_NO_FATAL_FAILURE(mScheduler->onScreenReleased(mConnectionHandle)); - std::string testString("dump"); - EXPECT_CALL(*mEventThread, dump(testString)).Times(1); - ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, testString)); - EXPECT_TRUE(testString != ""); + std::string output("dump"); + EXPECT_CALL(*mEventThread, dump(output)).Times(1); + ASSERT_NO_FATAL_FAILURE(mScheduler->dump(mConnectionHandle, output)); + EXPECT_FALSE(output.empty()); EXPECT_CALL(*mEventThread, setPhaseOffset(10)).Times(1); ASSERT_NO_FATAL_FAILURE(mScheduler->setPhaseOffset(mConnectionHandle, 10)); } + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index cb6980ed1a..ae7246780a 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -19,30 +19,25 @@ #include <gmock/gmock.h> #include <gui/ISurfaceComposer.h> +#include "Scheduler/DispSync.h" #include "Scheduler/EventThread.h" -#include "Scheduler/RefreshRateConfigs.h" #include "Scheduler/Scheduler.h" namespace android { class TestableScheduler : public Scheduler { public: - TestableScheduler(const scheduler::RefreshRateConfigs& refreshRateConfig) - : Scheduler([](bool) {}, refreshRateConfig) {} + explicit TestableScheduler(const scheduler::RefreshRateConfigs& configs) + : Scheduler([](bool) {}, configs) {} - // Creates EventThreadConnection with the given eventThread. Creates Scheduler::Connection - // and adds it to the list of connectins. Returns the ConnectionHandle for the - // Scheduler::Connection. This allows plugging in mock::EventThread. - sp<Scheduler::ConnectionHandle> addConnection(std::unique_ptr<EventThread> eventThread) { - sp<EventThreadConnection> eventThreadConnection = - new EventThreadConnection(eventThread.get(), ResyncCallback(), - ISurfaceComposer::eConfigChangedSuppress); - const int64_t id = sNextId++; - mConnections.emplace(id, - std::make_unique<Scheduler::Connection>(new ConnectionHandle(id), - eventThreadConnection, - std::move(eventThread))); - return mConnections[id]->handle; + TestableScheduler(std::unique_ptr<DispSync> primaryDispSync, + std::unique_ptr<EventControlThread> eventControlThread, + const scheduler::RefreshRateConfigs& configs) + : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs) {} + + // Used to inject mock event thread. + ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { + return Scheduler::createConnection(std::move(eventThread)); } /* ------------------------------------------------------------------------ @@ -62,7 +57,7 @@ public: mutableEventControlThread().reset(); mutablePrimaryDispSync().reset(); mConnections.clear(); - }; + } }; } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 64d34ee102..b85c1b622e 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -18,9 +18,9 @@ #include <compositionengine/Display.h> #include <compositionengine/Layer.h> +#include <compositionengine/LayerFECompositionState.h> #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/CompositionEngine.h> -#include <compositionengine/impl/LayerCompositionState.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include "BufferQueueLayer.h" @@ -32,12 +32,12 @@ #include "Layer.h" #include "NativeWindowSurface.h" #include "Scheduler/MessageQueue.h" +#include "Scheduler/RefreshRateConfigs.h" #include "StartPropertySetThread.h" #include "SurfaceFlinger.h" -#include "SurfaceFlingerFactory.h" +#include "SurfaceFlingerDefaultFactory.h" #include "SurfaceInterceptor.h" - -#include "TimeStats/TimeStats.h" +#include "TestableScheduler.h" namespace android { @@ -61,7 +61,7 @@ class Factory final : public surfaceflinger::Factory { public: ~Factory() = default; - std::unique_ptr<DispSync> createDispSync(const char*, bool, int64_t) override { + std::unique_ptr<DispSync> createDispSync(const char*, bool) override { // TODO: Use test-fixture controlled factory return nullptr; } @@ -151,11 +151,6 @@ public: return nullptr; } - std::shared_ptr<TimeStats> createTimeStats() override { - // TODO: Use test-fixture controlled factory - return std::make_shared<android::impl::TimeStats>(); - } - using CreateBufferQueueFunction = std::function<void(sp<IGraphicBufferProducer>* /* outProducer */, sp<IGraphicBufferConsumer>* /* outConsumer */, @@ -176,6 +171,8 @@ public: class TestableSurfaceFlinger { public: + TestableScheduler* scheduler() { return mScheduler; } + // Extend this as needed for accessing SurfaceFlinger private (and public) // functions. @@ -188,6 +185,33 @@ public: std::make_unique<impl::HWComposer>(std::move(composer))); } + void setupScheduler(std::unique_ptr<DispSync> primaryDispSync, + std::unique_ptr<EventControlThread> eventControlThread, + std::unique_ptr<EventThread> appEventThread, + std::unique_ptr<EventThread> sfEventThread) { + std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}}; + mFlinger->mRefreshRateConfigs = + std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, + configs, /*currentConfig=*/0); + mFlinger->mRefreshRateStats = + std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, + *mFlinger->mTimeStats, + /*currentConfig=*/0, + /*powerMode=*/HWC_POWER_MODE_OFF); + + mScheduler = + new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread), + *mFlinger->mRefreshRateConfigs); + + mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); + mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); + + mFlinger->mScheduler.reset(mScheduler); + mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle, + mFlinger->mSfConnectionHandle, + mFlinger->mPhaseOffsets->getCurrentOffsets()); + } + using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction; void setCreateBufferQueueFunction(CreateBufferQueueFunction f) { mFactory.mCreateBufferQueue = f; @@ -208,10 +232,12 @@ public: auto& mutableLayerCurrentState(sp<Layer> layer) { return layer->mCurrentState; } auto& mutableLayerDrawingState(sp<Layer> layer) { return layer->mDrawingState; } + auto& mutableStateLock() { return mFlinger->mStateLock; } + void setLayerSidebandStream(sp<Layer> layer, sp<NativeHandle> sidebandStream) { layer->mDrawingState.sidebandStream = sidebandStream; layer->mSidebandStream = sidebandStream; - layer->getCompositionLayer()->editState().frontEnd.sidebandStream = sidebandStream; + layer->getCompositionLayer()->editFEState().sidebandStream = sidebandStream; } void setLayerCompositionType(sp<Layer> layer, HWC2::Composition type) { @@ -296,6 +322,22 @@ public: return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries); } + auto& getTransactionQueue() { return mFlinger->mTransactionQueues; } + + auto setTransactionState(const Vector<ComposerState>& states, + const Vector<DisplayState>& displays, uint32_t flags, + const sp<IBinder>& applyToken, + const InputWindowCommands& inputWindowCommands, + int64_t desiredPresentTime, const client_cache_t& uncacheBuffer, + bool hasListenerCallbacks, + std::vector<ListenerCallbacks>& listenerCallbacks) { + return mFlinger->setTransactionState(states, displays, flags, applyToken, + inputWindowCommands, desiredPresentTime, uncacheBuffer, + hasListenerCallbacks, listenerCallbacks); + } + + auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); }; + /* ------------------------------------------------------------------------ * Read-only access to private data to assert post-conditions. */ @@ -332,16 +374,13 @@ public: auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; } auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; } auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; } + auto& mutableDebugDisableHWC() { return mFlinger->mDebugDisableHWC; } auto& mutableComposerSequenceId() { return mFlinger->getBE().mComposerSequenceId; } auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; } auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; } auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; } auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; } - auto& mutableScheduler() { return mFlinger->mScheduler; } - auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; } - auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; } - auto& mutableRefreshRateConfigs() { return mFlinger->mRefreshRateConfigs; } ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does @@ -353,7 +392,7 @@ public: mutableDrawingState().displays.clear(); mutableEventQueue().reset(); mutableInterceptor().reset(); - mutableScheduler().reset(); + mFlinger->mScheduler.reset(); mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); mFlinger->mCompositionEngine->setRenderEngine( std::unique_ptr<renderengine::RenderEngine>()); @@ -573,6 +612,7 @@ public: surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization); + TestableScheduler* mScheduler = nullptr; // We need to keep a reference to these so they are properly destroyed. std::vector<std::unique_ptr<HWC2Display>> mFakeHwcDisplays; diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index f35758debf..4eb9ec3672 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -17,6 +17,7 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <TimeStats/TimeStats.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -27,8 +28,6 @@ #include <random> #include <unordered_set> -#include "TimeStats/TimeStats.h" - #include "libsurfaceflinger_unittest_main.h" using namespace android::surfaceflinger; @@ -172,7 +171,7 @@ std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) { } static std::string genLayerName(int32_t layerID) { - return (layerID < 0 ? "invalid.dummy" : "com.dummy#") + std::to_string(layerID); + return (layerID < 0 ? "PopupWindow:b54fcd1#0" : "com.dummy#") + std::to_string(layerID); } void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) { @@ -210,6 +209,10 @@ int32_t TimeStatsTest::genRandomInt32(int32_t begin, int32_t end) { return distr(mRandomEngine); } +TEST_F(TimeStatsTest, enabledByDefault) { + ASSERT_TRUE(mTimeStats->isEnabled()); +} + TEST_F(TimeStatsTest, canEnableAndDisableTimeStats) { EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty()); ASSERT_TRUE(mTimeStats->isEnabled()); diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp new file mode 100644 index 0000000000..a465388e8d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -0,0 +1,318 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "CompositionTest" + +#include <compositionengine/Display.h> +#include <compositionengine/mock/DisplaySurface.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <gui/SurfaceComposerClient.h> +#include <log/log.h> +#include <utils/String8.h> + +#include "TestableScheduler.h" +#include "TestableSurfaceFlinger.h" +#include "mock/MockDispSync.h" +#include "mock/MockEventControlThread.h" +#include "mock/MockEventThread.h" +#include "mock/MockMessageQueue.h" + +namespace android { + +using testing::_; +using testing::Return; + +using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + +class TransactionApplicationTest : public testing::Test { +public: + TransactionApplicationTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); + + mFlinger.mutableEventQueue().reset(mMessageQueue); + setupScheduler(); + } + + ~TransactionApplicationTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); + } + + void setupScheduler() { + auto eventThread = std::make_unique<mock::EventThread>(); + auto sfEventThread = std::make_unique<mock::EventThread>(); + + EXPECT_CALL(*eventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*eventThread, createEventConnection(_, _)) + .WillOnce(Return( + new EventThreadConnection(eventThread.get(), ResyncCallback(), + ISurfaceComposer::eConfigChangedSuppress))); + + EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*sfEventThread, createEventConnection(_, _)) + .WillOnce(Return( + new EventThreadConnection(sfEventThread.get(), ResyncCallback(), + ISurfaceComposer::eConfigChangedSuppress))); + + EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0)); + EXPECT_CALL(*mPrimaryDispSync, getPeriod()) + .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE)); + + mFlinger.setupScheduler(std::unique_ptr<mock::DispSync>(mPrimaryDispSync), + std::make_unique<mock::EventControlThread>(), + std::move(eventThread), std::move(sfEventThread)); + } + + TestableScheduler* mScheduler; + TestableSurfaceFlinger mFlinger; + + std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>(); + mock::EventControlThread* mEventControlThread = new mock::EventControlThread(); + + mock::MessageQueue* mMessageQueue = new mock::MessageQueue(); + mock::DispSync* mPrimaryDispSync = new mock::DispSync(); + + struct TransactionInfo { + Vector<ComposerState> states; + Vector<DisplayState> displays; + uint32_t flags = 0; + sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); + InputWindowCommands inputWindowCommands; + int64_t desiredPresentTime = -1; + client_cache_t uncacheBuffer; + }; + + void checkEqual(TransactionInfo info, SurfaceFlinger::TransactionState state) { + EXPECT_EQ(0, info.states.size()); + EXPECT_EQ(0, state.states.size()); + + EXPECT_EQ(0, info.displays.size()); + EXPECT_EQ(0, state.displays.size()); + EXPECT_EQ(info.flags, state.flags); + EXPECT_EQ(info.desiredPresentTime, state.desiredPresentTime); + } + + void setupSingle(TransactionInfo& transaction, uint32_t flags, bool syncInputWindows, + int64_t desiredPresentTime) { + mTransactionNumber++; + transaction.flags |= flags; // ISurfaceComposer::eSynchronous; + transaction.inputWindowCommands.syncInputWindows = syncInputWindows; + transaction.desiredPresentTime = desiredPresentTime; + } + + void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) { + ASSERT_EQ(0, mFlinger.getTransactionQueue().size()); + // called in SurfaceFlinger::signalTransaction + EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); + EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()).WillOnce(Return(systemTime())); + TransactionInfo transaction; + setupSingle(transaction, flags, syncInputWindows, + /*desiredPresentTime*/ -1); + nsecs_t applicationTime = systemTime(); + mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags, + transaction.applyToken, transaction.inputWindowCommands, + transaction.desiredPresentTime, transaction.uncacheBuffer, + mHasListenerCallbacks, mCallbacks); + + // This transaction should not have been placed on the transaction queue. + // If transaction is synchronous or syncs input windows, SF + // applyTransactionState should time out (5s) wating for SF to commit + // the transaction or to receive a signal that syncInputWindows has + // completed. If this is animation, it should not time out waiting. + nsecs_t returnedTime = systemTime(); + if (flags & ISurfaceComposer::eSynchronous || syncInputWindows) { + EXPECT_GE(returnedTime, applicationTime + s2ns(5)); + } else { + EXPECT_LE(returnedTime, applicationTime + s2ns(5)); + } + auto transactionQueue = mFlinger.getTransactionQueue(); + EXPECT_EQ(0, transactionQueue.size()); + } + + void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) { + ASSERT_EQ(0, mFlinger.getTransactionQueue().size()); + // called in SurfaceFlinger::signalTransaction + EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); + + // first check will see desired present time has not passed, + // but afterwards it will look like the desired present time has passed + nsecs_t time = systemTime(); + EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()) + .WillOnce(Return(time + nsecs_t(5 * 1e8))); + TransactionInfo transaction; + setupSingle(transaction, flags, syncInputWindows, + /*desiredPresentTime*/ time + s2ns(1)); + nsecs_t applicationSentTime = systemTime(); + mFlinger.setTransactionState(transaction.states, transaction.displays, transaction.flags, + transaction.applyToken, transaction.inputWindowCommands, + transaction.desiredPresentTime, transaction.uncacheBuffer, + mHasListenerCallbacks, mCallbacks); + + nsecs_t returnedTime = systemTime(); + EXPECT_LE(returnedTime, applicationSentTime + s2ns(5)); + // This transaction should have been placed on the transaction queue + auto transactionQueue = mFlinger.getTransactionQueue(); + EXPECT_EQ(1, transactionQueue.size()); + } + + void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) { + ASSERT_EQ(0, mFlinger.getTransactionQueue().size()); + // called in SurfaceFlinger::signalTransaction + nsecs_t time = systemTime(); + EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); + EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()) + .WillOnce(Return(time + nsecs_t(5 * 1e8))); + // transaction that should go on the pending thread + TransactionInfo transactionA; + setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false, + /*desiredPresentTime*/ time + s2ns(1)); + + // transaction that would not have gone on the pending thread if not + // blocked + TransactionInfo transactionB; + setupSingle(transactionB, flags, syncInputWindows, + /*desiredPresentTime*/ -1); + + nsecs_t applicationSentTime = systemTime(); + mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags, + transactionA.applyToken, transactionA.inputWindowCommands, + transactionA.desiredPresentTime, transactionA.uncacheBuffer, + mHasListenerCallbacks, mCallbacks); + + // This thread should not have been blocked by the above transaction + // (5s is the timeout period that applyTransactionState waits for SF to + // commit the transaction) + EXPECT_LE(systemTime(), applicationSentTime + s2ns(5)); + + applicationSentTime = systemTime(); + mFlinger.setTransactionState(transactionB.states, transactionB.displays, transactionB.flags, + transactionB.applyToken, transactionB.inputWindowCommands, + transactionB.desiredPresentTime, transactionB.uncacheBuffer, + mHasListenerCallbacks, mCallbacks); + + // this thread should have been blocked by the above transaction + // if this is an animation, this thread should be blocked for 5s + // in setTransactionState waiting for transactionA to flush. Otherwise, + // the transaction should be placed on the pending queue + if (flags & ISurfaceComposer::eAnimation) { + EXPECT_GE(systemTime(), applicationSentTime + s2ns(5)); + } else { + EXPECT_LE(systemTime(), applicationSentTime + s2ns(5)); + } + + // check that there is one binder on the pending queue. + auto transactionQueue = mFlinger.getTransactionQueue(); + EXPECT_EQ(1, transactionQueue.size()); + + auto& [applyToken, transactionStates] = *(transactionQueue.begin()); + EXPECT_EQ(2, transactionStates.size()); + + auto& transactionStateA = transactionStates.front(); + transactionStates.pop(); + checkEqual(transactionA, transactionStateA); + auto& transactionStateB = transactionStates.front(); + checkEqual(transactionB, transactionStateB); + } + + bool mHasListenerCallbacks = false; + std::vector<ListenerCallbacks> mCallbacks; + int mTransactionNumber = 0; +}; + +TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { + ASSERT_EQ(0, mFlinger.getTransactionQueue().size()); + // called in SurfaceFlinger::signalTransaction + EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); + + // nsecs_t time = systemTime(); + EXPECT_CALL(*mPrimaryDispSync, expectedPresentTime()) + .WillOnce(Return(nsecs_t(5 * 1e8))) + .WillOnce(Return(s2ns(2))); + TransactionInfo transactionA; // transaction to go on pending queue + setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false, + /*desiredPresentTime*/ s2ns(1)); + mFlinger.setTransactionState(transactionA.states, transactionA.displays, transactionA.flags, + transactionA.applyToken, transactionA.inputWindowCommands, + transactionA.desiredPresentTime, transactionA.uncacheBuffer, + mHasListenerCallbacks, mCallbacks); + + auto& transactionQueue = mFlinger.getTransactionQueue(); + ASSERT_EQ(1, transactionQueue.size()); + + auto& [applyToken, transactionStates] = *(transactionQueue.begin()); + ASSERT_EQ(1, transactionStates.size()); + + auto& transactionState = transactionStates.front(); + checkEqual(transactionA, transactionState); + + // because flushing uses the cached expected present time, we send an empty + // transaction here (sending a null applyToken to fake it as from a + // different process) to re-query and reset the cached expected present time + TransactionInfo empty; + empty.applyToken = sp<IBinder>(); + mFlinger.setTransactionState(empty.states, empty.displays, empty.flags, empty.applyToken, + empty.inputWindowCommands, empty.desiredPresentTime, + empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks); + + // flush transaction queue should flush as desiredPresentTime has + // passed + mFlinger.flushTransactionQueues(); + + EXPECT_EQ(0, transactionQueue.size()); +} + +TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Synchronous) { + NotPlacedOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false); +} + +TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_Animation) { + NotPlacedOnTransactionQueue(ISurfaceComposer::eAnimation, /*syncInputWindows*/ false); +} + +TEST_F(TransactionApplicationTest, NotPlacedOnTransactionQueue_SyncInputWindows) { + NotPlacedOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true); +} + +TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Synchronous) { + PlaceOnTransactionQueue(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false); +} + +TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_Animation) { + PlaceOnTransactionQueue(ISurfaceComposer::eAnimation, /*syncInputWindows*/ false); +} + +TEST_F(TransactionApplicationTest, PlaceOnTransactionQueue_SyncInputWindows) { + PlaceOnTransactionQueue(/*flags*/ 0, /*syncInputWindows*/ true); +} + +TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Synchronous) { + BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false); +} + +TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_Animation) { + BlockedByPriorTransaction(ISurfaceComposer::eSynchronous, /*syncInputWindows*/ false); +} + +TEST_F(TransactionApplicationTest, BlockWithPriorTransaction_SyncInputWindows) { + BlockedByPriorTransaction(/*flags*/ 0, /*syncInputWindows*/ true); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index 3c7e1da334..98c6aa0236 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -41,7 +41,7 @@ using android::hardware::graphics::composer::V2_1::Error; using android::hardware::graphics::composer::V2_1::IComposer; using android::hardware::graphics::composer::V2_1::IComposerCallback; using android::hardware::graphics::composer::V2_1::Layer; -using android::hardware::graphics::composer::V2_3::IComposerClient; +using android::hardware::graphics::composer::V2_4::IComposerClient; class Composer : public Hwc2::Composer { public: @@ -71,7 +71,6 @@ public: MOCK_METHOD2(getDisplayName, Error(Display, std::string*)); MOCK_METHOD4(getDisplayRequests, Error(Display, uint32_t*, std::vector<Layer>*, std::vector<uint32_t>*)); - MOCK_METHOD2(getDisplayType, Error(Display, IComposerClient::DisplayType*)); MOCK_METHOD2(getDozeSupport, Error(Display, bool*)); MOCK_METHOD5(getHdrCapabilities, Error(Display, std::vector<Hdr>*, float*, float*, float*)); MOCK_METHOD1(getPerFrameMetadataKeys, @@ -118,10 +117,11 @@ public: MOCK_METHOD4(setDisplayContentSamplingEnabled, Error(Display, bool, uint8_t, uint64_t)); MOCK_METHOD4(getDisplayedContentSample, Error(Display, uint64_t, uint64_t, DisplayedFrameStats*)); - MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*)); MOCK_METHOD3(setLayerPerFrameMetadataBlobs, Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&)); MOCK_METHOD2(setDisplayBrightness, Error(Display, float)); + MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*)); + MOCK_METHOD2(getDisplayConnectionType, Error(Display, IComposerClient::DisplayConnectionType*)); }; } // namespace mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp new file mode 100644 index 0000000000..358dfdb856 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2019 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 "mock/MockFrameTracer.h" + +namespace android { +namespace mock { + +// Explicit default instantiation is recommended. +FrameTracer::FrameTracer() = default; +FrameTracer::~FrameTracer() = default; + +} // namespace mock +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h new file mode 100644 index 0000000000..f768b8114d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/MockFrameTracer.h @@ -0,0 +1,43 @@ +/* + * Copyright 2019 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. + */ + +#pragma once + +#include <gmock/gmock.h> + +#include "FrameTracer/FrameTracer.h" + +namespace android { +namespace mock { + +class FrameTracer : public android::FrameTracer { +public: + FrameTracer(); + ~FrameTracer(); + + MOCK_METHOD0(initialize, void()); + MOCK_METHOD0(registerDataSource, void()); + MOCK_METHOD2(traceNewLayer, void(int32_t, const std::string&)); + MOCK_METHOD6(traceTimestamp, + void(int32_t, uint64_t, uint64_t, nsecs_t, FrameEvent::BufferEventType, nsecs_t)); + MOCK_METHOD6(traceFence, + void(int32_t, uint64_t, uint64_t, const std::shared_ptr<FenceTime>&, + FrameEvent::BufferEventType, nsecs_t)); + MOCK_METHOD0(miniDump, std::string()); +}; + +} // namespace mock +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h index 1b1c1a7990..e781c0a10d 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h +++ b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h @@ -30,7 +30,6 @@ public: ~MessageQueue() override; MOCK_METHOD1(init, void(const sp<SurfaceFlinger>&)); - MOCK_METHOD2(setEventThread, void(android::EventThread*, ResyncCallback)); MOCK_METHOD1(setEventConnection, void(const sp<EventThreadConnection>& connection)); MOCK_METHOD0(waitMessage, void()); MOCK_METHOD2(postMessage, status_t(const sp<MessageBase>&, nsecs_t)); diff --git a/services/surfaceflinger/tests/utils/CallbackUtils.h b/services/surfaceflinger/tests/utils/CallbackUtils.h new file mode 100644 index 0000000000..51ae8c4e94 --- /dev/null +++ b/services/surfaceflinger/tests/utils/CallbackUtils.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2019 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. + */ +#pragma once + +#include <gtest/gtest.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SurfaceControl.h> +#include <ui/Fence.h> +#include <utils/Timers.h> +#include <thread> + +namespace android { + +namespace { + +struct CallbackData { + CallbackData() = default; + CallbackData(nsecs_t time, const sp<Fence>& fence, + const std::vector<SurfaceControlStats>& stats) + : latchTime(time), presentFence(fence), surfaceControlStats(stats) {} + + nsecs_t latchTime; + sp<Fence> presentFence; + std::vector<SurfaceControlStats> surfaceControlStats; +}; + +class ExpectedResult { +public: + enum Transaction { + NOT_PRESENTED = 0, + PRESENTED, + }; + + enum Buffer { + NOT_ACQUIRED = 0, + ACQUIRED, + }; + + enum PreviousBuffer { + NOT_RELEASED = 0, + RELEASED, + UNKNOWN, + }; + + void reset() { + mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; + mExpectedSurfaceResults.clear(); + } + + void addSurface(ExpectedResult::Transaction transactionResult, const sp<SurfaceControl>& layer, + ExpectedResult::Buffer bufferResult = ACQUIRED, + ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { + mTransactionResult = transactionResult; + mExpectedSurfaceResults.emplace(std::piecewise_construct, std::forward_as_tuple(layer), + std::forward_as_tuple(bufferResult, previousBufferResult)); + } + + void addSurfaces(ExpectedResult::Transaction transactionResult, + const std::vector<sp<SurfaceControl>>& layers, + ExpectedResult::Buffer bufferResult = ACQUIRED, + ExpectedResult::PreviousBuffer previousBufferResult = NOT_RELEASED) { + for (const auto& layer : layers) { + addSurface(transactionResult, layer, bufferResult, previousBufferResult); + } + } + + void addExpectedPresentTime(nsecs_t expectedPresentTime) { + mExpectedPresentTime = expectedPresentTime; + } + + void verifyCallbackData(const CallbackData& callbackData) const { + const auto& [latchTime, presentFence, surfaceControlStats] = callbackData; + if (mTransactionResult == ExpectedResult::Transaction::PRESENTED) { + ASSERT_GE(latchTime, 0) << "bad latch time"; + ASSERT_NE(presentFence, nullptr); + if (mExpectedPresentTime >= 0) { + ASSERT_EQ(presentFence->wait(3000), NO_ERROR); + ASSERT_GE(presentFence->getSignalTime(), mExpectedPresentTime - nsecs_t(5 * 1e6)); + // if the panel is running at 30 hz, at the worst case, our expected time just + // misses vsync and we have to wait another 33.3ms + ASSERT_LE(presentFence->getSignalTime(), + mExpectedPresentTime + nsecs_t(66.666666 * 1e6)); + } + } else { + ASSERT_EQ(presentFence, nullptr) << "transaction shouldn't have been presented"; + ASSERT_EQ(latchTime, -1) << "unpresented transactions shouldn't be latched"; + } + + ASSERT_EQ(surfaceControlStats.size(), mExpectedSurfaceResults.size()) + << "wrong number of surfaces"; + + for (const auto& stats : surfaceControlStats) { + ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control"; + + const auto& expectedSurfaceResult = mExpectedSurfaceResults.find(stats.surfaceControl); + ASSERT_NE(expectedSurfaceResult, mExpectedSurfaceResults.end()) + << "unexpected surface control"; + expectedSurfaceResult->second.verifySurfaceControlStats(stats, latchTime); + } + } + +private: + class ExpectedSurfaceResult { + public: + ExpectedSurfaceResult(ExpectedResult::Buffer bufferResult, + ExpectedResult::PreviousBuffer previousBufferResult) + : mBufferResult(bufferResult), mPreviousBufferResult(previousBufferResult) {} + + void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats, + nsecs_t latchTime) const { + const auto& [surfaceControl, acquireTime, previousReleaseFence] = surfaceControlStats; + + ASSERT_EQ(acquireTime > 0, mBufferResult == ExpectedResult::Buffer::ACQUIRED) + << "bad acquire time"; + ASSERT_LE(acquireTime, latchTime) << "acquire time should be <= latch time"; + + if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::RELEASED) { + ASSERT_NE(previousReleaseFence, nullptr) + << "failed to set release prev buffer fence"; + } else if (mPreviousBufferResult == ExpectedResult::PreviousBuffer::NOT_RELEASED) { + ASSERT_EQ(previousReleaseFence, nullptr) + << "should not have set released prev buffer fence"; + } + } + + private: + ExpectedResult::Buffer mBufferResult; + ExpectedResult::PreviousBuffer mPreviousBufferResult; + }; + + struct SCHash { + std::size_t operator()(const sp<SurfaceControl>& sc) const { + return std::hash<IBinder*>{}(sc->getHandle().get()); + } + }; + ExpectedResult::Transaction mTransactionResult = ExpectedResult::Transaction::NOT_PRESENTED; + nsecs_t mExpectedPresentTime = -1; + std::unordered_map<sp<SurfaceControl>, ExpectedSurfaceResult, SCHash> mExpectedSurfaceResults; +}; + +class CallbackHelper { +public: + static void function(void* callbackContext, nsecs_t latchTime, const sp<Fence>& presentFence, + const std::vector<SurfaceControlStats>& stats) { + if (!callbackContext) { + ALOGE("failed to get callback context"); + } + CallbackHelper* helper = static_cast<CallbackHelper*>(callbackContext); + std::lock_guard lock(helper->mMutex); + helper->mCallbackDataQueue.emplace(latchTime, presentFence, stats); + helper->mConditionVariable.notify_all(); + } + + void getCallbackData(CallbackData* outData) { + std::unique_lock lock(mMutex); + + if (mCallbackDataQueue.empty()) { + ASSERT_NE(mConditionVariable.wait_for(lock, std::chrono::seconds(3)), + std::cv_status::timeout) + << "did not receive callback"; + } + + *outData = std::move(mCallbackDataQueue.front()); + mCallbackDataQueue.pop(); + } + + void verifyFinalState() { + // Wait to see if there are extra callbacks + std::this_thread::sleep_for(500ms); + + std::lock_guard lock(mMutex); + EXPECT_EQ(mCallbackDataQueue.size(), 0) << "extra callbacks received"; + mCallbackDataQueue = {}; + } + + void* getContext() { return static_cast<void*>(this); } + + std::mutex mMutex; + std::condition_variable mConditionVariable; + std::queue<CallbackData> mCallbackDataQueue; +}; +} +} // namespace android diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h new file mode 100644 index 0000000000..07916b60a7 --- /dev/null +++ b/services/surfaceflinger/tests/utils/ColorUtils.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 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. + */ +#pragma once + +#include <ui/ColorSpace.h> + +namespace android { + +namespace { + +struct Color { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + + static const Color RED; + static const Color GREEN; + static const Color BLUE; + static const Color WHITE; + static const Color BLACK; + static const Color TRANSPARENT; +}; + +const Color Color::RED{255, 0, 0, 255}; +const Color Color::GREEN{0, 255, 0, 255}; +const Color Color::BLUE{0, 0, 255, 255}; +const Color Color::WHITE{255, 255, 255, 255}; +const Color Color::BLACK{0, 0, 0, 255}; +const Color Color::TRANSPARENT{0, 0, 0, 0}; + +class ColorTransformHelper { +public: + static void DegammaColorSingle(half& s) { + if (s <= 0.03928f) + s = s / 12.92f; + else + s = pow((s + 0.055f) / 1.055f, 2.4f); + } + + static void DegammaColor(half3& color) { + DegammaColorSingle(color.r); + DegammaColorSingle(color.g); + DegammaColorSingle(color.b); + } + + static void GammaColorSingle(half& s) { + if (s <= 0.0031308f) { + s = s * 12.92f; + } else { + s = 1.055f * pow(s, (1.0f / 2.4f)) - 0.055f; + } + } + + static void GammaColor(half3& color) { + GammaColorSingle(color.r); + GammaColorSingle(color.g); + GammaColorSingle(color.b); + } + + static void applyMatrix(half3& color, const mat3& mat) { + half3 ret = half3(0); + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + ret[i] = ret[i] + color[j] * mat[j][i]; + } + } + color = ret; + } +}; +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h new file mode 100644 index 0000000000..5480b00a4d --- /dev/null +++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2019 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. + */ +#pragma once + +#include <ui/Rect.h> +#include <utils/String8.h> +#include "TransactionUtils.h" + +namespace android { + +namespace { + +// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check +// individual pixel values for testing purposes. +class ScreenCapture : public RefBase { +public: + static void captureScreen(std::unique_ptr<ScreenCapture>* sc) { + captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken()); + } + + static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) { + const auto sf = ComposerService::getComposerService(); + SurfaceComposerClient::Transaction().apply(true); + + sp<GraphicBuffer> outBuffer; + ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false)); + *sc = std::make_unique<ScreenCapture>(outBuffer); + } + + static void captureLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, + Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + SurfaceComposerClient::Transaction().apply(true); + + sp<GraphicBuffer> outBuffer; + ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale)); + *sc = std::make_unique<ScreenCapture>(outBuffer); + } + + static void captureChildLayers(std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, + Rect crop = Rect::EMPTY_RECT, float frameScale = 1.0) { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + SurfaceComposerClient::Transaction().apply(true); + + sp<GraphicBuffer> outBuffer; + ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, &outBuffer, crop, frameScale, true)); + *sc = std::make_unique<ScreenCapture>(outBuffer); + } + + static void captureChildLayersExcluding( + std::unique_ptr<ScreenCapture>* sc, sp<IBinder>& parentHandle, + std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> excludeLayers) { + sp<ISurfaceComposer> sf(ComposerService::getComposerService()); + SurfaceComposerClient::Transaction().apply(true); + + sp<GraphicBuffer> outBuffer; + ASSERT_EQ(NO_ERROR, + sf->captureLayers(parentHandle, &outBuffer, ui::Dataspace::V0_SRGB, + ui::PixelFormat::RGBA_8888, Rect::EMPTY_RECT, excludeLayers, + 1.0f, true)); + *sc = std::make_unique<ScreenCapture>(outBuffer); + } + + void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) { + ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat()); + TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance); + } + + void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) { + ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat()); + const bool leftBorder = rect.left > 0; + const bool topBorder = rect.top > 0; + const bool rightBorder = rect.right < int32_t(mOutBuffer->getWidth()); + const bool bottomBorder = rect.bottom < int32_t(mOutBuffer->getHeight()); + + if (topBorder) { + Rect top(rect.left, rect.top - 1, rect.right, rect.top); + if (leftBorder) { + top.left -= 1; + } + if (rightBorder) { + top.right += 1; + } + expectColor(top, color, tolerance); + } + if (leftBorder) { + Rect left(rect.left - 1, rect.top, rect.left, rect.bottom); + expectColor(left, color, tolerance); + } + if (rightBorder) { + Rect right(rect.right, rect.top, rect.right + 1, rect.bottom); + expectColor(right, color, tolerance); + } + if (bottomBorder) { + Rect bottom(rect.left, rect.bottom, rect.right, rect.bottom + 1); + if (leftBorder) { + bottom.left -= 1; + } + if (rightBorder) { + bottom.right += 1; + } + expectColor(bottom, color, tolerance); + } + } + + void expectQuadrant(const Rect& rect, const Color& topLeft, const Color& topRight, + const Color& bottomLeft, const Color& bottomRight, bool filtered = false, + uint8_t tolerance = 0) { + ASSERT_TRUE((rect.right - rect.left) % 2 == 0 && (rect.bottom - rect.top) % 2 == 0); + + const int32_t centerX = rect.left + (rect.right - rect.left) / 2; + const int32_t centerY = rect.top + (rect.bottom - rect.top) / 2; + // avoid checking borders due to unspecified filtering behavior + const int32_t offsetX = filtered ? 2 : 0; + const int32_t offsetY = filtered ? 2 : 0; + expectColor(Rect(rect.left, rect.top, centerX - offsetX, centerY - offsetY), topLeft, + tolerance); + expectColor(Rect(centerX + offsetX, rect.top, rect.right, centerY - offsetY), topRight, + tolerance); + expectColor(Rect(rect.left, centerY + offsetY, centerX - offsetX, rect.bottom), bottomLeft, + tolerance); + expectColor(Rect(centerX + offsetX, centerY + offsetY, rect.right, rect.bottom), + bottomRight, tolerance); + } + + void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) { + ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat()); + const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x)); + if (r != pixel[0] || g != pixel[1] || b != pixel[2]) { + String8 err(String8::format("pixel @ (%3d, %3d): " + "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]", + x, y, r, g, b, pixel[0], pixel[1], pixel[2])); + EXPECT_EQ(String8(), err) << err.string(); + } + } + + void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); } + + void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); } + + void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); } + + explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) { + mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels)); + } + + ~ScreenCapture() { mOutBuffer->unlock(); } + +private: + sp<GraphicBuffer> mOutBuffer; + uint8_t* mPixels = nullptr; +}; +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h new file mode 100644 index 0000000000..22df2559b1 --- /dev/null +++ b/services/surfaceflinger/tests/utils/TransactionUtils.h @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2019 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. + */ + +#pragma once + +#include <chrono> +#include <gtest/gtest.h> + +#include <android/native_window.h> +#include <hardware/hwcomposer_defs.h> + +#include <binder/IPCThreadState.h> + +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> + +#include <private/gui/ComposerService.h> + +#include <ui/GraphicBuffer.h> +#include <ui/Rect.h> + +#include "ColorUtils.h" + +namespace android { + +namespace { + +using namespace std::chrono_literals; +using Transaction = SurfaceComposerClient::Transaction; + +std::ostream& operator<<(std::ostream& os, const Color& color) { + os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a); + return os; +} + +class TransactionUtils { +public: + // Fill a region with the specified color. + static void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, + const Color& color) { + Rect r(0, 0, buffer.width, buffer.height); + if (!r.intersect(rect, &r)) { + return; + } + + int32_t width = r.right - r.left; + int32_t height = r.bottom - r.top; + + for (int32_t row = 0; row < height; row++) { + uint8_t* dst = static_cast<uint8_t*>(buffer.bits) + + (buffer.stride * (r.top + row) + r.left) * 4; + for (int32_t column = 0; column < width; column++) { + dst[0] = color.r; + dst[1] = color.g; + dst[2] = color.b; + dst[3] = color.a; + dst += 4; + } + } + } + + // Fill a region with the specified color. + static void fillGraphicBufferColor(const sp<GraphicBuffer>& buffer, const Rect& rect, + const Color& color) { + Rect r(0, 0, buffer->width, buffer->height); + if (!r.intersect(rect, &r)) { + return; + } + + int32_t width = r.right - r.left; + int32_t height = r.bottom - r.top; + + uint8_t* pixels; + buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, + reinterpret_cast<void**>(&pixels)); + + for (int32_t row = 0; row < height; row++) { + uint8_t* dst = pixels + (buffer->getStride() * (r.top + row) + r.left) * 4; + for (int32_t column = 0; column < width; column++) { + dst[0] = color.r; + dst[1] = color.g; + dst[2] = color.b; + dst[3] = color.a; + dst += 4; + } + } + buffer->unlock(); + } + + // Check if a region has the specified color. + static void expectBufferColor(const sp<GraphicBuffer>& outBuffer, uint8_t* pixels, + const Rect& rect, const Color& color, uint8_t tolerance) { + int32_t x = rect.left; + int32_t y = rect.top; + int32_t width = rect.right - rect.left; + int32_t height = rect.bottom - rect.top; + + int32_t bufferWidth = int32_t(outBuffer->getWidth()); + int32_t bufferHeight = int32_t(outBuffer->getHeight()); + if (x + width > bufferWidth) { + x = std::min(x, bufferWidth); + width = bufferWidth - x; + } + if (y + height > bufferHeight) { + y = std::min(y, bufferHeight); + height = bufferHeight - y; + } + + auto colorCompare = [tolerance](uint8_t a, uint8_t b) { + uint8_t tmp = a >= b ? a - b : b - a; + return tmp <= tolerance; + }; + for (int32_t j = 0; j < height; j++) { + const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4; + for (int32_t i = 0; i < width; i++) { + const uint8_t expected[4] = {color.r, color.g, color.b, color.a}; + EXPECT_TRUE(std::equal(src, src + 4, expected, colorCompare)) + << "pixel @ (" << x + i << ", " << y + j << "): " + << "expected (" << color << "), " + << "got (" << Color{src[0], src[1], src[2], src[3]} << ")"; + src += 4; + } + } + } + + // Fill an RGBA_8888 formatted surface with a single color. + static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b, + bool unlock = true) { + ANativeWindow_Buffer outBuffer; + sp<Surface> s = sc->getSurface(); + ASSERT_TRUE(s != nullptr); + ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr)); + uint8_t* img = reinterpret_cast<uint8_t*>(outBuffer.bits); + for (int y = 0; y < outBuffer.height; y++) { + for (int x = 0; x < outBuffer.width; x++) { + uint8_t* pixel = img + (4 * (y * outBuffer.stride + x)); + pixel[0] = r; + pixel[1] = g; + pixel[2] = b; + pixel[3] = 255; + } + } + if (unlock) { + ASSERT_EQ(NO_ERROR, s->unlockAndPost()); + } + } +}; + +enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY }; + +// Environment for starting up binder threads. This is required for testing +// virtual displays, as BufferQueue parameters may be queried over binder. +class BinderEnvironment : public ::testing::Environment { +public: + void SetUp() override { ProcessState::self()->startThreadPool(); } +}; + +/** RAII Wrapper around get/seteuid */ +class UIDFaker { + uid_t oldId; + +public: + UIDFaker(uid_t uid) { + oldId = geteuid(); + seteuid(uid); + } + ~UIDFaker() { seteuid(oldId); } +}; +} // namespace +} // namespace android diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp index a7fd912294..b71964ba00 100644 --- a/services/vr/bufferhubd/producer_channel.cpp +++ b/services/vr/bufferhubd/producer_channel.cpp @@ -350,7 +350,7 @@ Status<RemoteChannelHandle> ProducerChannel::CreateConsumer( while (!buffer_state_->compare_exchange_weak( current_buffer_state, updated_buffer_state, std::memory_order_acq_rel, std::memory_order_acquire)) { - ALOGI( + ALOGV( "%s: Failed to post to the new consumer. " "Current buffer state was changed to %" PRIx32 " when trying to acquire the buffer and modify the buffer state to " diff --git a/services/vr/hardware_composer/Android.bp b/services/vr/hardware_composer/Android.bp index c202b5c07c..4df7b7c6bd 100644 --- a/services/vr/hardware_composer/Android.bp +++ b/services/vr/hardware_composer/Android.bp @@ -12,10 +12,14 @@ cc_library_shared { ], shared_libs: [ - "android.frameworks.vr.composer@1.0", + "android.frameworks.vr.composer@2.0", "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.composer@2.3", + "android.hardware.graphics.composer@2.1-resources", "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@3.0", + "android.hardware.graphics.mapper@4.0", "libbase", "libbufferhubqueue", "libbinder", @@ -32,11 +36,11 @@ cc_library_shared { header_libs: [ "android.hardware.graphics.composer@2.1-command-buffer", - "android.hardware.graphics.composer@2.1-hal", + "android.hardware.graphics.composer@2.3-hal", ], export_header_lib_headers: [ - "android.hardware.graphics.composer@2.1-hal", + "android.hardware.graphics.composer@2.3-hal", ], export_static_lib_headers: [ @@ -44,8 +48,10 @@ cc_library_shared { ], export_shared_lib_headers: [ - "android.frameworks.vr.composer@1.0", + "android.frameworks.vr.composer@2.0", "android.hardware.graphics.composer@2.1", + "android.hardware.graphics.composer@2.2", + "android.hardware.graphics.composer@2.3", ], export_include_dirs: ["."], @@ -102,8 +108,8 @@ cc_binary { "libvr_hwc-binder", ], shared_libs: [ - "android.frameworks.vr.composer@1.0", - "android.hardware.graphics.composer@2.1", + "android.frameworks.vr.composer@2.0", + "android.hardware.graphics.composer@2.3", "libbase", "libbinder", "liblog", diff --git a/services/vr/hardware_composer/impl/vr_composer_client.cpp b/services/vr/hardware_composer/impl/vr_composer_client.cpp index 786d5fac98..36f6b32e3f 100644 --- a/services/vr/hardware_composer/impl/vr_composer_client.cpp +++ b/services/vr/hardware_composer/impl/vr_composer_client.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h> +#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h> #include <hardware/gralloc.h> #include <hardware/gralloc1.h> #include <log/log.h> @@ -27,8 +27,7 @@ namespace android { namespace dvr { -using android::hardware::graphics::common::V1_0::PixelFormat; -using android::frameworks::vr::composer::V1_0::IVrComposerClient; +using android::frameworks::vr::composer::V2_0::IVrComposerClient; VrComposerClient::VrComposerClient(dvr::VrHwc& hal) : ComposerClient(&hal), mVrHal(hal) { @@ -51,7 +50,8 @@ VrComposerClient::VrCommandEngine::VrCommandEngine(VrComposerClient& client) VrComposerClient::VrCommandEngine::~VrCommandEngine() {} bool VrComposerClient::VrCommandEngine::executeCommand( - IComposerClient::Command command, uint16_t length) { + hardware::graphics::composer::V2_1::IComposerClient::Command command, + uint16_t length) { IVrComposerClient::VrCommand vrCommand = static_cast<IVrComposerClient::VrCommand>(command); switch (vrCommand) { @@ -107,12 +107,14 @@ bool VrComposerClient::VrCommandEngine::executeSetLayerBufferMetadata( IVrComposerClient::BufferMetadata VrComposerClient::VrCommandEngine::readBufferMetadata() { IVrComposerClient::BufferMetadata metadata = { - .width = read(), - .height = read(), - .stride = read(), - .layerCount = read(), - .format = static_cast<PixelFormat>(readSigned()), - .usage = read64(), + .width = read(), + .height = read(), + .stride = read(), + .layerCount = read(), + .format = + static_cast<android::hardware::graphics::common::V1_2::PixelFormat>( + readSigned()), + .usage = read64(), }; return metadata; } diff --git a/services/vr/hardware_composer/impl/vr_composer_client.h b/services/vr/hardware_composer/impl/vr_composer_client.h index 0b7ce5e665..1b2b5f4f56 100644 --- a/services/vr/hardware_composer/impl/vr_composer_client.h +++ b/services/vr/hardware_composer/impl/vr_composer_client.h @@ -17,10 +17,12 @@ #ifndef ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H #define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_COMPOSER_CLIENT_H -#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h> -#include <composer-command-buffer/2.1/ComposerCommandBuffer.h> +#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h> +#include <composer-command-buffer/2.3/ComposerCommandBuffer.h> #include <composer-hal/2.1/ComposerClient.h> #include <composer-hal/2.1/ComposerCommandEngine.h> +#include <composer-hal/2.2/ComposerClient.h> +#include <composer-hal/2.3/ComposerClient.h> namespace android { namespace dvr { @@ -28,8 +30,8 @@ namespace dvr { class VrHwc; using hardware::graphics::composer::V2_1::hal::ComposerCommandEngine; -using hardware::graphics::composer::V2_1::hal::ComposerHal; -using hardware::graphics::composer::V2_1::hal::detail::ComposerClientImpl; +using hardware::graphics::composer::V2_3::hal::ComposerHal; +using hardware::graphics::composer::V2_3::hal::detail::ComposerClientImpl; using ComposerClient = ComposerClientImpl<IVrComposerClient, ComposerHal>; @@ -44,8 +46,9 @@ class VrComposerClient : public ComposerClient { explicit VrCommandEngine(VrComposerClient& client); ~VrCommandEngine() override; - bool executeCommand(IComposerClient::Command command, - uint16_t length) override; + bool executeCommand( + hardware::graphics::composer::V2_1::IComposerClient::Command command, + uint16_t length) override; private: bool executeSetLayerInfo(uint16_t length); diff --git a/services/vr/hardware_composer/impl/vr_hwc.cpp b/services/vr/hardware_composer/impl/vr_hwc.cpp index fb7932d804..e530b16b1b 100644 --- a/services/vr/hardware_composer/impl/vr_hwc.cpp +++ b/services/vr/hardware_composer/impl/vr_hwc.cpp @@ -27,7 +27,7 @@ #include "vr_composer_client.h" using namespace android::hardware::graphics::common::V1_0; -using namespace android::hardware::graphics::composer::V2_1; +using namespace android::hardware::graphics::composer::V2_3; using android::base::StringPrintf; using android::hardware::hidl_handle; @@ -36,12 +36,12 @@ using android::hardware::hidl_vec; using android::hardware::Return; using android::hardware::Void; +namespace types = android::hardware::graphics::common; + namespace android { namespace dvr { namespace { -using android::hardware::graphics::common::V1_0::PixelFormat; - const Display kDefaultDisplayId = 1; const Config kDefaultConfigId = 1; @@ -269,7 +269,8 @@ void VrHwc::registerEventCallback(EventCallback* callback) { // onHotplug() call, so it's important to release mutex_ here. lock.unlock(); event_callback_->onHotplug(kDefaultDisplayId, - IComposerCallback::Connection::CONNECTED); + hardware::graphics::composer::V2_1:: + IComposerCallback::Connection::CONNECTED); lock.lock(); UpdateVsyncCallbackEnabledLocked(); } @@ -282,15 +283,6 @@ void VrHwc::unregisterEventCallback() { uint32_t VrHwc::getMaxVirtualDisplayCount() { return 1; } -Error VrHwc::createVirtualDisplay(uint32_t width, uint32_t height, - PixelFormat* format, Display* outDisplay) { - *format = PixelFormat::RGBA_8888; - *outDisplay = display_count_; - displays_[display_count_].reset(new HwcDisplay(width, height)); - display_count_++; - return Error::NONE; -} - Error VrHwc::destroyVirtualDisplay(Display display) { std::lock_guard<std::mutex> guard(mutex_); if (display == kDefaultDisplayId || displays_.erase(display) == 0) @@ -332,24 +324,6 @@ Error VrHwc::getActiveConfig(Display display, Config* outConfig) { return Error::NONE; } -Error VrHwc::getClientTargetSupport(Display display, uint32_t /* width */, - uint32_t /* height */, - PixelFormat /* format */, - Dataspace /* dataspace */) { - std::lock_guard<std::mutex> guard(mutex_); - if (!FindDisplay(display)) - return Error::BAD_DISPLAY; - - return Error::NONE; -} - -Error VrHwc::getColorModes(Display /* display */, - hidl_vec<ColorMode>* outModes) { - std::vector<ColorMode> color_modes(1, ColorMode::NATIVE); - *outModes = hidl_vec<ColorMode>(color_modes); - return Error::NONE; -} - Error VrHwc::getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute, int32_t* outValue) { @@ -441,17 +415,6 @@ Error VrHwc::getDozeSupport(Display display, bool* outSupport) { return Error::NONE; } -Error VrHwc::getHdrCapabilities(Display /* display */, - hidl_vec<Hdr>* /* outTypes */, - float* outMaxLuminance, - float* outMaxAverageLuminance, - float* outMinLuminance) { - *outMaxLuminance = 0; - *outMaxAverageLuminance = 0; - *outMinLuminance = 0; - return Error::NONE; -} - Error VrHwc::setActiveConfig(Display display, Config config) { std::lock_guard<std::mutex> guard(mutex_); auto display_ptr = FindDisplay(display); @@ -464,47 +427,6 @@ Error VrHwc::setActiveConfig(Display display, Config config) { return Error::NONE; } -Error VrHwc::setColorMode(Display display, ColorMode mode) { - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3) - return Error::BAD_PARAMETER; - - display_ptr->set_color_mode(mode); - return Error::NONE; -} - -Error VrHwc::setPowerMode(Display display, IComposerClient::PowerMode mode) { - bool dozeSupported = false; - - Error dozeSupportError = getDozeSupport(display, &dozeSupported); - - if (dozeSupportError != Error::NONE) - return dozeSupportError; - - std::lock_guard<std::mutex> guard(mutex_); - auto display_ptr = FindDisplay(display); - if (!display_ptr) - return Error::BAD_DISPLAY; - - if (mode < IComposerClient::PowerMode::OFF || - mode > IComposerClient::PowerMode::DOZE_SUSPEND) { - return Error::BAD_PARAMETER; - } - - if (!dozeSupported && - (mode == IComposerClient::PowerMode::DOZE || - mode == IComposerClient::PowerMode::DOZE_SUSPEND)) { - return Error::UNSUPPORTED; - } - - display_ptr->set_power_mode(mode); - return Error::NONE; -} - Error VrHwc::setVsyncEnabled(Display display, IComposerClient::Vsync enabled) { std::lock_guard<std::mutex> guard(mutex_); auto display_ptr = FindDisplay(display); @@ -956,6 +878,23 @@ Return<void> VrHwc::createClient(createClient_cb hidl_cb) { return Void(); } +Return<void> VrHwc::createClient_2_3(IComposer::createClient_2_3_cb hidl_cb) { + std::lock_guard<std::mutex> guard(mutex_); + + Error status = Error::NONE; + sp<VrComposerClient> client; + if (!client_.promote().get()) { + client = new VrComposerClient(*this); + } else { + ALOGE("Already have a client"); + status = Error::NO_RESOURCES; + } + + client_ = client; + hidl_cb(status, client); + return Void(); +} + void VrHwc::ForceDisplaysRefresh() { std::lock_guard<std::mutex> guard(mutex_); if (event_callback_ != nullptr) { @@ -994,6 +933,26 @@ void VrHwc::UpdateVsyncCallbackEnabledLocked() { vsync_callback_->SetEventCallback(send_vsync ? event_callback_ : nullptr); } +Return<void> VrHwc::debug(const hidl_handle& fd, + const hidl_vec<hidl_string>& args) { + std::string result; + + { + std::lock_guard<std::mutex> guard(mutex_); + for (const auto& pair : displays_) { + result += StringPrintf("Display id: %d\n", static_cast<int>(pair.first)); + pair.second->dumpDebugInfo(&result); + } + result += "\n"; + } + + FILE* out = fdopen(dup(fd->data[0]), "w"); + fprintf(out, "%s", result.c_str()); + fclose(out); + + return Void(); +} + void HwcLayer::dumpDebugInfo(std::string* result) const { if (!result) { return; @@ -1024,5 +983,196 @@ void VrHwc::VsyncCallback::SetEventCallback(EventCallback* callback) { callback_ = callback; } +// composer::V2_2::ComposerHal +Error VrHwc::setReadbackBuffer(Display display, + const native_handle_t* bufferHandle, + android::base::unique_fd fenceFd) { + return Error::NONE; +} + +Error VrHwc::getReadbackBufferFence(Display display, + android::base::unique_fd* outFenceFd) { + return Error::NONE; +} + +Error VrHwc::createVirtualDisplay_2_2(uint32_t width, uint32_t height, + types::V1_1::PixelFormat* format, + Display* outDisplay) { + *format = types::V1_1::PixelFormat::RGBA_8888; + *outDisplay = display_count_; + displays_[display_count_].reset(new HwcDisplay(width, height)); + display_count_++; + return Error::NONE; +} + +Error VrHwc::setPowerMode_2_2(Display display, + IComposerClient::PowerMode mode) { + bool dozeSupported = false; + + Error dozeSupportError = getDozeSupport(display, &dozeSupported); + + if (dozeSupportError != Error::NONE) + return dozeSupportError; + + std::lock_guard<std::mutex> guard(mutex_); + auto display_ptr = FindDisplay(display); + if (!display_ptr) + return Error::BAD_DISPLAY; + + if (mode < IComposerClient::PowerMode::OFF || + mode > IComposerClient::PowerMode::DOZE_SUSPEND) { + return Error::BAD_PARAMETER; + } + + if (!dozeSupported && (mode == IComposerClient::PowerMode::DOZE || + mode == IComposerClient::PowerMode::DOZE_SUSPEND)) { + return Error::UNSUPPORTED; + } + + display_ptr->set_power_mode(mode); + return Error::NONE; +} + +Error VrHwc::setLayerFloatColor(Display display, Layer layer, + IComposerClient::FloatColor color) { + return Error::NONE; +} + +Error VrHwc::getRenderIntents(Display display, types::V1_1::ColorMode mode, + std::vector<RenderIntent>* outIntents) { + return Error::NONE; +} + +std::array<float, 16> VrHwc::getDataspaceSaturationMatrix( + types::V1_1::Dataspace dataspace) { + return {}; +} + +// composer::V2_3::ComposerHal +Error VrHwc::getHdrCapabilities_2_3(Display /*display*/, + hidl_vec<Hdr>* /*outTypes*/, + float* outMaxLuminance, + float* outMaxAverageLuminance, + float* outMinLuminance) { + *outMaxLuminance = 0; + *outMaxAverageLuminance = 0; + *outMinLuminance = 0; + return Error::NONE; +} + +Error VrHwc::setLayerPerFrameMetadata_2_3( + Display display, Layer layer, + const std::vector<IComposerClient::PerFrameMetadata>& metadata) { + return Error::NONE; +} + +Error VrHwc::getPerFrameMetadataKeys_2_3( + Display display, + std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) { + return Error::NONE; +} + +Error VrHwc::setColorMode_2_3(Display display, ColorMode mode, + RenderIntent intent) { + std::lock_guard<std::mutex> guard(mutex_); + auto display_ptr = FindDisplay(display); + if (!display_ptr) + return Error::BAD_DISPLAY; + + if (mode < ColorMode::NATIVE || mode > ColorMode::DISPLAY_P3) + return Error::BAD_PARAMETER; + + display_ptr->set_color_mode(mode); + return Error::NONE; +} + +Error VrHwc::getRenderIntents_2_3(Display display, ColorMode mode, + std::vector<RenderIntent>* outIntents) { + return Error::NONE; +} + +Error VrHwc::getColorModes_2_3(Display display, hidl_vec<ColorMode>* outModes) { + return Error::NONE; +} + +Error VrHwc::getClientTargetSupport_2_3(Display display, uint32_t width, + uint32_t height, PixelFormat format, + Dataspace dataspace) { + return Error::NONE; +} + +Error VrHwc::getReadbackBufferAttributes_2_3(Display display, + PixelFormat* outFormat, + Dataspace* outDataspace) { + return Error::NONE; +} + +Error VrHwc::getDisplayIdentificationData(Display display, uint8_t* outPort, + std::vector<uint8_t>* outData) { + int error = 0; + auto display_client = display::DisplayClient::Create(&error); + if (!display_client) { + ALOGE("Could not connect to display service : %s(%d)", strerror(error), + error); + return Error::BAD_CONFIG; + } + auto edid_data = display_client->GetConfigurationData( + display::ConfigFileType::kDeviceEdid); + auto display_identification_port = + display_client->GetDisplayIdentificationPort(); + *outPort = display_identification_port.get(); + + std::copy(edid_data.get().begin(), edid_data.get().end(), + std::back_inserter(*outData)); + return Error::NONE; +} + +Error VrHwc::setLayerColorTransform(Display display, Layer layer, + const float* matrix) { + return Error::NONE; +} + +Error VrHwc::getDisplayedContentSamplingAttributes( + Display display, PixelFormat& format, Dataspace& dataspace, + hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) { + return Error::NONE; +} + +Error VrHwc::setDisplayedContentSamplingEnabled( + Display display, IComposerClient::DisplayedContentSampling enable, + hidl_bitfield<IComposerClient::FormatColorComponent> componentMask, + uint64_t maxFrames) { + return Error::NONE; +} + +Error VrHwc::getDisplayedContentSample(Display display, uint64_t maxFrames, + uint64_t timestamp, uint64_t& frameCount, + hidl_vec<uint64_t>& sampleComponent0, + hidl_vec<uint64_t>& sampleComponent1, + hidl_vec<uint64_t>& sampleComponent2, + hidl_vec<uint64_t>& sampleComponent3) { + return Error::NONE; +} + +Error VrHwc::getDisplayCapabilities( + Display display, + std::vector<IComposerClient::DisplayCapability>* outCapabilities) { + return Error::NONE; +} + +Error VrHwc::setLayerPerFrameMetadataBlobs( + Display display, Layer layer, + std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) { + return Error::NONE; +} + +Error VrHwc::getDisplayBrightnessSupport(Display display, bool* outSupport) { + return Error::NONE; +} + +Error VrHwc::setDisplayBrightness(Display display, float brightness) { + return Error::NONE; +} + } // namespace dvr } // namespace android diff --git a/services/vr/hardware_composer/impl/vr_hwc.h b/services/vr/hardware_composer/impl/vr_hwc.h index e8c0212039..3e3a6307fa 100644 --- a/services/vr/hardware_composer/impl/vr_hwc.h +++ b/services/vr/hardware_composer/impl/vr_hwc.h @@ -17,9 +17,9 @@ #define ANDROID_DVR_HARDWARE_COMPOSER_IMPL_VR_HWC_H #include <android-base/unique_fd.h> -#include <android/frameworks/vr/composer/1.0/IVrComposerClient.h> -#include <android/hardware/graphics/composer/2.1/IComposer.h> -#include <composer-hal/2.1/ComposerHal.h> +#include <android/frameworks/vr/composer/2.0/IVrComposerClient.h> +#include <android/hardware/graphics/composer/2.3/IComposer.h> +#include <composer-hal/2.3/ComposerHal.h> #include <private/dvr/vsync_service.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> @@ -28,15 +28,21 @@ #include <mutex> #include <unordered_map> -using namespace android::frameworks::vr::composer::V1_0; +using namespace android::frameworks::vr::composer::V2_0; using namespace android::hardware::graphics::common::V1_0; -using namespace android::hardware::graphics::composer::V2_1; +using namespace android::hardware::graphics::composer::V2_3; +using android::hardware::hidl_bitfield; using android::hardware::hidl_handle; using android::hardware::hidl_string; using android::hardware::hidl_vec; using android::hardware::Return; using android::hardware::Void; +using android::hardware::graphics::composer::V2_1::Config; +using android::hardware::graphics::composer::V2_1::Display; +using android::hardware::graphics::composer::V2_1::Error; +using android::hardware::graphics::composer::V2_1::Layer; +using android::hardware::graphics::composer::V2_3::IComposerClient; namespace android { @@ -46,16 +52,23 @@ namespace dvr { class VrComposerClient; -using android::hardware::graphics::common::V1_0::PixelFormat; -using android::hardware::graphics::composer::V2_1::hal::ComposerHal; +using android::hardware::graphics::composer::V2_3::hal::ComposerHal; + +namespace types = android::hardware::graphics::common; + +using types::V1_1::RenderIntent; +using types::V1_2::ColorMode; +using types::V1_2::Dataspace; +using types::V1_2::Hdr; +using types::V1_2::PixelFormat; class ComposerView { public: struct ComposerLayer { - using Recti = hardware::graphics::composer::V2_1::IComposerClient::Rect; - using Rectf = hardware::graphics::composer::V2_1::IComposerClient::FRect; + using Recti = hardware::graphics::composer::V2_3::IComposerClient::Rect; + using Rectf = hardware::graphics::composer::V2_3::IComposerClient::FRect; using BlendMode = - hardware::graphics::composer::V2_1::IComposerClient::BlendMode; + hardware::graphics::composer::V2_3::IComposerClient::BlendMode; Layer id; sp<GraphicBuffer> buffer; @@ -111,7 +124,7 @@ class ComposerView { struct HwcLayer { using Composition = - hardware::graphics::composer::V2_1::IComposerClient::Composition; + hardware::graphics::composer::V2_3::IComposerClient::Composition; explicit HwcLayer(Layer new_id) { info.id = new_id; } @@ -205,96 +218,157 @@ class VrHwc : public IComposer, public ComposerHal, public ComposerView { Display display, Layer layer, const IVrComposerClient::BufferMetadata& metadata); - // ComposerHal + // composer::V2_1::ComposerHal bool hasCapability(hwc2_capability_t capability) override; std::string dumpDebugInfo() override { return {}; } - void registerEventCallback(EventCallback* callback) override; + + void registerEventCallback(ComposerHal::EventCallback* callback) override; void unregisterEventCallback() override; uint32_t getMaxVirtualDisplayCount() override; - Error createVirtualDisplay(uint32_t width, uint32_t height, - PixelFormat* format, Display* outDisplay) override; Error destroyVirtualDisplay(Display display) override; Error createLayer(Display display, Layer* outLayer) override; Error destroyLayer(Display display, Layer layer) override; Error getActiveConfig(Display display, Config* outConfig) override; - Error getClientTargetSupport(Display display, - uint32_t width, uint32_t height, - PixelFormat format, Dataspace dataspace) override; - Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override; Error getDisplayAttribute(Display display, Config config, - IComposerClient::Attribute attribute, int32_t* outValue) override; + IComposerClient::Attribute attribute, + int32_t* outValue) override; Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override; Error getDisplayName(Display display, hidl_string* outName) override; Error getDisplayType(Display display, - IComposerClient::DisplayType* outType) override; + IComposerClient::DisplayType* outType) override; Error getDozeSupport(Display display, bool* outSupport) override; - Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, - float* outMaxLuminance, float* outMaxAverageLuminance, - float* outMinLuminance) override; Error setActiveConfig(Display display, Config config) override; - Error setColorMode(Display display, ColorMode mode) override; - Error setPowerMode(Display display, IComposerClient::PowerMode mode) override; Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override; Error setColorTransform(Display display, const float* matrix, - int32_t hint) override; + int32_t hint) override; Error setClientTarget(Display display, buffer_handle_t target, - int32_t acquireFence, int32_t dataspace, - const std::vector<hwc_rect_t>& damage) override; + int32_t acquireFence, int32_t dataspace, + const std::vector<hwc_rect_t>& damage) override; Error setOutputBuffer(Display display, buffer_handle_t buffer, - int32_t releaseFence) override; - Error validateDisplay(Display display, - std::vector<Layer>* outChangedLayers, - std::vector<IComposerClient::Composition>* outCompositionTypes, - uint32_t* outDisplayRequestMask, - std::vector<Layer>* outRequestedLayers, - std::vector<uint32_t>* outRequestMasks) override; + int32_t releaseFence) override; + Error validateDisplay( + Display display, std::vector<Layer>* outChangedLayers, + std::vector<IComposerClient::Composition>* outCompositionTypes, + uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers, + std::vector<uint32_t>* outRequestMasks) override; Error acceptDisplayChanges(Display display) override; Error presentDisplay(Display display, int32_t* outPresentFence, - std::vector<Layer>* outLayers, - std::vector<int32_t>* outReleaseFences) override; + std::vector<Layer>* outLayers, + std::vector<int32_t>* outReleaseFences) override; - Error setLayerCursorPosition(Display display, Layer layer, - int32_t x, int32_t y) override; - Error setLayerBuffer(Display display, Layer layer, - buffer_handle_t buffer, int32_t acquireFence) override; + Error setLayerCursorPosition(Display display, Layer layer, int32_t x, + int32_t y) override; + Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer, + int32_t acquireFence) override; Error setLayerSurfaceDamage(Display display, Layer layer, - const std::vector<hwc_rect_t>& damage) override; + const std::vector<hwc_rect_t>& damage) override; Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override; Error setLayerColor(Display display, Layer layer, - IComposerClient::Color color) override; + IComposerClient::Color color) override; Error setLayerCompositionType(Display display, Layer layer, - int32_t type) override; + int32_t type) override; Error setLayerDataspace(Display display, Layer layer, - int32_t dataspace) override; + int32_t dataspace) override; Error setLayerDisplayFrame(Display display, Layer layer, - const hwc_rect_t& frame) override; + const hwc_rect_t& frame) override; Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override; Error setLayerSidebandStream(Display display, Layer layer, - buffer_handle_t stream) override; + buffer_handle_t stream) override; Error setLayerSourceCrop(Display display, Layer layer, - const hwc_frect_t& crop) override; + const hwc_frect_t& crop) override; Error setLayerTransform(Display display, Layer layer, - int32_t transform) override; + int32_t transform) override; Error setLayerVisibleRegion(Display display, Layer layer, - const std::vector<hwc_rect_t>& visible) override; + const std::vector<hwc_rect_t>& visible) override; Error setLayerZOrder(Display display, Layer layer, uint32_t z) override; + // composer::V2_2::ComposerHal + Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle, + android::base::unique_fd fenceFd) override; + Error getReadbackBufferFence(Display display, + android::base::unique_fd* outFenceFd) override; + Error createVirtualDisplay_2_2(uint32_t width, uint32_t height, + types::V1_1::PixelFormat* format, + Display* outDisplay) override; + Error setPowerMode_2_2(Display display, + IComposerClient::PowerMode mode) override; + Error setLayerFloatColor(Display display, Layer layer, + IComposerClient::FloatColor color) override; + Error getRenderIntents(Display display, types::V1_1::ColorMode mode, + std::vector<RenderIntent>* outIntents) override; + std::array<float, 16> getDataspaceSaturationMatrix( + types::V1_1::Dataspace dataspace) override; + + // composer::V2_3::ComposerHal + Error getHdrCapabilities_2_3(Display display, hidl_vec<Hdr>* outTypes, + float* outMaxLuminance, + float* outMaxAverageLuminance, + float* outMinLuminance) override; + Error setLayerPerFrameMetadata_2_3( + Display display, Layer layer, + const std::vector<IComposerClient::PerFrameMetadata>& metadata) override; + Error getPerFrameMetadataKeys_2_3( + Display display, + std::vector<IComposerClient::PerFrameMetadataKey>* outKeys) override; + Error setColorMode_2_3(Display display, ColorMode mode, + RenderIntent intent) override; + Error getRenderIntents_2_3(Display display, ColorMode mode, + std::vector<RenderIntent>* outIntents) override; + Error getColorModes_2_3(Display display, + hidl_vec<ColorMode>* outModes) override; + Error getClientTargetSupport_2_3(Display display, uint32_t width, + uint32_t height, PixelFormat format, + Dataspace dataspace) override; + Error getReadbackBufferAttributes_2_3(Display display, PixelFormat* outFormat, + Dataspace* outDataspace) override; + Error getDisplayIdentificationData(Display display, uint8_t* outPort, + std::vector<uint8_t>* outData) override; + Error setLayerColorTransform(Display display, Layer layer, + const float* matrix) override; + Error getDisplayedContentSamplingAttributes( + Display display, PixelFormat& format, Dataspace& dataspace, + hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) + override; + Error setDisplayedContentSamplingEnabled( + Display display, IComposerClient::DisplayedContentSampling enable, + hidl_bitfield<IComposerClient::FormatColorComponent> componentMask, + uint64_t maxFrames) override; + Error getDisplayedContentSample( + Display display, uint64_t maxFrames, uint64_t timestamp, + uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0, + hidl_vec<uint64_t>& sampleComponent1, + hidl_vec<uint64_t>& sampleComponent2, + hidl_vec<uint64_t>& sampleComponent3) override; + Error getDisplayCapabilities(Display display, + std::vector<IComposerClient::DisplayCapability>* + outCapabilities) override; + Error setLayerPerFrameMetadataBlobs( + Display display, Layer layer, + std::vector<IComposerClient::PerFrameMetadataBlob>& blobs) override; + Error getDisplayBrightnessSupport(Display display, bool* outSupport) override; + Error setDisplayBrightness(Display display, float brightness) override; + // IComposer: Return<void> getCapabilities(getCapabilities_cb hidl_cb) override; Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override; Return<void> createClient(createClient_cb hidl_cb) override; + Return<void> createClient_2_3( + IComposer::createClient_2_3_cb hidl_cb) override; // ComposerView: void ForceDisplaysRefresh() override; void RegisterObserver(Observer* observer) override; void UnregisterObserver(Observer* observer) override; + Return<void> debug(const hidl_handle& fd, + const hidl_vec<hidl_string>& args) override; + private: class VsyncCallback : public BnVsyncCallback { public: diff --git a/services/vr/virtual_touchpad/virtual_touchpad.rc b/services/vr/virtual_touchpad/virtual_touchpad.rc index 99315ef341..0de0f9eec7 100644 --- a/services/vr/virtual_touchpad/virtual_touchpad.rc +++ b/services/vr/virtual_touchpad/virtual_touchpad.rc @@ -1,5 +1,5 @@ service virtual_touchpad /system/bin/virtual_touchpad class core user system - group system input + group system input uhid writepid /dev/cpuset/system/tasks diff --git a/vulkan/Android.bp b/vulkan/Android.bp index 7747734cea..4934970aaa 100644 --- a/vulkan/Android.bp +++ b/vulkan/Android.bp @@ -31,6 +31,5 @@ cc_library_headers { subdirs = [ "nulldrv", "libvulkan", - "tools", "vkjson", ] diff --git a/vulkan/README.md b/vulkan/README.md index 9fba7281fd..185aa39da6 100644 --- a/vulkan/README.md +++ b/vulkan/README.md @@ -2,6 +2,10 @@ This subdirectory contains Android's Vulkan loader, as well as some Vulkan-related tools useful to platform developers. +## Documentation + +The former contents of doc/implementors_guide/ are now at https://source.android.com/devices/graphics/implement-vulkan. + ## Coding Style We follow the [Chromium coding style](https://www.chromium.org/developers/coding-style) for naming and formatting, except with four-space indentation instead of two spaces. In general, any C++ features supported by the prebuilt platform toolchain are allowed. @@ -10,19 +14,9 @@ Use "clang-format -style=file" to format all C/C++ code, except code imported ve ## Code Generation -We generate several parts of the loader and tools from a Vulkan API description file, stored in `api/vulkan.api`. Code generation must be done manually because the generator tools aren't part of the platform toolchain (yet?). Files named `foo_gen.*` are generated from the API file and a template file named `foo.tmpl`. - - To run the generator: +We generate several parts of the loader and tools driectly from the Vulkan Registry (external/vulkan-headers/registry/vk.xml). Code generation must be done manually because the generator is not part of the platform toolchain (yet?). Files named `foo_gen.*` are generated by the code generator. -### One-time setup -- Install [golang](https://golang.org/), if you don't have it already. -- Create a directory (e.g. `$HOME/lib/go`) for local go sources and binaries and add it to `$GOPATH`. -- `$ git clone https://android.googlesource.com/platform/tools/gpu $GOPATH/src/android.googlesource.com/platform/tools/gpu` -- `$ go get android.googlesource.com/platform/tools/gpu/api/...` -- You should now have `$GOPATH/bin/apic`. You might want to add `$GOPATH/bin` to your `$PATH`. +### Run The Code Generator -### Generating code -To generate `libvulkan/*_gen.*`, -- `$ cd libvulkan` -- `$ apic template ../api/vulkan.api code-generator.tmpl` -Similar for `nulldrv/null_driver_gen.*`. +Install Python3 (if not already installed) and execute below: +`$ ./scripts/code_generator.py` diff --git a/vulkan/api/platform.api b/vulkan/api/platform.api deleted file mode 100644 index a7c4c30b5e..0000000000 --- a/vulkan/api/platform.api +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) 2015 The Khronos Group Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and/or associated documentation files (the -// "Materials"), to deal in the Materials without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Materials, and to -// permit persons to whom the Materials are furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Materials. -// -// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. - -// Platform types, as defined or included in vk_platform.h - -type u64 size_t - -// VK_USE_PLATFORM_XLIB_KHR -@internal class Display {} -@internal class Window {} -@internal type u64 VisualID - -// VK_USE_PLATFORM_XCB_KHR -@internal class xcb_connection_t {} -@internal type u32 xcb_window_t -@internal type u32 xcb_visualid_t - -// VK_USE_PLATFORM_WAYLAND_KHR -@internal class wl_display {} -@internal class wl_surface {} - -// VK_USE_PLATFORM_MIR_KHR -@internal class MirConnection {} -@internal class MirSurface {} - -// VK_USE_PLATFORM_ANDROID_KHR -@internal class ANativeWindow {} -@internal class AHardwareBuffer {} -@internal type void* buffer_handle_t - -// VK_USE_PLATFORM_WIN32_KHR -@internal type void* HINSTANCE -@internal type void* HWND -@internal type void* HANDLE -@internal type u32 DWORD -@internal type u16* LPCWSTR -@internal class SECURITY_ATTRIBUTES {} - -// VK_USE_PLATFORM_XLIB_XRANDR_EXT -@internal type u64 RROutput - -// VK_USE_PLATFORM_FUCHSIA -@internal type u32 zx_handle_t
\ No newline at end of file diff --git a/vulkan/api/templates/asciidoc.tmpl b/vulkan/api/templates/asciidoc.tmpl deleted file mode 100644 index 3009e19cad..0000000000 --- a/vulkan/api/templates/asciidoc.tmpl +++ /dev/null @@ -1,151 +0,0 @@ -{{Include "vulkan_common.tmpl"}} -{{if not (Global "AsciiDocPath")}}{{Global "AsciiDocPath" "../../doc/specs/vulkan/"}}{{end}} -{{$ | Macro "AsciiDoc.Main"}} - - -{{/* -------------------------------------------------------------------------------- - AsciiDoc generation main entry point. -------------------------------------------------------------------------------- -*/}} -{{define "AsciiDoc.Main"}} - {{$docPath := Global "AsciiDocPath"}} - - {{/* Generate AsciiDoc files for API enums and bitfields (flags). */}} - {{range $e := $.Enums}} - {{if not $e.IsBitfield}} - {{$filename := print $docPath "enums/" (Macro "EnumName" $e) ".txt"}} - {{Macro "AsciiDoc.Write" "Code" (Macro "AsciiDoc.Enum" $e) "File" $filename}} - {{else}} - {{$filename := print $docPath "flags/" (Macro "EnumName" $e) ".txt"}} - {{Macro "AsciiDoc.Write" "Code" (Macro "AsciiDoc.Flag" $e) "File" $filename}} - {{end}} - {{end}} - - {{/* Generate AsciiDoc files for API commands (protos). */}} - {{range $f := (AllCommands $)}} - {{if not (GetAnnotation $f "pfn")}} - {{$filename := print $docPath "protos/" $f.Name ".txt"}} - {{Macro "AsciiDoc.Write" "Code" (Macro "AsciiDoc.Proto" $f) "File" $filename}} - {{end}} - {{end}} - - {{/* Generate AsciiDoc files for API structs. */}} - {{range $c := $.Classes}} - {{if not (GetAnnotation $c "internal")}} - {{$filename := print $docPath "structs/" $c.Name ".txt"}} - {{Macro "AsciiDoc.Write" "Code" (Macro "AsciiDoc.Struct" $c) "File" $filename}} - {{end}} - {{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the AsciiDoc contents for the specified API enum. -------------------------------------------------------------------------------- -*/}} -{{define "AsciiDoc.Enum"}} - {{AssertType $ "Enum"}} - - {{Macro "Docs" $.Docs}} - typedef enum { - {{range $i, $e := $.Entries}} - {{Macro "EnumEntry" $e}} = {{AsSigned $e.Value}}, {{Macro "Docs" $e.Docs}} - {{end}} - ¶ - {{$name := Macro "EnumName" $ | TrimRight "ABCDEFGHIJKLMNOQRSTUVWXYZ" | SplitPascalCase | Upper | JoinWith "_"}} - {{$first := Macro "EnumFirstEntry" $}} - {{$last := Macro "EnumLastEntry" $}} - {{$name}}_BEGIN_RANGE = {{$first}}, - {{$name}}_END_RANGE = {{$last}}, - {{$name}}_NUM = ({{$last}} - {{$first}} + 1), - {{$name}}_MAX_ENUM = 0x7FFFFFFF - } {{Macro "EnumName" $}}; -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the AsciiDoc contents for the specified API bitfield. -------------------------------------------------------------------------------- -*/}} -{{define "AsciiDoc.Flag"}} - {{AssertType $ "Enum"}} - - {{Macro "Docs" $.Docs}} - typedef VkFlags {{Macro "EnumName" $}}; - {{if $.Entries}} - typedef enum { - {{range $e := $.Entries}} - {{Macro "BitfieldEntryName" $e}} = {{printf "%#.8x" $e.Value}}, {{Macro "Docs" $e.Docs}} - {{end}} - } {{Macro "EnumName" $ | TrimRight "s"}}Bits; - {{end}} -{{end}} - - - -{{/* -------------------------------------------------------------------------------- - Emits the AsciiDoc contents for the specified API class. -------------------------------------------------------------------------------- -*/}} -{{define "AsciiDoc.Struct"}} - {{AssertType $ "Class"}} - - {{Macro "Docs" $.Docs}} - typedef {{if GetAnnotation $ "union"}}union{{else}}struct{{end}} { - {{range $f := $.Fields}} - {{Node "Type" $f}} {{$f.Name}}{{Macro "ArrayPostfix" (TypeOf $f)}}; {{Macro "Docs" $f.Docs}} - {{end}} - } {{Macro "StructName" $}}; -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the AsciiDoc contents for the specified API function. -------------------------------------------------------------------------------- -*/}} -{{define "AsciiDoc.Proto"}} - {{AssertType $ "Function"}} - - {{Macro "Docs" $.Docs}} - {{Node "Type" $.Return}} VKAPI {{Macro "FunctionName" $}}({{Macro "Parameters" $}}); -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Wraps the specified Code in AsciiDoc source tags then writes to the specified File. -------------------------------------------------------------------------------- -*/}} -{{define "AsciiDoc.Write"}} - {{AssertType $.Code "string"}} - {{AssertType $.File "string"}} - - {{$code := $.Code | Format (Global "clang-format")}} - {{JoinWith "\n" (Macro "AsciiDoc.Header") $code (Macro "AsciiDoc.Footer") ""| Write $.File}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits an AsciiDoc source header. -------------------------------------------------------------------------------- -*/}} -{{define "AsciiDoc.Header"}} -[source,{basebackend@docbook:c++:cpp}] ------------------------------------------------------------------------------- -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits an AsciiDoc source footer. -------------------------------------------------------------------------------- -*/}} -{{define "AsciiDoc.Footer"}} ------------------------------------------------------------------------------- -{{end}} diff --git a/vulkan/api/templates/vk_xml.tmpl b/vulkan/api/templates/vk_xml.tmpl deleted file mode 100644 index 893bde7833..0000000000 --- a/vulkan/api/templates/vk_xml.tmpl +++ /dev/null @@ -1,435 +0,0 @@ -{{Include "vulkan_common.tmpl"}} -{{Macro "DefineGlobals" $}} -{{$ | Macro "vk.xml" | Reflow 4 | Write "vk.xml"}} - - -{{/* -------------------------------------------------------------------------------- - Entry point -------------------------------------------------------------------------------- -*/}} -{{define "vk.xml"}} -<?xml version="1.0" encoding="UTF-8"?> -<registry> - »<comment>« -Copyright (c) 2015 The Khronos Group Inc. -¶ -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: -¶ -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. -¶ -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -¶ ------------------------------------------------------------------------- -¶ -This file, vk.xml, is the Vulkan API Registry.» - </comment> -¶ - <!-- SECTION: Vulkan type definitions --> - <types>» - <type name="vk_platform" category="include">#include "vk_platform.h"</type> -¶ - <type category="define">#define <name>VK_MAKE_VERSION</name>(major, minor, patch) \ - «((major << 22) | (minor << 12) | patch)</type>» -¶ - <type category="define">// Vulkan API version supported by this file«« -#define <name>VK_API_VERSION</name> <type>VK_MAKE_VERSION</type>({{Global "VERSION_MAJOR"}}, {{Global "VERSION_MINOR"}}, {{Global "VERSION_PATCH"}})</type> -¶ - »»<type category="define">«« -#if (_MSC_VER >= 1800 || __cplusplus >= 201103L) -#define <name>VK_NONDISP_HANDLE_OPERATOR_BOOL</name>() explicit operator bool() const { return handle != 0; } -#else -#define VK_NONDISP_HANDLE_OPERATOR_BOOL() -«#endif - »»»</type> -¶ - <type category="define">««« -#define <name>VK_DEFINE_HANDLE</name>(obj) typedef struct obj##_T* obj;</type> - »»»<type category="define">««« -#if defined(__cplusplus) - »»#if (_MSC_VER >= 1800 || __cplusplus >= 201103L) - »// The bool operator only works if there are no implicit conversions from an obj to - // a bool-compatible type, which can then be used to unintentionally violate type safety. - // C++11 and above supports the "explicit" keyword on conversion operators to stop this - // from happening. Otherwise users of C++ below C++11 won't get direct access to evaluating - // the object handle as a bool in expressions like: - // if (obj) vkDestroy(obj); - #define VK_NONDISP_HANDLE_OPERATOR_BOOL() explicit operator bool() const { return handle != 0; } - #define VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \ - explicit obj(uint64_t x) : handle(x) { } \ - obj(decltype(nullptr)) : handle(0) { } - «#else» - #define VK_NONDISP_HANDLE_OPERATOR_BOOL() - #define VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \ - obj(uint64_t x) : handle(x) { } - «#endif - #define <name>VK_DEFINE_NONDISP_HANDLE</name>(obj) \» - struct obj { \ - obj() { } \ - VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \ - obj& operator =(uint64_t x) { handle = x; return *this; } \ - bool operator==(const obj& other) const { return handle == other.handle; } \ - bool operator!=(const obj& other) const { return handle != other.handle; } \ - bool operator!() const { return !handle; } \ - VK_NONDISP_HANDLE_OPERATOR_BOOL() \ - uint64_t handle; \ - }; -««#else - »#define VK_DEFINE_NONDISP_HANDLE(obj) typedef struct obj##_T { uint64_t handle; } obj;« -#endif - »»</type> -¶ - <type category="define"> -#if defined(__cplusplus) && ((defined(_MSC_VER) && _MSC_VER >= 1800) || __cplusplus >= 201103L) - »#define <name>VK_NULL_HANDLE</name> nullptr -«#else - »#define VK_NULL_HANDLE 0 -«#endif - »»</type> -¶ - <type requires="vk_platform" name="VkDeviceSize"/> - <type requires="vk_platform" name="VkSampleMask"/> - <type requires="vk_platform" name="VkFlags"/> - <!-- Basic C types, pulled in via vk_platform.h --> - <type requires="vk_platform" name="char"/> - <type requires="vk_platform" name="float"/> - <type requires="vk_platform" name="VkBool32"/> - <type requires="vk_platform" name="uint8_t"/> - <type requires="vk_platform" name="uint32_t"/> - <type requires="vk_platform" name="uint64_t"/> - <type requires="vk_platform" name="int32_t"/> - <type requires="vk_platform" name="size_t"/> - <!-- Bitfield types --> - {{range $e := $.Enums}} - {{if $e.IsBitfield}} - {{$bits := print (Macro "EnumName" $e | TrimRight "s") "Bits"}} - <type{{if $e.Entries}} requires="{{$bits}}"{{end}} category="bitmask">typedef <type>VkFlags</type> <name>{{$e.Name}}</name>;</type>§ - {{if $e.Entries}}{{Macro "XML.Docs" $e.Docs}} - {{else}}{{Macro "XML.Docs" (Strings $e.Docs "(no bits yet)")}} - {{end}} - {{end}} - {{end}} -¶ - <!-- Types which can be void pointers or class pointers, selected at compile time --> - {{range $i, $p := $.Pseudonyms}} - {{ if (GetAnnotation $p "dispatchHandle")}} - {{if Global "VK_DEFINE_HANDLE_TYPE_DEFINED"}} - <type category="handle">VK_DEFINE_HANDLE(<name>{{$p.Name}}</name>)</type> - {{else}} - {{Global "VK_DEFINE_HANDLE_TYPE_DEFINED" "YES"}} - <type category="handle"><type>VK_DEFINE_HANDLE</type>(<name>{{$p.Name}}</name>)</type> - {{end}} - {{else if (GetAnnotation $p "nonDispatchHandle")}} - {{if Global "VK_DEFINE_NONDISP_HANDLE_TYPE_DEFINED"}} - <type category="handle">VK_DEFINE_NONDISP_HANDLE(<name>{{$p.Name}}</name>)</type> - {{else}} - {{Global "VK_DEFINE_NONDISP_HANDLE_TYPE_DEFINED" "YES"}} - <type category="handle"><type>VK_DEFINE_NONDISP_HANDLE</type>(<name>{{$p.Name}}</name>)</type> - {{end}} - {{end}} - {{end}} -¶ - <!-- Types generated from corresponding <enums> tags below --> - {{range $e := SortBy $.Enums "EnumName"}} - {{if and $e.Entries (not (GetAnnotation $e "internal"))}} - {{if $e.IsBitfield}} - <type name="{{Macro "EnumName" $e | TrimRight "s"}}Bits" category="enum"/> - {{else}} - <type name="{{$e.Name}}" category="enum"/> - {{end}} - {{end}} - {{end}} -¶ - <!-- The PFN_vk*Function types are used by VkAllocCallbacks below --> - <type>typedef void* (VKAPI *<name>PFN_vkAllocFunction</name>)(« - void* pUserData, - size_t size, - size_t alignment, - <type>VkSystemAllocType</type> allocType);</type>» - <type>typedef void (VKAPI *<name>PFN_vkFreeFunction</name>)(« - void* pUserData, - void* pMem);</type>» -¶ - <!-- The PFN_vkVoidFunction type are used by VkGet*ProcAddr below --> - <type>typedef void (VKAPI *<name>PFN_vkVoidFunction</name>)(void);</type> -¶ - <!-- Struct types --> - {{range $c := $.Classes}} - {{if not (GetAnnotation $c "internal")}} - {{Macro "Struct" $c}} - {{end}} - {{end}} - «</types> -¶ - <!-- SECTION: Vulkan enumerant (token) definitions. --> -¶ - <enums namespace="VK" comment="Misc. hardcoded constants - not an enumerated type">» - <!-- This is part of the header boilerplate --> - {{range $d := $.Definitions}} - {{if HasPrefix $d.Name "VK_"}} - <enum value="{{$d.Expression}}" name="{{$d.Name}}"/>{{Macro "XML.Docs" $d.Docs}} - {{end}} - {{end}} - <enum value="1000.0f" name="VK_LOD_CLAMP_NONE"/> - <enum value="(-0U)" name="VK_REMAINING_MIP_LEVELS"/> - <enum value="(~0U)" name="VK_REMAINING_ARRAY_LAYERS"/> - <enum value="(_0ULL)" name="VK_WHOLE_SIZE"/> - <enum value="(~0U)" name="VK_ATTACHMENT_UNUSED"/> - <enum value="(~0U)" name="VK_QUEUE_FAMILY_IGNORED"/> - <enum value="(~0U)" name="VK_SUBPASS_EXTERNAL"/> - «</enums> -¶ - <!-- Unlike OpenGL, most tokens in Vulkan are actual typed enumerants in» - their own numeric namespaces. The "name" attribute is the C enum - type name, and is pulled in from a <type> definition above - (slightly clunky, but retains the type / enum distinction). "type" - attributes of "enum" or "bitmask" indicate that these values should - be generated inside an appropriate definition. -->« -¶ - {{range $e := $.Enums}} - {{if not (or $e.IsBitfield (GetAnnotation $e "internal"))}} - {{Macro "XML.Enum" $e}} - {{end}} - {{end}} -¶ - <!-- Flags --> - {{range $e := $.Enums}} - {{if $e.IsBitfield}} - {{Macro "XML.Bitfield" $e}} - {{end}} - {{end}} -¶ - <!-- SECTION: Vulkan command definitions --> - <commands namespace="vk">» - {{range $f := AllCommands $}} - {{if not (GetAnnotation $f "pfn")}} - {{Macro "XML.Function" $f}} - {{end}} - {{end}} - «</commands> -¶ - <!-- SECTION: Vulkan API interface definitions --> - <feature api="vulkan" name="VK_VERSION_1_0" number="1.0">» - <require comment="Header boilerplate">» - <type name="vk_platform"/> - «</require> - <require comment="API version">» - <type name="VK_API_VERSION"/> - «</require> - <require comment="API constants">» - <enum name="VK_LOD_CLAMP_NONE"/> - <enum name="VK_REMAINING_MIP_LEVELS"/> - <enum name="VK_REMAINING_ARRAY_LAYERS"/> - <enum name="VK_WHOLE_SIZE"/> - <enum name="VK_ATTACHMENT_UNUSED"/> - <enum name="VK_TRUE"/> - <enum name="VK_FALSE"/> - «</require> - <require comment="All functions (TODO: split by type)">» - {{range $f := AllCommands $}} - {{if not (GetAnnotation $f "pfn")}} - <command name="{{$f.Name}}"/> - {{end}} - {{end}} - </require> - «<require comment="Types not directly used by the API">» - <!-- Include <type name="typename"/> here for e.g. structs that» - are not parameter types of commands, but still need to be - defined in the API. - «--> - <type name="VkBufferMemoryBarrier"/> - <type name="VkDispatchIndirectCmd"/> - <type name="VkDrawIndexedIndirectCmd"/> - <type name="VkDrawIndirectCmd"/> - <type name="VkImageMemoryBarrier"/> - <type name="VkMemoryBarrier"/> - «</require> - «</feature> -¶ - <!-- SECTION: Vulkan extension interface definitions (none yet) --> -«</registry> -{{end}} - -{{/* -------------------------------------------------------------------------------- - Emits the C declaration for the specified bitfield. -------------------------------------------------------------------------------- -*/}} -{{define "XML.Bitfield"}} - {{AssertType $ "Enum"}} - - {{if $.Entries}} - <enums namespace="VK" name="{{Macro "EnumName" $ | TrimRight "s"}}Bits" type="bitmask">» - {{range $e := $.Entries}} - {{$pos := Bitpos $e.Value}} - <enum § - {{if gt $pos -1}} bitpos="{{$pos}}" § - {{else}}value="{{if $e.Value}}{{printf "0x%.8X" $e.Value}}{{else}}0{{end}}" § - {{end}}name="{{Macro "BitfieldEntryName" $e}}" § - {{if $d := $e.Docs}} comment="{{$d | JoinWith " "}}"{{end}}/> - {{end}} - «</enums> - {{end}} - -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C declaration for the specified enum. -------------------------------------------------------------------------------- -*/}} -{{define "XML.Enum"}} - {{AssertType $ "Enum"}} - - <enums namespace="VK" name="{{Macro "EnumName" $}}" type="enum" § - expand="{{Macro "EnumName" $ | SplitPascalCase | Upper | JoinWith "_"}}"{{if $.Docs}} comment="{{$.Docs | JoinWith " "}}"{{end}}>» - {{range $i, $e := $.Entries}} - <enum value="{{AsSigned $e.Value}}" name="{{Macro "BitfieldEntryName" $e}}"{{if $e.Docs}} comment="{{$e.Docs | JoinWith " "}}"{{end}}/> - {{end}} - {{if $lu := GetAnnotation $ "lastUnused"}} - <unused start="{{index $lu.Arguments 0}}"/> - {{end}} - «</enums> -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C declaration for the specified class. -------------------------------------------------------------------------------- -*/}} -{{define "Struct"}} - {{AssertType $ "Class"}} - - <type category="{{Macro "StructType" $}}" name="{{Macro "StructName" $}}"{{if $.Docs}} comment="{{$.Docs | JoinWith " "}}"{{end}}>» - {{range $f := $.Fields}} - <member>{{Node "XML.Type" $f}} <name>{{$f.Name}}</name>{{Macro "XML.ArrayPostfix" $f}}</member>{{Macro "XML.Docs" $f.Docs}} - {{end}} - «</type> -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits either 'struct' or 'union' for the specified class. -------------------------------------------------------------------------------- -*/}} -{{define "StructType"}} - {{AssertType $ "Class"}} - - {{if GetAnnotation $ "union"}}union{{else}}struct{{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C function pointer typedef declaration for the specified command. -------------------------------------------------------------------------------- -*/}} -{{define "XML.Function"}} - {{AssertType $ "Function"}} - - {{$ts := GetAnnotation $ "threadSafety"}} - <command{{if $ts}} threadsafe="{{index $ts.Arguments 0}}"{{end}}>» - <proto>{{Node "XML.Type" $.Return}} <name>{{$.Name}}</name></proto> - {{range $p := $.CallParameters}} - <param>{{Node "XML.Type" $p}} <name>{{$p.Name}}{{Macro "ArrayPostfix" $p}}</name></param> - {{end}} - «</command> -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the XML translation for the specified documentation block (string array). -------------------------------------------------------------------------------- -*/}} -{{define "XML.Docs"}} - {{if $}} <!-- {{JoinWith " " $ | Replace "<" "" | Replace ">" ""}} -->{{end}} -{{end}} - -{{/* -------------------------------------------------------------------------------- - Emits the C translation for the specified type. -------------------------------------------------------------------------------- -*/}} -{{define "XML.Type.Class" }}<type>{{Macro "StructName" $.Type}}</type>{{end}} -{{define "XML.Type.Pseudonym" }}<type>{{$.Type.Name}}</type>{{end}} -{{define "XML.Type.Enum" }}<type>{{$.Type.Name}}</type>{{end}} -{{define "XML.Type.StaticArray"}}{{Node "XML.Type" $.Type.ValueType}}{{end}} -{{define "XML.Type.Pointer" }}{{if $.Type.Const}}{{Node "XML.ConstType" $.Type.To}}{{else}}{{Node "XML.Type" $.Type.To}}{{end}}*{{end}} -{{define "XML.Type.Slice" }}<type>{{Node "XML.Type" $.Type.To}}</type>*{{end}} -{{define "XML.Type#s8" }}<type>int8_t</type>{{end}} -{{define "XML.Type#u8" }}<type>uint8_t</type>{{end}} -{{define "XML.Type#s16" }}<type>int16_t</type>{{end}} -{{define "XML.Type#u16" }}<type>uint16_t</type>{{end}} -{{define "XML.Type#s32" }}<type>int32_t</type>{{end}} -{{define "XML.Type#u32" }}<type>uint32_t</type>{{end}} -{{define "XML.Type#f32" }}<type>float</type>{{end}} -{{define "XML.Type#s64" }}<type>int64_t</type>{{end}} -{{define "XML.Type#u64" }}<type>uint64_t</type>{{end}} -{{define "XML.Type#f64" }}<type>double</type>{{end}} -{{define "XML.Type#char" }}<type>char</type>{{end}} -{{define "XML.Type#void" }}void{{end}} - -{{define "XML.ConstType_Default"}}const {{Node "XML.Type" $.Type}}{{end}} -{{define "XML.ConstType.Pointer"}}{{Node "XML.Type" $.Type}} const{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits a C type and name for the given parameter -------------------------------------------------------------------------------- -*/}} -{{define "XML.Parameter"}} - {{AssertType $ "Parameter"}} - - <type>{{Macro "ParameterType" $}}</type> <name>{{$.Name}}{{Macro "ArrayPostfix" $}}</name> -{{end}} - -{{/* -------------------------------------------------------------------------------- - Emits a comma-separated list of C type-name paired parameters for the given - command. -------------------------------------------------------------------------------- -*/}} -{{define "XML.Parameters"}} - {{AssertType $ "Function"}} - - {{ForEach $.CallParameters "XML.Parameter" | JoinWith ", "}} - {{if not $.CallParameters}}<type>void</type>{{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the fixed-size-array postfix for pseudonym types annotated with @array -------------------------------------------------------------------------------- -*/}} -{{define "XML.ArrayPostfix"}}{{Node "XML.ArrayPostfix" $}}{{end}} -{{define "XML.ArrayPostfix.StaticArray"}}[{{Node "XML.NamedValue" $.Type.SizeExpr}}]{{end}} -{{define "XML.ArrayPostfix_Default"}}{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the value of the given constant, or the <enum> tagged name if existant. -------------------------------------------------------------------------------- -*/}} -{{define "XML.NamedValue.Definition"}}<enum>{{$.Node.Name}}</enum>{{end}} -{{define "XML.NamedValue.EnumEntry"}}<enum>{{$.Node.Name}}</enum>{{end}} -{{define "XML.NamedValue_Default"}}{{$.Node}}{{end}} diff --git a/vulkan/api/templates/vulkan_common.tmpl b/vulkan/api/templates/vulkan_common.tmpl deleted file mode 100644 index f694c56238..0000000000 --- a/vulkan/api/templates/vulkan_common.tmpl +++ /dev/null @@ -1,223 +0,0 @@ -{{$clang_style := "{BasedOnStyle: Google, AccessModifierOffset: -4, ColumnLimit: 200, ContinuationIndentWidth: 8, IndentWidth: 4, AlignOperands: true, CommentPragmas: '.*'}"}} -{{Global "clang-format" (Strings "clang-format" "-style" $clang_style)}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C translation for the specified type. -------------------------------------------------------------------------------- -*/}} -{{define "Type.Class" }}{{if GetAnnotation $.Type "internal"}}struct {{end}}{{Macro "StructName" $.Type}}{{end}} -{{define "Type.Pseudonym" }}{{$.Type.Name}}{{end}} -{{define "Type.Enum" }}{{$.Type.Name}}{{end}} -{{define "Type.StaticArray"}}{{Node "Type" $.Type.ValueType}}{{end}} -{{define "Type.Pointer" }}{{if $.Type.Const}}{{Node "ConstType" $.Type.To}}{{else}}{{Node "Type" $.Type.To}}{{end}}*{{end}} -{{define "Type.Slice" }}{{Log "%T %+v" $.Node $.Node}}{{Node "Type" $.Type.To}}*{{end}} -{{define "Type#bool" }}bool{{end}} -{{define "Type#int" }}int{{end}} -{{define "Type#uint" }}unsigned int{{end}} -{{define "Type#s8" }}int8_t{{end}} -{{define "Type#u8" }}uint8_t{{end}} -{{define "Type#s16" }}int16_t{{end}} -{{define "Type#u16" }}uint16_t{{end}} -{{define "Type#s32" }}int32_t{{end}} -{{define "Type#u32" }}uint32_t{{end}} -{{define "Type#f32" }}float{{end}} -{{define "Type#s64" }}int64_t{{end}} -{{define "Type#u64" }}uint64_t{{end}} -{{define "Type#f64" }}double{{end}} -{{define "Type#void" }}void{{end}} -{{define "Type#char" }}char{{end}} - -{{define "ConstType_Default"}}const {{Node "Type" $.Type}}{{end}} -{{define "ConstType.Pointer"}}{{Node "Type" $.Type}} const{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C translation for the specified documentation block (string array). -------------------------------------------------------------------------------- -*/}} -{{define "Docs"}} - {{if $}}// {{$ | JoinWith "\n// "}}{{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the name of a bitfield entry. -------------------------------------------------------------------------------- -*/}} -{{define "BitfieldEntryName"}} - {{AssertType $ "EnumEntry"}} - - {{Macro "EnumEntry" $}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the name of an enum type. -------------------------------------------------------------------------------- -*/}} -{{define "EnumName"}}{{AssertType $ "Enum"}}{{$.Name}}{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the name of an enum entry. -------------------------------------------------------------------------------- -*/}} -{{define "EnumEntry"}} - {{AssertType $.Owner "Enum"}} - {{AssertType $.Name "string"}} - - {{$.Name}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the name of the first entry of an enum. -------------------------------------------------------------------------------- -*/}} -{{define "EnumFirstEntry"}} - {{AssertType $ "Enum"}} - - {{range $i, $e := $.Entries}} - {{if not $i}}{{$e.Name}}{{end}} - {{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the name of the last entry of an enum. -------------------------------------------------------------------------------- -*/}}{{define "EnumLastEntry"}} - {{AssertType $ "Enum"}} - - {{range $i, $e := $.Entries}} - {{if not (HasMore $i $.Entries)}}{{$e.Name}}{{end}} - {{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the name of a struct (class) type. -------------------------------------------------------------------------------- -*/}} -{{define "StructName"}}{{AssertType $ "Class"}}{{$.Name}}{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the name of a function. -------------------------------------------------------------------------------- -*/}} -{{define "FunctionName"}}{{AssertType $ "Function"}}{{$.Name}}{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the fixed-size-array postfix for pseudonym types annotated with @array -------------------------------------------------------------------------------- -*/}} -{{define "ArrayPostfix"}}{{Node "ArrayPostfix" $}}{{end}} -{{define "ArrayPostfix.StaticArray"}}[{{$.Type.Size}}]{{end}} -{{define "ArrayPostfix_Default"}}{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits a C type and name for the given parameter -------------------------------------------------------------------------------- -*/}} -{{define "Parameter"}} - {{AssertType $ "Parameter"}} - - {{if GetAnnotation $ "readonly"}}const {{end}}{{Macro "ParameterType" $}} {{$.Name}}{{Macro "ArrayPostfix" $}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits a C name for the given parameter -------------------------------------------------------------------------------- -*/}} -{{define "ParameterName"}} - {{AssertType $ "Parameter"}} - - {{$.Name}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits a C type for the given parameter -------------------------------------------------------------------------------- -*/}} -{{define "ParameterType"}}{{AssertType $ "Parameter"}}{{Node "Type" $}}{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits a comma-separated list of C type-name paired parameters for the given - command. -------------------------------------------------------------------------------- -*/}} -{{define "Parameters"}} - {{AssertType $ "Function"}} - - {{ForEach $.CallParameters "Parameter" | JoinWith ", "}} - {{if not $.CallParameters}}void{{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C function pointer name for the specified command. -------------------------------------------------------------------------------- -*/}} -{{define "FunctionPtrName"}} - {{AssertType $ "Function"}} - - PFN_{{$.Name}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Parses const variables as text Globals. -------------------------------------------------------------------------------- -*/}} -{{define "DefineGlobals"}} - {{AssertType $ "API"}} - - {{range $d := $.Definitions}} - {{Global $d.Name $d.Expression}} - {{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Given a function, return "Global", "Instance", or "Device" depending on which - dispatch table the function belongs to. -------------------------------------------------------------------------------- -*/}} -{{define "Vtbl#VkInstance" }}Instance{{end}} -{{define "Vtbl#VkPhysicalDevice"}}Instance{{end}} -{{define "Vtbl#VkDevice" }}Device{{end}} -{{define "Vtbl#VkQueue" }}Device{{end}} -{{define "Vtbl#VkCommandBuffer" }}Device{{end}} -{{define "Vtbl_Default" }}Global{{end}} -{{define "Vtbl"}} - {{AssertType $ "Function"}} - - {{if gt (len $.CallParameters) 0}} - {{Node "Vtbl" (index $.CallParameters 0)}} - {{else}}Global - {{end}} -{{end}} diff --git a/vulkan/api/templates/vulkan_h.tmpl b/vulkan/api/templates/vulkan_h.tmpl deleted file mode 100644 index 83a5e40804..0000000000 --- a/vulkan/api/templates/vulkan_h.tmpl +++ /dev/null @@ -1,295 +0,0 @@ -{{Include "vulkan_common.tmpl"}} -{{Macro "DefineGlobals" $}} -{{$ | Macro "vulkan.h" | Format (Global "clang-format") | Write "../include/vulkan.h"}} - - -{{/* -------------------------------------------------------------------------------- - Entry point -------------------------------------------------------------------------------- -*/}} -{{define "vulkan.h"}} -#ifndef __vulkan_h_ -#define __vulkan_h_ 1 -¶ -#ifdef __cplusplus -extern "C" { -#endif -¶ -/* -** Copyright (c) 2015-2016 The Khronos Group Inc. -** -** Permission is hereby granted, free of charge, to any person obtaining a -** copy of this software and/or associated documentation files (the -** "Materials"), to deal in the Materials without restriction, including -** without limitation the rights to use, copy, modify, merge, publish, -** distribute, sublicense, and/or sell copies of the Materials, and to -** permit persons to whom the Materials are furnished to do so, subject to -** the following conditions: -** -** The above copyright notice and this permission notice shall be included -** in all copies or substantial portions of the Materials. -** -** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. -*/ -¶ -/* -** This header is generated from the Khronos Vulkan API Registry. -** -*/ -¶ -#define VK_VERSION_1_0 1 -#include "vk_platform.h" -¶ -#define VK_MAKE_VERSION(major, minor, patch) (((major) << 22) | ((minor) << 12) | (patch)) -¶ -// Vulkan API version supported by this file -#define VK_API_VERSION \ - VK_MAKE_VERSION({{Global "VERSION_MAJOR"}}, {{Global "VERSION_MINOR"}}, {{Global "VERSION_PATCH"}}) -¶ -#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) -#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) -#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) -¶ -#if defined(__cplusplus) && ((defined(_MSC_VER) && _MSC_VER >= 1800 || __cplusplus >= 201103L) - #define VK_NULL_HANDLE nullptr -#else - #define VK_NULL_HANDLE 0 -#endif -¶ -#define VK_DEFINE_HANDLE(obj) typedef struct obj##_T* obj; -¶ -#if defined(__cplusplus) -#if ((defined(_MSC_VER) && _MSC_VER >= 1800 || __cplusplus >= 201103L) -// The bool operator only works if there are no implicit conversions from an obj to -// a bool-compatible type, which can then be used to unintentionally violate type safety. -// C++11 and above supports the "explicit" keyword on conversion operators to stop this -// from happening. Otherwise users of C++ below C++11 won't get direct access to evaluating -// the object handle as a bool in expressions like: -// if (obj) vkDestroy(obj); -#define VK_NONDISP_HANDLE_OPERATOR_BOOL() \ - explicit operator bool() const { return handle != 0; } -#define VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \ - explicit obj(uint64_t x) : handle(x) { } \ - obj(decltype(nullptr)) : handle(0) { } -#else -#define VK_NONDISP_HANDLE_OPERATOR_BOOL() -#define VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \ - obj(uint64_t x) : handle(x) { } -#endif -#define VK_DEFINE_NONDISP_HANDLE(obj) \ - struct obj { \ - obj() : handle(0) { } \ - VK_NONDISP_HANDLE_CONSTRUCTOR_FROM_UINT64(obj) \ - obj& operator=(uint64_t x) { \ - handle = x; \ - return *this; \ - } \ - bool operator==(const obj& other) const { return handle == other.handle; } \ - bool operator!=(const obj& other) const { return handle != other.handle; } \ - bool operator!() const { return !handle; } \ - VK_NONDISP_HANDLE_OPERATOR_BOOL() \ - uint64_t handle; \ - }; -#else -#define VK_DEFINE_NONDISP_HANDLE(obj) \ - typedef struct obj##_T { uint64_t handle; } obj; -#endif -¶ -#define VK_LOD_CLAMP_NONE 1000.0f -#define VK_REMAINING_MIP_LEVELS (~0U) -#define VK_REMAINING_ARRAY_LAYERS (~0U) -#define VK_WHOLE_SIZE (~0ULL) -#define VK_ATTACHMENT_UNUSED (~0U) -define VK_QUEUE_FAMILY_IGNORED (~0U) -define VK_SUBPASS_EXTERNAL (~0U) -{{range $d := $.Definitions}} - {{if HasPrefix $d.Name "VK_"}}#define {{$d.Name}} {{$d.Expression}}{{end}} -{{end}} -¶ -{{range $i, $p := $.Pseudonyms}} - {{if GetAnnotation $p "dispatchHandle"}}VK_DEFINE_HANDLE({{$p.Name}}) - {{else if GetAnnotation $p "nonDispatchHandle"}}VK_DEFINE_NONDISP_HANDLE({{$p.Name}}) - {{end}} -{{end}} -¶ -// ------------------------------------------------------------------------------------------------ -// Enumerations -¶ - {{range $e := $.Enums}} - {{if not $e.IsBitfield}} - {{Macro "Enum" $e}} - {{end}} - {{end}} -¶ -// ------------------------------------------------------------------------------------------------ -// Flags -¶ - {{range $e := $.Enums}} - {{if $e.IsBitfield}} - {{Macro "Bitfield" $e}} - {{end}} - {{end}} -¶ -// ------------------------------------------------------------------------------------------------ -// Vulkan structures -¶ - {{/* Function pointers */}} - {{range $f := AllCommands $}} - {{if GetAnnotation $f "pfn"}} - {{Macro "FunctionTypedef" $f}} - {{end}} - {{end}} -¶ - {{range $c := $.Classes}} - {{if not (GetAnnotation $c "internal")}} - {{Macro "Struct" $c}} - {{end}} - {{end}} -¶ -// ------------------------------------------------------------------------------------------------ -// API functions -¶ - {{range $f := AllCommands $}} - {{if not (GetAnnotation $f "pfn")}} - {{Macro "FunctionTypedef" $f}} - {{end}} - {{end}} -¶ -#ifdef VK_NO_PROTOTYPES -¶ - {{range $f := AllCommands $}} - {{if not (GetAnnotation $f "pfn")}} - {{Macro "FunctionDecl" $f}} - {{end}} - {{end}} -¶ -#endif -¶ -#ifdef __cplusplus -} -#endif -¶ -#endif -{{end}} - -{{/* -------------------------------------------------------------------------------- - Emits the C declaration for the specified bitfield. -------------------------------------------------------------------------------- -*/}} -{{define "Bitfield"}} - {{AssertType $ "Enum"}} - - {{Macro "Docs" $.Docs}} - typedef VkFlags {{Macro "EnumName" $}}; - {{if $.Entries}} - typedef enum { - {{range $b := $.Entries}} - {{Macro "BitfieldEntryName" $b}} = {{printf "0x%.8X" $b.Value}}, {{Macro "Docs" $b.Docs}} - {{end}} - } {{Macro "EnumName" $ | TrimRight "s"}}Bits; - {{end}} - ¶ -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C declaration for the specified enum. -------------------------------------------------------------------------------- -*/}} -{{define "Enum"}} - {{AssertType $ "Enum"}} - - {{Macro "Docs" $.Docs}} - typedef enum { - {{range $i, $e := $.Entries}} - {{Macro "EnumEntry" $e}} = {{printf "0x%.8X" $e.Value}}, {{Macro "Docs" $e.Docs}} - {{end}} - ¶ - {{$name := Macro "EnumName" $ | TrimRight "ABCDEFGHIJKLMNOQRSTUVWXYZ" | SplitPascalCase | Upper | JoinWith "_"}} - {{if GetAnnotation $ "enumMaxOnly"}} - VK_MAX_ENUM({{$name | SplitOn "VK_"}}) - {{else}} - {{$first := Macro "EnumFirstEntry" $ | SplitOn $name | TrimLeft "_"}} - {{$last := Macro "EnumLastEntry" $ | SplitOn $name | TrimLeft "_"}} - VK_ENUM_RANGE({{$name | SplitOn "VK_"}}, {{$first}}, {{$last}}) - {{end}} - } {{Macro "EnumName" $}}; - ¶ -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C declaration for the specified class. -------------------------------------------------------------------------------- -*/}} -{{define "Struct"}} - {{AssertType $ "Class"}} - - {{Macro "Docs" $.Docs}} - typedef {{Macro "StructType" $}} { - {{ForEach $.Fields "Field" | JoinWith "\n"}} - } {{Macro "StructName" $}}; - ¶ -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C declaration for the specified class field. -------------------------------------------------------------------------------- -*/}} -{{define "Field"}} - {{AssertType $ "Field"}} - - {{Node "Type" $}} {{$.Name}}§ - {{Macro "ArrayPostfix" (TypeOf $)}}; {{Macro "Docs" $.Docs}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits either 'struct' or 'union' for the specified class. -------------------------------------------------------------------------------- -*/}} -{{define "StructType"}} - {{AssertType $ "Class"}} - - {{if GetAnnotation $ "union"}}union{{else}}struct{{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C function pointer typedef declaration for the specified command. -------------------------------------------------------------------------------- -*/}} -{{define "FunctionTypedef"}} - {{AssertType $ "Function"}} - - typedef {{Node "Type" $.Return}} (VKAPI* {{Macro "FunctionPtrName" $}})({{Macro "Parameters" $}}); -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits the C function declaration for the specified command. -------------------------------------------------------------------------------- -*/}} -{{define "FunctionDecl"}} - {{AssertType $ "Function"}} - - {{if not (GetAnnotation $ "fptr")}} - {{Macro "Docs" $.Docs}} - {{Node "Type" $.Return}} VKAPI {{Macro "FunctionName" $}}({{Macro "Parameters" $}}); - {{end}} -{{end}} diff --git a/vulkan/api/vulkan.api b/vulkan/api/vulkan.api deleted file mode 100644 index 76503c8c17..0000000000 --- a/vulkan/api/vulkan.api +++ /dev/null @@ -1,12163 +0,0 @@ -// Copyright (c) 2015 The Khronos Group Inc. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and/or associated documentation files (the -// "Materials"), to deal in the Materials without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Materials, and to -// permit persons to whom the Materials are furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Materials. -// -// THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -// MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. - -import platform "platform.api" - -/////////////// -// Constants // -/////////////// - -// API version (major.minor.patch) -define VERSION_MAJOR 1 -define VERSION_MINOR 1 -define VERSION_PATCH 96 - -// API limits -define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256 -define VK_UUID_SIZE 16 -define VK_MAX_EXTENSION_NAME_SIZE 256 -define VK_MAX_DESCRIPTION_SIZE 256 -define VK_MAX_MEMORY_TYPES 32 -define VK_MAX_MEMORY_HEAPS 16 /// The maximum number of unique memory heaps, each of which supporting 1 or more memory types. -@vulkan1_1 -define VK_MAX_DEVICE_GROUP_SIZE 32 -@vulkan1_1 -define VK_LUID_SIZE 8 -@vulkan1_1 -define VK_QUEUE_FAMILY_EXTERNAL -2 -@extension("VK_EXT_queue_family_foreign") -define VK_QUEUE_FAMILY_FOREIGN_EXT -3 -@extension("VK_MAX_DRIVER_NAME_SIZE_KHR") // 197 -define VK_MAX_DRIVER_NAME_SIZE_KHR 256 -@extension("VK_MAX_DRIVER_NAME_SIZE_KHR") // 197 -define VK_MAX_DRIVER_INFO_SIZE_KHR 256 - -// API keywords -define VK_TRUE 1 -define VK_FALSE 0 - -// API keyword, but needs special handling by some templates -define NULL_HANDLE 0 - -// 1 -@extension("VK_KHR_surface") define VK_KHR_SURFACE_SPEC_VERSION 25 -@extension("VK_KHR_surface") define VK_KHR_SURFACE_EXTENSION_NAME "VK_KHR_surface" - -// 2 -@extension("VK_KHR_swapchain") define VK_KHR_SWAPCHAIN_SPEC_VERSION 70 -@extension("VK_KHR_swapchain") define VK_KHR_SWAPCHAIN_EXTENSION_NAME "VK_KHR_swapchain" - -// 3 -@extension("VK_KHR_display") define VK_KHR_DISPLAY_SPEC_VERSION 21 -@extension("VK_KHR_display") define VK_KHR_DISPLAY_EXTENSION_NAME "VK_KHR_display" - -// 4 -@extension("VK_KHR_display_swapchain") define VK_KHR_DISPLAY_SWAPCHAIN_SPEC_VERSION 9 -@extension("VK_KHR_display_swapchain") define VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME "VK_KHR_display_swapchain" - -// 5 -@extension("VK_KHR_xlib_surface") define VK_KHR_XLIB_SURFACE_SPEC_VERSION 6 -@extension("VK_KHR_xlib_surface") define VK_KHR_XLIB_SURFACE_NAME "VK_KHR_xlib_surface" - -// 6 -@extension("VK_KHR_xcb_surface") define VK_KHR_XCB_SURFACE_SPEC_VERSION 6 -@extension("VK_KHR_xcb_surface") define VK_KHR_XCB_SURFACE_NAME "VK_KHR_xcb_surface" - -// 7 -@extension("VK_KHR_wayland_surface") define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 6 -@extension("VK_KHR_wayland_surface") define VK_KHR_WAYLAND_SURFACE_NAME "VK_KHR_wayland_surface" - -// 8 - VK_KHR_mir_surface removed - -// 9 -@extension("VK_KHR_android_surface") define VK_KHR_ANDROID_SURFACE_SPEC_VERSION 6 -@extension("VK_KHR_android_surface") define VK_KHR_ANDROID_SURFACE_NAME "VK_KHR_android_surface" - -// 10 -@extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_SPEC_VERSION 6 -@extension("VK_KHR_win32_surface") define VK_KHR_WIN32_SURFACE_NAME "VK_KHR_win32_surface" - -// 11 -@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_SPEC_VERSION 8 -@extension("VK_ANDROID_native_buffer") define VK_ANDROID_NATIVE_BUFFER_NAME "VK_ANDROID_native_buffer" - -// 12 -@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_SPEC_VERSION 9 -@extension("VK_EXT_debug_report") define VK_EXT_DEBUG_REPORT_NAME "VK_EXT_debug_report" - -// 13 -@extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_SPEC_VERSION 1 -@extension("VK_NV_glsl_shader") define VK_NV_GLSL_SHADER_NAME "VK_NV_glsl_shader" - -// 14 -@extension("VK_EXT_depth_range_unrestricted") define VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION 1 -@extension("VK_EXT_depth_range_unrestricted") define VK_EXT_DEPTH_RANGE_UNRESTRICTED_NAME "VK_EXT_depth_range_unrestricted" - -// 15 -@extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION 1 -@extension("VK_KHR_sampler_mirror_clamp_to_edge") define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_NAME "VK_KHR_sampler_mirror_clamp_to_edge" - -// 16 -@extension("VK_IMG_filter_cubic") define VK_IMG_FILTER_CUBIC_SPEC_VERSION 1 -@extension("VK_IMG_filter_cubic") define VK_IMG_FILTER_CUBIC_NAME "VK_IMG_filter_cubic" - -// 19 -@extension("VK_AMD_rasterization_order") define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION 1 -@extension("VK_AMD_rasterization_order") define VK_AMD_RASTERIZATION_ORDER_NAME "VK_AMD_rasterization_order" - -// 21 -@extension("VK_AMD_shader_trinary_minmax") define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1 -@extension("VK_AMD_shader_trinary_minmax") define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax" - -// 22 -@extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1 -@extension("VK_AMD_shader_explicit_vertex_parameter") define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter" - -// 23 -@extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_SPEC_VERSION 4 -@extension("VK_EXT_debug_marker") define VK_EXT_DEBUG_MARKER_NAME "VK_EXT_debug_marker" - -// 26 -@extension("VK_AMD_gcn_shader") define VK_AMD_GCN_SHADER_SPEC_VERSION 1 -@extension("VK_AMD_gcn_shader") define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader" - -// 27 -@extension("VK_NV_dedicated_allocation") define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1 -@extension("VK_NV_dedicated_allocation") define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation" - -// 28 -@extension("VK_IMG_format_pvrtc") define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1 -@extension("VK_IMG_format_pvrtc") define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc" - -// 29 -@extension("VK_EXT_transform_feedback") define VK_EXT_TRANSFORM_FEEDBACK_SPEC_VERSION 1 -@extension("VK_EXT_transform_feedback") define VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME "VK_EXT_transform_feedback" - -// 34 -@extension("VK_AMD_draw_indirect_count") define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 1 -@extension("VK_AMD_draw_indirect_count") define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count" - -// 36 -@extension("VK_AMD_negative_viewport_height") define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1 -@extension("VK_AMD_negative_viewport_height") define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height" - -// 37 -@extension("VK_AMD_gpu_shader_half_float") define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 1 -@extension("VK_AMD_gpu_shader_half_float") define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float" - -// 38 -@extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1 -@extension("VK_AMD_shader_ballot") define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot" - -// 42 -@extension("VK_AMD_texture_gather_bias_lod") define VK_AMD_TEXTURE_GATHER_BIAS_LOD_SPEC_VERSION 1 -@extension("VK_AMD_texture_gather_bias_lod") define VK_AMD_TEXTURE_GATHER_BIAS_LOD_EXTENSION_NAME "VK_AMD_texture_gather_bias_lod" - -// 43 -@extension("VK_AMD_shader_info") define VK_AMD_SHADER_INFO_SPEC_VERSION 1 -@extension("VK_AMD_shader_info") define VK_AMD_SHADER_INFO_EXTENSION_NAME "VK_AMD_shader_info" - -// 47 -@extension("VK_AMD_shader_image_load_store_lod") define VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_SPEC_VERSION 1 -@extension("VK_AMD_shader_image_load_store_lod") define VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME "VK_AMD_shader_image_load_store_lod" - -// 51 -@extension("VK_NV_corner_sampled_image") define VK_NV_CORNER_SAMPLED_IMAGE_SPEC_VERSION 2 -@extension("VK_NV_corner_sampled_image") define VK_NV_CORNER_SAMPLED_IMAGE_EXTENSION_NAME "VK_NV_corner_sampled_image" - -// 54 -@extension("VK_KHR_multiview") define VK_KHR_MULTIVIEW_SPEC_VERSION 1 -@extension("VK_KHR_multiview") define VK_KHR_MULTIVIEW_EXTENSION_NAME "VK_KHR_multiview" - -// 56 -@extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 -@extension("VK_NV_external_memory_capabilities") define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities" - -// 57 -@extension("VK_NV_external_memory") define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1 -@extension("VK_NV_external_memory") define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory" - -// 58 -@extension("VK_NV_external_memory_win32") define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1 -@extension("VK_NV_external_memory_win32") define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32" - -// 59 -@extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 1 -@extension("VK_NV_win32_keyed_mutex") define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex" - -// 60 -@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 1 -@extension("VK_KHR_get_physical_device_properties2") define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2" - -// 61 -@extension("VK_KHR_device_group") define VK_KHR_DEVICE_GROUP_SPEC_VERSION 3 -@extension("VK_KHR_device_group") define VK_KHR_DEVICE_GROUP_EXTENSION_NAME "VK_KHR_device_group" - -// 62 -@extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 1 -@extension("VK_EXT_validation_flags") define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags" - -// 63 -@extension("VK_NN_vi_surface") define VK_NN_VI_SURFACE_SPEC_VERSION 1 -@extension("VK_NN_vi_surface") define VK_NN_VI_SURFACE_EXTENSION_NAME "VK_NN_vi_surface" - -// 64 -@extension("VK_KHR_shader_draw_parameters") define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1 -@extension("VK_KHR_shader_draw_parameters") define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters" - -// 65 -@extension("VK_EXT_shader_subgroup_ballot") define VK_EXT_SHADER_SUBGROUP_BALLOT_SPEC_VERSION 1 -@extension("VK_EXT_shader_subgroup_ballot") define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot" - -// 66 -@extension("VK_EXT_shader_subgroup_vote") define VK_EXT_SHADER_SUBGROUP_VOTE_SPEC_VERSION 1 -@extension("VK_EXT_shader_subgroup_vote") define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote" - -// 68 -@extension("VK_EXT_astc_decode_mode") define VK_EXT_ASTC_DECODE_MODE_SPEC_VERSION 1 -@extension("VK_EXT_astc_decode_mode") define VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME "VK_EXT_astc_decode_mode" - -// 70 -@extension("VK_KHR_maintenance1") define VK_KHR_MAINTENANCE1_SPEC_VERSION 2 -@extension("VK_KHR_maintenance1") define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1" - -// 71 -@extension("VK_KHR_device_group_creation") define VK_KHR_DEVICE_GROUP_CREATION_SPEC_VERSION 1 -@extension("VK_KHR_device_group_creation") define VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHR_device_group_creation" - -// 72 -@extension("VK_KHR_external_memory_capabilities") define VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 -@extension("VK_KHR_external_memory_capabilities") define VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_memory_capabilities" - -// 73 -@extension("VK_KHR_external_memory") define VK_KHR_EXTERNAL_MEMORY_SPEC_VERSION 1 -@extension("VK_KHR_external_memory") define VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHR_external_memory" - -// 74 -@extension("VK_KHR_external_memory_win32") define VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1 -@extension("VK_KHR_external_memory_win32") define VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHR_external_memory_win32" - -// 75 -@extension("VK_KHR_external_memory_fd") define VK_KHR_EXTERNAL_MEMORY_FD_SPEC_VERSION 1 -@extension("VK_KHR_external_memory_fd") define VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME "VK_KHR_external_memory_fd" - -// 76 -@extension("VK_KHR_win32_keyed_mutex") define VK_KHR_WIN32_KEYED_MUTEX_SPEC_VERSION 1 -@extension("VK_KHR_win32_keyed_mutex") define VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHR_win32_keyed_mutex" - -// 77 -@extension("VK_KHR_external_semaphore_capabilities") define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1 -@extension("VK_KHR_external_semaphore_capabilities") define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_semaphore_capabilities" - -// 78 -@extension("VK_KHR_external_semaphore") define VK_KHR_EXTERNAL_SEMAPHORE_SPEC_VERSION 1 -@extension("VK_KHR_external_semaphore") define VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHR_external_semaphore" - -// 79 -@extension("VK_KHR_external_semaphore_win32") define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1 -@extension("VK_KHR_external_semaphore_win32") define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHR_external_semaphore_win32" - -// 80 -@extension("VK_KHR_external_semaphore_fd") define VK_KHR_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION 1 -@extension("VK_KHR_external_semaphore_fd") define VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME "VK_KHR_external_semaphore_fd" - -// 81 -@extension("VK_KHR_push_descriptor") define VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION 2 -@extension("VK_KHR_push_descriptor") define VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME "VK_KHR_push_descriptor" - -// 82 -@extension("VK_EXT_conditional_rendering") define VK_EXT_CONDITIONAL_RENDERING_SPEC_VERSION 1 -@extension("VK_EXT_conditional_rendering") define VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME "VK_EXT_conditional_rendering" - -// 83 -@extension("VK_KHR_shader_float16_int8") define VK_KHR_SHADER_FLOAT16_INT8_SPEC_VERSION 1 -@extension("VK_KHR_shader_float16_int8") define VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME "VK_KHR_shader_float16_int8" - -// 84 -@extension("VK_KHR_16bit_storage") define VK_KHR_16BIT_STORAGE_SPEC_VERSION 1 -@extension("VK_KHR_16bit_storage") define VK_KHR_16BIT_STORAGE_EXTENSION_NAME "VK_KHR_16bit_storage" - -// 85 -@extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1 -@extension("VK_KHR_incremental_present") define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present" - -// 86 -@extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1 -@extension("VK_KHR_descriptor_update_template") define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template" - -// 87 -@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3 -@extension("VK_NVX_device_generated_commands") define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands" - -// 88 -@extension("VK_NV_clip_space_w_scaling") define VK_NV_CLIP_SPACE_W_SCALING_SPEC_VERSION 1 -@extension("VK_NV_clip_space_w_scaling") define VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME "VK_NV_clip_space_w_scaling" - -// 89 -@extension("VK_EXT_direct_mode_display") define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1 -@extension("VK_EXT_direct_mode_display") define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display" - -// 90 -@extension("VK_EXT_acquire_xlib_display") define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1 -@extension("VK_EXT_acquire_xlib_display") define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display" - -// 91 -@extension("VK_EXT_display_surface_counter") define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1 -@extension("VK_EXT_display_surface_counter") define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter" - -// 92 -@extension("VK_EXT_display_control") define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1 -@extension("VK_EXT_display_control") define VK_EXT_DISPLAY_CONTROL_COUNTER_EXTENSION_NAME "VK_EXT_display_control" - -// 93 -@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1 -@extension("VK_GOOGLE_display_timing") define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing" - -// 95 -@extension("VK_NV_sample_mask_override_coverage") define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_SPEC_VERSION 1 -@extension("VK_NV_sample_mask_override_coverage") define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME "VK_NV_sample_mask_override_coverage" - -// 96 -@extension("VK_NV_geometry_shader_passthrough") define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_SPEC_VERSION 1 -@extension("VK_NV_geometry_shader_passthrough") define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME "VK_NV_geometry_shader_passthrough" - -// 97 -@extension("VK_NV_viewport_array2") define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1 -@extension("VK_NV_viewport_array2") define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2" - -// 98 -@extension("VK_NVX_multiview_per_view_attributes") define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION 1 -@extension("VK_NVX_multiview_per_view_attributes") define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME "VK_NVX_multiview_per_view_attributes" - -// 99 -@extension("VK_NV_viewport_swizzle") define VK_NV_VIEWPORT_SWIZZLE_SPEC_VERSION 1 -@extension("VK_NV_viewport_swizzle") define VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME "VK_NV_viewport_swizzle" - -// 100 -@extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1 -@extension("VK_EXT_discard_rectangles") define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles" - -// 102 -@extension("VK_EXT_conservative_rasterization") define VK_EXT_CONSERVATIVE_RASTERIZATION_SPEC_VERSION 1 -@extension("VK_EXT_conservative_rasterization") define VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME "VK_EXT_conservative_rasterization" - -// 105 -@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_SPEC_VERSION 3 -@extension("VK_EXT_swapchain_colorspace") define VK_EXT_SWAPCHAIN_COLORSPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace" - -// 106 -@extension("VK_EXT_hdr_metadata") define VK_EXT_HDR_METADATA_SPEC_VERSION 1 -@extension("VK_EXT_hdr_metadata") define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata" - -// 110 -@extension("VK_KHR_create_renderpass2") define VK_KHR_CREATE_RENDERPASS2_SPEC_VERSION 1 -@extension("VK_KHR_create_renderpass2") define VK_KHR_CREATE_RENDERPASS2_EXTENSION_NAME "VK_KHR_create_renderpass2" - -// 112 -@extension("VK_KHR_shared_presentable_image") define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1 -@extension("VK_KHR_shared_presentable_image") define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image" - -// 113 -@extension("VK_KHR_external_fence_capabilities") define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_SPEC_VERSION 1 -@extension("VK_KHR_external_fence_capabilities") define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_fence_capabilities" - -// 114 -@extension("VK_KHR_external_fence") define VK_KHR_EXTERNAL_FENCE_SPEC_VERSION 1 -@extension("VK_KHR_external_fence") define VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME "VK_KHR_external_fence" - -// 115 -@extension("VK_KHR_external_fence_win32") define VK_KHR_EXTERNAL_FENCE_WIN32_SPEC_VERSION 1 -@extension("VK_KHR_external_fence_win32") define VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME "VK_KHR_external_fence_win32" - -// 116 -@extension("VK_KHR_external_fence_fd") define VK_KHR_EXTERNAL_FENCE_FD_SPEC_VERSION 1 -@extension("VK_KHR_external_fence_fd") define VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME "VK_KHR_external_fence_fd" - -// 118 -@extension("VK_KHR_maintenance2") define VK_KHR_MAINTENANCE2_SPEC_VERSION 1 -@extension("VK_KHR_maintenance2") define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2" - -// 120 -@extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1 -@extension("VK_KHR_get_surface_capabilities2") define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2" - -// 121 -@extension("VK_KHR_variable_pointers") define VK_KHR_VARIABLE_POINTERS_SPEC_VERSION 1 -@extension("VK_KHR_variable_pointers") define VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME "VK_KHR_variable_pointers" - -// 122 -@extension("VK_KHR_get_display_properties2") define VK_KHR_GET_DISPLAY_PROPERTIES_2_SPEC_VERSION 1 -@extension("VK_KHR_get_display_properties2") define VK_KHR_GET_DISPLAY_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_display_properties2" - -// 123 -@extension("VK_MVK_ios_surface") define VK_MVK_IOS_SURFACE_SPEC_VERSION 1 -@extension("VK_MVK_ios_surface") define VK_MVK_IOS_SURFACE_EXTENSION_NAME "VK_MVK_ios_surface" - -// 124 -@extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_SPEC_VERSION 1 -@extension("VK_MVK_macos_surface") define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface" - -// 126 -@extension("VK_EXT_external_memory_dma_buf") define VK_EXT_EXTERNAL_MEMORY_DMA_BUF_SPEC_VERSION 1 -@extension("VK_EXT_external_memory_dma_buf") define VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME "VK_EXT_external_memory_dma_buf" - -// 127 -@extension("VK_EXT_queue_family_foreign") define VK_EXT_QUEUE_FAMILY_FOREIGN_SPEC_VERSION 1 -@extension("VK_EXT_queue_family_foreign") define VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME "VK_EXT_queue_family_foreign" - -// 128 -@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3 -@extension("VK_KHR_dedicated_allocation") define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation" - -// 128 -@extension("VK_EXT_debug_utils") define VK_EXT_DEBUG_UTILS_SPEC_VERSION 1 -@extension("VK_EXT_debug_utils") define VK_EXT_DEBUG_UTILS_EXTENSION_NAME "VK_EXT_debug_utils" - -// 130 -@extension("VK_ANDROID_external_memory_android_hardware_buffer") define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION 3 -@extension("VK_ANDROID_external_memory_android_hardware_buffer") define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME "VK_ANDROID_external_memory_android_hardware_buffer" - -// 131 -@extension("VK_EXT_sampler_filter_minmax") define VK_EXT_SAMPLER_FILTER_MINMAX_SPEC_VERSION 1 -@extension("VK_EXT_sampler_filter_minmax") define VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME "VK_EXT_sampler_filter_minmax" - -// 132 -@extension("VK_KHR_storage_buffer_storage_class") define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_SPEC_VERSION 1 -@extension("VK_KHR_storage_buffer_storage_class") define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME "VK_KHR_storage_buffer_storage_class" - -// 133 -@extension("VK_AMD_gpu_shader_int16") define VK_AMD_GPU_SHADER_INT16_SPEC_VERSION 1 -@extension("VK_AMD_gpu_shader_int16") define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16" - -// 137 -@extension("VK_AMD_mixed_attachment_samples") define VK_AMD_MIXED_ATTACHMENT_SAMPLES_SPEC_VERSION 1 -@extension("VK_AMD_mixed_attachment_samples") define VK_AMD_MIXED_ATTACHMENT_SAMPLES_EXTENSION_NAME "VK_AMD_mixed_attachment_samples" - -// 138 -@extension("VK_AMD_shader_fragment_mask") define VK_AMD_SHADER_FRAGMENT_MASK_SPEC_VERSION 1 -@extension("VK_AMD_shader_fragment_mask") define VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME "VK_AMD_shader_fragment_mask" - -// 139 -@extension("VK_EXT_inline_uniform_block") define VK_EXT_INLINE_UNIFORM_BLOCK_SPEC_VERSION 1 -@extension("VK_EXT_inline_uniform_block") define VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME "VK_EXT_inline_uniform_block" - -// 141 -@extension("VK_EXT_shader_stencil_export") define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1 -@extension("VK_EXT_shader_stencil_export") define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export" - -// 144 -@extension("VK_EXT_sample_locations") define VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION 1 -@extension("VK_EXT_sample_locations") define VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME "VK_EXT_sample_locations" - -// 145 -@extension("VK_KHR_relaxed_block_layout") define VK_KHR_RELAXED_BLOCK_LAYOUT_SPEC_VERSION 1 -@extension("VK_KHR_relaxed_block_layout") define VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME "VK_KHR_relaxed_block_layout" - -// 147 -@extension("VK_KHR_get_memory_requirements2") define VK_KHR_GET_MEMORY_REQUIREMENTS2_SPEC_VERSION 1 -@extension("VK_KHR_get_memory_requirements2") define VK_KHR_GET_MEMORY_REQUIREMENTS2_EXTENSION_NAME "VK_KHR_get_memory_requirements2" - -// 148 -@extension("VK_KHR_image_format_list") define VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION 1 -@extension("VK_KHR_image_format_list") define VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME "VK_KHR_image_format_list" - -// 149 -@extension("VK_EXT_blend_operation_advanced") define VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION 2 -@extension("VK_EXT_blend_operation_advanced") define VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME "VK_EXT_blend_operation_advanced" - -// 150 -@extension("VK_NV_fragment_coverage_to_color") define VK_NV_FRAGMENT_COVERAGE_TO_COLOR_SPEC_VERSION 1 -@extension("VK_NV_fragment_coverage_to_color") define VK_NV_FRAGMENT_COVERAGE_TO_COLOR_EXTENSION_NAME "VK_NV_fragment_coverage_to_color" - -// 153 -@extension("VK_NV_framebuffer_mixed_samples") define VK_NV_FRAMEBUFFER_MIXED_SAMPLES_SPEC_VERSION 1 -@extension("VK_NV_framebuffer_mixed_samples") define VK_NV_FRAMEBUFFER_MIXED_SAMPLES_EXTENSION_NAME "VK_NV_framebuffer_mixed_samples" - -// 154 -@extension("VK_NV_fill_rectangle") define VK_NV_FILL_RECTANGLE_SPEC_VERSION 1 -@extension("VK_NV_fill_rectangle") define VK_NV_FILL_RECTANGLE_EXTENSION_NAME "VK_NV_fill_rectangle" - -// 156 -@extension("VK_EXT_post_depth_coverage") define VK_EXT_POST_DEPTH_COVERAGE_SPEC_VERSION 1 -@extension("VK_EXT_post_depth_coverage") define VK_EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME "VK_EXT_post_depth_coverage" - -// 157 -@extension("VK_KHR_sampler_ycbcr_conversion") define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 1 -@extension("VK_KHR_sampler_ycbcr_conversion") define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion" - -// 158 -@extension("VK_KHR_bind_memory2") define VK_KHR_BIND_MEMORY2_SPEC_VERSION 1 -@extension("VK_KHR_bind_memory2") define VK_KHR_BIND_MEMORY2_EXTENSION_NAME "VK_KHR_bind_memory2" - -// 159 -@extension("VK_EXT_image_drm_format_modifier") define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_SPEC_VERSION 1 -@extension("VK_EXT_image_drm_format_modifier") define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME "VK_EXT_image_drm_format_modifier" - -// 161 -@extension("VK_EXT_validation_cache") define VK_EXT_VALIDATION_CACHE_SPEC_VERSION 1 -@extension("VK_EXT_validation_cache") define VK_EXT_VALIDATION_CACHE_EXTENSION_NAME "VK_EXT_validation_cache" - -// 162 -@extension("VK_EXT_descriptor_indexing") define VK_EXT_DESCRIPTOR_INDEXING_SPEC_VERSION 2 -@extension("VK_EXT_descriptor_indexing") define VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME "VK_EXT_descriptor_indexing" - -// 163 -@extension("VK_EXT_shader_viewport_index_layer") define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_SPEC_VERSION 1 -@extension("VK_EXT_shader_viewport_index_layer") define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME "VK_EXT_shader_viewport_index_layer" - -// 165 -@extension("VK_NV_shading_rate_image") define VK_NV_SHADING_RATE_IMAGE_SPEC_VERSION 3 -@extension("VK_NV_shading_rate_image") define VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME "VK_NV_shading_rate_image" - -// 166 -@extension("VK_NV_ray_tracing") define VK_NV_RAY_TRACING_SPEC_VERSION 3 -@extension("VK_NV_ray_tracing") define VK_NV_RAY_TRACING_EXTENSION_NAME "VK_NV_ray_tracing" - -// 167 -@extension("VK_NV_representative_fragment_test") define VK_NV_REPRESENTATIVE_FRAGMENT_TEST_SPEC_VERSION 1 -@extension("VK_NV_representative_fragment_test") define VK_NV_REPRESENTATIVE_FRAGMENT_TEST_EXTENSION_NAME "VK_NV_representative_fragment_test" - -// 169 -@extension("VK_KHR_maintenance3") define VK_KHR_MAINTENANCE3_SPEC_VERSION 1 -@extension("VK_KHR_maintenance3") define VK_KHR_MAINTENANCE3_EXTENSION_NAME "VK_KHR_maintenance3" - -// 170 -@extension("VK_KHR_draw_indirect_count") define VK_KHR_DRAW_INDIRECT_COUNT_SPEC_VERSION 1 -@extension("VK_KHR_draw_indirect_count") define VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_KHR_draw_indirect_count" - -// 175 -@extension("VK_EXT_global_priority") define VK_EXT_GLOBAL_PRIORITY_SPEC_VERSION 1 -@extension("VK_EXT_global_priority") define VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME "VK_EXT_global_priority" - -// 178 -@extension("VK_KHR_8bit_storage") define VK_KHR_8BIT_STORAGE_SPEC_VERSION 1 -@extension("VK_KHR_8bit_storage") define VK_KHR_8BIT_STORAGE_EXTENSION_NAME "VK_KHR_8bit_storage" - -// 179 -@extension("VK_EXT_external_memory_host") define VK_EXT_EXTERNAL_MEMORY_HOST_SPEC_VERSION 1 -@extension("VK_EXT_external_memory_host") define VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME "VK_EXT_external_memory_host" - -// 180 -@extension("VK_AMD_buffer_marker") define VK_AMD_BUFFER_MARKER_SPEC_VERSION 1 -@extension("VK_AMD_buffer_marker") define VK_AMD_BUFFER_MARKER_EXTENSION_NAME "VK_AMD_buffer_marker" - -// 181 -@extension("VK_KHR_shader_atomic_int64") define VK_KHR_SHADER_ATOMIC_INT64_SPEC_VERSION 1 -@extension("VK_KHR_shader_atomic_int64") define VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME "VK_KHR_shader_atomic_int64" - -// 186 -@extension("VK_AMD_shader_core_properties") define VK_AMD_SHADER_CORE_PROPERTIES_SPEC_VERSION 1 -@extension("VK_AMD_shader_core_properties") define VK_AMD_SHADER_CORE_PROPERTIES_EXTENSION_NAME "VK_AMD_shader_core_properties" - -// 190 -@extension("VK_AMD_memory_overallocation_behavior") define VK_AMD_MEMORY_OVERALLOCATION_BEHAVIOR_SPEC_VERSION 1 -@extension("VK_AMD_memory_overallocation_behavior") define VK_AMD_MEMORY_OVERALLOCATION_BEHAVIOR_EXTENSION_NAME "VK_AMD_memory_overallocation_behavior" - -// 191 -@extension("VK_EXT_vertex_attribute_divisor") define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_SPEC_VERSION 2 -@extension("VK_EXT_vertex_attribute_divisor") define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME "VK_EXT_vertex_attribute_divisor" - -// 197 -@extension("VK_KHR_driver_properties") define VK_KHR_DRIVER_PROPERTIES_SPEC_VERSION 1 -@extension("VK_KHR_driver_properties") define VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME "VK_KHR_driver_properties" - -// 198 -@extension("VK_KHR_shader_float_controls") define VK_KHR_SHADER_FLOAT_CONTROLS_SPEC_VERSION 1 -@extension("VK_KHR_shader_float_controls") define VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME "VK_KHR_shader_float_controls" - -// 199 -@extension("VK_NV_shader_subgroup_partitioned") define VK_NV_SHADER_SUBGROUP_PARTITIONED_SPEC_VERSION 1 -@extension("VK_NV_shader_subgroup_partitioned") define VK_NV_SHADER_SUBGROUP_PARTITIONED_EXTENSION_NAME "VK_NV_shader_subgroup_partitioned" - -// 201 -@extension("VK_KHR_swapchain_mutable_format") define VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_SPEC_VERSION 1 -@extension("VK_KHR_swapchain_mutable_format") define VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME "VK_KHR_swapchain_mutable_format" - -// 202 -@extension("VK_NV_compute_shader_derivatives") define VK_NV_COMPUTE_SHADER_DERIVATIVES_SPEC_VERSION 1 -@extension("VK_NV_compute_shader_derivatives") define VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME "VK_NV_compute_shader_derivatives" - -// 203 -@extension("VK_NV_mesh_shader") define VK_NV_MESH_SHADER_SPEC_VERSION 1 -@extension("VK_NV_mesh_shader") define VK_NV_MESH_SHADER_EXTENSION_NAME "VK_NV_mesh_shader" - -// 204 -@extension("VK_NV_fragment_shader_barycentric") define VK_NV_FRAGMENT_SHADER_BARYCENTRIC_SPEC_VERSION 1 -@extension("VK_NV_fragment_shader_barycentric") define VK_NV_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME "VK_NV_fragment_shader_barycentric" - -// 205 -@extension("VK_NV_shader_image_footprint") define VK_NV_SHADER_IMAGE_FOOTPRINT_SPEC_VERSION 1 -@extension("VK_NV_shader_image_footprint") define VK_NV_SHADER_IMAGE_FOOTPRINT_EXTENSION_NAME "VK_NV_shader_image_footprint" - -// 206 -@extension("VK_NV_scissor_exclusive") define VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION 1 -@extension("VK_NV_scissor_exclusive") define VK_NV_SCISSOR_EXCLUSIVE_EXTENSION_NAME "VK_NV_scissor_exclusive" - -// 207 -@extension("VK_NV_device_diagnostic_checkpoints") define VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_SPEC_VERSION 2 -@extension("VK_NV_device_diagnostic_checkpoints") define VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME "VK_NV_device_diagnostic_checkpoints" - -// 212 -@extension("VK_KHR_vulkan_memory_model") define VK_KHR_VULKAN_MEMORY_MODEL_SPEC_VERSION 2 -@extension("VK_KHR_vulkan_memory_model") define VK_KHR_VULKAN_MEMORY_MODEL_EXTENSION_NAME "VK_KHR_vulkan_memory_model" - -// 213 -@extension("VK_EXT_pci_bus_info") define VK_EXT_PCI_BUS_INFO_SPEC_VERSION 2 -@extension("VK_EXT_pci_bus_info") define VK_EXT_PCI_BUS_INFO_EXENSION_NAME "VK_EXT_pci_bus_info" - -// 215 -@extension("VK_FUCHSIA_imagepipe_surface") define VK_FUCHSIA_IMAGEPIPE_SURFACE_SPEC_VERSION 1 -@extension("VK_FUCHSIA_imagepipe_surface") define VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME "VK_FUCHSIA_imagepipe_surface" - -// 219 -@extension("VK_EXT_fragment_density_map") define VK_EXT_FRAGMENT_DENSITY_MAP_SPEC_VERSION 1 -@extension("VK_EXT_fragment_density_map") define VK_EXT_FRAGMENT_DENSITY_MAP_EXTENSION_NAME "VK_EXT_fragment_density_map" - -// 222 -@extension("VK_EXT_scalar_block_layout") define VK_EXT_SCALAR_BLOCK_LAYOUT_SPEC_VERSION 1 -@extension("VK_EXT_scalar_block_layout") define VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME "VK_EXT_scalar_block_layout" - -// 224 -@extension("VK_GOOGLE_hlsl_functionality1") define VK_GOOGLE_HLSL_FUNCTIONALITY1_SPEC_VERSION 1 -@extension("VK_GOOGLE_hlsl_functionality1") define VK_GOOGLE_HLSL_FUNCTIONALITY1_EXTENSION_NAME "VK_GOOGLE_hlsl_functionality1" - -// 225 -@extension("VK_GOOGLE_decorate_string") define VK_GOOGLE_DECORATE_STRING_SPEC_VERSION 1 -@extension("VK_GOOGLE_decorate_string") define VK_GOOGLE_DECORATE_STRING_EXTENSION_NAME "VK_GOOGLE_decorate_string" - -// 247 -@extension("VK_EXT_separate_stencil_usage") define VK_EXT_SEPARATE_STENCIL_USAGE_SPEC_VERSION 1 -@extension("VK_EXT_separate_stencil_usage") define VK_EXT_SEPARATE_STENCIL_USAGE_EXTENSION_NAME "VK_EXT_separate_stencil_usage" - -///////////// -// Types // -///////////// - -type u32 VkBool32 -type u32 VkFlags -type u64 VkDeviceSize -type u32 VkSampleMask - -/// Dispatchable handle types. -@dispatchHandle type u64 VkInstance -@dispatchHandle type u64 VkPhysicalDevice -@dispatchHandle type u64 VkDevice -@dispatchHandle type u64 VkQueue -@dispatchHandle type u64 VkCommandBuffer - -/// Non dispatchable handle types. -@nonDispatchHandle type u64 VkDeviceMemory -@nonDispatchHandle type u64 VkCommandPool -@nonDispatchHandle type u64 VkBuffer -@nonDispatchHandle type u64 VkBufferView -@nonDispatchHandle type u64 VkImage -@nonDispatchHandle type u64 VkImageView -@nonDispatchHandle type u64 VkShaderModule -@nonDispatchHandle type u64 VkPipeline -@nonDispatchHandle type u64 VkPipelineLayout -@nonDispatchHandle type u64 VkSampler -@nonDispatchHandle type u64 VkDescriptorSet -@nonDispatchHandle type u64 VkDescriptorSetLayout -@nonDispatchHandle type u64 VkDescriptorPool -@nonDispatchHandle type u64 VkFence -@nonDispatchHandle type u64 VkSemaphore -@nonDispatchHandle type u64 VkEvent -@nonDispatchHandle type u64 VkQueryPool -@nonDispatchHandle type u64 VkFramebuffer -@nonDispatchHandle type u64 VkRenderPass -@nonDispatchHandle type u64 VkPipelineCache - -@vulkan1_1 -@nonDispatchHandle type u64 VkSamplerYcbcrConversion -@nonDispatchHandle type u64 VkDescriptorUpdateTemplate - -// 1 -@extension("VK_KHR_surface") @nonDispatchHandle type u64 VkSurfaceKHR - -// 2 -@extension("VK_KHR_swapchain") @nonDispatchHandle type u64 VkSwapchainKHR - -// 3 -@extension("VK_KHR_display") @nonDispatchHandle type u64 VkDisplayKHR -@extension("VK_KHR_display") @nonDispatchHandle type u64 VkDisplayModeKHR - -// 12 -@extension("VK_EXT_debug_report") @nonDispatchHandle type u64 VkDebugReportCallbackEXT - -// 86 -@extension("VK_KHR_descriptor_update_template") @nonDispatchHandle type u64 VkDescriptorUpdateTemplateKHR - -// 87 -@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkObjectTableNVX -@extension("VK_NVX_device_generated_commands") @nonDispatchHandle type u64 VkIndirectCommandsLayoutNVX - -// 129 -@extension("VK_EXT_debug_utils") @nonDispatchHandle type u64 VkDebugUtilsMessengerEXT - -// 157 -@extension("VK_KHR_sampler_ycbcr_conversion") @nonDispatchHandle type u64 VkSamplerYcbcrConversionKHR - -// 161 -@extension("VK_EXT_validation_cache") @nonDispatchHandle type u64 VkValidationCacheEXT - -// 166 -@extension("VK_NV_ray_tracing") @nonDispatchHandle type u64 VkAccelerationStructureNV - -///////////// -// Enums // -///////////// - -enum VkImageLayout { - VK_IMAGE_LAYOUT_UNDEFINED = 0x00000000, /// Implicit layout an image is when its contents are undefined due to various reasons (e.g. right after creation) - VK_IMAGE_LAYOUT_GENERAL = 0x00000001, /// General layout when image can be used for any kind of access - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL = 0x00000002, /// Optimal layout when image is only used for color attachment read/write - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL = 0x00000003, /// Optimal layout when image is only used for depth/stencil attachment read/write - VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL = 0x00000004, /// Optimal layout when image is used for read only depth/stencil attachment and shader access - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL = 0x00000005, /// Optimal layout when image is used for read only shader access - VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 0x00000006, /// Optimal layout when image is used only as source of transfer operations - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 0x00000007, /// Optimal layout when image is used only as destination of transfer operations - VK_IMAGE_LAYOUT_PREINITIALIZED = 0x00000008, /// Initial layout used when the data is populated by the CPU - - //@vulkan1_1 - VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL = 1000117000, - VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL = 1000117001, - - //@extension("VK_KHR_swapchain") // 2 - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, - - //@extension("VK_KHR_shared_presentable_image") // 112 - VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000, - - //@extension("VK_KHR_maintenance2") // 118 - VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = 1000117000, - VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = 1000117001, - - //@extension("VK_NV_shading_rate_image") // 165 - VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV = 1000164003, - - //@extension("VK_EXT_fragment_density_map") // 219 - VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT = 1000218000, -} - -enum VkAttachmentLoadOp { - VK_ATTACHMENT_LOAD_OP_LOAD = 0x00000000, - VK_ATTACHMENT_LOAD_OP_CLEAR = 0x00000001, - VK_ATTACHMENT_LOAD_OP_DONT_CARE = 0x00000002, -} - -enum VkAttachmentStoreOp { - VK_ATTACHMENT_STORE_OP_STORE = 0x00000000, - VK_ATTACHMENT_STORE_OP_DONT_CARE = 0x00000001, -} - -enum VkImageType { - VK_IMAGE_TYPE_1D = 0x00000000, - VK_IMAGE_TYPE_2D = 0x00000001, - VK_IMAGE_TYPE_3D = 0x00000002, -} - -enum VkImageTiling { - VK_IMAGE_TILING_OPTIMAL = 0x00000000, - VK_IMAGE_TILING_LINEAR = 0x00000001, - - //@extension("VK_EXT_image_drm_format_modifier") // 159 - VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT = 1000158000, -} - -enum VkImageViewType { - VK_IMAGE_VIEW_TYPE_1D = 0x00000000, - VK_IMAGE_VIEW_TYPE_2D = 0x00000001, - VK_IMAGE_VIEW_TYPE_3D = 0x00000002, - VK_IMAGE_VIEW_TYPE_CUBE = 0x00000003, - VK_IMAGE_VIEW_TYPE_1D_ARRAY = 0x00000004, - VK_IMAGE_VIEW_TYPE_2D_ARRAY = 0x00000005, - VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 0x00000006, -} - -enum VkCommandBufferLevel { - VK_COMMAND_BUFFER_LEVEL_PRIMARY = 0x00000000, - VK_COMMAND_BUFFER_LEVEL_SECONDARY = 0x00000001, -} - -enum VkComponentSwizzle { - VK_COMPONENT_SWIZZLE_IDENTITY = 0x00000000, - VK_COMPONENT_SWIZZLE_ZERO = 0x00000001, - VK_COMPONENT_SWIZZLE_ONE = 0x00000002, - VK_COMPONENT_SWIZZLE_R = 0x00000003, - VK_COMPONENT_SWIZZLE_G = 0x00000004, - VK_COMPONENT_SWIZZLE_B = 0x00000005, - VK_COMPONENT_SWIZZLE_A = 0x00000006, -} - -enum VkDescriptorType { - VK_DESCRIPTOR_TYPE_SAMPLER = 0x00000000, - VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER = 0x00000001, - VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE = 0x00000002, - VK_DESCRIPTOR_TYPE_STORAGE_IMAGE = 0x00000003, - VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER = 0x00000004, - VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER = 0x00000005, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER = 0x00000006, - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER = 0x00000007, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 0x00000008, - VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 0x00000009, - VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 0x0000000a, - - //@extension("VK_EXT_inline_uniform_block") // 139 - VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT = 1000138000, - - //@extension("VK_NV_ray_tracing") // 166 - VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000, -} - -enum VkQueryType { - VK_QUERY_TYPE_OCCLUSION = 0x00000000, - VK_QUERY_TYPE_PIPELINE_STATISTICS = 0x00000001, /// Optional - VK_QUERY_TYPE_TIMESTAMP = 0x00000002, - - //@extension("VK_EXT_transform_feedback") // 29 - VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT = 1000028004, - - //@extension("VK_NV_ray_tracing") // 166 - VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV = 1000165000, -} - -enum VkBorderColor { - VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK = 0x00000000, - VK_BORDER_COLOR_INT_TRANSPARENT_BLACK = 0x00000001, - VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK = 0x00000002, - VK_BORDER_COLOR_INT_OPAQUE_BLACK = 0x00000003, - VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE = 0x00000004, - VK_BORDER_COLOR_INT_OPAQUE_WHITE = 0x00000005, -} - -enum VkPipelineBindPoint { - VK_PIPELINE_BIND_POINT_GRAPHICS = 0x00000000, - VK_PIPELINE_BIND_POINT_COMPUTE = 0x00000001, - - //@extension("VK_NV_ray_tracing") // 166 - VK_PIPELINE_BIND_POINT_RAY_TRACING_NV = 1000165000, -} - -enum VkPrimitiveTopology { - VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0x00000000, - VK_PRIMITIVE_TOPOLOGY_LINE_LIST = 0x00000001, - VK_PRIMITIVE_TOPOLOGY_LINE_STRIP = 0x00000002, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST = 0x00000003, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP = 0x00000004, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN = 0x00000005, - VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY = 0x00000006, - VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 0x00000007, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 0x00000008, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 0x00000009, - VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 0x0000000a, -} - -enum VkSharingMode { - VK_SHARING_MODE_EXCLUSIVE = 0x00000000, - VK_SHARING_MODE_CONCURRENT = 0x00000001, -} - -enum VkIndexType { - VK_INDEX_TYPE_UINT16 = 0x00000000, - VK_INDEX_TYPE_UINT32 = 0x00000001, - - //@extension("VK_NV_ray_tracing") // 166 - VK_INDEX_TYPE_NONE_NV = 1000165000, -} - -enum VkFilter { - VK_FILTER_NEAREST = 0x00000000, - VK_FILTER_LINEAR = 0x00000001, - - //@extension("VK_IMG_filter_cubic") // 16 - VK_FILTER_CUBIC_IMG = 1000015000, -} - -enum VkSamplerMipmapMode { - VK_SAMPLER_MIPMAP_MODE_NEAREST = 0x00000001, /// Choose nearest mip level - VK_SAMPLER_MIPMAP_MODE_LINEAR = 0x00000002, /// Linear filter between mip levels -} - -enum VkSamplerAddressMode { - VK_SAMPLER_ADDRESS_MODE_REPEAT = 0x00000000, - VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT = 0x00000001, - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 0x00000002, - VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 0x00000003, - VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 0x00000004, -} - -enum VkCompareOp { - VK_COMPARE_OP_NEVER = 0x00000000, - VK_COMPARE_OP_LESS = 0x00000001, - VK_COMPARE_OP_EQUAL = 0x00000002, - VK_COMPARE_OP_LESS_OR_EQUAL = 0x00000003, - VK_COMPARE_OP_GREATER = 0x00000004, - VK_COMPARE_OP_NOT_EQUAL = 0x00000005, - VK_COMPARE_OP_GREATER_OR_EQUAL = 0x00000006, - VK_COMPARE_OP_ALWAYS = 0x00000007, -} - -enum VkPolygonMode { - VK_POLYGON_MODE_FILL = 0x00000000, - VK_POLYGON_MODE_LINE = 0x00000001, - VK_POLYGON_MODE_POINT = 0x00000002, - - //@extension("VK_NV_fill_rectangle") // 154 - VK_POLYGON_MODE_FILL_RECTANGLE_NV = 1000153000, -} - -enum VkFrontFace { - VK_FRONT_FACE_COUNTER_CLOCKWISE = 0x00000000, - VK_FRONT_FACE_CLOCKWISE = 0x00000001, -} - -enum VkBlendFactor { - VK_BLEND_FACTOR_ZERO = 0x00000000, - VK_BLEND_FACTOR_ONE = 0x00000001, - VK_BLEND_FACTOR_SRC_COLOR = 0x00000002, - VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR = 0x00000003, - VK_BLEND_FACTOR_DST_COLOR = 0x00000004, - VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR = 0x00000005, - VK_BLEND_FACTOR_SRC_ALPHA = 0x00000006, - VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA = 0x00000007, - VK_BLEND_FACTOR_DST_ALPHA = 0x00000008, - VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA = 0x00000009, - VK_BLEND_FACTOR_CONSTANT_COLOR = 0x0000000a, - VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR = 0x0000000b, - VK_BLEND_FACTOR_CONSTANT_ALPHA = 0x0000000c, - VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA = 0x0000000d, - VK_BLEND_FACTOR_SRC_ALPHA_SATURATE = 0x0000000e, - VK_BLEND_FACTOR_SRC1_COLOR = 0x0000000f, - VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR = 0x00000010, - VK_BLEND_FACTOR_SRC1_ALPHA = 0x00000011, - VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA = 0x00000012, -} - -enum VkBlendOp { - VK_BLEND_OP_ADD = 0x00000000, - VK_BLEND_OP_SUBTRACT = 0x00000001, - VK_BLEND_OP_REVERSE_SUBTRACT = 0x00000002, - VK_BLEND_OP_MIN = 0x00000003, - VK_BLEND_OP_MAX = 0x00000004, - - //@extension("VK_EXT_blend_operation_advanced") // 149 - VK_BLEND_OP_ZERO_EXT = 1000148000, - VK_BLEND_OP_SRC_EXT = 1000148001, - VK_BLEND_OP_DST_EXT = 1000148002, - VK_BLEND_OP_SRC_OVER_EXT = 1000148003, - VK_BLEND_OP_DST_OVER_EXT = 1000148004, - VK_BLEND_OP_SRC_IN_EXT = 1000148005, - VK_BLEND_OP_DST_IN_EXT = 1000148006, - VK_BLEND_OP_SRC_OUT_EXT = 1000148007, - VK_BLEND_OP_DST_OUT_EXT = 1000148008, - VK_BLEND_OP_SRC_ATOP_EXT = 1000148009, - VK_BLEND_OP_DST_ATOP_EXT = 1000148010, - VK_BLEND_OP_XOR_EXT = 1000148011, - VK_BLEND_OP_MULTIPLY_EXT = 1000148012, - VK_BLEND_OP_SCREEN_EXT = 1000148013, - VK_BLEND_OP_OVERLAY_EXT = 1000148014, - VK_BLEND_OP_DARKEN_EXT = 1000148015, - VK_BLEND_OP_LIGHTEN_EXT = 1000148016, - VK_BLEND_OP_COLORDODGE_EXT = 1000148017, - VK_BLEND_OP_COLORBURN_EXT = 1000148018, - VK_BLEND_OP_HARDLIGHT_EXT = 1000148019, - VK_BLEND_OP_SOFTLIGHT_EXT = 1000148020, - VK_BLEND_OP_DIFFERENCE_EXT = 1000148021, - VK_BLEND_OP_EXCLUSION_EXT = 1000148022, - VK_BLEND_OP_INVERT_EXT = 1000148023, - VK_BLEND_OP_INVERT_RGB_EXT = 1000148024, - VK_BLEND_OP_LINEARDODGE_EXT = 1000148025, - VK_BLEND_OP_LINEARBURN_EXT = 1000148026, - VK_BLEND_OP_VIVIDLIGHT_EXT = 1000148027, - VK_BLEND_OP_LINEARLIGHT_EXT = 1000148028, - VK_BLEND_OP_PINLIGHT_EXT = 1000148029, - VK_BLEND_OP_HARDMIX_EXT = 1000148030, - VK_BLEND_OP_HSL_HUE_EXT = 1000148031, - VK_BLEND_OP_HSL_SATURATION_EXT = 1000148032, - VK_BLEND_OP_HSL_COLOR_EXT = 1000148033, - VK_BLEND_OP_HSL_LUMINOSITY_EXT = 1000148034, - VK_BLEND_OP_PLUS_EXT = 1000148035, - VK_BLEND_OP_PLUS_CLAMPED_EXT = 1000148036, - VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT = 1000148037, - VK_BLEND_OP_PLUS_DARKER_EXT = 1000148038, - VK_BLEND_OP_MINUS_EXT = 1000148039, - VK_BLEND_OP_MINUS_CLAMPED_EXT = 1000148040, - VK_BLEND_OP_CONTRAST_EXT = 1000148041, - VK_BLEND_OP_INVERT_OVG_EXT = 1000148042, - VK_BLEND_OP_RED_EXT = 1000148043, - VK_BLEND_OP_GREEN_EXT = 1000148044, - VK_BLEND_OP_BLUE_EXT = 1000148045, -} - -enum VkStencilOp { - VK_STENCIL_OP_KEEP = 0x00000000, - VK_STENCIL_OP_ZERO = 0x00000001, - VK_STENCIL_OP_REPLACE = 0x00000002, - VK_STENCIL_OP_INCREMENT_AND_CLAMP = 0x00000003, - VK_STENCIL_OP_DECREMENT_AND_CLAMP = 0x00000004, - VK_STENCIL_OP_INVERT = 0x00000005, - VK_STENCIL_OP_INCREMENT_AND_WRAP = 0x00000006, - VK_STENCIL_OP_DECREMENT_AND_WRAP = 0x00000007, -} - -enum VkLogicOp { - VK_LOGIC_OP_CLEAR = 0x00000000, - VK_LOGIC_OP_AND = 0x00000001, - VK_LOGIC_OP_AND_REVERSE = 0x00000002, - VK_LOGIC_OP_COPY = 0x00000003, - VK_LOGIC_OP_AND_INVERTED = 0x00000004, - VK_LOGIC_OP_NO_OP = 0x00000005, - VK_LOGIC_OP_XOR = 0x00000006, - VK_LOGIC_OP_OR = 0x00000007, - VK_LOGIC_OP_NOR = 0x00000008, - VK_LOGIC_OP_EQUIVALENT = 0x00000009, - VK_LOGIC_OP_INVERT = 0x0000000a, - VK_LOGIC_OP_OR_REVERSE = 0x0000000b, - VK_LOGIC_OP_COPY_INVERTED = 0x0000000c, - VK_LOGIC_OP_OR_INVERTED = 0x0000000d, - VK_LOGIC_OP_NAND = 0x0000000e, - VK_LOGIC_OP_SET = 0x0000000f, -} - -enum VkSystemAllocationScope { - VK_SYSTEM_ALLOCATION_SCOPE_COMMAND = 0x00000000, - VK_SYSTEM_ALLOCATION_SCOPE_OBJECT = 0x00000001, - VK_SYSTEM_ALLOCATION_SCOPE_CACHE = 0x00000002, - VK_SYSTEM_ALLOCATION_SCOPE_DEVICE = 0x00000003, - VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE = 0x00000004, -} - -enum VkInternalAllocationType { - VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE = 0x00000000, -} - -enum VkPhysicalDeviceType { - VK_PHYSICAL_DEVICE_TYPE_OTHER = 0x00000000, - VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 0x00000001, - VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 0x00000002, - VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 0x00000003, - VK_PHYSICAL_DEVICE_TYPE_CPU = 0x00000004, -} - -enum VkVertexInputRate { - VK_VERTEX_INPUT_RATE_VERTEX = 0x00000000, - VK_VERTEX_INPUT_RATE_INSTANCE = 0x00000001, -} - -/// Vulkan format definitions -enum VkFormat { - VK_FORMAT_UNDEFINED = 0, - VK_FORMAT_R4G4_UNORM_PACK8 = 1, - VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, - VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, - VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, - VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, - VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, - VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, - VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, - VK_FORMAT_R8_UNORM = 9, - VK_FORMAT_R8_SNORM = 10, - VK_FORMAT_R8_USCALED = 11, - VK_FORMAT_R8_SSCALED = 12, - VK_FORMAT_R8_UINT = 13, - VK_FORMAT_R8_SINT = 14, - VK_FORMAT_R8_SRGB = 15, - VK_FORMAT_R8G8_UNORM = 16, - VK_FORMAT_R8G8_SNORM = 17, - VK_FORMAT_R8G8_USCALED = 18, - VK_FORMAT_R8G8_SSCALED = 19, - VK_FORMAT_R8G8_UINT = 20, - VK_FORMAT_R8G8_SINT = 21, - VK_FORMAT_R8G8_SRGB = 22, - VK_FORMAT_R8G8B8_UNORM = 23, - VK_FORMAT_R8G8B8_SNORM = 24, - VK_FORMAT_R8G8B8_USCALED = 25, - VK_FORMAT_R8G8B8_SSCALED = 26, - VK_FORMAT_R8G8B8_UINT = 27, - VK_FORMAT_R8G8B8_SINT = 28, - VK_FORMAT_R8G8B8_SRGB = 29, - VK_FORMAT_B8G8R8_UNORM = 30, - VK_FORMAT_B8G8R8_SNORM = 31, - VK_FORMAT_B8G8R8_USCALED = 32, - VK_FORMAT_B8G8R8_SSCALED = 33, - VK_FORMAT_B8G8R8_UINT = 34, - VK_FORMAT_B8G8R8_SINT = 35, - VK_FORMAT_B8G8R8_SRGB = 36, - VK_FORMAT_R8G8B8A8_UNORM = 37, - VK_FORMAT_R8G8B8A8_SNORM = 38, - VK_FORMAT_R8G8B8A8_USCALED = 39, - VK_FORMAT_R8G8B8A8_SSCALED = 40, - VK_FORMAT_R8G8B8A8_UINT = 41, - VK_FORMAT_R8G8B8A8_SINT = 42, - VK_FORMAT_R8G8B8A8_SRGB = 43, - VK_FORMAT_B8G8R8A8_UNORM = 44, - VK_FORMAT_B8G8R8A8_SNORM = 45, - VK_FORMAT_B8G8R8A8_USCALED = 46, - VK_FORMAT_B8G8R8A8_SSCALED = 47, - VK_FORMAT_B8G8R8A8_UINT = 48, - VK_FORMAT_B8G8R8A8_SINT = 49, - VK_FORMAT_B8G8R8A8_SRGB = 50, - VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, - VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, - VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, - VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, - VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, - VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, - VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, - VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, - VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, - VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, - VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, - VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, - VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, - VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, - VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, - VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, - VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, - VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, - VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, - VK_FORMAT_R16_UNORM = 70, - VK_FORMAT_R16_SNORM = 71, - VK_FORMAT_R16_USCALED = 72, - VK_FORMAT_R16_SSCALED = 73, - VK_FORMAT_R16_UINT = 74, - VK_FORMAT_R16_SINT = 75, - VK_FORMAT_R16_SFLOAT = 76, - VK_FORMAT_R16G16_UNORM = 77, - VK_FORMAT_R16G16_SNORM = 78, - VK_FORMAT_R16G16_USCALED = 79, - VK_FORMAT_R16G16_SSCALED = 80, - VK_FORMAT_R16G16_UINT = 81, - VK_FORMAT_R16G16_SINT = 82, - VK_FORMAT_R16G16_SFLOAT = 83, - VK_FORMAT_R16G16B16_UNORM = 84, - VK_FORMAT_R16G16B16_SNORM = 85, - VK_FORMAT_R16G16B16_USCALED = 86, - VK_FORMAT_R16G16B16_SSCALED = 87, - VK_FORMAT_R16G16B16_UINT = 88, - VK_FORMAT_R16G16B16_SINT = 89, - VK_FORMAT_R16G16B16_SFLOAT = 90, - VK_FORMAT_R16G16B16A16_UNORM = 91, - VK_FORMAT_R16G16B16A16_SNORM = 92, - VK_FORMAT_R16G16B16A16_USCALED = 93, - VK_FORMAT_R16G16B16A16_SSCALED = 94, - VK_FORMAT_R16G16B16A16_UINT = 95, - VK_FORMAT_R16G16B16A16_SINT = 96, - VK_FORMAT_R16G16B16A16_SFLOAT = 97, - VK_FORMAT_R32_UINT = 98, - VK_FORMAT_R32_SINT = 99, - VK_FORMAT_R32_SFLOAT = 100, - VK_FORMAT_R32G32_UINT = 101, - VK_FORMAT_R32G32_SINT = 102, - VK_FORMAT_R32G32_SFLOAT = 103, - VK_FORMAT_R32G32B32_UINT = 104, - VK_FORMAT_R32G32B32_SINT = 105, - VK_FORMAT_R32G32B32_SFLOAT = 106, - VK_FORMAT_R32G32B32A32_UINT = 107, - VK_FORMAT_R32G32B32A32_SINT = 108, - VK_FORMAT_R32G32B32A32_SFLOAT = 109, - VK_FORMAT_R64_UINT = 110, - VK_FORMAT_R64_SINT = 111, - VK_FORMAT_R64_SFLOAT = 112, - VK_FORMAT_R64G64_UINT = 113, - VK_FORMAT_R64G64_SINT = 114, - VK_FORMAT_R64G64_SFLOAT = 115, - VK_FORMAT_R64G64B64_UINT = 116, - VK_FORMAT_R64G64B64_SINT = 117, - VK_FORMAT_R64G64B64_SFLOAT = 118, - VK_FORMAT_R64G64B64A64_UINT = 119, - VK_FORMAT_R64G64B64A64_SINT = 120, - VK_FORMAT_R64G64B64A64_SFLOAT = 121, - VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, - VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, - VK_FORMAT_D16_UNORM = 124, - VK_FORMAT_X8_D24_UNORM_PACK32 = 125, - VK_FORMAT_D32_SFLOAT = 126, - VK_FORMAT_S8_UINT = 127, - VK_FORMAT_D16_UNORM_S8_UINT = 128, - VK_FORMAT_D24_UNORM_S8_UINT = 129, - VK_FORMAT_D32_SFLOAT_S8_UINT = 130, - VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, - VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, - VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, - VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, - VK_FORMAT_BC2_UNORM_BLOCK = 135, - VK_FORMAT_BC2_SRGB_BLOCK = 136, - VK_FORMAT_BC3_UNORM_BLOCK = 137, - VK_FORMAT_BC3_SRGB_BLOCK = 138, - VK_FORMAT_BC4_UNORM_BLOCK = 139, - VK_FORMAT_BC4_SNORM_BLOCK = 140, - VK_FORMAT_BC5_UNORM_BLOCK = 141, - VK_FORMAT_BC5_SNORM_BLOCK = 142, - VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, - VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, - VK_FORMAT_BC7_UNORM_BLOCK = 145, - VK_FORMAT_BC7_SRGB_BLOCK = 146, - VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, - VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, - VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, - VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, - VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, - VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, - VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, - VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, - VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, - VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, - VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, - VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, - VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, - VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, - VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, - VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, - VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, - VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, - VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, - VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, - VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, - VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, - VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, - VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, - VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, - VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, - VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, - VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, - VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, - VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, - VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, - VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, - VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, - VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, - VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, - VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, - VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, - VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, - - //@vulkan1_1 - VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000, - VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001, - VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002, - VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003, - VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004, - VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005, - VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006, - VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007, - VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008, - VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009, - VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010, - VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012, - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014, - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016, - VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017, - VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018, - VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019, - VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020, - VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022, - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024, - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026, - VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027, - VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028, - VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029, - VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, - VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, - VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, - VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, - - //@extension("VK_IMG_format_pvrtc") // 28 - VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, - VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, - VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, - VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003, - VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004, - VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, - VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, - VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, - - //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 - VK_FORMAT_G8B8G8R8_422_UNORM_KHR = 1000156000, - VK_FORMAT_B8G8R8G8_422_UNORM_KHR = 1000156001, - VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = 1000156002, - VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR = 1000156003, - VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR = 1000156004, - VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR = 1000156005, - VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR = 1000156006, - VK_FORMAT_R10X6_UNORM_PACK16_KHR = 1000156007, - VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR = 1000156008, - VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR = 1000156009, - VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR = 1000156010, - VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR = 1000156011, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR = 1000156012, - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR = 1000156013, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR = 1000156014, - VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR = 1000156015, - VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR = 1000156016, - VK_FORMAT_R12X4_UNORM_PACK16_KHR = 1000156017, - VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR = 1000156018, - VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR = 1000156019, - VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR = 1000156020, - VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR = 1000156021, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR = 1000156022, - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR = 1000156023, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR = 1000156024, - VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR = 1000156025, - VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR = 1000156026, - VK_FORMAT_G16B16G16R16_422_UNORM_KHR = 1000156027, - VK_FORMAT_B16G16R16G16_422_UNORM_KHR = 1000156028, - VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR = 1000156029, - VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR = 1000156030, - VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = 1000156031, - VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = 1000156032, - VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = 1000156033, -} - -/// Structure type enumerant -enum VkStructureType { - VK_STRUCTURE_TYPE_APPLICATION_INFO = 0, - VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO = 1, - VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO = 2, - VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO = 3, - VK_STRUCTURE_TYPE_SUBMIT_INFO = 4, - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO = 5, - VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE = 6, - VK_STRUCTURE_TYPE_BIND_SPARSE_INFO = 7, - VK_STRUCTURE_TYPE_FENCE_CREATE_INFO = 8, - VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO = 9, - VK_STRUCTURE_TYPE_EVENT_CREATE_INFO = 10, - VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO = 11, - VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO = 12, - VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO = 13, - VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO = 14, - VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO = 15, - VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO = 16, - VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO = 17, - VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO = 18, - VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO = 19, - VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO = 20, - VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO = 21, - VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO = 22, - VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO = 23, - VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO = 24, - VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO = 25, - VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO = 26, - VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO = 27, - VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO = 28, - VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO = 29, - VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO = 30, - VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO = 31, - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO = 32, - VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO = 33, - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO = 34, - VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET = 35, - VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET = 36, - VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO = 37, - VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO = 38, - VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO = 39, - VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO = 40, - VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO = 41, - VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO = 42, - VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO = 43, - VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER = 44, - VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER = 45, - VK_STRUCTURE_TYPE_MEMORY_BARRIER = 46, - VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO = 47, - VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO = 48, - - //@vulkan1_1 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES = 1000094000, - VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO = 1000157000, - VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO = 1000157001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES = 1000083000, - VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS = 1000127000, - VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO = 1000127001, - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO = 1000060000, - VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO = 1000060003, - VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO = 1000060004, - VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO = 1000060005, - VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO = 1000060006, - VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO = 1000060013, - VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO = 1000060014, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES = 1000070000, - VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO = 1000070001, - VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2 = 1000146000, - VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2 = 1000146001, - VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2 = 1000146002, - VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 = 1000146003, - VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2 = 1000146004, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 = 1000059000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 = 1000059001, - VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2 = 1000059002, - VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2 = 1000059003, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2 = 1000059004, - VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2 = 1000059005, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2 = 1000059006, - VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2 = 1000059007, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2 = 1000059008, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES = 1000117000, - VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO = 1000117001, - VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO = 1000117002, - VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO = 1000117003, - VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO = 1000053000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES = 1000053001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES = 1000053002, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES = 1000120000, - VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO = 1000145000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES = 1000145001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES = 1000145002, - VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2 = 1000145003, - VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO = 1000156000, - VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO = 1000156001, - VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO = 1000156002, - VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO = 1000156003, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES = 1000156004, - VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES = 1000156005, - VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO = 1000085000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO = 1000071000, - VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES = 1000071001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO = 1000071002, - VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES = 1000071003, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES = 1000071004, - VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO = 1000072000, - VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO = 1000072001, - VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO = 1000072002, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO = 1000112000, - VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES = 1000112001, - VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO = 1000113000, - VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO = 1000077000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO = 1000076000, - VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES = 1000076001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES = 1000168000, - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT = 1000168001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES = 1000063000, - VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR = 1000060007, - - //@extension("VK_KHR_swapchain") // 2 - VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR = 1000001000, - VK_STRUCTURE_TYPE_PRESENT_INFO_KHR = 1000001001, - // added as interaction from VK_KHR_device_group / VK 1.1 - VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR = 1000060008, - VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR = 1000060009, - VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHR = 1000060010, - VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHR = 1000060011, - VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHR = 1000060012, - - //@extension("VK_KHR_display") // 3 - VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR = 1000002000, - VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR = 1000002001, - - //@extension("VK_KHR_display_swapchain") // 4 - VK_STRUCTURE_TYPE_DISPLAY_DISPLAY_PRESENT_INFO_KHR = 1000003000, - - //@extension("VK_KHR_xlib_surface") // 5 - VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, - - //@extension("VK_KHR_xcb_surface") // 6 - VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, - - //@extension("VK_KHR_wayland_surface") // 7 - VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, - - //@extension("VK_KHR_android_surface") // 9 - VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, - - //@extension("VK_KHR_win32_surface") // 10 - VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, - - //@extension("VK_ANDROID_native_buffer") // 11 - VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID = 1000010000, - VK_STRUCTURE_TYPE_SWAPCHAIN_IMAGE_CREATE_INFO_ANDROID = 1000010001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENTATION_PROPERTIES_ANDROID = 1000010002, - - //@extension("VK_EXT_debug_report") // 12 - VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, - - //@extension("VK_AMD_rasterization_order") // 19 - VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000, - - //@extension("VK_EXT_debug_marker") // 23 - VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000, - VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001, - VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002, - - //@extension("VK_NV_dedicated_allocation") // 27 - VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000, - VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001, - VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002, - - //@extension("VK_EXT_transform_feedback") // 29 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT = 1000028000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT = 1000028001, - VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT = 1000028002, - - //@extension("VK_AMD_texture_gather_bias_lod") // 42 - VK_STRUCTURE_TYPE_TEXTURE_LOD_GATHER_FORMAT_PROPERTIES_AMD = 1000041000, - - //@extension("VK_NV_corner_sampled_image") // 51 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CORNER_SAMPLED_IMAGE_FEATURES_NV = 1000050000, - - //@extension("VK_KHR_multiview") // 54 - VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR = 1000053000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR = 1000053001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR = 1000053002, - - //@extension("VK_NV_external_memory") // 57 - VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000, - VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001, - - //@extension("VK_NV_external_memory_win32") // 58 - VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000, - VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001, - - //@extension("VK_NV_win32_keyed_mutex") // 59 - VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000, - - //@extension("VK_KHR_get_physical_device_properties2") // 60 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = 1000059000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = 1000059001, - VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = 1000059002, - VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059003, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = 1000059004, - VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = 1000059005, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = 1000059006, - VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008, - - //@extension("VK_KHR_device_group") // 61 - VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR = 1000060000, - VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHR = 1000060003, - VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHR = 1000060004, - VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHR = 1000060005, - VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHR = 1000060006, - VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR = 1000060007, - // tokens 08-12 are listed with VK_KHR_swapchain - VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHR = 1000060013, - VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHR = 1000060014, - - //@extension("VK_EXT_validation_flags") // 62 - VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000, - - //@extension("VK_NN_vi_surface") // 63 - VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN = 1000062000, - - //@extension("VK_EXT_astc_decode_mode") // 68 - VK_STRUCTURE_TYPE_IMAGE_VIEW_ASTC_DECODE_MODE_EXT = 1000067000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT = 1000067001, - - //@extension("VK_KHR_device_group_creation") // 71 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR = 1000070000, - VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHR = 1000070001, - - //@extension("VK_KHR_external_memory_capabilities") // 72 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR = 1000071000, - VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR = 1000071001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR = 1000071002, - VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR = 1000071003, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR = 1000071004, - - //@extension("VK_KHR_external_memory") // 73 - VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR = 1000072000, - VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR = 1000072001, - VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR = 1000072002, - - //@extension("VK_KHR_external_memory_win32") // 74 - VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073000, - VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073001, - VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR = 1000073002, - - //@extension("VK_KHR_external_memory_fd") // 75 - VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR = 1000074000, - VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR = 1000074001, - - //@extension("VK_KHR_win32_keyed_mutex") // 76 - VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR = 1000075000, - - //@extension("VK_KHR_external_semaphore_capabilities") // 77 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR = 1000076000, - VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR = 1000076001, - - //@extension("VK_KHR_external_semaphore") // 78 - VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR = 1000077000, - - //@extension("VK_KHR_external_semaphore_win32") // 79 - VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR = 1000078000, - VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR = 1000078001, - VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHR = 1000078002, - - //@extension("VK_KHR_external_semaphore_fd") // 80 - VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR = 1000079000, - VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR = 1000079001, - - //@extension("VK_KHR_push_descriptor") // 81 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR = 1000080000, - - //@extension("VK_KHR_16bit_storage") // 84 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR = 1000083000, - - //@extension("VK_KHR_incremental_present") // 85 - VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000, - - //@extension("VK_EXT_conditional_rendering") // 82 - VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT = 1000081000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT = 1000081001, - VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT = 1000081002, - - //@extension("VK_KHR_shader_float16_int8") // 83 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR = 1000082000, - - //@extension("VK_KHR_descriptor_update_template") // 86 - VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = 1000085000, - - //@extension("VK_NVX_device_generated_commands") // 87 - VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000, - VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001, - VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002, - VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003, - VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004, - VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005, - - //@extension("VK_NV_clip_space_w_scaling") // 88 - VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000, - - //@extension("VK_EXT_display_surface_counter") // 91 - VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT = 1000090000, - - //@extension("VK_EXT_display_control") // 92 - VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000, - VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT = 1000091001, - VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT = 1000091002, - VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT = 1000091003, - - //@extension("VK_GOOGLE_display_timing") // 93 - VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000, - - //@extension("VK_NVX_multiview_per_view_attributes") // 98 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX = 1000097000, - - //@extension("VK_NV_viewport_swizzle") // 99 - VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV = 1000098000, - - //@extension("VK_EXT_discard_rectangles") // 100 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT = 1000099000, - VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT = 1000099001, - - //@extension("VK_EXT_conservative_rasterization") // 102 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT = 1000101000, - VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT = 1000101001, - - //@extension("VK_KHR_create_renderpass2") // 110 - VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR = 1000109000, - VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR = 1000109001, - VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR = 1000109002, - VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR = 1000109003, - VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR = 1000109004, - VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO_KHR = 1000109005, - VK_STRUCTURE_TYPE_SUBPASS_END_INFO_KHR = 1000109006, - - //@extension("VK_EXT_hdr_metadata") // 106 - VK_STRUCTURE_TYPE_HDR_METADATA_EXT = 1000105000, - - //@extension("VK_KHR_shared_presentable_image") // 112 - VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR = 1000111000, - - //@extension("VK_KHR_external_fence_capabilities") // 113 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO_KHR = 1000112000, - VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES_KHR = 1000112001, - - //@extension("VK_KHR_external_fence") // 114 - VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO_KHR = 1000113000, - - //@extension("VK_KHR_external_fence_win32") // 115 - VK_STRUCTURE_TYPE_IMPORT_FENCE_WIN32_HANDLE_INFO_KHR = 1000114000, - VK_STRUCTURE_TYPE_EXPORT_FENCE_WIN32_HANDLE_INFO_KHR = 1000114001, - VK_STRUCTURE_TYPE_FENCE_GET_WIN32_HANDLE_INFO_KHR = 1000114002, - - //@extension("VK_KHR_external_fence_fd") // 117 - VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR = 1000115000, - VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR = 1000115001, - - //@extension("VK_KHR_maintenance2") // 118 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR = 1000117000, - VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = 1000117001, - VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR = 1000117002, - VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = 1000117003, - - //@extension("VK_KHR_get_surface_capabilities2") // 120 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000, - VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR = 1000119001, - VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR = 1000119002, - - //@extension("VK_KHR_variable_pointers") // 121 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR = 1000120000, - - //@extension("VK_KHR_display_properties2") // 122 - VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR = 1000121000, - VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR = 1000121001, - VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR = 1000121002, - VK_STRUCTURE_TYPE_DISPLAY_PLANE_INFO_2_KHR = 1000121003, - VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR = 1000121004, - - //@extension("VK_MVK_ios_surface") // 123 - VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK = 1000122000, - - //@extension("VK_MVK_macos_surface") // 124 - VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, - - //@extension("VK_KHR_dedicated_allocation") // 128 - VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR = 1000127000, - VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR = 1000127001, - - //@extension("VK_EXT_debug_utils") // 129 - VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT = 1000128000, - VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT = 1000128001, - VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT = 1000128002, - VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT = 1000128003, - VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000128004, - - //@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130 - VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID = 1000129000, - VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID = 1000129001, - VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID = 1000129002, - VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129003, - VK_STRUCTURE_TYPE_MEMORY_GET_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129004, - VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID = 1000129005, - - //@extension("VK_EXT_sampler_filter_minmax") // 131 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = 1000130000, - VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = 1000130001, - - //@extension("VK_EXT_inline_uniform_block") // 139 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT = 1000138000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT = 1000138001, - VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT = 1000138002, - VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT = 1000138003, - - //@extension("VK_EXT_sample_locations") // 144 - VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT = 1000143000, - VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001, - VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT = 1000143003, - VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT = 1000143004, - - //@extension("VK_KHR_get_memory_requirements2") // 147 - VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146000, - VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146001, - VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = 1000146002, - VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR = 1000146003, - VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = 1000146004, - - //@extension("VK_KHR_image_format_list") // 148 - VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR = 1000147000, - - //@extension("VK_EXT_blend_operation_advanced") // 149 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001, - VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT = 1000148002, - - //@extension("VK_NV_fragment_coverage_to_color") // 150 - VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000, - - //@extension("VK_NV_framebuffer_mixed_samples") // 153 - VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000, - - //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 - VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = 1000156000, - VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR = 1000156001, - VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR = 1000156002, - VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR = 1000156003, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = 1000156004, - VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = 1000156005, - - //@extension("VK_EXT_image_drm_format_modifier") // 159 - VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT = 1000158000, - VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT = 1000158001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT = 1000158002, - VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT = 1000158003, - VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT = 1000158004, - VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT = 1000158005, - - //@extension("VK_KHR_bind_memory2") // 158 - VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR = 1000157000, - VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR = 1000157001, - - //@extension("VK_EXT_validation_cache") // 161 - VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000, - VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001, - - //@extension("VK_EXT_descriptor_indexing") // 162 - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT = 1000161000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT = 1000161001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT = 1000161002, - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT = 1000161003, - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT_EXT = 1000161004, - - //@extension("VK_NV_shading_rate_image") // 165 - VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SHADING_RATE_IMAGE_STATE_CREATE_INFO_NV = 1000164000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV = 1000164001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_PROPERTIES_NV = 1000164002, - VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_COARSE_SAMPLE_ORDER_STATE_CREATE_INFO_NV = 1000164005, - - //@extension("VK_NV_ray_tracing") // 166 - VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV = 1000165000, - VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV = 1000165001, - VK_STRUCTURE_TYPE_GEOMETRY_NV = 1000165003, - VK_STRUCTURE_TYPE_GEOMETRY_TRIANGLES_NV = 1000165004, - VK_STRUCTURE_TYPE_GEOMETRY_AABB_NV = 1000165005, - VK_STRUCTURE_TYPE_BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV = 1000165006, - VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV = 1000165007, - VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV = 1000165008, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV = 1000165009, - VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV = 1000165011, - VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV = 1000165012, - - //@extension("VK_NV_representative_fragment_test") // 167 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV = 1000166000, - VK_STRUCTURE_TYPE_PIPELINE_REPRESENTATIVE_FRAGMENT_TEST_STATE_CREATE_INFO_NV = 1000166001, - - //@extension("VK_KHR_maintenance3") // 169 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES_KHR = 1000168000, - VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT_KHR = 1000168001, - - //@extension("VK_EXT_global_priority") // 175 - VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT = 1000174000, - - //@extension("VK_KHR_8bit_storage") // 178 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR = 1000177000, - - //@extension("VK_EXT_external_memory_host") // 179 - VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT = 1000178000, - VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT = 1000178001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT = 1000178002, - - //@extension("VK_KHR_shader_atomic_int64") // 181 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR = 1000180000, - - //@extension("VK_EXT_calibrated_timestamps") // 185 - VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT = 1000184000, - - //@extension("VK_KHR_driver_properties") // 197 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR = 1000196000, - - //@extension("VK_KHR_shader_float_controls") // 198 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR = 1000197000, - - //@extension("VK_AMD_shader_core_properties") // 186 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD = 1000185000, - - //@extension("VK_AMD_memory_overallocation_behavior") // 190 - VK_STRUCTURE_TYPE_DEVICE_MEMORY_OVERALLOCATION_CREATE_INFO_AMD = 1000189000, - - //@extension("VK_EXT_vertex_attribute_divisor") // 191 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT = 1000190000, - VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT = 1000190001, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT = 1000190002, - - //@extension("VK_NV_device_diagnostic_checkpoints") // 207 - VK_STRUCTURE_TYPE_CHECKPOINT_DATA_NV = 1000206000, - VK_STRUCTURE_TYPE_QUEUE_FAMILY_CHECKPOINT_PROPERTIES_NV = 1000206001, - - //@extension("VK_KHR_vulkan_memory_model") // 212 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES_KHR = 1000211000, - - //@extension("VK_EXT_pci_bus_info") // 213 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT = 1000212000, - - //@extension("VK_FUCHSIA_imagepipe_surface") // 215 - VK_STRUCTURE_TYPE_IMAGEPIPE_SURFACE_CREATE_INFO_FUCHSIA = 1000214000, - - //@extension("VK_EXT_fragment_density_map") // 219 - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT = 1000218000, - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT = 1000218001, - VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT = 1000218002, - - //@extension("VK_EXT_scalar_block_layout") - VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT = 1000221000, - - //@extension("VK_EXT_separate_stencil_usage") // 247 - VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO_EXT = 1000246000, -} - -enum VkSubpassContents { - VK_SUBPASS_CONTENTS_INLINE = 0x00000000, - VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS = 0x00000001, -} - -enum VkPipelineCacheHeaderVersion { - VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1, -} - -@lastUnused(-11) -/// Error and return codes -enum VkResult { - // Return codes for successful operation execution (positive values) - VK_SUCCESS = 0, - VK_NOT_READY = 1, - VK_TIMEOUT = 2, - VK_EVENT_SET = 3, - VK_EVENT_RESET = 4, - VK_INCOMPLETE = 5, - - //@extension("VK_KHR_swapchain") // 2 - VK_SUBOPTIMAL_KHR = 1000001003, - - // Error codes (negative values) - VK_ERROR_OUT_OF_HOST_MEMORY = 0xFFFFFFFF, // -1 - VK_ERROR_OUT_OF_DEVICE_MEMORY = 0xFFFFFFFE, // -2 - VK_ERROR_INITIALIZATION_FAILED = 0xFFFFFFFD, // -3 - VK_ERROR_DEVICE_LOST = 0xFFFFFFFC, // -4 - VK_ERROR_MEMORY_MAP_FAILED = 0xFFFFFFFB, // -5 - VK_ERROR_LAYER_NOT_PRESENT = 0xFFFFFFFA, // -6 - VK_ERROR_EXTENSION_NOT_PRESENT = 0xFFFFFFF9, // -7 - VK_ERROR_FEATURE_NOT_PRESENT = 0xFFFFFFF8, // -8 - VK_ERROR_INCOMPATIBLE_DRIVER = 0xFFFFFFF7, // -9 - VK_ERROR_TOO_MANY_OBJECTS = 0xFFFFFFF6, // -10 - VK_ERROR_FORMAT_NOT_SUPPORTED = 0xFFFFFFF5, // -11 - VK_ERROR_FRAGMENTED_POOL = 0xFFFFFFF4, // -12 - - //@vulkan1_1 - VK_ERROR_OUT_OF_POOL_MEMORY = 0xC4642878, // -1000069000 - VK_ERROR_INVALID_EXTERNAL_HANDLE = 0xC4641CBD, // -1000072003 - - //@extension("VK_KHR_surface") // 1 - VK_ERROR_SURFACE_LOST_KHR = 0xC4653600, // -1000000000 - VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = 0xC46535FF, // -1000000001 - - //@extension("VK_KHR_swapchain") // 2 - VK_ERROR_OUT_OF_DATE_KHR = 0xC4653214, // -1000001004 - - //@extension("VK_KHR_display_swapchain") // 4 - VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = 0xC4652A47, // -1000003001 - - //@extension("VK_EXT_debug_report") // 12 - VK_ERROR_VALIDATION_FAILED_EXT = 0xC4650B07, // -1000011001 - - //@extension("VK_NV_glsl_shader") // 13 - VK_ERROR_INVALID_SHADER_NV = 0xC4650720, // -1000012000 - - //@extension("VK_KHR_maintenance1") // 70 - VK_ERROR_OUT_OF_POOL_MEMORY_KHR = 0xC4642878, // -1000069000 - - //@extension("VK_KHR_external_memory") // 73 - VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR = 0xC4641CBD, // -1000072003 - - //@extension("VK_EXT_image_drm_format_modifier") // 159 - VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT = 0xC462CCD0, // -1000158000 - - //@extension("VK_EXT_descriptor_indexing") // 162 - VK_ERROR_FRAGMENTATION_EXT = 0xc462c118, // -1000161000 - - //@extension("VK_EXT_global_priority") // 175 - VK_ERROR_NOT_PERMITTED_EXT = 0xC4628E4F, // -1000174001 -} - -enum VkDynamicState { - VK_DYNAMIC_STATE_VIEWPORT = 0x00000000, - VK_DYNAMIC_STATE_SCISSOR = 0x00000001, - VK_DYNAMIC_STATE_LINE_WIDTH = 0x00000002, - VK_DYNAMIC_STATE_DEPTH_BIAS = 0x00000003, - VK_DYNAMIC_STATE_BLEND_CONSTANTS = 0x00000004, - VK_DYNAMIC_STATE_DEPTH_BOUNDS = 0x00000005, - VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK = 0x00000006, - VK_DYNAMIC_STATE_STENCIL_WRITE_MASK = 0x00000007, - VK_DYNAMIC_STATE_STENCIL_REFERENCE = 0x00000008, - - //@extension("VK_NV_clip_space_w_scaling") // 88 - VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000, - - //@extension("VK_EXT_discard_rectangles") // 100 - VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000, - - //@extension("VK_EXT_sample_locations") // 144 - VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT = 1000143000, - - //@extension("VK_NV_shading_rate_image") // 165 - VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV = 1000164004, - VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV = 1000164006, - - //@extension("VK_NV_scissor_exclusive") // 206 - VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV = 1000205001, -} - -enum VkObjectType { - VK_OBJECT_TYPE_UNKNOWN = 0, - VK_OBJECT_TYPE_INSTANCE = 1, - VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2, - VK_OBJECT_TYPE_DEVICE = 3, - VK_OBJECT_TYPE_QUEUE = 4, - VK_OBJECT_TYPE_SEMAPHORE = 5, - VK_OBJECT_TYPE_COMMAND_BUFFER = 6, - VK_OBJECT_TYPE_FENCE = 7, - VK_OBJECT_TYPE_DEVICE_MEMORY = 8, - VK_OBJECT_TYPE_BUFFER = 9, - VK_OBJECT_TYPE_IMAGE = 10, - VK_OBJECT_TYPE_EVENT = 11, - VK_OBJECT_TYPE_QUERY_POOL = 12, - VK_OBJECT_TYPE_BUFFER_VIEW = 13, - VK_OBJECT_TYPE_IMAGE_VIEW = 14, - VK_OBJECT_TYPE_SHADER_MODULE = 15, - VK_OBJECT_TYPE_PIPELINE_CACHE = 16, - VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17, - VK_OBJECT_TYPE_RENDER_PASS = 18, - VK_OBJECT_TYPE_PIPELINE = 19, - VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20, - VK_OBJECT_TYPE_SAMPLER = 21, - VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22, - VK_OBJECT_TYPE_DESCRIPTOR_SET = 23, - VK_OBJECT_TYPE_FRAMEBUFFER = 24, - VK_OBJECT_TYPE_COMMAND_POOL = 25, - - //@vulkan1_1 - VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000, - VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000, - - //@extension("VK_KHR_surface") // 1 - VK_OBJECT_TYPE_SURFACE_KHR = 1000000000, - - //@extension("VK_KHR_swapchain") // 2 - VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000, - - //@extension("VK_KHR_display") // 3 - VK_OBJECT_TYPE_DISPLAY_KHR = 1000002000, - VK_OBJECT_TYPE_DISPLAY_MODE_KHR = 1000002001, - - //@extension("VK_KHR_debug_report") // 12 - VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000, - - //@extension("VK_KHR_descriptor_update_template") // 86 - VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = 1000085000, - - //@extension("VK_NVX_device_generated_commands") // 87 - VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000, - VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001, - - //@extension("VK_EXT_debug_utils") // 129 - VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000, - - //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 - VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = 1000156000, - - //@extension("VK_EXT_validation_cache") // 161 - VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000, - - //@extension("VK_NV_ray_tracing") // 166 - VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000, -} - - -//@vulkan1_1 enums - -enum VkPointClippingBehavior { - VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES = 0, - VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY = 1, -} - -enum VkTessellationDomainOrigin { - VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT = 0, - VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT = 1, -} - -enum VkSamplerYcbcrModelConversion { - VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY = 1, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709 = 2, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601 = 3, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 = 4, -} - -enum VkSamplerYcbcrRange { - VK_SAMPLER_YCBCR_RANGE_ITU_FULL = 0, - VK_SAMPLER_YCBCR_RANGE_ITU_NARROW = 1, -} - -enum VkChromaLocation { - VK_CHROMA_LOCATION_COSITED_EVEN = 0, - VK_CHROMA_LOCATION_MIDPOINT = 1, -} - -enum VkDescriptorUpdateTemplateType { - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET = 0, - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1, -} - -enum VkVendorId { - VK_VENDOR_ID_VIV = 0x10001, - VK_VENDOR_ID_VSI = 0x10002, - VK_VENDOR_ID_KAZAN = 0x10003, -} - -@extension("VK_KHR_surface") // 1 -enum VkPresentModeKHR { - VK_PRESENT_MODE_IMMEDIATE_KHR = 0x00000000, - VK_PRESENT_MODE_MAILBOX_KHR = 0x00000001, - VK_PRESENT_MODE_FIFO_KHR = 0x00000002, - VK_PRESENT_MODE_FIFO_RELAXED_KHR = 0x00000003, - - //@extension("VK_KHR_shared_presentable_image") // 112 - VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR = 1000111000, - VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR = 1000111001, -} - -@extension("VK_KHR_surface") // 1 -enum VkColorSpaceKHR { - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0x00000000, - - //@extension("VK_EXT_swapchain_colorspace") // 105 - VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT = 1000104001, - VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT = 1000104002, - VK_COLOR_SPACE_DCI_P3_LINEAR_EXT = 1000104003, - VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT = 1000104004, - VK_COLOR_SPACE_BT709_LINEAR_EXT = 1000104005, - VK_COLOR_SPACE_BT709_NONLINEAR_EXT = 1000104006, - VK_COLOR_SPACE_BT2020_LINEAR_EXT = 1000104007, - VK_COLOR_SPACE_HDR10_ST2084_EXT = 1000104008, - VK_COLOR_SPACE_DOLBYVISION_EXT = 1000104009, - VK_COLOR_SPACE_HDR10_HLG_EXT = 1000104010, - VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT = 1000104011, - VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT = 1000104012, - VK_COLOR_SPACE_PASS_THROUGH_EXT = 1000104013, - VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT = 1000104014, -} - -@extension("VK_EXT_debug_report") // 12 -enum VkDebugReportObjectTypeEXT { - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0, - VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1, - VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2, - VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3, - VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4, - VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5, - VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6, - VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7, - VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8, - VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9, - VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10, - VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11, - VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12, - VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13, - VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14, - VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15, - VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16, - VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17, - VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18, - VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19, - VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20, - VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21, - VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22, - VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23, - VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24, - VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25, - VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26, - VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27, - VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT = 28, - VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29, - VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30, - VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31, - VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32, - - //extension("VK_EXT_validation_cache") // 161 - VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT = 33, - - //extension("VK_KHR_descriptor_update_template") // 86 - VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = 1000085000, - - //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 - VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT = 1000156000, - - //@extension("VK_NV_ray_tracing") // 166 - VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT = 1000165000, -} - -@extension("VK_AMD_rasterization_order") // 19 -enum VkRasterizationOrderAMD { - VK_RASTERIZATION_ORDER_STRICT_AMD = 0, - VK_RASTERIZATION_ORDER_RELAXED_AMD = 1, -} - -@extension("VK_AMD_shader_info") // 43 -enum VkShaderInfoTypeAMD { - VK_SHADER_INFO_TYPE_STATISTICS_AMD = 0, - VK_SHADER_INFO_TYPE_BINARY_AMD = 1, - VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD = 2, -} - -@extension("VK_EXT_validation_flags") // 62 -enum VkValidationCheckEXT { - VK_VALIDATION_CHECK_ALL_EXT = 0, - VK_VALIDATION_CHECK_SHADERS_EXT = 1, -} - -@extension("VK_KHR_descriptor_update_template") // 86 -enum VkDescriptorUpdateTemplateTypeKHR { - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR = 0, - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1, -} - -@extension("VK_NVX_device_generated_commands") // 87 -enum VkIndirectCommandsTokenTypeNVX { - VK_INDIRECT_COMMANDS_TOKEN_TYPE_PIPELINE_NVX = 0, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_DESCRIPTOR_SET_NVX = 1, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_NVX = 2, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_NVX = 3, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_NVX = 4, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_NVX = 5, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_NVX = 6, - VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_NVX = 7, -} - -@extension("VK_NVX_device_generated_commands") // 87 -enum VkObjectEntryTypeNVX { - VK_OBJECT_ENTRY_TYPE_DESCRIPTOR_SET_NVX = 0, - VK_OBJECT_ENTRY_TYPE_PIPELINE_NVX = 1, - VK_OBJECT_ENTRY_TYPE_INDEX_BUFFER_NVX = 2, - VK_OBJECT_ENTRY_TYPE_VERTEX_BUFFER_NVX = 3, - VK_OBJECT_ENTRY_TYPE_PUSH_CONSTANT_NVX = 4, -} - -@extension("VK_EXT_display_control") // 92 -enum VkDisplayPowerStateEXT { - VK_DISPLAY_POWER_STATE_OFF_EXT = 0, - VK_DISPLAY_POWER_STATE_SUSPEND_EXT = 1, - VK_DISPLAY_POWER_STATE_ON_EXT = 2, -} - -@extension("VK_EXT_display_control") // 92 -enum VkDeviceEventTypeEXT { - VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT = 0, -} - -@extension("VK_EXT_display_control") // 92 -enum VkDisplayEventTypeEXT { - VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT = 0, -} - -@extension("VK_NV_viewport_swizzle") // 99 -enum VkViewportCoordinateSwizzleNV { - VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV = 0, - VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV = 1, - VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV = 2, - VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV = 3, - VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV = 4, - VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV = 5, - VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV = 6, - VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV = 7, -} - -@extension("VK_EXT_discard_rectangles") // 100 -enum VkDiscardRectangleModeEXT { - VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0, - VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1, -} - -@extension("VK_EXT_conservative_rasterization") // 102 -enum VkConservativeRasterizationModeEXT { - VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT = 0, - VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT = 1, - VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT = 2, -} - -@extension("VK_KHR_maintenance2") // 118 -enum VkPointClippingBehaviorKHR { - VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR = 0, - VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR = 1, -} - -@extension("VK_KHR_maintenance2") // 118 -enum VkTessellationDomainOriginKHR { - VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR = 0, - VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR = 1, -} - -@extension("VK_EXT_sampler_filter_minmax") // 131 -enum VkSamplerReductionModeEXT { - VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT = 0, - VK_SAMPLER_REDUCTION_MODE_MIN_EXT = 1, - VK_SAMPLER_REDUCTION_MODE_MAX_EXT = 2, -} - -@extension("VK_EXT_blend_operation_advanced") // 149 -enum VkBlendOverlapEXT { - VK_BLEND_OVERLAP_UNCORRELATED_EXT = 0, - VK_BLEND_OVERLAP_DISJOINT_EXT = 1, - VK_BLEND_OVERLAP_CONJOINT_EXT = 2, -} - -@extension("VK_NV_framebuffer_mixed_samples") // 153 -enum VkCoverageModulationModeNV { - VK_COVERAGE_MODULATION_MODE_NONE_NV = 0, - VK_COVERAGE_MODULATION_MODE_RGB_NV = 1, - VK_COVERAGE_MODULATION_MODE_ALPHA_NV = 2, - VK_COVERAGE_MODULATION_MODE_RGBA_NV = 3, -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -enum VkSamplerYcbcrModelConversionKHR { - VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR = 0, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR = 1, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR = 2, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR = 3, - VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR = 4, -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -enum VkSamplerYcbcrRangeKHR { - VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR = 0, - VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR = 1, -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -enum VkChromaLocationKHR { - VK_CHROMA_LOCATION_COSITED_EVEN_KHR = 0, - VK_CHROMA_LOCATION_MIDPOINT_KHR = 1, -} - -@extension("VK_EXT_validation_cache") // 161 -enum VkValidationCacheHeaderVersionEXT { - VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT = 1, -} - -@extension("VK_NV_shading_rate_image") // 165 -enum VkShadingRatePaletteEntryNV { - VK_SHADING_RATE_PALETTE_ENTRY_NO_INVOCATIONS_NV = 0, - VK_SHADING_RATE_PALETTE_ENTRY_16_INVOCATIONS_PER_PIXEL_NV = 1, - VK_SHADING_RATE_PALETTE_ENTRY_8_INVOCATIONS_PER_PIXEL_NV = 2, - VK_SHADING_RATE_PALETTE_ENTRY_4_INVOCATIONS_PER_PIXEL_NV = 3, - VK_SHADING_RATE_PALETTE_ENTRY_2_INVOCATIONS_PER_PIXEL_NV = 4, - VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_PIXEL_NV = 5, - VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X1_PIXELS_NV = 6, - VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_1X2_PIXELS_NV = 7, - VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X2_PIXELS_NV = 8, - VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X2_PIXELS_NV = 9, - VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X4_PIXELS_NV = 10, - VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV = 11, -} - -@extension("VK_NV_shading_rate_image") // 165 -enum VkCoarseSampleOrderTypeNV { - VK_COARSE_SAMPLE_ORDER_TYPE_DEFAULT_NV = 0, - VK_COARSE_SAMPLE_ORDER_TYPE_CUSTOM_NV = 1, - VK_COARSE_SAMPLE_ORDER_TYPE_PIXEL_MAJOR_NV = 2, - VK_COARSE_SAMPLE_ORDER_TYPE_SAMPLE_MAJOR_NV = 3, -} - -@extension("VK_NV_ray_tracing") // 166 -enum VkRayTracingShaderGroupTypeNV { - VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV = 0, - VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_NV = 1, - VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_NV = 2, -} - -@extension("VK_NV_ray_tracing") // 166 -enum VkGeometryTypeNV { - VK_GEOMETRY_TYPE_TRIANGLES_NV = 0, - VK_GEOMETRY_TYPE_AABBS_NV = 1, -} - -@extension("VK_NV_ray_tracing") // 166 -enum VkAccelerationStructureTypeNV { - VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV = 0, - VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV = 1, -} - -@extension("VK_NV_ray_tracing") // 166 -enum VkCopyAccelerationStructureModeNV { - VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_NV = 0, - VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV = 1, -} - -@extension("VK_NV_ray_tracing") // 166 -enum VkAccelerationStructureMemoryRequirementsTypeNV { - VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV = 0, - VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV = 1, - VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV = 2, -} - -@extension("VK_EXT_global_priority") // 175 -enum VkQueueGlobalPriorityEXT { - VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT = 128, - VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT = 256, - VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT = 512, - VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT = 1024, -} - -@extension("VK_EXT_calibrated_timestamps") // 185 -enum VkTimeDomainEXT { - VK_TIME_DOMAIN_DEVICE_EXT = 0, - VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT = 1, - VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT = 2, - VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT = 3, -} - -@extension("VK_AMD_memory_overallocation_behavior") // 190 -enum VkMemoryOverallocationBehaviorAMD { - VK_MEMORY_OVERALLOCATION_BEHAVIOR_DEFAULT_AMD = 0, - VK_MEMORY_OVERALLOCATION_BEHAVIOR_ALLOWED_AMD = 1, - VK_MEMORY_OVERALLOCATION_BEHAVIOR_DISALLOWED_AMD = 2, -} - -@extension("VK_KHR_driver_properties") // 197 -enum VkDriverIdKHR { - VK_DRIVER_ID_AMD_PROPRIETARY_KHR = 1, - VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR = 2, - VK_DRIVER_ID_MESA_RADV_KHR = 3, - VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR = 4, - VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR = 5, - VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR = 6, - VK_DRIVER_ID_IMAGINATION_PROPRIETARY_KHR = 7, - VK_DRIVER_ID_QUALCOMM_PROPRIETARY_KHR = 8, - VK_DRIVER_ID_ARM_PROPRIETARY_KHR = 9, - VK_DRIVER_ID_GOOGLE_PASTEL_KHR = 10, -} - -///////////////// -// Bitfields // -///////////////// - -/// Queue capabilities -type VkFlags VkQueueFlags -bitfield VkQueueFlagBits { - VK_QUEUE_GRAPHICS_BIT = 0x00000001, /// Queue supports graphics operations - VK_QUEUE_COMPUTE_BIT = 0x00000002, /// Queue supports compute operations - VK_QUEUE_TRANSFER_BIT = 0x00000004, /// Queue supports transfer operations - VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008, /// Queue supports sparse resource memory management operations - - //@vulkan1_1 - VK_QUEUE_PROTECTED_BIT = 0x00000010, -} - -/// Memory properties passed into vkAllocMemory(). -type VkFlags VkMemoryPropertyFlags -bitfield VkMemoryPropertyFlagBits { - VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 0x00000001, - VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 0x00000002, - VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004, - VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008, - VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010, - - //@vulkan1_1 - VK_MEMORY_PROPERTY_PROTECTED_BIT = 0x00000020, -} - -/// Memory heap flags -type VkFlags VkMemoryHeapFlags -bitfield VkMemoryHeapFlagBits { - VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001, - - //@vulkan1_1 - VK_MEMORY_HEAP_MULTI_INSTANCE_BIT = 0x00000002, - - //@extension("VK_KHR_device_group_creation") // 71 - VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHR = 0x00000002, -} - -/// Access flags -type VkFlags VkAccessFlags -bitfield VkAccessFlagBits { - VK_ACCESS_INDIRECT_COMMAND_READ_BIT = 0x00000001, - VK_ACCESS_INDEX_READ_BIT = 0x00000002, - VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT = 0x00000004, - VK_ACCESS_UNIFORM_READ_BIT = 0x00000008, - VK_ACCESS_INPUT_ATTACHMENT_READ_BIT = 0x00000010, - VK_ACCESS_SHADER_READ_BIT = 0x00000020, - VK_ACCESS_SHADER_WRITE_BIT = 0x00000040, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT = 0x00000080, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT = 0x00000100, - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 0x00000200, - VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 0x00000400, - VK_ACCESS_TRANSFER_READ_BIT = 0x00000800, - VK_ACCESS_TRANSFER_WRITE_BIT = 0x00001000, - VK_ACCESS_HOST_READ_BIT = 0x00002000, - VK_ACCESS_HOST_WRITE_BIT = 0x00004000, - VK_ACCESS_MEMORY_READ_BIT = 0x00008000, - VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000, - - //@extension("VK_NVX_device_generated_commands") // 87 - VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000, - VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000, - - //@extension("VK_EXT_blend_operation_advanced") // 149 - VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT = 0x00080000, - - //@extension("VK_EXT_conditional_rendering") // 82 - VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT = 0x00100000, - - //@extension("VK_NV_shading_rate_image") // 165 - VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV = 0x00800000, - - //@extension("VK_NV_ray_tracing") // 166 - VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV = 0x00200000, - VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV = 0x00400000, - - //@extension("VK_EXT_fragment_density_map") // 219 - VK_ACCESS_FRAGMENT_DENSITY_MAP_READ_BIT_EXT = 0x01000000, - - //@extension("VK_EXT_transform_feedback") // 29 - VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT = 0x02000000, - VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT = 0x04000000, - VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT = 0x08000000, -} - -/// Buffer usage flags -type VkFlags VkBufferUsageFlags -bitfield VkBufferUsageFlagBits { - VK_BUFFER_USAGE_TRANSFER_SRC_BIT = 0x00000001, /// Can be used as a source of transfer operations - VK_BUFFER_USAGE_TRANSFER_DST_BIT = 0x00000002, /// Can be used as a destination of transfer operations - VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000004, /// Can be used as TBO - VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT = 0x00000008, /// Can be used as IBO - VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT = 0x00000010, /// Can be used as UBO - VK_BUFFER_USAGE_STORAGE_BUFFER_BIT = 0x00000020, /// Can be used as SSBO - VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 0x00000040, /// Can be used as source of fixed function index fetch (index buffer) - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 0x00000080, /// Can be used as source of fixed function vertex fetch (VBO) - VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 0x00000100, /// Can be the source of indirect parameters (e.g. indirect buffer, parameter buffer) - - //@extension("VK_EXT_conditional_rendering") // 82 - VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT = 0x00000200, - - //@extension("VK_NV_ray_tracing") // 166 - VK_BUFFER_USAGE_RAY_TRACING_BIT_NV = 0x00000400, - - //@extension("VK_EXT_transform_feedback") // 29 - VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT = 0x00000800, - VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT = 0x00001000, -} - -/// Buffer creation flags -type VkFlags VkBufferCreateFlags -bitfield VkBufferCreateFlagBits { - VK_BUFFER_CREATE_SPARSE_BINDING_BIT = 0x00000001, /// Buffer should support sparse backing - VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, /// Buffer should support sparse backing with partial residency - VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 0x00000004, /// Buffer should support constent data access to physical memory blocks mapped into multiple locations of sparse buffers - - //@vulkan1_1 - VK_BUFFER_CREATE_PROTECTED_BIT = 0x00000008, -} - -/// Shader stage flags -type VkFlags VkShaderStageFlags -bitfield VkShaderStageFlagBits { - VK_SHADER_STAGE_VERTEX_BIT = 0x00000001, - VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT = 0x00000002, - VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT = 0x00000004, - VK_SHADER_STAGE_GEOMETRY_BIT = 0x00000008, - VK_SHADER_STAGE_FRAGMENT_BIT = 0x00000010, - VK_SHADER_STAGE_COMPUTE_BIT = 0x00000020, - VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F, - - VK_SHADER_STAGE_ALL = 0x7FFFFFFF, - - //@extension("VK_NV_ray_tracing") // 166 - VK_SHADER_STAGE_RAYGEN_BIT_NV = 0x00000100, - VK_SHADER_STAGE_ANY_HIT_BIT_NV = 0x00000200, - VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV = 0x00000400, - VK_SHADER_STAGE_MISS_BIT_NV = 0x00000800, - VK_SHADER_STAGE_INTERSECTION_BIT_NV = 0x00001000, - VK_SHADER_STAGE_CALLABLE_BIT_NV = 0x00002000, - - //@extension("VK_NV_mesh_shader") // 203 - VK_SHADER_STAGE_TASK_BIT_NV = 0x00000040, - VK_SHADER_STAGE_MESH_BIT_NV = 0x00000080, -} - -/// Descriptor pool create flags -type VkFlags VkDescriptorPoolCreateFlags -bitfield VkDescriptorPoolCreateFlagBits { - VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 0x00000001, - - //@extension("VK_EXT_descriptor_indexing") // 162 - VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT = 0x00000002, -} - -/// Descriptor pool reset flags -type VkFlags VkDescriptorPoolResetFlags -//bitfield VkDescriptorPoolResetFlagBits { -//} - -/// Image usage flags -type VkFlags VkImageUsageFlags -bitfield VkImageUsageFlagBits { - VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001, /// Can be used as a source of transfer operations - VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002, /// Can be used as a destination of transfer operations - VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004, /// Can be sampled from (SAMPLED_IMAGE and COMBINED_IMAGE_SAMPLER descriptor types) - VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008, /// Can be used as storage image (STORAGE_IMAGE descriptor type) - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010, /// Can be used as framebuffer color attachment - VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020, /// Can be used as framebuffer depth/stencil attachment - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040, /// Image data not needed outside of rendering - VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 0x00000080, /// Can be used as framebuffer input attachment - - //@extension("VK_NV_shading_rate_image") // 165 - VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV = 0x00000100, - - //@extension("VK_EXT_fragment_density_map") // 219 - VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT = 0x00000200, -} - -/// Image creation flags -type VkFlags VkImageCreateFlags -bitfield VkImageCreateFlagBits { - VK_IMAGE_CREATE_SPARSE_BINDING_BIT = 0x00000001, /// Image should support sparse backing - VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, /// Image should support sparse backing with partial residency - VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 0x00000004, /// Image should support constent data access to physical memory blocks mapped into multiple locations of sparse images - VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 0x00000008, /// Allows image views to have different format than the base image - VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010, /// Allows creating image views with cube type from the created image - - //@vulkan1_1 - VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT = 0x00000020, - VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT = 0x00000040, - VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT = 0x00000080, - VK_IMAGE_CREATE_EXTENDED_USAGE_BIT = 0x00000100, - VK_IMAGE_CREATE_DISJOINT_BIT = 0x00000200, - VK_IMAGE_CREATE_ALIAS_BIT = 0x00000400, - VK_IMAGE_CREATE_PROTECTED_BIT = 0x00000800, - - //@extension("VK_KHR_maintenance1") // 70 - VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = 0x00000020, - - //@extension("VK_KHR_device_group") // 61 - VK_IMAGE_CREATE_BIND_SFR_BIT_KHR = 0x00000040, - - //@extension("VK_KHR_maintenance2") // 118 - VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR = 0x00000080, - VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR = 0x00000100, - - //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 - VK_IMAGE_CREATE_DISJOINT_BIT_KHR = 0x00000200, - - //@extension("VK_KHR_bind_memory2") // 158 - VK_IMAGE_CREATE_ALIAS_BIT_KHR = 0x00000400, - - //@extension("VK_EXT_sample_locations") // 144 - VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000, - - //@extension("VK_NV_corner_sampled_image") // 51 - VK_IMAGE_CREATE_CORNER_SAMPLED_BIT_NV = 0x00002000, - - //@extension("VK_EXT_fragment_density_map") // 219 - VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT = 0x00004000, -} - -/// Image view creation flags -type VkFlags VkImageViewCreateFlags -bitfield VkImageViewCreateFlagBits { - //@extension("VK_EXT_fragment_density_map") // 219 - VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT = 0x00000001, -} - -/// Pipeline creation flags -type VkFlags VkPipelineCreateFlags -bitfield VkPipelineCreateFlagBits { - VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 0x00000001, - VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 0x00000002, - VK_PIPELINE_CREATE_DERIVATIVE_BIT = 0x00000004, - - //@vulkan1_1 - VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT = 0x00000008, - VK_PIPELINE_CREATE_DISPATCH_BASE = 0x00000010, - - //@extension("VK_KHR_device_group") // 61 - VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHR = 0x00000008, - VK_PIPELINE_CREATE_DISPATCH_BASE_KHR = 0x00000010, - - //@extension("VK_NV_ray_tracing") // 166 - VK_PIPELINE_CREATE_DEFER_COMPILE_BIT_NV = 0x00000020, -} - -/// Color component flags -type VkFlags VkColorComponentFlags -bitfield VkColorComponentFlagBits { - VK_COLOR_COMPONENT_R_BIT = 0x00000001, - VK_COLOR_COMPONENT_G_BIT = 0x00000002, - VK_COLOR_COMPONENT_B_BIT = 0x00000004, - VK_COLOR_COMPONENT_A_BIT = 0x00000008, -} - -/// Fence creation flags -type VkFlags VkFenceCreateFlags -bitfield VkFenceCreateFlagBits { - VK_FENCE_CREATE_SIGNALED_BIT = 0x00000001, -} - -/// Semaphore creation flags -type VkFlags VkSemaphoreCreateFlags -//bitfield VkSemaphoreCreateFlagBits { -//} - -/// Format capability flags -type VkFlags VkFormatFeatureFlags -bitfield VkFormatFeatureFlagBits { - VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT = 0x00000001, /// Format can be used for sampled images (SAMPLED_IMAGE and COMBINED_IMAGE_SAMPLER descriptor types) - VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT = 0x00000002, /// Format can be used for storage images (STORAGE_IMAGE descriptor type) - VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT = 0x00000004, /// Format supports atomic operations in case it's used for storage images - VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000008, /// Format can be used for uniform texel buffers (TBOs) - VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT = 0x00000010, /// Format can be used for storage texel buffers (IBOs) - VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT = 0x00000020, /// Format supports atomic operations in case it's used for storage texel buffers - VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT = 0x00000040, /// Format can be used for vertex buffers (VBOs) - VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT = 0x00000080, /// Format can be used for color attachment images - VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT = 0x00000100, /// Format supports blending in case it's used for color attachment images - VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000200, /// Format can be used for depth/stencil attachment images - VK_FORMAT_FEATURE_BLIT_SRC_BIT = 0x00000400, /// Format can be used as the source image of blits with vkCommandBlitImage - VK_FORMAT_FEATURE_BLIT_DST_BIT = 0x00000800, /// Format can be used as the destination image of blits with vkCommandBlitImage - VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 0x00001000, - - //@vulkan1_1 - VK_FORMAT_FEATURE_TRANSFER_SRC_BIT = 0x00004000, - VK_FORMAT_FEATURE_TRANSFER_DST_BIT = 0x00008000, - VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT = 0x00020000, - VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT = 0x00040000, - VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT = 0x00080000, - VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT = 0x00100000, - VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT = 0x00200000, - VK_FORMAT_FEATURE_DISJOINT_BIT = 0x00400000, - VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT = 0x00800000, - - //@extension("VK_IMG_filter_cubic") // 16 - VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG = 0x00002000, - - //@extension("VK_KHR_maintenance1") // 70 - VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = 0x00004000, - VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = 0x00008000, - - //@extension("VK_EXT_sampler_filter_minmax") // 131 - VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT = 0x00010000, - - //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 - VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = 0x00020000, - VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = 0x00040000, - VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = 0x00080000, - VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR = 0x00100000, - VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = 0x00200000, - VK_FORMAT_FEATURE_DISJOINT_BIT_KHR = 0x00400000, - VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR = 0x00800000, -} - -/// Query control flags -type VkFlags VkQueryControlFlags -bitfield VkQueryControlFlagBits { - VK_QUERY_CONTROL_PRECISE_BIT = 0x00000001, -} - -/// Query result flags -type VkFlags VkQueryResultFlags -bitfield VkQueryResultFlagBits { - VK_QUERY_RESULT_64_BIT = 0x00000001, /// Results of the queries are written to the destination buffer as 64-bit values - VK_QUERY_RESULT_WAIT_BIT = 0x00000002, /// Results of the queries are waited on before proceeding with the result copy - VK_QUERY_RESULT_WITH_AVAILABILITY_BIT = 0x00000004, /// Besides the results of the query, the availability of the results is also written - VK_QUERY_RESULT_PARTIAL_BIT = 0x00000008, /// Copy the partial results of the query even if the final results aren't available -} - -/// Shader module creation flags -type VkFlags VkShaderModuleCreateFlags -//bitfield VkShaderModuleCreateFlagBits { -//} - -/// Event creation flags -type VkFlags VkEventCreateFlags -//bitfield VkEventCreateFlagBits { -//} - -/// Command buffer usage flags -type VkFlags VkCommandBufferUsageFlags -bitfield VkCommandBufferUsageFlagBits { - VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT = 0x00000001, - VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT = 0x00000002, - VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT = 0x00000004, -} - -/// Pipeline statistics flags -type VkFlags VkQueryPipelineStatisticFlags -bitfield VkQueryPipelineStatisticFlagBits { - VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT = 0x00000001, /// Optional - VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT = 0x00000002, /// Optional - VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT = 0x00000004, /// Optional - VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT = 0x00000008, /// Optional - VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT = 0x00000010, /// Optional - VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT = 0x00000020, /// Optional - VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT = 0x00000040, /// Optional - VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT = 0x00000080, /// Optional - VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT = 0x00000100, /// Optional - VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT = 0x00000200, /// Optional - VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT = 0x00000400, /// Optional -} - -/// Memory mapping flags -type VkFlags VkMemoryMapFlags -//bitfield VkMemoryMapFlagBits { -//} - -/// Bitfield of image aspects -type VkFlags VkImageAspectFlags -bitfield VkImageAspectFlagBits { - VK_IMAGE_ASPECT_COLOR_BIT = 0x00000001, - VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002, - VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004, - VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008, - - //@vulkan1_1 - VK_IMAGE_ASPECT_PLANE_0_BIT = 0x00000010, - VK_IMAGE_ASPECT_PLANE_1_BIT = 0x00000020, - VK_IMAGE_ASPECT_PLANE_2_BIT = 0x00000040, - - //@extension("VK_KHR_sampler_ycbcr_conversion") // 157 - VK_IMAGE_ASPECT_PLANE_0_BIT_KHR = 0x00000010, - VK_IMAGE_ASPECT_PLANE_1_BIT_KHR = 0x00000020, - VK_IMAGE_ASPECT_PLANE_2_BIT_KHR = 0x00000040, - - //@extension("VK_EXT_transform_feedback") // 29 - VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT = 0x00000080, - VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT = 0x00000100, - VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT = 0x00000200, - VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT = 0x00000400, -} - -/// Sparse memory bind flags -type VkFlags VkSparseMemoryBindFlags -bitfield VkSparseMemoryBindFlagBits { - VK_SPARSE_MEMORY_BIND_METADATA_BIT = 0x00000001, -} - -/// Sparse image memory requirements flags -type VkFlags VkSparseImageFormatFlags -bitfield VkSparseImageFormatFlagBits { - VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT = 0x00000001, /// Image uses a single miptail region for all array slices - VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT = 0x00000002, /// Image requires mip levels to be an exact multiple of the sparse iamge block size for non-mip-tail levels. - VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT = 0x00000004, /// Image uses a non-standard sparse block size -} - -/// Pipeline stages -type VkFlags VkPipelineStageFlags -bitfield VkPipelineStageFlagBits { - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT = 0x00000001, /// Before subsequent commands are processed - VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT = 0x00000002, /// Draw/DispatchIndirect command fetch - VK_PIPELINE_STAGE_VERTEX_INPUT_BIT = 0x00000004, /// Vertex/index fetch - VK_PIPELINE_STAGE_VERTEX_SHADER_BIT = 0x00000008, /// Vertex shading - VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT = 0x00000010, /// Tessellation control shading - VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT = 0x00000020, /// Tessellation evaluation shading - VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT = 0x00000040, /// Geometry shading - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT = 0x00000080, /// Fragment shading - VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT = 0x00000100, /// Early fragment (depth/stencil) tests - VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT = 0x00000200, /// Late fragment (depth/stencil) tests - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT = 0x00000400, /// Color attachment writes - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT = 0x00000800, /// Compute shading - VK_PIPELINE_STAGE_TRANSFER_BIT = 0x00001000, /// Transfer/copy operations - VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT = 0x00002000, - VK_PIPELINE_STAGE_HOST_BIT = 0x00004000, /// Indicates host (CPU) is a source/sink of the dependency - - VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000, /// All stages of the graphics pipeline - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000, /// All graphics, compute, copy, and transition commands - - //@extension("VK_NVX_device_generated_commands") // 87 - VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000, - - //@extension("VK_EXT_conditional_rendering") // 82 - VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT = 0x00040000, - - //@extension("VK_NV_mesh_shader") // 203 - VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV = 0x00080000, - VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV = 0x00100000, - - //@extension("VK_NV_ray_tracing") // 166 - VK_PIPELINE_STAGE_RAY_TRACING_BIT_NV = 0x00200000, - - //@extension("VK_NV_shading_rate_image") // 165 - VK_PIPELINE_STAGE_SHADING_RATE_IMAGE_BIT_NV = 0x00400000, - - //@extension("VK_EXT_fragment_density_map") // 219 - VK_PIPELINE_STAGE_FRAGMENT_DENSITY_PROCESS_BIT_EXT = 0x00800000, - - //@extension("VK_EXT_transform_feedback") // 29 - VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT = 0x01000000, - - //@extension("VK_NV_ray_tracing") // 166 - VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV = 0x02000000, -} - -/// Render pass attachment description flags -type VkFlags VkAttachmentDescriptionFlags -bitfield VkAttachmentDescriptionFlagBits { - VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 0x00000001, /// The attachment may alias physical memory of another attachment in the same renderpass -} - -/// Subpass description flags -type VkFlags VkSubpassDescriptionFlags -bitfield VkSubpassDescriptionFlagBits { - //@extension("VK_NVX_multiview_per_view_attributes") // 98 - VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX = 0x00000001, - VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002, -} - -/// Command pool creation flags -type VkFlags VkCommandPoolCreateFlags -bitfield VkCommandPoolCreateFlagBits { - VK_COMMAND_POOL_CREATE_TRANSIENT_BIT = 0x00000001, /// Command buffers have a short lifetime - VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT = 0x00000002, /// Command buffers may release their memory individually - - //@vulkan1_1 - VK_COMMAND_POOL_CREATE_PROTECTED_BIT = 0x00000004, -} - -/// Command pool reset flags -type VkFlags VkCommandPoolResetFlags -bitfield VkCommandPoolResetFlagBits { - VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT = 0x00000001, /// Release resources owned by the pool -} - -type VkFlags VkCommandBufferResetFlags -bitfield VkCommandBufferResetFlagBits { - VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT = 0x00000001, /// Release resources owned by the buffer -} - -type VkFlags VkSampleCountFlags -bitfield VkSampleCountFlagBits { - VK_SAMPLE_COUNT_1_BIT = 0x00000001, - VK_SAMPLE_COUNT_2_BIT = 0x00000002, - VK_SAMPLE_COUNT_4_BIT = 0x00000004, - VK_SAMPLE_COUNT_8_BIT = 0x00000008, - VK_SAMPLE_COUNT_16_BIT = 0x00000010, - VK_SAMPLE_COUNT_32_BIT = 0x00000020, - VK_SAMPLE_COUNT_64_BIT = 0x00000040, -} - -type VkFlags VkStencilFaceFlags -bitfield VkStencilFaceFlagBits { - VK_STENCIL_FACE_FRONT_BIT = 0x00000001, /// Front face - VK_STENCIL_FACE_BACK_BIT = 0x00000002, /// Back face - VK_STENCIL_FRONT_AND_BACK = 0x00000003, -} - -/// Instance creation flags -type VkFlags VkInstanceCreateFlags -//bitfield VkInstanceCreateFlagBits { -//} - -/// Device creation flags -type VkFlags VkDeviceCreateFlags -//bitfield VkDeviceCreateFlagBits { -//} - -/// Device queue creation flags -type VkFlags VkDeviceQueueCreateFlags -@vulkan1_1 -bitfield VkDeviceQueueCreateFlagBits { - VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT = 0x00000001, -} - -/// Query pool creation flags -type VkFlags VkQueryPoolCreateFlags -//bitfield VkQueryPoolCreateFlagBits { -//} - -/// Buffer view creation flags -type VkFlags VkBufferViewCreateFlags -//bitfield VkBufferViewCreateFlagBits { -//} - -/// Pipeline cache creation flags -type VkFlags VkPipelineCacheCreateFlags -//bitfield VkPipelineCacheCreateFlagBits { -//} - -/// Pipeline shader stage creation flags -type VkFlags VkPipelineShaderStageCreateFlags -//bitfield VkPipelineShaderStageCreateFlagBits { -//} - -/// Descriptor set layout creation flags -type VkFlags VkDescriptorSetLayoutCreateFlags -bitfield VkDescriptorSetLayoutCreateFlagBits { - //@extension("VK_KHR_push_descriptor") // 81 - VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR = 0x00000001, - - //@extension("VK_EXT_descriptor_indexing") // 162 - VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT = 0x00000002, -} - -/// Pipeline vertex input state creation flags -type VkFlags VkPipelineVertexInputStateCreateFlags -//bitfield VkPipelineVertexInputStateCreateFlagBits { -//} - -/// Pipeline input assembly state creation flags -type VkFlags VkPipelineInputAssemblyStateCreateFlags -//bitfield VkPipelineInputAssemblyStateCreateFlagBits { -//} - -/// Tessellation state creation flags -type VkFlags VkPipelineTessellationStateCreateFlags -//bitfield VkPipelineTessellationStateCreateFlagBits { -//} - -/// Viewport state creation flags -type VkFlags VkPipelineViewportStateCreateFlags -//bitfield VkPipelineViewportStateCreateFlagBits { -//} - -/// Rasterization state creation flags -type VkFlags VkPipelineRasterizationStateCreateFlags -//bitfield VkPipelineRasterizationStateCreateFlagBits { -//} - -/// Multisample state creation flags -type VkFlags VkPipelineMultisampleStateCreateFlags -//bitfield VkPipelineMultisampleStateCreateFlagBits { -//} - -/// Color blend state creation flags -type VkFlags VkPipelineColorBlendStateCreateFlags -//bitfield VkPipelineColorBlendStateCreateFlagBits { -//} - -/// Depth/stencil state creation flags -type VkFlags VkPipelineDepthStencilStateCreateFlags -//bitfield VkPipelineDepthStencilStateCreateFlagBits { -//} - -/// Dynamic state creation flags -type VkFlags VkPipelineDynamicStateCreateFlags -//bitfield VkPipelineDynamicStateCreateFlagBits { -//} - -/// Pipeline layout creation flags -type VkFlags VkPipelineLayoutCreateFlags -//bitfield VkPipelineLayoutCreateFlagBits { -//} - -/// Sampler creation flags -type VkFlags VkSamplerCreateFlags -bitfield VkSamplerCreateFlagBits { - //@extension("VK_EXT_fragment_density_map") // 219 - VK_SAMPLER_CREATE_SUBSAMPLED_BIT_EXT = 0x00000001, - VK_SAMPLER_CREATE_SUBSAMPLED_COARSE_RECONSTRUCTION_BIT_EXT = 0x00000002, -} - -/// Render pass creation flags -type VkFlags VkRenderPassCreateFlags -//bitfield VkRenderPassCreateFlagBits { -//} - -/// Framebuffer creation flags -type VkFlags VkFramebufferCreateFlags -//bitfield VkFramebufferCreateFlagBits { -//} - -/// Dependency flags -type VkFlags VkDependencyFlags -bitfield VkDependencyFlagBits { - VK_DEPENDENCY_BY_REGION_BIT = 0x00000001, - - //@vulkan1_1 - VK_DEPENDENCY_DEVICE_GROUP_BIT = 0x00000004, - VK_DEPENDENCY_VIEW_LOCAL_BIT = 0x00000002, - - //@extension("VK_KHR_multiview") // 54 - VK_DEPENDENCY_VIEW_LOCAL_BIT_KHR = 0x00000002, - - //@extension("VK_KHR_device_group") // 61 - VK_DEPENDENCY_DEVICE_GROUP_BIT_KHR = 0x00000004, -} - -/// Cull mode flags -type VkFlags VkCullModeFlags -bitfield VkCullModeFlagBits { - VK_CULL_MODE_NONE = 0x00000000, - VK_CULL_MODE_FRONT_BIT = 0x00000001, - VK_CULL_MODE_BACK_BIT = 0x00000002, - VK_CULL_MODE_FRONT_AND_BACK = 0x00000003, -} - -//@vulkan1_1 flags - -/// Subgroup feature flags -type VkFlags VkSubgroupFeatureFlags -bitfield VkSubgroupFeatureFlagBits { - VK_SUBGROUP_FEATURE_BASIC_BIT = 0x00000001, - VK_SUBGROUP_FEATURE_VOTE_BIT = 0x00000002, - VK_SUBGROUP_FEATURE_ARITHMETIC_BIT = 0x00000004, - VK_SUBGROUP_FEATURE_BALLOT_BIT = 0x00000008, - VK_SUBGROUP_FEATURE_SHUFFLE_BIT = 0x00000010, - VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT = 0x00000020, - VK_SUBGROUP_FEATURE_CLUSTERED_BIT = 0x00000040, - VK_SUBGROUP_FEATURE_QUAD_BIT = 0x00000080, - - //@extension("VK_NV_shader_subgroup_partitioned") // 199 - VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV = 0x00000100, -} - -/// Peer memory feature flags -type VkFlags VkPeerMemoryFeatureFlags -bitfield VkPeerMemoryFeatureFlagBits { - VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT = 0x00000001, - VK_PEER_MEMORY_FEATURE_COPY_DST_BIT = 0x00000002, - VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT = 0x00000004, - VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT = 0x00000008, -} - -/// Memory allocation flags -type VkFlags VkMemoryAllocateFlags -bitfield VkMemoryAllocateFlagBits { - VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT = 0x00000001, -} - -type VkFlags VkCommandPoolTrimFlags -//bitfield VkCommandPoolTrimFlagBits { -//} - -type VkFlags VkDescriptorUpdateTemplateCreateFlags -//bitfield VkDescriptorUpdateTemplateCreateFlagBits { -//} - -/// External memory handle type flags -type VkFlags VkExternalMemoryHandleTypeFlags -bitfield VkExternalMemoryHandleTypeFlagBits { - VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT = 0x00000001, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT = 0x00000002, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT = 0x00000004, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT = 0x00000008, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT = 0x00000010, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT = 0x00000020, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT = 0x00000040, - - //@extension("VK_EXT_external_memory_host") // 179 - VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT = 0x00000080, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT = 0x00000100, - - //@extension("VK_EXT_external_memory_dma_buf") // 126 - VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT = 0x00000200, - - //@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130 - VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID = 0x00000400, -} - -/// External memory feature flags -type VkFlags VkExternalMemoryFeatureFlags -bitfield VkExternalMemoryFeatureFlagBits { - VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT = 0x00000001, - VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT = 0x00000002, - VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT = 0x00000004, -} - -/// External fence handle type flags -type VkFlags VkExternalFenceHandleTypeFlags -bitfield VkExternalFenceHandleTypeFlagBits { - VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT = 0x00000001, - VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT = 0x00000002, - VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT = 0x00000004, - VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT = 0x00000008, -} - -/// External fence feature flags -type VkFlags VkExternalFenceFeatureFlags -bitfield VkExternalFenceFeatureFlagBits { - VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT = 0x00000001, - VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT = 0x00000002, -} - -/// Fence import flags -type VkFlags VkFenceImportFlags -bitfield VkFenceImportFlagBits { - VK_FENCE_IMPORT_TEMPORARY_BIT = 0x00000001, -} - -/// Semaphore import flags -type VkFlags VkSemaphoreImportFlags -bitfield VkSemaphoreImportFlagBits { - VK_SEMAPHORE_IMPORT_TEMPORARY_BIT = 0x00000001, -} - -/// External semaphore handle type flags -type VkFlags VkExternalSemaphoreHandleTypeFlags -bitfield VkExternalSemaphoreHandleTypeFlagBits { - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT = 0x00000001, - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT = 0x00000002, - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT = 0x00000004, - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT = 0x00000008, - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT = 0x00000010, -} - -/// External semaphore feature flags -type VkFlags VkExternalSemaphoreFeatureFlags -bitfield VkExternalSemaphoreFeatureFlagBits { - VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT = 0x00000001, - VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT = 0x00000002, -} - -@extension("VK_KHR_surface") // 1 -type VkFlags VkSurfaceTransformFlagsKHR -@extension("VK_KHR_surface") // 1 -bitfield VkSurfaceTransformFlagBitsKHR { - VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR = 0x00000001, - VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR = 0x00000002, - VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR = 0x00000004, - VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR = 0x00000008, - VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR = 0x00000010, - VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR = 0x00000020, - VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR = 0x00000040, - VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR = 0x00000080, - VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 0x00000100, -} - -@extension("VK_KHR_surface") // 1 -type VkFlags VkCompositeAlphaFlagsKHR -@extension("VK_KHR_surface") // 1 -bitfield VkCompositeAlphaFlagBitsKHR { - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR = 0x00000001, - VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR = 0x00000002, - VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR = 0x00000004, - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR = 0x00000008, -} - -@extension("VK_KHR_swapchain") // 2 -type VkFlags VkSwapchainCreateFlagsKHR -@extension("VK_KHR_swapchain") // 2 -bitfield VkSwapchainCreateFlagBitsKHR { - //@vulkan1_1 - VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR = 0x00000001, - VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR = 0x00000002, - - //@extension("VK_KHR_swapchain_mutable_format") // 201 - VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR = 0x00000004, -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -type VkFlags VkDeviceGroupPresentModeFlagsKHR -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -bitfield VkDeviceGroupPresentModeFlagBitsKHR { - VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHR = 0x00000001, - VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHR = 0x00000002, - VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHR = 0x00000004, - VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHR = 0x00000008, -} - -@extension("VK_KHR_display") // 3 -type VkFlags VkDisplayPlaneAlphaFlagsKHR -@extension("VK_KHR_display") // 3 -bitfield VkDisplayPlaneAlphaFlagBitsKHR { - VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR = 0x00000001, - VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR = 0x00000002, - VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR = 0x00000004, - VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR = 0x00000008, -} - -@extension("VK_KHR_display") // 3 -type VkFlags VkDisplaySurfaceCreateFlagsKHR -//@extension("VK_KHR_display") // 3 -//bitfield VkDisplaySurfaceCreateFlagBitsKHR { -//} - -@extension("VK_KHR_display") // 3 -type VkFlags VkDisplayModeCreateFlagsKHR -//@extension("VK_KHR_display") // 3 -//bitfield VkDisplayModeCreateFlagBitsKHR { -//} - -@extension("VK_KHR_xlib_surface") // 5 -type VkFlags VkXlibSurfaceCreateFlagsKHR -//@extension("VK_KHR_xlib_surface") // 5 -//bitfield VkXlibSurfaceCreateFlagBitsKHR { -//} - -@extension("VK_KHR_xcb_surface") // 6 -type VkFlags VkXcbSurfaceCreateFlagsKHR -//@extension("VK_KHR_xcb_surface") // 6 -//bitfield VkXcbSurfaceCreateFlagBitsKHR { -//} - -@extension("VK_KHR_wayland_surface") // 7 -type VkFlags VkWaylandSurfaceCreateFlagsKHR -//@extension("VK_KHR_wayland_surface") // 7 -//bitfield VkWaylandSurfaceCreateFlagBitsKHR { -//} - -@extension("VK_KHR_android_surface") // 9 -type VkFlags VkAndroidSurfaceCreateFlagsKHR -//@extension("VK_KHR_android_surface") // 9 -//bitfield VkAndroidSurfaceCreateFlagBitsKHR { -//} - -@extension("VK_KHR_win32_surface") // 10 -type VkFlags VkWin32SurfaceCreateFlagsKHR -//@extension("VK_KHR_win32_surface") // 10 -//bitfield VkWin32SurfaceCreateFlagBitsKHR { -//} - -@extension("VK_ANDROID_native_buffer") // 11 -type VkFlags VkSwapchainImageUsageFlagsANDROID -@extension("VK_ANDROID_native_buffer") // 11 -bitfield VkSwapchainImageUsageFlagBitsANDROID { - VK_SWAPCHAIN_IMAGE_USAGE_FLAGS_SHARED_BIT_ANDROID = 0x00000001, -} - -@extension("VK_EXT_debug_report") // 12 -type VkFlags VkDebugReportFlagsEXT -@extension("VK_EXT_debug_report") // 12 -bitfield VkDebugReportFlagBitsEXT { - VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 0x00000001, - VK_DEBUG_REPORT_WARNING_BIT_EXT = 0x00000002, - VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 0x00000004, - VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008, - VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010, -} - -@extension("VK_EXT_transform_feedback") // 29 -type VkFlags VkPipelineRasterizationStateStreamCreateFlagsEXT -//@extension("VK_EXT_transform_feedback") // 29 -//bitfield VkPipelineRasterizationStateStreamCreateFlagBitsEXT { -//} - -@extension("VK_NV_external_memory_capabilities") // 56 -type VkFlags VkExternalMemoryHandleTypeFlagsNV -@extension("VK_NV_external_memory_capabilities") // 56 -bitfield VkExternalMemoryHandleTypeFlagBitsNV { - VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008, -} - -@extension("VK_NV_external_memory_capabilities") // 56 -type VkFlags VkExternalMemoryFeatureFlagsNV -@extension("VK_NV_external_memory_capabilities") // 56 -bitfield VkExternalMemoryFeatureFlagBitsNV { - VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001, - VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002, - VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004, -} - -@extension("VK_KHR_device_group") // 61 -type VkFlags VkPeerMemoryFeatureFlagsKHR -@extension("VK_KHR_device_group") // 61 -bitfield VkPeerMemoryFeatureFlagBitsKHR { - VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT_KHR = 0x00000001, - VK_PEER_MEMORY_FEATURE_COPY_DST_BIT_KHR = 0x00000002, - VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT_KHR = 0x00000004, - VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT_KHR = 0x00000008, -} - -@extension("VK_KHR_device_group") // 61 -type VkFlags VkMemoryAllocateFlagsKHR -@extension("VK_KHR_device_group") // 61 -bitfield VkMemoryAllocateFlagBitsKHR { - VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHR = 0x00000001, -} - -@extension("VK_NN_vi_surface") // 63 -type VkFlags VkViSurfaceCreateFlagsNN -//@extension("VK_NN_vi_surface") // 63 -//bitfield VkViSurfaceCreateFlagBitsNN { -//} - -@extension("VK_KHR_maintenance1") // 70 -type VkFlags VkCommandPoolTrimFlagsKHR -//@extension("VK_KHR_maintenance1") // 70 -//bitfield VkCommandPoolTrimFlagBitsKHR { -//} - -@extension("VK_KHR_external_memory_capabilities") // 72 -type VkFlags VkExternalMemoryHandleTypeFlagsKHR -@extension("VK_KHR_external_memory_capabilities") // 72 -bitfield VkExternalMemoryHandleTypeFlagBitsKHR { - VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR = 0x00000001, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR = 0x00000002, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR = 0x00000004, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT_KHR = 0x00000008, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT_KHR = 0x00000010, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT_KHR = 0x00000020, - VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHR = 0x00000040, -} - -@extension("VK_KHR_external_memory_capabilities") // 72 -type VkFlags VkExternalMemoryFeatureFlagsKHR -@extension("VK_KHR_external_memory_capabilities") // 72 -bitfield VkExternalMemoryFeatureFlagBitsKHR { - VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR = 0x00000001, - VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR = 0x00000002, - VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR = 0x00000004, -} - -@extension("VK_KHR_external_semaphore_capabilities") // 77 -type VkFlags VkExternalSemaphoreHandleTypeFlagsKHR -@extension("VK_KHR_external_semaphore_capabilities") // 77 -bitfield VkExternalSemaphoreHandleTypeFlagBitsKHR { - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR = 0x00000001 - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR = 0x00000002 - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR = 0x00000004 - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT_KHR = 0x00000008 - VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FENCE_FD_BIT_KHR = 0x00000010 -} - -@extension("VK_KHR_external_semaphore_capabilities") // 77 -type VkFlags VkExternalSemaphoreFeatureFlagsKHR -@extension("VK_KHR_external_semaphore_capabilities") // 77 -bitfield VkExternalSemaphoreFeatureFlagBitsKHR { - VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR = 0x00000001, - VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR = 0x00000002, -} - -@extension("VK_KHR_external_semaphore") // 78 -type VkFlags VkSemaphoreImportFlagsKHR -@extension("VK_KHR_external_semaphore") // 78 -bitfield VkSemaphoreImportFlagBitsKHR { - VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR = 0x00000001, -} - -@extension("VK_EXT_conditional_rendering") // 82 -type VkFlags VkConditionalRenderingFlagsEXT -@extension("VK_EXT_conditional_rendering") // 82 -bitfield VkConditionalRenderingFlagBitsEXT { - VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT = 0x00000001, -} - -@extension("VK_KHR_descriptor_update_template") // 86 -type VkFlags VkDescriptorUpdateTemplateCreateFlagsKHR -//@extension("VK_KHR_descriptor_update_template") // 86 -//bitfield VkDescriptorUpdateTemplateCreateFlagBitsKHR { -//} - -@extension("VK_NVX_device_generated_commands") // 87 -type VkFlags VkIndirectCommandsLayoutUsageFlagsNVX -@extension("VK_NVX_device_generated_commands") // 87 -bitfield VkIndirectCommandsLayoutUsageFlagBitsNVX { - VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001, - VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002, - VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004, - VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008, -} - -@extension("VK_NVX_device_generated_commands") // 87 -type VkFlags VkObjectEntryUsageFlagsNVX -@extension("VK_NVX_device_generated_commands") // 87 -bitfield VkObjectEntryUsageFlagBitsNVX { - VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001, - VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002, -} - -@extension("VK_EXT_display_surface_counter") // 91 -type VkFlags VkSurfaceCounterFlagsEXT -@extension("VK_EXT_display_surface_counter") // 91 -bitfield VkSurfaceCounterFlagBitsEXT { - VK_SURFACE_COUNTER_VBLANK_EXT = 0x00000001, -} - -@extension("VK_NV_viewport_swizzle") // 99 -type VkFlags VkPipelineViewportSwizzleStateCreateFlagsNV -//@extension("VK_NV_viewport_swizzle") // 99 -//bitfield VkPipelineViewportSwizzleStateCreateFlagBitsNV { -//} - -@extension("VK_EXT_discard_rectangles") // 100 -type VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT -//@extension("VK_EXT_discard_rectangles") // 100 -//bitfield VkPipelineDiscardRectangleStateCreateFlagBitsEXT { -//} - -@extension("VK_EXT_conservative_rasterization") // 102 -type VkFlags VkPipelineRasterizationConservativeStateCreateFlagsEXT -//@extension("VK_EXT_conservative_rasterization") // 102 -//bitfield VkPipelineRasterizationConservativeStateCreateFlagBitsEXT { -//} - -@extension("VK_KHR_external_fence_capabilities") // 113 -type VkFlags VkExternalFenceHandleTypeFlagsKHR -@extension("VK_KHR_external_fence_capabilities") // 113 -bitfield VkExternalFenceHandleTypeFlagBitsKHR { - VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR = 0x00000001, - VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR = 0x00000002, - VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR = 0x00000004, - VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR = 0x00000008, -} - -@extension("VK_KHR_external_fence_capabilities") // 113 -type VkFlags VkExternalFenceFeatureFlagsKHR -@extension("VK_KHR_external_fence_capabilities") // 113 -bitfield VkExternalFenceFeatureFlagBitsKHR { - VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT_KHR = 0x00000001, - VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT_KHR = 0x00000002, -} - -@extension("VK_KHR_external_fence") // 114 -type VkFlags VkFenceImportFlagsKHR -@extension("VK_KHR_external_fence") // 114 -bitfield VkFenceImportFlagBitsKHR { - VK_FENCE_IMPORT_TEMPORARY_BIT_KHR = 0x00000001, -} - -@extension("VK_MVK_ios_surface") // 123 -type VkFlags VkIOSSurfaceCreateFlagsMVK -//@extension("VK_MVK_ios_surface") // 123 -//bitfield VkIOSSurfaceCreateFlagBitsMVK { -//} - -@extension("VK_MVK_macos_surface") // 124 -type VkFlags VkMacOSSurfaceCreateFlagsMVK -//@extension("VK_MVK_macos_surface") // 124 -//bitfield VkMacOSSurfaceCreateFlagBitsMVK { -//} - -@extension("VK_EXT_debug_utils") // 129 -type VkFlags VkDebugUtilsMessengerCallbackDataFlagsEXT -//@extension("VK_EXT_debug_utils") // 129 -//bitfield VkDebugUtilsMessengerCallbackDataFlagBitsEXT { -//} - -@extension("VK_EXT_debug_utils") // 129 -type VkFlags VkDebugUtilsMessengerCreateFlagsEXT -//@extension("VK_EXT_debug_utils") // 129 -//bitfield VkDebugUtilsMessengerCreateFlagBitsEXT { -//} - -@extension("VK_EXT_debug_utils") // 129 -type VkFlags VkDebugUtilsMessageSeverityFlagsEXT -@extension("VK_EXT_debug_utils") // 129 -bitfield VkDebugUtilsMessageSeverityFlagBitsEXT { - VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT = 0x00000001, - VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT = 0x00000010, - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT = 0x00000100, - VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT = 0x00001000, -} - -@extension("VK_EXT_debug_utils") // 129 -type VkFlags VkDebugUtilsMessageTypeFlagsEXT -@extension("VK_EXT_debug_utils") // 129 -bitfield VkDebugUtilsMessageTypeFlagBitsEXT { - VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT = 0x00000001, - VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT = 0x00000002, - VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT = 0x00000004, -} - -@extension("VK_NV_fragment_coverage_to_color") // 150 -type VkFlags VkPipelineCoverageToColorStateCreateFlagsNV -@extension("VK_NV_fragment_coverage_to_color") // 150 -//bitfield VkPipelineCoverageToColorStateCreateFlagBitsNV { -//} - -@extension("VK_NV_framebuffer_mixed_samples") // 153 -type VkFlags VkPipelineCoverageModulationStateCreateFlagsNV -@extension("VK_NV_framebuffer_mixed_samples") // 153 -//bitfield VkPipelineCoverageModulationStateCreateFlagBitsNV { -//} - -@extension("VK_EXT_validation_cache") // 161 -type VkFlags VkValidationCacheCreateFlagsEXT -@extension("VK_EXT_validation_cache") // 161 -//bitfield VkValidationCacheCreateFlagBitsEXT { -//} - -@extension("VK_EXT_descriptor_indexing") // 162 -type VkFlags VkDescriptorBindingFlagsEXT -@extension("VK_EXT_descriptor_indexing") // 162 -bitfield VkDescriptorBindingFlagBitsEXT { - VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT = 0x00000001, - VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT = 0x00000002, - VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT = 0x00000004, - VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT = 0x00000008, -} - -@extension("VK_NV_ray_tracing") // 166 -type VkFlags VkGeometryFlagsNV -@extension("VK_NV_ray_tracing") // 166 -bitfield VkGeometryFlagBitsNV { - VK_GEOMETRY_OPAQUE_BIT_NV = 0x00000001, - VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_NV = 0x00000002, -} - -@extension("VK_NV_ray_tracing") // 166 -type VkFlags VkGeometryInstanceFlagsNV -@extension("VK_NV_ray_tracing") // 166 -bitfield VkGeometryInstanceFlagBitsNV { - VK_GEOMETRY_INSTANCE_TRIANGLE_CULL_DISABLE_BIT_NV = 0x00000001, - VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_NV = 0x00000002, - VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_NV = 0x00000004, - VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_NV = 0x00000008, -} - -@extension("VK_NV_ray_tracing") // 166 -type VkFlags VkBuildAccelerationStructureFlagsNV -@extension("VK_NV_ray_tracing") // 166 -bitfield VkBuildAccelerationStructureFlagBitsNV { - VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_NV = 0x00000001, - VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_NV = 0x00000002, - VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_NV = 0x00000004, - VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_NV = 0x00000008, - VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_NV = 0x00000010, -} - -@extension("VK_FUCHSIA_imagepipe_surface") // 215 -type VkFlags VkImagePipeSurfaceCreateFlagsFUCHSIA -//@extension("VK_FUCHSIA_imagepipe_surface") // 215 -//bitfield VkImagePipeSurfaceCreateFlagBitsFUCHSIA { -//} - -////////////////// -// Structures // -////////////////// - -class VkOffset2D { - s32 x - s32 y -} - -class VkOffset3D { - s32 x - s32 y - s32 z -} - -class VkExtent2D { - u32 width - u32 height -} - -class VkExtent3D { - u32 width - u32 height - u32 depth -} - -class VkViewport { - f32 x - f32 y - f32 width - f32 height - f32 minDepth - f32 maxDepth -} - -class VkRect2D { - VkOffset2D offset - VkExtent2D extent -} - -class VkClearRect { - VkRect2D rect - u32 baseArrayLayer - u32 layerCount -} - -class VkComponentMapping { - VkComponentSwizzle r - VkComponentSwizzle g - VkComponentSwizzle b - VkComponentSwizzle a -} - -class VkPhysicalDeviceProperties { - u32 apiVersion - u32 driverVersion - u32 vendorID - u32 deviceID - VkPhysicalDeviceType deviceType - char[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE] deviceName - u8[VK_UUID_SIZE] pipelineCacheUUID - VkPhysicalDeviceLimits limits - VkPhysicalDeviceSparseProperties sparseProperties -} - -class VkExtensionProperties { - char[VK_MAX_EXTENSION_NAME_SIZE] extensionName /// extension name - u32 specVersion /// version of the extension specification implemented -} - -class VkLayerProperties { - char[VK_MAX_EXTENSION_NAME_SIZE] layerName /// layer name - u32 specVersion /// version of the layer specification implemented - u32 implementationVersion /// build or release version of the layer's library - char[VK_MAX_DESCRIPTION_SIZE] description /// Free-form description of the layer -} - -class VkSubmitInfo { - VkStructureType sType /// Type of structure. Should be VK_STRUCTURE_TYPE_SUBMIT_INFO - const void* pNext /// Next structure in chain - u32 waitSemaphoreCount - const VkSemaphore* pWaitSemaphores - const VkPipelineStageFlags* pWaitDstStageMask - u32 commandBufferCount - const VkCommandBuffer* pCommandBuffers - u32 signalSemaphoreCount - const VkSemaphore* pSignalSemaphores -} - -class VkApplicationInfo { - VkStructureType sType /// Type of structure. Should be VK_STRUCTURE_TYPE_APPLICATION_INFO - const void* pNext /// Next structure in chain - const char* pApplicationName - u32 applicationVersion - const char* pEngineName - u32 engineVersion - u32 apiVersion -} - -class VkAllocationCallbacks { - void* pUserData - PFN_vkAllocationFunction pfnAllocation - PFN_vkReallocationFunction pfnReallocation - PFN_vkFreeFunction pfnFree - PFN_vkInternalAllocationNotification pfnInternalAllocation - PFN_vkInternalFreeNotification pfnInternalFree -} - -class VkDeviceQueueCreateInfo { - VkStructureType sStype /// Should be VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkDeviceQueueCreateFlags flags - u32 queueFamilyIndex - u32 queueCount - const f32* pQueuePriorities -} - -class VkDeviceCreateInfo { - VkStructureType sType /// Should be VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkDeviceCreateFlags flags - u32 queueCreateInfoCount - const VkDeviceQueueCreateInfo* pQueueCreateInfos - u32 enabledLayerCount - const char* const* ppEnabledLayerNames /// Ordered list of layer names to be enabled - u32 enabledExtensionCount - const char* const* ppEnabledExtensionNames - const VkPhysicalDeviceFeatures* pEnabledFeatures -} - -class VkInstanceCreateInfo { - VkStructureType sType /// Should be VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkInstanceCreateFlags flags - const VkApplicationInfo* pApplicationInfo - u32 enabledLayerCount - const char* const* ppEnabledLayerNames /// Ordered list of layer names to be enabled - u32 enabledExtensionCount - const char* const* ppEnabledExtensionNames /// Extension names to be enabled -} - -class VkQueueFamilyProperties { - VkQueueFlags queueFlags /// Queue flags - u32 queueCount - u32 timestampValidBits - VkExtent3D minImageTransferGranularity -} - -class VkPhysicalDeviceMemoryProperties { - u32 memoryTypeCount - VkMemoryType[VK_MAX_MEMORY_TYPES] memoryTypes - u32 memoryHeapCount - VkMemoryHeap[VK_MAX_MEMORY_HEAPS] memoryHeaps -} - -class VkMemoryAllocateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO - const void* pNext /// Pointer to next structure - VkDeviceSize allocationSize /// Size of memory allocation - u32 memoryTypeIndex /// Index of the of the memory type to allocate from -} - -class VkMemoryRequirements { - VkDeviceSize size /// Specified in bytes - VkDeviceSize alignment /// Specified in bytes - u32 memoryTypeBits /// Bitfield of the allowed memory type indices into memoryTypes[] for this object -} - -class VkSparseImageFormatProperties { - VkImageAspectFlagBits aspectMask - VkExtent3D imageGranularity - VkSparseImageFormatFlags flags -} - -class VkSparseImageMemoryRequirements { - VkSparseImageFormatProperties formatProperties - u32 imageMipTailFirstLod - VkDeviceSize imageMipTailSize /// Specified in bytes, must be a multiple of image block size / alignment - VkDeviceSize imageMipTailOffset /// Specified in bytes, must be a multiple of image block size / alignment - VkDeviceSize imageMipTailStride /// Specified in bytes, must be a multiple of image block size / alignment -} - -class VkMemoryType { - VkMemoryPropertyFlags propertyFlags /// Memory properties of this memory type - u32 heapIndex /// Index of the memory heap allocations of this memory type are taken from -} - -class VkMemoryHeap { - VkDeviceSize size /// Available memory in the heap - VkMemoryHeapFlags flags /// Flags for the heap -} - -class VkMappedMemoryRange { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE - const void* pNext /// Pointer to next structure - VkDeviceMemory memory /// Mapped memory object - VkDeviceSize offset /// Offset within the mapped memory the range starts from - VkDeviceSize size /// Size of the range within the mapped memory -} - -class VkFormatProperties { - VkFormatFeatureFlags linearTilingFeatures /// Format features in case of linear tiling - VkFormatFeatureFlags optimalTilingFeatures /// Format features in case of optimal tiling - VkFormatFeatureFlags bufferFeatures /// Format features supported by buffers -} - -class VkImageFormatProperties { - VkExtent3D maxExtent /// max image dimensions for this resource type - u32 maxMipLevels /// max number of mipmap levels for this resource type - u32 maxArrayLayers /// max array layers for this resource type - VkSampleCountFlags sampleCounts /// supported sample counts for this resource type - VkDeviceSize maxResourceSize /// max size (in bytes) of this resource type -} - -class VkDescriptorImageInfo { - VkSampler sampler - VkImageView imageView - VkImageLayout imageLayout -} - -class VkDescriptorBufferInfo { - VkBuffer buffer /// Buffer used for this descriptor when the descriptor is UNIFORM_BUFFER[_DYNAMIC] - VkDeviceSize offset /// Base offset from buffer start in bytes to update in the descriptor set. - VkDeviceSize range /// Size in bytes of the buffer resource for this descriptor update. -} - -class VkWriteDescriptorSet { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET - const void* pNext /// Pointer to next structure - VkDescriptorSet dstSet /// Destination descriptor set - u32 dstBinding /// Binding within the destination descriptor set to write - u32 dstArrayElement /// Array element within the destination binding to write - u32 descriptorCount /// Number of descriptors to write (determines the size of the array pointed by <pDescriptors>) - VkDescriptorType descriptorType /// Descriptor type to write (determines which fields of the array pointed by <pDescriptors> are going to be used) - const VkDescriptorImageInfo* pImageInfo - const VkDescriptorBufferInfo* pBufferInfo - const VkBufferView* pTexelBufferView -} - -class VkCopyDescriptorSet { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET - const void* pNext /// Pointer to next structure - VkDescriptorSet srcSet /// Source descriptor set - u32 srcBinding /// Binding within the source descriptor set to copy from - u32 srcArrayElement /// Array element within the source binding to copy from - VkDescriptorSet dstSet /// Destination descriptor set - u32 dstBinding /// Binding within the destination descriptor set to copy to - u32 dstArrayElement /// Array element within the destination binding to copy to - u32 descriptorCount /// Number of descriptors to copy -} - -class VkBufferCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO - const void* pNext /// Pointer to next structure. - VkBufferCreateFlags flags /// Buffer creation flags - VkDeviceSize size /// Specified in bytes - VkBufferUsageFlags usage /// Buffer usage flags - VkSharingMode sharingMode - u32 queueFamilyIndexCount - const u32* pQueueFamilyIndices -} - -class VkBufferViewCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO - const void* pNext /// Pointer to next structure. - VkBufferViewCreateFlags flags - VkBuffer buffer - VkFormat format /// Optionally specifies format of elements - VkDeviceSize offset /// Specified in bytes - VkDeviceSize range /// View size specified in bytes -} - -class VkImageSubresource { - VkImageAspectFlagBits aspectMask - u32 mipLevel - u32 arrayLayer -} - -class VkImageSubresourceRange { - VkImageAspectFlags aspectMask - u32 baseMipLevel - u32 levelCount - u32 baseArrayLayer - u32 layerCount -} - -class VkMemoryBarrier { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_MEMORY_BARRIER - const void* pNext /// Pointer to next structure. - VkAccessFlags srcAccessMask - VkAccessFlags dstAccessMask -} - -class VkBufferMemoryBarrier { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER - const void* pNext /// Pointer to next structure. - VkAccessFlags srcAccessMask - VkAccessFlags dstAccessMask - u32 srcQueueFamilyIndex /// Queue family to transition ownership from - u32 dstQueueFamilyIndex /// Queue family to transition ownership to - VkBuffer buffer /// Buffer to sync - VkDeviceSize offset /// Offset within the buffer to sync - VkDeviceSize size /// Amount of bytes to sync -} - -class VkImageMemoryBarrier { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER - const void* pNext /// Pointer to next structure. - VkAccessFlags srcAccessMask - VkAccessFlags dstAccessMask - VkImageLayout oldLayout /// Current layout of the image - VkImageLayout newLayout /// New layout to transition the image to - u32 srcQueueFamilyIndex /// Queue family to transition ownership from - u32 dstQueueFamilyIndex /// Queue family to transition ownership to - VkImage image /// Image to sync - VkImageSubresourceRange subresourceRange /// Subresource range to sync -} - -class VkImageCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO - const void* pNext /// Pointer to next structure. - VkImageCreateFlags flags /// Image creation flags - VkImageType imageType - VkFormat format - VkExtent3D extent - u32 mipLevels - u32 arrayLayers - VkSampleCountFlagBits samples - VkImageTiling tiling - VkImageUsageFlags usage /// Image usage flags - VkSharingMode sharingMode /// Cross-queue-family sharing mode - u32 queueFamilyIndexCount /// Number of queue families to share across - const u32* pQueueFamilyIndices /// Array of queue family indices to share across - VkImageLayout initialLayout /// Initial image layout for all subresources -} - -class VkSubresourceLayout { - VkDeviceSize offset /// Specified in bytes - VkDeviceSize size /// Specified in bytes - VkDeviceSize rowPitch /// Specified in bytes - VkDeviceSize arrayPitch /// Specified in bytes - VkDeviceSize depthPitch /// Specified in bytes -} - -class VkImageViewCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO - const void* pNext /// Pointer to next structure - VkImageViewCreateFlags flags - VkImage image - VkImageViewType viewType - VkFormat format - VkComponentMapping components - VkImageSubresourceRange subresourceRange -} - -class VkBufferCopy { - VkDeviceSize srcOffset /// Specified in bytes - VkDeviceSize dstOffset /// Specified in bytes - VkDeviceSize size /// Specified in bytes -} - -class VkSparseMemoryBind { - VkDeviceSize resourceOffset /// Specified in bytes - VkDeviceSize size /// Specified in bytes - VkDeviceMemory memory - VkDeviceSize memoryOffset /// Specified in bytes - VkSparseMemoryBindFlags flags -} - -class VkSparseImageMemoryBind { - VkImageSubresource subresource - VkOffset3D offset - VkExtent3D extent - VkDeviceMemory memory - VkDeviceSize memoryOffset /// Specified in bytes - VkSparseMemoryBindFlags flags -} - -class VkSparseBufferMemoryBindInfo { - VkBuffer buffer - u32 bindCount - const VkSparseMemoryBind* pBinds -} - -class VkSparseImageOpaqueMemoryBindInfo { - VkImage image - u32 bindCount - const VkSparseMemoryBind* pBinds -} - -class VkSparseImageMemoryBindInfo { - VkImage image - u32 bindCount - const VkSparseMemoryBind* pBinds -} - -class VkBindSparseInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_BIND_SPARSE_INFO - const void* pNext - u32 waitSemaphoreCount - const VkSemaphore* pWaitSemaphores - u32 numBufferBinds - const VkSparseBufferMemoryBindInfo* pBufferBinds - u32 numImageOpaqueBinds - const VkSparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds - u32 numImageBinds - const VkSparseImageMemoryBindInfo* pImageBinds - u32 signalSemaphoreCount - const VkSemaphore* pSignalSemaphores -} - -class VkImageSubresourceLayers { - VkImageAspectFlags aspectMask - u32 mipLevel - u32 baseArrayLayer - u32 layerCount -} - -class VkImageCopy { - VkImageSubresourceLayers srcSubresource - VkOffset3D srcOffset /// Specified in pixels for both compressed and uncompressed images - VkImageSubresourceLayers dstSubresource - VkOffset3D dstOffset /// Specified in pixels for both compressed and uncompressed images - VkExtent3D extent /// Specified in pixels for both compressed and uncompressed images -} - -class VkImageBlit { - VkImageSubresourceLayers srcSubresource - VkOffset3D[2] srcOffsets - VkImageSubresourceLayers dstSubresource - VkOffset3D[2] dstOffsets -} - -class VkBufferImageCopy { - VkDeviceSize bufferOffset /// Specified in bytes - u32 bufferRowLength /// Specified in texels - u32 bufferImageHeight - VkImageSubresourceLayers imageSubresource - VkOffset3D imageOffset /// Specified in pixels for both compressed and uncompressed images - VkExtent3D imageExtent /// Specified in pixels for both compressed and uncompressed images -} - -class VkImageResolve { - VkImageSubresourceLayers srcSubresource - VkOffset3D srcOffset - VkImageSubresourceLayers dstSubresource - VkOffset3D dstOffset - VkExtent3D extent -} - -class VkShaderModuleCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkShaderModuleCreateFlags flags /// Reserved - platform.size_t codeSize /// Specified in bytes - const u32* pCode /// Binary code of size codeSize -} - -class VkDescriptorSetLayoutBinding { - u32 binding - VkDescriptorType descriptorType /// Type of the descriptors in this binding - u32 descriptorCount /// Number of descriptors in this binding - VkShaderStageFlags stageFlags /// Shader stages this binding is visible to - const VkSampler* pImmutableSamplers /// Immutable samplers (used if descriptor type is SAMPLER or COMBINED_IMAGE_SAMPLER, is either NULL or contains <count> number of elements) -} - -class VkDescriptorSetLayoutCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO - const void* pNext /// Pointer to next structure - VkDescriptorSetLayoutCreateFlags flags - u32 bindingCount /// Number of bindings in the descriptor set layout - const VkDescriptorSetLayoutBinding* pBindings /// Array of descriptor set layout bindings -} - -class VkDescriptorPoolSize { - VkDescriptorType type - u32 descriptorCount -} - -class VkDescriptorPoolCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO - const void* pNext /// Pointer to next structure - VkDescriptorPoolCreateFlags flags - u32 maxSets - u32 poolSizeCount - const VkDescriptorPoolSize* pPoolSizes -} - -class VkDescriptorSetAllocateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO - const void* pNext /// Pointer to next structure - VkDescriptorPool descriptorPool - u32 setCount - const VkDescriptorSetLayout* pSetLayouts -} - -class VkSpecializationMapEntry { - u32 constantID /// The SpecConstant ID specified in the BIL - u32 offset /// Offset of the value in the data block - platform.size_t size /// Size in bytes of the SpecConstant -} - -class VkSpecializationInfo { - u32 mapEntryCount /// Number of entries in the map - const VkSpecializationMapEntry* pMapEntries /// Array of map entries - platform.size_t dataSize /// Size in bytes of pData - const void* pData /// Pointer to SpecConstant data -} - -class VkPipelineShaderStageCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineShaderStageCreateFlags flags - VkShaderStageFlagBits stage - VkShaderModule module - const char* pName - const VkSpecializationInfo* pSpecializationInfo -} - -class VkComputePipelineCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineCreateFlags flags /// Pipeline creation flags - VkPipelineShaderStageCreateInfo stage - VkPipelineLayout layout /// Interface layout of the pipeline - VkPipeline basePipelineHandle /// If VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is nonzero, it specifies the handle of the base pipeline this is a derivative of - s32 basePipelineIndex /// If VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is not -1, it specifies an index into pCreateInfos of the base pipeline this is a derivative of -} - -class VkVertexInputBindingDescription { - u32 binding /// Vertex buffer binding id - u32 stride /// Distance between vertices in bytes (0 = no advancement) - VkVertexInputRate inputRate /// Rate at which binding is incremented -} - -class VkVertexInputAttributeDescription { - u32 location /// location of the shader vertex attrib - u32 binding /// Vertex buffer binding id - VkFormat format /// format of source data - u32 offset /// Offset of first element in bytes from base of vertex -} - -class VkPipelineVertexInputStateCreateInfo { - VkStructureType sType /// Should be VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineVertexInputStateCreateFlags flags - u32 vertexBindingDescriptionCount /// number of bindings - const VkVertexInputBindingDescription* pVertexBindingDescriptions - u32 vertexAttributeDescriptionCount /// number of attributes - const VkVertexInputAttributeDescription* pVertexAttributeDescriptions -} - -class VkPipelineInputAssemblyStateCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineInputAssemblyStateCreateFlags flags - VkPrimitiveTopology topology - VkBool32 primitiveRestartEnable -} - -class VkPipelineTessellationStateCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineTessellationStateCreateFlags flags - u32 patchControlPoints -} - -class VkPipelineViewportStateCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineViewportStateCreateFlags flags - u32 viewportCount - const VkViewport* pViewports - u32 scissorCount - const VkRect2D* pScissors -} - -class VkPipelineRasterizationStateCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineRasterizationStateCreateFlags flags - VkBool32 depthClampEnable - VkBool32 rasterizerDiscardEnable - VkPolygonMode polygonMode /// optional (GL45) - VkCullModeFlags cullMode - VkFrontFace frontFace - VkBool32 depthBiasEnable - f32 depthBiasConstantFactor - f32 depthBiasClamp - f32 depthBiasSlopeFactor - f32 lineWidth -} - -class VkPipelineMultisampleStateCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineMultisampleStateCreateFlags flags - VkSampleCountFlagBits rasterizationSamples /// Number of samples used for rasterization - VkBool32 sampleShadingEnable /// optional (GL45) - f32 minSampleShading /// optional (GL45) - const VkSampleMask* pSampleMask - VkBool32 alphaToCoverageEnable - VkBool32 alphaToOneEnable -} - -class VkPipelineColorBlendAttachmentState { - VkBool32 blendEnable - VkBlendFactor srcColorBlendFactor - VkBlendFactor dstColorBlendFactor - VkBlendOp colorBlendOp - VkBlendFactor srcAlphaBlendFactor - VkBlendFactor dstAlphaBlendFactor - VkBlendOp alphaBlendOp - VkColorComponentFlags colorWriteMask -} - -class VkPipelineColorBlendStateCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineColorBlendStateCreateFlags flags - VkBool32 logicOpEnable - VkLogicOp logicOp - u32 attachmentCount /// # of pAttachments - const VkPipelineColorBlendAttachmentState* pAttachments - f32[4] blendConstants -} - -class VkStencilOpState { - VkStencilOp failOp - VkStencilOp passOp - VkStencilOp depthFailOp - VkCompareOp compareOp - u32 compareMask - u32 writeMask - u32 reference -} - -class VkPipelineDepthStencilStateCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineDepthStencilStateCreateFlags flags - VkBool32 depthTestEnable - VkBool32 depthWriteEnable - VkCompareOp depthCompareOp - VkBool32 depthBoundsTestEnable /// optional (depth_bounds_test) - VkBool32 stencilTestEnable - VkStencilOpState front - VkStencilOpState back - f32 minDepthBounds - f32 maxDepthBounds -} - -class VkPipelineDynamicStateCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineDynamicStateCreateFlags flags - u32 dynamicStateCount - const VkDynamicState* pDynamicStates -} - -class VkGraphicsPipelineCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineCreateFlags flags /// Pipeline creation flags - u32 stageCount - const VkPipelineShaderStageCreateInfo* pStages /// One entry for each active shader stage - const VkPipelineVertexInputStateCreateInfo* pVertexInputState - const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState - const VkPipelineTessellationStateCreateInfo* pTessellationState - const VkPipelineViewportStateCreateInfo* pViewportState - const VkPipelineRasterizationStateCreateInfo* pRasterizationState - const VkPipelineMultisampleStateCreateInfo* pMultisampleState - const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState - const VkPipelineColorBlendStateCreateInfo* pColorBlendState - const VkPipelineDynamicStateCreateInfo* pDynamicState - VkPipelineLayout layout /// Interface layout of the pipeline - VkRenderPass renderPass - u32 subpass - VkPipeline basePipelineHandle /// If VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is nonzero, it specifies the handle of the base pipeline this is a derivative of - s32 basePipelineIndex /// If VK_PIPELINE_CREATE_DERIVATIVE_BIT is set and this value is not -1, it specifies an index into pCreateInfos of the base pipeline this is a derivative of -} - -class VkPipelineCacheCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineCacheCreateFlags flags - platform.size_t initialDataSize /// Size of initial data to populate cache, in bytes - const void* pInitialData /// Initial data to populate cache -} - -class VkPushConstantRange { - VkShaderStageFlags stageFlags /// Which stages use the range - u32 offset /// Start of the range, in bytes - u32 size /// Length of the range, in bytes -} - -class VkPipelineLayoutCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO - const void* pNext /// Pointer to next structure - VkPipelineLayoutCreateFlags flags - u32 descriptorSetCount /// Number of descriptor sets interfaced by the pipeline - const VkDescriptorSetLayout* pSetLayouts /// Array of <setCount> number of descriptor set layout objects defining the layout of the - u32 pushConstantRangeCount /// Number of push-constant ranges used by the pipeline - const VkPushConstantRange* pPushConstantRanges /// Array of pushConstantRangeCount number of ranges used by various shader stages -} - -class VkSamplerCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO - const void* pNext /// Pointer to next structure - VkSamplerCreateFlags flags - VkFilter magFilter /// Filter mode for magnification - VkFilter minFilter /// Filter mode for minifiation - VkSamplerMipmapMode mipmapMode /// Mipmap selection mode - VkSamplerAddressMode addressModeU - VkSamplerAddressMode addressModeV - VkSamplerAddressMode addressModeW - f32 mipLodBias - VkBool32 anisotropyEnable - f32 maxAnisotropy - VkBool32 compareEnable - VkCompareOp compareOp - f32 minLod - f32 maxLod - VkBorderColor borderColor - VkBool32 unnormalizedCoordinates -} - -class VkCommandPoolCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO - const void* pNext /// Pointer to next structure - VkCommandPoolCreateFlags flags /// Command pool creation flags - u32 queueFamilyIndex -} - -class VkCommandBufferAllocateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO - const void* pNext /// Pointer to next structure - VkCommandPool commandPool - VkCommandBufferLevel level - u32 commandBufferCount -} - -class VkCommandBufferInheritanceInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO - const void* pNext /// Pointer to next structure - VkRenderPass renderPass /// Render pass for secondary command buffers - u32 subpass - VkFramebuffer framebuffer /// Framebuffer for secondary command buffers - VkBool32 occlusionQueryEnable - VkQueryControlFlags queryFlags - VkQueryPipelineStatisticFlags pipelineStatistics -} - -class VkCommandBufferBeginInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO - const void* pNext /// Pointer to next structure - VkCommandBufferUsageFlags flags /// Command buffer usage flags - const VkCommandBufferInheritanceInfo* pInheritanceInfo -} - -class VkRenderPassBeginInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO - const void* pNext /// Pointer to next structure - VkRenderPass renderPass - VkFramebuffer framebuffer - VkRect2D renderArea - u32 clearValueCount - const VkClearValue* pClearValues -} - -@union -/// Union allowing specification of floating point, integer, or unsigned integer color data. Actual value selected is based on image/attachment being cleared. -class VkClearColorValue { - f32[4] float32 - s32[4] int32 - u32[4] uint32 -} - -class VkClearDepthStencilValue { - f32 depth - u32 stencil -} - -@union -/// Union allowing specification of color, depth, and stencil color values. Actual value selected is based on attachment being cleared. -class VkClearValue { - VkClearColorValue color - VkClearDepthStencilValue depthStencil -} - -class VkClearAttachment { - VkImageAspectFlags aspectMask - u32 colorAttachment - VkClearValue clearValue -} - -class VkAttachmentDescription { - VkAttachmentDescriptionFlags flags - VkFormat format - VkSampleCountFlagBits samples - VkAttachmentLoadOp loadOp /// Load op for color or depth data - VkAttachmentStoreOp storeOp /// Store op for color or depth data - VkAttachmentLoadOp stencilLoadOp /// Load op for stencil data - VkAttachmentStoreOp stencilStoreOp /// Store op for stencil data - VkImageLayout initialLayout - VkImageLayout finalLayout -} - -class VkAttachmentReference { - u32 attachment - VkImageLayout layout -} - -class VkSubpassDescription { - VkSubpassDescriptionFlags flags - VkPipelineBindPoint pipelineBindPoint /// Must be VK_PIPELINE_BIND_POINT_GRAPHICS for now - u32 inputAttachmentCount - const VkAttachmentReference* pInputAttachments - u32 colorAttachmentCount - const VkAttachmentReference* pColorAttachments - const VkAttachmentReference* pResolveAttachments - const VkAttachmentReference* pDepthStencilAttachment - u32 preserveAttachmentCount - const u32* pPreserveAttachments -} - -class VkSubpassDependency { - u32 srcSubpass - u32 dstSubpass - VkPipelineStageFlags srcStageMask - VkPipelineStageFlags dstStageMask - VkAccessFlags srcAccessMask - VkAccessFlags dstAccessMask - VkDependencyFlags dependencyFlags -} - -class VkRenderPassCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO - const void* pNext /// Pointer to next structure - VkRenderPassCreateFlags flags - u32 attachmentCount - const VkAttachmentDescription* pAttachments - u32 subpassCount - const VkSubpassDescription* pSubpasses - u32 dependencyCount - const VkSubpassDependency* pDependencies -} - -class VkEventCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_EVENT_CREATE_INFO - const void* pNext /// Pointer to next structure - VkEventCreateFlags flags /// Event creation flags -} - -class VkFenceCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_FENCE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkFenceCreateFlags flags /// Fence creation flags -} - -class VkPhysicalDeviceFeatures { - VkBool32 robustBufferAccess /// out of bounds buffer accesses are well defined - VkBool32 fullDrawIndexUint32 /// full 32-bit range of indices for indexed draw calls - VkBool32 imageCubeArray /// image views which are arrays of cube maps - VkBool32 independentBlend /// blending operations are controlled per-attachment - VkBool32 geometryShader /// geometry stage - VkBool32 tessellationShader /// tessellation control and evaluation stage - VkBool32 sampleRateShading /// per-sample shading and interpolation - VkBool32 dualSrcBlend /// blend operations which take two sources - VkBool32 logicOp /// logic operations - VkBool32 multiDrawIndirect /// multi draw indirect - VkBool32 drawIndirectFirstInstance - VkBool32 depthClamp /// depth clamping - VkBool32 depthBiasClamp /// depth bias clamping - VkBool32 fillModeNonSolid /// point and wireframe fill modes - VkBool32 depthBounds /// depth bounds test - VkBool32 wideLines /// lines with width greater than 1 - VkBool32 largePoints /// points with size greater than 1 - VkBool32 alphaToOne /// The fragment alpha channel can be forced to maximum representable alpha value - VkBool32 multiViewport - VkBool32 samplerAnisotropy - VkBool32 textureCompressionETC2 /// ETC texture compression formats - VkBool32 textureCompressionASTC_LDR /// ASTC LDR texture compression formats - VkBool32 textureCompressionBC /// BC1-7 texture compressed formats - VkBool32 occlusionQueryPrecise - VkBool32 pipelineStatisticsQuery /// pipeline statistics query - VkBool32 vertexPipelineStoresAndAtomics - VkBool32 fragmentStoresAndAtomics - VkBool32 shaderTessellationAndGeometryPointSize - VkBool32 shaderImageGatherExtended /// texture gather with run-time values and independent offsets - VkBool32 shaderStorageImageExtendedFormats /// the extended set of formats can be used for storage images - VkBool32 shaderStorageImageMultisample /// multisample images can be used for storage images - VkBool32 shaderStorageImageReadWithoutFormat - VkBool32 shaderStorageImageWriteWithoutFormat - VkBool32 shaderUniformBufferArrayDynamicIndexing /// arrays of uniform buffers can be accessed with dynamically uniform indices - VkBool32 shaderSampledImageArrayDynamicIndexing /// arrays of sampled images can be accessed with dynamically uniform indices - VkBool32 shaderStorageBufferArrayDynamicIndexing /// arrays of storage buffers can be accessed with dynamically uniform indices - VkBool32 shaderStorageImageArrayDynamicIndexing /// arrays of storage images can be accessed with dynamically uniform indices - VkBool32 shaderClipDistance /// clip distance in shaders - VkBool32 shaderCullDistance /// cull distance in shaders - VkBool32 shaderFloat64 /// 64-bit floats (doubles) in shaders - VkBool32 shaderInt64 /// 64-bit integers in shaders - VkBool32 shaderInt16 /// 16-bit integers in shaders - VkBool32 shaderResourceResidency /// shader can use texture operations that return resource residency information (requires sparseNonResident support) - VkBool32 shaderResourceMinLod /// shader can use texture operations that specify minimum resource LOD - VkBool32 sparseBinding /// Sparse resources support: Resource memory can be managed at opaque page level rather than object level - VkBool32 sparseResidencyBuffer /// Sparse resources support: GPU can access partially resident buffers - VkBool32 sparseResidencyImage2D /// Sparse resources support: GPU can access partially resident 2D (non-MSAA non-DepthStencil) images - VkBool32 sparseResidencyImage3D /// Sparse resources support: GPU can access partially resident 3D images - VkBool32 sparseResidency2Samples /// Sparse resources support: GPU can access partially resident MSAA 2D images with 2 samples - VkBool32 sparseResidency4Samples /// Sparse resources support: GPU can access partially resident MSAA 2D images with 4 samples - VkBool32 sparseResidency8Samples /// Sparse resources support: GPU can access partially resident MSAA 2D images with 8 samples - VkBool32 sparseResidency16Samples /// Sparse resources support: GPU can access partially resident MSAA 2D images with 16 samples - VkBool32 sparseResidencyAliased /// Sparse resources support: GPU can correctly access data aliased into multiple locations (opt-in) - VkBool32 variableMultisampleRate - VkBool32 inheritedQueries -} - -class VkPhysicalDeviceLimits { - /// resource maximum sizes - u32 maxImageDimension1D /// max 1D image dimension - u32 maxImageDimension2D /// max 2D image dimension - u32 maxImageDimension3D /// max 3D image dimension - u32 maxImageDimensionCube /// max cubemap image dimension - u32 maxImageArrayLayers /// max layers for image arrays - u32 maxTexelBufferElements - u32 maxUniformBufferRange /// max uniform buffer size (bytes) - u32 maxStorageBufferRange /// max storage buffer size (bytes) - u32 maxPushConstantsSize /// max size of the push constants pool (bytes) - /// memory limits - u32 maxMemoryAllocationCount /// max number of device memory allocations supported - u32 maxSamplerAllocationCount - VkDeviceSize bufferImageGranularity /// Granularity (in bytes) at which buffers and images can be bound to adjacent memory for simultaneous usage - VkDeviceSize sparseAddressSpaceSize /// Total address space available for sparse allocations (bytes) - /// descriptor set limits - u32 maxBoundDescriptorSets /// max number of descriptors sets that can be bound to a pipeline - u32 maxPerStageDescriptorSamplers /// max num of samplers allowed per-stage in a descriptor set - u32 maxPerStageDescriptorUniformBuffers /// max num of uniform buffers allowed per-stage in a descriptor set - u32 maxPerStageDescriptorStorageBuffers /// max num of storage buffers allowed per-stage in a descriptor set - u32 maxPerStageDescriptorSampledImages /// max num of sampled images allowed per-stage in a descriptor set - u32 maxPerStageDescriptorStorageImages /// max num of storage images allowed per-stage in a descriptor set - u32 maxPerStageDescriptorInputAttachments - u32 maxPerStageResources - u32 maxDescriptorSetSamplers /// max num of samplers allowed in all stages in a descriptor set - u32 maxDescriptorSetUniformBuffers /// max num of uniform buffers allowed in all stages in a descriptor set - u32 maxDescriptorSetUniformBuffersDynamic /// max num of dynamic uniform buffers allowed in all stages in a descriptor set - u32 maxDescriptorSetStorageBuffers /// max num of storage buffers allowed in all stages in a descriptor set - u32 maxDescriptorSetStorageBuffersDynamic /// max num of dynamic storage buffers allowed in all stages in a descriptor set - u32 maxDescriptorSetSampledImages /// max num of sampled images allowed in all stages in a descriptor set - u32 maxDescriptorSetStorageImages /// max num of storage images allowed in all stages in a descriptor set - u32 maxDescriptorSetInputAttachments - /// vertex stage limits - u32 maxVertexInputAttributes /// max num of vertex input attribute slots - u32 maxVertexInputBindings /// max num of vertex input binding slots - u32 maxVertexInputAttributeOffset /// max vertex input attribute offset added to vertex buffer offset - u32 maxVertexInputBindingStride /// max vertex input binding stride - u32 maxVertexOutputComponents /// max num of output components written by vertex shader - /// tessellation control stage limits - u32 maxTessellationGenerationLevel /// max level supported by tess primitive generator - u32 maxTessellationPatchSize /// max patch size (vertices) - u32 maxTessellationControlPerVertexInputComponents /// max num of input components per-vertex in TCS - u32 maxTessellationControlPerVertexOutputComponents /// max num of output components per-vertex in TCS - u32 maxTessellationControlPerPatchOutputComponents /// max num of output components per-patch in TCS - u32 maxTessellationControlTotalOutputComponents /// max total num of per-vertex and per-patch output components in TCS - u32 maxTessellationEvaluationInputComponents /// max num of input components per vertex in TES - u32 maxTessellationEvaluationOutputComponents /// max num of output components per vertex in TES - /// geometry stage limits - u32 maxGeometryShaderInvocations /// max invocation count supported in geometry shader - u32 maxGeometryInputComponents /// max num of input components read in geometry stage - u32 maxGeometryOutputComponents /// max num of output components written in geometry stage - u32 maxGeometryOutputVertices /// max num of vertices that can be emitted in geometry stage - u32 maxGeometryTotalOutputComponents /// max total num of components (all vertices) written in geometry stage - /// fragment stage limits - u32 maxFragmentInputComponents /// max num of input compontents read in fragment stage - u32 maxFragmentOutputAttachments /// max num of output attachments written in fragment stage - u32 maxFragmentDualSrcAttachments /// max num of output attachments written when using dual source blending - u32 maxFragmentCombinedOutputResources /// max total num of storage buffers, storage images and output buffers - /// compute stage limits - u32 maxComputeSharedMemorySize /// max total storage size of work group local storage (bytes) - u32[3] maxComputeWorkGroupCount /// max num of compute work groups that may be dispatched by a single command (x,y,z) - u32 maxComputeWorkGroupInvocations /// max total compute invocations in a single local work group - u32[3] maxComputeWorkGroupSize /// max local size of a compute work group (x,y,z) - - u32 subPixelPrecisionBits /// num bits of subpixel precision in screen x and y - u32 subTexelPrecisionBits /// num bits of subtexel precision - u32 mipmapPrecisionBits /// num bits of mipmap precision - - u32 maxDrawIndexedIndexValue /// max index value for indexed draw calls (for 32-bit indices) - u32 maxDrawIndirectCount - - f32 maxSamplerLodBias /// max absolute sampler level of detail bias - f32 maxSamplerAnisotropy /// max degree of sampler anisotropy - - u32 maxViewports /// max number of active viewports - u32[2] maxViewportDimensions /// max viewport dimensions (x,y) - f32[2] viewportBoundsRange /// viewport bounds range (min,max) - u32 viewportSubPixelBits /// num bits of subpixel precision for viewport - - platform.size_t minMemoryMapAlignment /// min required alignment of pointers returned by MapMemory (bytes) - VkDeviceSize minTexelBufferOffsetAlignment /// min required alignment for texel buffer offsets (bytes) - VkDeviceSize minUniformBufferOffsetAlignment /// min required alignment for uniform buffer sizes and offsets (bytes) - VkDeviceSize minStorageBufferOffsetAlignment /// min required alignment for storage buffer offsets (bytes) - - s32 minTexelOffset /// min texel offset for OpTextureSampleOffset - u32 maxTexelOffset /// max texel offset for OpTextureSampleOffset - s32 minTexelGatherOffset /// min texel offset for OpTextureGatherOffset - u32 maxTexelGatherOffset /// max texel offset for OpTextureGatherOffset - f32 minInterpolationOffset /// furthest negative offset for interpolateAtOffset - f32 maxInterpolationOffset /// furthest positive offset for interpolateAtOffset - u32 subPixelInterpolationOffsetBits /// num of subpixel bits for interpolateAtOffset - - u32 maxFramebufferWidth /// max width for a framebuffer - u32 maxFramebufferHeight /// max height for a framebuffer - u32 maxFramebufferLayers /// max layer count for a layered framebuffer - VkSampleCountFlags framebufferColorSampleCounts - VkSampleCountFlags framebufferDepthSampleCounts - VkSampleCountFlags framebufferStencilSampleCounts - VkSampleCountFlags framebufferNoAttachmentSampleCounts - u32 maxColorAttachments /// max num of framebuffer color attachments - - VkSampleCountFlags sampledImageColorSampleCounts - VkSampleCountFlags sampledImageIntegerSampleCounts - VkSampleCountFlags sampledImageDepthSampleCounts - VkSampleCountFlags sampledImageStencilSampleCounts - VkSampleCountFlags storageImageSampleCounts - u32 maxSampleMaskWords /// max num of sample mask words - VkBool32 timestampComputeAndGraphics - - f32 timestampPeriod - - u32 maxClipDistances /// max number of clip distances - u32 maxCullDistances /// max number of cull distances - u32 maxCombinedClipAndCullDistances /// max combined number of user clipping - - u32 discreteQueuePriorities - - f32[2] pointSizeRange /// range (min,max) of supported point sizes - f32[2] lineWidthRange /// range (min,max) of supported line widths - f32 pointSizeGranularity /// granularity of supported point sizes - f32 lineWidthGranularity /// granularity of supported line widths - VkBool32 strictLines - VkBool32 standardSampleLocations - - VkDeviceSize optimalBufferCopyOffsetAlignment - VkDeviceSize optimalBufferCopyRowPitchAlignment - VkDeviceSize nonCoherentAtomSize -} - -class VkPhysicalDeviceSparseProperties { - VkBool32 residencyStandard2DBlockShape /// Sparse resources support: GPU will access all 2D (single sample) sparse resources using the standard block shapes (based on pixel format) - VkBool32 residencyStandard2DMultisampleBlockShape /// Sparse resources support: GPU will access all 2D (multisample) sparse resources using the standard block shapes (based on pixel format) - VkBool32 residencyStandard3DBlockShape /// Sparse resources support: GPU will access all 3D sparse resources using the standard block shapes (based on pixel format) - VkBool32 residencyAlignedMipSize /// Sparse resources support: Images with mip-level dimensions that are NOT a multiple of the block size will be placed in the mip tail - VkBool32 residencyNonResidentStrict /// Sparse resources support: GPU can safely access non-resident regions of a resource, all reads return as if data is 0, writes are discarded -} - -class VkSemaphoreCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO - const void* pNext /// Pointer to next structure - VkSemaphoreCreateFlags flags /// Semaphore creation flags -} - -class VkQueryPoolCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO - const void* pNext /// Pointer to next structure - VkQueryPoolCreateFlags flags - VkQueryType queryType - u32 queryCount - VkQueryPipelineStatisticFlags pipelineStatistics /// Optional -} - -class VkFramebufferCreateInfo { - VkStructureType sType /// Must be VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO - const void* pNext /// Pointer to next structure - VkFramebufferCreateFlags flags - VkRenderPass renderPass - u32 attachmentCount - const VkImageView* pAttachments - u32 width - u32 height - u32 layers -} - -class VkDrawIndirectCommand { - u32 vertexCount - u32 instanceCount - u32 firstVertex - u32 firstInstance -} - -class VkDrawIndexedIndirectCommand { - u32 indexCount - u32 instanceCount - u32 firstIndex - s32 vertexOffset - u32 firstInstance -} - -class VkDispatchIndirectCommand { - u32 x - u32 y - u32 z -} - -class VkBaseOutStructure { - VkStructureType sType - void* pNext -} - -class VkBaseInStructure { - VkStructureType sType - const void* pNext -} - -//@vulkan1_1 structures - -class VkPhysicalDeviceSubgroupProperties { - VkStructureType sType - void* pNext - u32 subgroupSize - VkShaderStageFlags supportedStages - VkSubgroupFeatureFlags supportedOperations - VkBool32 quadOperationsInAllStages -} - -class VkBindBufferMemoryInfo { - VkStructureType sType - const void* pNext - VkBuffer buffer - VkDeviceMemory memory - VkDeviceSize memoryOffset -} - -class VkBindImageMemoryInfo { - VkStructureType sType - const void* pNext - VkImage image - VkDeviceMemory memory - VkDeviceSize memoryOffset -} - -class VkPhysicalDevice16BitStorageFeatures { - VkStructureType sType - void* pNext - VkBool32 storageBuffer16BitAccess - VkBool32 uniformAndStorageBuffer16BitAccess - VkBool32 storagePushConstant16 - VkBool32 storageInputOutput16 -} - -class VkMemoryDedicatedRequirements { - VkStructureType sType - void* pNext - VkBool32 prefersDedicatedAllocation - VkBool32 requiresDedicatedAllocation -} - -class VkMemoryDedicatedAllocateInfo { - VkStructureType sType - const void* pNext - VkImage image - VkBuffer buffer -} - -class VkMemoryAllocateFlagsInfo { - VkStructureType sType - const void* pNext - VkMemoryAllocateFlags flags - u32 deviceMask -} - -class VkDeviceGroupRenderPassBeginInfo { - VkStructureType sType - const void* pNext - u32 deviceMask - u32 deviceRenderAreaCount - const VkRect2D* pDeviceRenderAreas -} - -class VkDeviceGroupCommandBufferBeginInfo { - VkStructureType sType - const void* pNext - u32 deviceMask -} - -class VkDeviceGroupSubmitInfo { - VkStructureType sType - const void* pNext - u32 waitSemaphoreCount - const u32* pWaitSemaphoreDeviceIndices - u32 commandBufferCount - const u32* pCommandBufferDeviceMasks - u32 signalSemaphoreCount - const u32* pSignalSemaphoreDeviceIndices -} - -class VkDeviceGroupBindSparseInfo { - VkStructureType sType - const void* pNext - u32 resourceDeviceIndex - u32 memoryDeviceIndex -} - -class VkBindBufferMemoryDeviceGroupInfo { - VkStructureType sType - const void* pNext - u32 deviceIndexCount - const u32* pDeviceIndices -} - -class VkBindImageMemoryDeviceGroupInfo { - VkStructureType sType - const void* pNext - u32 deviceIndexCount - const u32* pDeviceIndices - u32 splitInstanceBindRegionCount - const VkRect2D* pSplitInstanceBindRegions -} - -class VkPhysicalDeviceGroupProperties { - VkStructureType sType - void* pNext - u32 physicalDeviceCount - VkPhysicalDevice[VK_MAX_DEVICE_GROUP_SIZE] physicalDevices - VkBool32 subsetAllocation -} - -class VkDeviceGroupDeviceCreateInfo { - VkStructureType sType - const void* pNext - u32 physicalDeviceCount - const VkPhysicalDevice* pPhysicalDevices -} - -class VkBufferMemoryRequirementsInfo2 { - VkStructureType sType - const void* pNext - VkBuffer buffer -} - -class VkImageMemoryRequirementsInfo2 { - VkStructureType sType - const void* pNext - VkImage image -} - -class VkImageSparseMemoryRequirementsInfo2 { - VkStructureType sType - const void* pNext - VkImage image -} - -class VkMemoryRequirements2 { - VkStructureType sType - void* pNext - VkMemoryRequirements memoryRequirements -} - -class VkSparseImageMemoryRequirements2 { - VkStructureType sType - void* pNext - VkSparseImageMemoryRequirements memoryRequirements -} - -class VkPhysicalDeviceFeatures2 { - VkStructureType sType - void* pNext - VkPhysicalDeviceFeatures features -} - -class VkPhysicalDeviceProperties2 { - VkStructureType sType - void* pNext - VkPhysicalDeviceProperties properties -} - -class VkFormatProperties2 { - VkStructureType sType - void* pNext - VkFormatProperties formatProperties -} - -class VkImageFormatProperties2 { - VkStructureType sType - void* pNext - VkImageFormatProperties imageFormatProperties -} - -class VkPhysicalDeviceImageFormatInfo2 { - VkStructureType sType - const void* pNext - VkFormat format - VkImageType type - VkImageTiling tiling - VkImageUsageFlags usage - VkImageCreateFlags flags -} - -class VkQueueFamilyProperties2 { - VkStructureType sType - void* pNext - VkQueueFamilyProperties queueFamilyProperties -} - -class VkPhysicalDeviceMemoryProperties2 { - VkStructureType sType - void* pNext - VkPhysicalDeviceMemoryProperties memoryProperties -} - -class VkSparseImageFormatProperties2 { - VkStructureType sType - void* pNext - VkSparseImageFormatProperties properties -} - -class VkPhysicalDeviceSparseImageFormatInfo2 { - VkStructureType sType - const void* pNext - VkFormat format - VkImageType type - VkSampleCountFlagBits samples - VkImageUsageFlags usage - VkImageTiling tiling -} - -class VkPhysicalDevicePointClippingProperties { - VkStructureType sType - void* pNext - VkPointClippingBehavior pointClippingBehavior -} - -class VkInputAttachmentAspectReference { - u32 subpass - u32 inputAttachmentIndex - VkImageAspectFlags aspectMask -} - -class VkRenderPassInputAttachmentAspectCreateInfo { - VkStructureType sType - const void* pNext - u32 aspectReferenceCount - const VkInputAttachmentAspectReference* pAspectReferences -} - -class VkImageViewUsageCreateInfo { - VkStructureType sType - const void* pNext - VkImageUsageFlags usage -} - -class VkPipelineTessellationDomainOriginStateCreateInfo { - VkStructureType sType - const void* pNext - VkTessellationDomainOrigin domainOrigin -} - -class VkRenderPassMultiviewCreateInfo { - VkStructureType sType - const void* pNext - u32 subpassCount - const u32* pViewMasks - u32 dependencyCount - const s32* pViewOffsets - u32 correlationMaskCount - const u32* pCorrelationMasks -} - -class VkPhysicalDeviceMultiviewFeatures { - VkStructureType sType - void* pNext - VkBool32 multiview - VkBool32 multiviewGeometryShader - VkBool32 multiviewTessellationShader -} - -class VkPhysicalDeviceMultiviewProperties { - VkStructureType sType - void* pNext - u32 maxMultiviewViewCount - u32 maxMultiviewInstanceIndex -} - -class VkPhysicalDeviceVariablePointerFeatures { - VkStructureType sType - void* pNext - VkBool32 variablePointersStorageBuffer - VkBool32 variablePointers -} - -class VkPhysicalDeviceProtectedMemoryFeatures { - VkStructureType sType - void* pNext - VkBool32 protectedMemory -} - -class VkPhysicalDeviceProtectedMemoryProperties { - VkStructureType sType - void* pNext - VkBool32 protectedNoFault -} - -class VkDeviceQueueInfo2 { - VkStructureType sType - const void* pNext - VkDeviceQueueCreateFlags flags - u32 queueFamilyIndex - u32 queueIndex -} - -class VkProtectedSubmitInfo { - VkStructureType sType - const void* pNext - VkBool32 protectedSubmit -} - -class VkSamplerYcbcrConversionCreateInfo { - VkStructureType sType - const void* pNext - VkFormat format - VkSamplerYcbcrModelConversion ycbcrModel - VkSamplerYcbcrRange ycbcrRange - VkComponentMapping components - VkChromaLocation xChromaOffset - VkChromaLocation yChromaOffset - VkFilter chromaFilter - VkBool32 forceExplicitReconstruction -} - -class VkSamplerYcbcrConversionInfo { - VkStructureType sType - const void* pNext - VkSamplerYcbcrConversion conversion -} - -class VkBindImagePlaneMemoryInfo { - VkStructureType sType - const void* pNext - VkImageAspectFlagBits planeAspect -} - -class VkImagePlaneMemoryRequirementsInfo { - VkStructureType sType - const void* pNext - VkImageAspectFlagBits planeAspect -} - -class VkPhysicalDeviceSamplerYcbcrConversionFeatures { - VkStructureType sType - void* pNext - VkBool32 samplerYcbcrConversion -} - -class VkSamplerYcbcrConversionImageFormatProperties { - VkStructureType sType - void* pNext - u32 combinedImageSamplerDescriptorCount -} - -class VkDescriptorUpdateTemplateEntry { - u32 dstBinding - u32 dstArrayElement - u32 descriptorCount - VkDescriptorType descriptorType - platform.size_t offset - platform.size_t stride -} - -class VkDescriptorUpdateTemplateCreateInfo { - VkStructureType sType - const void* pNext - VkDescriptorUpdateTemplateCreateFlags flags - u32 descriptorUpdateEntryCount - const VkDescriptorUpdateTemplateEntry* pDescriptorUpdateEntries - VkDescriptorUpdateTemplateType templateType - VkDescriptorSetLayout descriptorSetLayout - VkPipelineBindPoint pipelineBindPoint - VkPipelineLayout pipelineLayout - u32 set -} - -class VkExternalMemoryProperties { - VkExternalMemoryFeatureFlags externalMemoryFeatures - VkExternalMemoryHandleTypeFlags exportFromImportedHandleTypes - VkExternalMemoryHandleTypeFlags compatibleHandleTypes -} - -class VkPhysicalDeviceExternalImageFormatInfo { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagBits handleType -} - -class VkExternalImageFormatProperties { - VkStructureType sType - void* pNext - VkExternalMemoryProperties externalMemoryProperties -} - -class VkPhysicalDeviceExternalBufferInfo { - VkStructureType sType - const void* pNext - VkBufferCreateFlags flags - VkBufferUsageFlags usage - VkExternalMemoryHandleTypeFlagBits handleType -} - -class VkExternalBufferProperties { - VkStructureType sType - void* pNext - VkExternalMemoryProperties externalMemoryProperties -} - -class VkPhysicalDeviceIDProperties { - VkStructureType sType - void* pNext - u8[VK_UUID_SIZE] deviceUUID - u8[VK_UUID_SIZE] driverUUID - u8[VK_LUID_SIZE] deviceLUID - u32 deviceNodeMask - VkBool32 deviceLUIDValid -} - -class VkExternalMemoryImageCreateInfo { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlags handleTypes -} - -class VkExternalMemoryBufferCreateInfo { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlags handleTypes -} - -class VkExportMemoryAllocateInfo { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlags handleTypes -} - -class VkPhysicalDeviceExternalFenceInfo { - VkStructureType sType - const void* pNext - VkExternalFenceHandleTypeFlagBits handleType -} - -class VkExternalFenceProperties { - VkStructureType sType - void* pNext - VkExternalFenceHandleTypeFlags exportFromImportedHandleTypes - VkExternalFenceHandleTypeFlags compatibleHandleTypes - VkExternalFenceFeatureFlags externalFenceFeatures -} - -class VkExportFenceCreateInfo { - VkStructureType sType - const void* pNext - VkExternalFenceHandleTypeFlags handleTypes -} - -class VkExportSemaphoreCreateInfo { - VkStructureType sType - const void* pNext - VkExternalSemaphoreHandleTypeFlags handleTypes -} - -class VkPhysicalDeviceExternalSemaphoreInfo { - VkStructureType sType - const void* pNext - VkExternalSemaphoreHandleTypeFlagBits handleType -} - -class VkExternalSemaphoreProperties { - VkStructureType sType - void* pNext - VkExternalSemaphoreHandleTypeFlags exportFromImportedHandleTypes - VkExternalSemaphoreHandleTypeFlags compatibleHandleTypes - VkExternalSemaphoreFeatureFlags externalSemaphoreFeatures -} - -class VkPhysicalDeviceMaintenance3Properties { - VkStructureType sType - void* pNext - u32 maxPerSetDescriptors - VkDeviceSize maxMemoryAllocationSize -} - -class VkDescriptorSetLayoutSupport { - VkStructureType sType - void* pNext - VkBool32 supported -} - -class VkPhysicalDeviceShaderDrawParameterFeatures { - VkStructureType sType - void* pNext - VkBool32 shaderDrawParameters -} - - -@extension("VK_KHR_surface") // 1 -class VkSurfaceCapabilitiesKHR { - u32 minImageCount - u32 maxImageCount - VkExtent2D currentExtent - VkExtent2D minImageExtent - VkExtent2D maxImageExtent - u32 maxImageArrayLayers - VkSurfaceTransformFlagsKHR supportedTransforms - VkSurfaceTransformFlagBitsKHR currentTransform - VkCompositeAlphaFlagsKHR supportedCompositeAlpha - VkImageUsageFlags supportedUsageFlags -} - -@extension("VK_KHR_surface") // 1 -class VkSurfaceFormatKHR { - VkFormat format - VkColorSpaceKHR colorSpace -} - -@extension("VK_KHR_swapchain") // 2 -class VkSwapchainCreateInfoKHR { - VkStructureType sType - const void* pNext - VkSwapchainCreateFlagsKHR flags - VkSurfaceKHR surface - u32 minImageCount - VkFormat imageFormat - VkColorSpaceKHR imageColorSpace - VkExtent2D imageExtent - u32 imageArrayLayers - VkImageUsageFlags imageUsage - VkSharingMode sharingMode - u32 queueFamilyIndexCount - const u32* pQueueFamilyIndices - VkSurfaceTransformFlagBitsKHR preTransform - VkCompositeAlphaFlagBitsKHR compositeAlpha - VkPresentModeKHR presentMode - VkBool32 clipped - VkSwapchainKHR oldSwapchain -} - -@extension("VK_KHR_swapchain") // 2 -class VkPresentInfoKHR { - VkStructureType sType - const void* pNext - u32 waitSemaphoreCount - const VkSemaphore* pWaitSemaphores - u32 swapchainCount - const VkSwapchainKHR* pSwapchains - const u32* pImageIndices - VkResult* pResults -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -class VkImageSwapchainCreateInfoKHR { - VkStructureType sType - const void* pNext - VkSwapchainKHR swapchain -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -class VkBindImageMemorySwapchainInfoKHR { - VkStructureType sType - const void* pNext - VkSwapchainKHR swapchain - u32 imageIndex -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -class VkAcquireNextImageInfoKHR { - VkStructureType sType - const void* pNext - VkSwapchainKHR swapchain - u64 timeout - VkSemaphore semaphore - VkFence fence - u32 deviceMask -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -class VkDeviceGroupPresentCapabilitiesKHR { - VkStructureType sType - const void* pNext - u32[VK_MAX_DEVICE_GROUP_SIZE] presentMask - VkDeviceGroupPresentModeFlagsKHR modes -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -class VkDeviceGroupPresentInfoKHR { - VkStructureType sType - const void* pNext - u32 swapchainCount - const u32* pDeviceMasks - VkDeviceGroupPresentModeFlagBitsKHR mode -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -class VkDeviceGroupSwapchainCreateInfoKHR { - VkStructureType sType - const void* pNext - VkDeviceGroupPresentModeFlagsKHR modes -} - -@extension("VK_KHR_display") // 3 -class VkDisplayPropertiesKHR { - VkDisplayKHR display - const char* displayName - VkExtent2D physicalDimensions - VkExtent2D physicalResolution - VkSurfaceTransformFlagsKHR supportedTransforms - VkBool32 planeReorderPossible - VkBool32 persistentContent -} - -@extension("VK_KHR_display") // 3 -class VkDisplayModeParametersKHR { - VkExtent2D visibleRegion - u32 refreshRate -} - -@extension("VK_KHR_display") // 3 -class VkDisplayModePropertiesKHR { - VkDisplayModeKHR displayMode - VkDisplayModeParametersKHR parameters -} - -@extension("VK_KHR_display") // 3 -class VkDisplayModeCreateInfoKHR { - VkStructureType sType - const void* pNext - VkDisplayModeCreateFlagsKHR flags - VkDisplayModeParametersKHR parameters -} - -@extension("VK_KHR_display") // 3 -class VkDisplayPlanePropertiesKHR { - VkDisplayKHR currentDisplay - u32 currentStackIndex -} - -@extension("VK_KHR_display") // 3 -class VkDisplayPlaneCapabilitiesKHR { - VkDisplayPlaneAlphaFlagsKHR supportedAlpha - VkOffset2D minSrcPosition - VkOffset2D maxSrcPosition - VkExtent2D minSrcExtent - VkExtent2D maxSrcExtent - VkOffset2D minDstPosition - VkOffset2D maxDstPosition - VkExtent2D minDstExtent - VkExtent2D maxDstExtent -} - -@extension("VK_KHR_display") // 3 -class VkDisplaySurfaceCreateInfoKHR { - VkStructureType sType - const void* pNext - VkDisplaySurfaceCreateFlagsKHR flags - VkDisplayModeKHR displayMode - u32 planeIndex - u32 planeStackIndex - VkSurfaceTransformFlagBitsKHR transform - f32 globalAlpha - VkDisplayPlaneAlphaFlagBitsKHR alphaMode - VkExtent2D imageExtent -} - -@extension("VK_KHR_display_swapchain") // 4 -class VkDisplayPresentInfoKHR { - VkStructureType sType - const void* pNext - VkRect2D srcRect - VkRect2D dstRect - VkBool32 persistent -} - -@extension("VK_KHR_xlib_surface") // 5 -class VkXlibSurfaceCreateInfoKHR { - VkStructureType sType - const void* pNext - VkXlibSurfaceCreateFlagsKHR flags - platform.Display* dpy - platform.Window window -} - -@extension("VK_KHR_xcb_surface") // 6 -class VkXcbSurfaceCreateInfoKHR { - VkStructureType sType - const void* pNext - VkXcbSurfaceCreateFlagsKHR flags - platform.xcb_connection_t* connection - platform.xcb_window_t window -} - -@extension("VK_KHR_wayland_surface") // 7 -class VkWaylandSurfaceCreateInfoKHR { - VkStructureType sType - const void* pNext - VkWaylandSurfaceCreateFlagsKHR flags - platform.wl_display* display - platform.wl_surface* surface -} - -@extension("VK_KHR_android_surface") // 9 -class VkAndroidSurfaceCreateInfoKHR { - VkStructureType sType - const void* pNext - VkAndroidSurfaceCreateFlagsKHR flags - platform.ANativeWindow* window -} - -@extension("VK_KHR_win32_surface") // 10 -class VkWin32SurfaceCreateInfoKHR { - VkStructureType sType - const void* pNext - VkWin32SurfaceCreateFlagsKHR flags - platform.HINSTANCE hinstance - platform.HWND hwnd -} - -@extension("VK_ANDROID_native_buffer") // 11 -@internal class Gralloc1Usage { - u64 consumer - u64 producer -} - -@extension("VK_ANDROID_native_buffer") // 11 -class VkNativeBufferANDROID { - VkStructureType sType - const void* pNext - platform.buffer_handle_t handle - s32 stride - s32 format - s32 usage - Gralloc1Usage usage2 -} - -@extension("VK_ANDROID_native_buffer") // 11 -class VkSwapchainImageCreateInfoANDROID { - VkStructureType sType - const void* pNext - VkSwapchainImageUsageFlagsANDROID flags -} - -@extension("VK_ANDROID_native_buffer") // 11 -class VkPhysicalDevicePresentationPropertiesANDROID { - VkStructureType sType - void* pNext - VkBool32 sharedImage -} - -@extension("VK_EXT_debug_report") // 12 -class VkDebugReportCallbackCreateInfoEXT { - VkStructureType sType - const void* pNext - VkDebugReportFlagsEXT flags - PFN_vkDebugReportCallbackEXT pfnCallback - void* pUserData -} - -@extension("VK_AMD_rasterization_order") // 19 -class VkPipelineRasterizationStateRasterizationOrderAMD { - VkStructureType sType - const void* pNext - VkRasterizationOrderAMD rasterizationOrder -} - -@extension("VK_EXT_debug_marker") // 23 -class VkDebugMarkerObjectNameInfoEXT { - VkStructureType sType - const void* pNext - VkDebugReportObjectTypeEXT objectType - u64 object - const char* pObjectName -} - -@extension("VK_EXT_debug_marker") // 23 -class VkDebugMarkerObjectTagInfoEXT { - VkStructureType sType - const void* pNext - VkDebugReportObjectTypeEXT objectType - u64 object - u64 tagName - platform.size_t tagSize - const void* pTag -} - -@extension("VK_EXT_debug_marker") // 23 -class VkDebugMarkerMarkerInfoEXT { - VkStructureType sType - const void* pNext - const char* pMarkerName - f32[4] color -} - -@extension("VK_NV_dedicated_allocation") // 27 -class VkDedicatedAllocationImageCreateInfoNV { - VkStructureType sType - const void* pNext - VkBool32 dedicatedAllocation -} - -@extension("VK_NV_dedicated_allocation") // 27 -class VkDedicatedAllocationBufferCreateInfoNV { - VkStructureType sType - const void* pNext - VkBool32 dedicatedAllocation -} - -@extension("VK_NV_dedicated_allocation") // 27 -class VkDedicatedAllocationMemoryAllocateInfoNV { - VkStructureType sType - const void* pNext - VkImage image - VkBuffer buffer -} - -@extension("VK_EXT_transform_feedback") // 29 -class VkPhysicalDeviceTransformFeedbackFeaturesEXT { - VkStructureType sType - void* pNext - VkBool32 transformFeedback - VkBool32 geometryStreams -} - -@extension("VK_EXT_transform_feedback") // 29 -class VkPhysicalDeviceTransformFeedbackPropertiesEXT { - VkStructureType sType - void* pNext - u32 maxTransformFeedbackStreams - u32 maxTransformFeedbackBuffers - VkDeviceSize maxTransformFeedbackBufferSize - u32 maxTransformFeedbackStreamDataSize - u32 maxTransformFeedbackBufferDataSize - u32 maxTransformFeedbackBufferDataStride - VkBool32 transformFeedbackQueries - VkBool32 transformFeedbackStreamsLinesTriangles - VkBool32 transformFeedbackRasterizationStreamSelect - VkBool32 transformFeedbackDraw -} - -@extension("VK_EXT_transform_feedback") // 29 -class VkPipelineRasterizationStateStreamCreateInfoEXT { - VkStructureType sType - const void* pNext - VkPipelineRasterizationStateStreamCreateFlagsEXT flags - u32 rasterizationStream -} - -@extension("VK_AMD_texture_gather_bias_lod") // 42 -class VkTextureLODGatherFormatPropertiesAMD { - VkStructureType sType - void* pNext - VkBool32 supportsTextureGatherLODBiasAMD -} - -@extension("VK_AMD_shader_info") // 43 -class VkShaderResourceUsageAMD { - u32 numUsedVgprs - u32 numUsedSgprs - u32 ldsSizePerLocalWorkGroup - platform.size_t ldsUsageSizeInBytes - platform.size_t scratchMemUsageInBytes -} - -@extension("VK_AMD_shader_info") // 43 -class VkShaderStatisticsInfoAMD { - VkShaderStageFlags shaderStageMask - VkShaderResourceUsageAMD resourceUsage - u32 numPhysicalVgprs - u32 numPhysicalSgprs - u32 numAvailableVgprs - u32 numAvailableSgprs - u32[3] computeWorkGroupSize -} - -@extension("VK_NV_corner_sampled_image") // 51 -class VkPhysicalDeviceCornerSampledImageFeaturesNV { - VkStructureType sType - void* pNext - VkBool32 cornerSampledImage -} - -@extension("VK_KHR_multiview") // 54 -class VkRenderPassMultiviewCreateInfoKHR { - VkStructureType sType - const void* pNext - u32 subpassCount - const u32* pViewMasks - u32 dependencyCount - const s32* pViewOffsets - u32 correlationMaskCount - const u32* pCorrelationMasks -} - -@extension("VK_KHR_multiview") // 54 -class VkPhysicalDeviceMultiviewFeaturesKHR { - VkStructureType sType - void* pNext - VkBool32 multiview - VkBool32 multiviewGeometryShader - VkBool32 multiviewTessellationShader -} - -@extension("VK_KHR_multiview") // 54 -class VkPhysicalDeviceMultiviewPropertiesKHR { - VkStructureType sType - void* pNext - u32 maxMultiviewViewCount - u32 maxMultiviewInstanceIndex -} - -@extension("VK_NV_external_memory_capabilities") // 56 -class VkExternalImageFormatPropertiesNV { - VkImageFormatProperties imageFormatProperties - VkExternalMemoryFeatureFlagsNV externalMemoryFeatures - VkExternalMemoryHandleTypeFlagsNV exportFromImportedHandleTypes - VkExternalMemoryHandleTypeFlagsNV compatibleHandleTypes -} - -@extension("VK_NV_external_memory") // 57 -class VkExternalMemoryImageCreateInfoNV { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagsNV handleTypes -} - -@extension("VK_NV_external_memory") // 57 -class VkExportMemoryAllocateInfoNV { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagsNV handleTypes -} - -@extension("VK_NV_external_memory_win32") // 58 -class VkImportMemoryWin32HandleInfoNV { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagsNV handleType - platform.HANDLE handle -} - -@extension("VK_NV_external_memory_win32") // 58 -class VkExportMemoryWin32HandleInfoNV { - VkStructureType sType - const void* pNext - const platform.SECURITY_ATTRIBUTES* pAttributes - platform.DWORD dwAccess -} - -@extension("VK_NV_win32_keyed_mutex") // 59 -class VkWin32KeyedMutexAcquireReleaseInfoNV { - VkStructureType sType - const void* pNext - u32 acquireCount - const VkDeviceMemory* pAcquireSyncs - const u64* pAcquireKeys - const u32* pAcquireTimeoutMilliseconds - u32 releaseCount - const VkDeviceMemory* pReleaseSyncs - const u64* pReleaseKeys -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -class VkPhysicalDeviceFeatures2KHR { - VkStructureType sType - void* pNext - VkPhysicalDeviceFeatures features -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -class VkPhysicalDeviceProperties2KHR { - VkStructureType sType - void* pNext - VkPhysicalDeviceProperties properties -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -class VkFormatProperties2KHR { - VkStructureType sType - void* pNext - VkFormatProperties formatProperties -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -class VkImageFormatProperties2KHR { - VkStructureType sType - void* pNext - VkImageFormatProperties imageFormatProperties -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -class VkPhysicalDeviceImageFormatInfo2KHR { - VkStructureType sType - const void* pNext - VkFormat format - VkImageType type - VkImageTiling tiling - VkImageUsageFlags usage - VkImageCreateFlags flags -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -class VkQueueFamilyProperties2KHR { - VkStructureType sType - void* pNext - VkQueueFamilyProperties queueFamilyProperties -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -class VkPhysicalDeviceMemoryProperties2KHR { - VkStructureType sType - void* pNext - VkPhysicalDeviceMemoryProperties memoryProperties -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -class VkSparseImageFormatProperties2KHR { - VkStructureType sType - void* pNext - VkSparseImageFormatProperties properties -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -class VkPhysicalDeviceSparseImageFormatInfo2KHR { - VkStructureType sType - const void* pNext - VkFormat format - VkImageType type - VkSampleCountFlagBits samples - VkImageUsageFlags usage - VkImageTiling tiling -} - -@extension("VK_KHR_device_group") // 61 -class VkMemoryAllocateFlagsInfoKHR { - VkStructureType sType - const void* pNext - VkMemoryAllocateFlagsKHR flags - u32 deviceMask -} - -@extension("VK_KHR_device_group") // 61 -class VkBindBufferMemoryDeviceGroupInfoKHR { - VkStructureType sType - const void* pNext - u32 deviceIndexCount - const u32* pDeviceIndices -} - -@extension("VK_KHR_device_group") // 61 -class VkBindImageMemoryDeviceGroupInfoKHR { - VkStructureType sType - const void* pNext - u32 deviceIndexCount - const u32* pDeviceIndices - u32 SFRRectCount - const VkRect2D* pSFRRects -} - -@extension("VK_KHR_device_group") // 61 -class VkDeviceGroupRenderPassBeginInfoKHR { - VkStructureType sType - const void* pNext - u32 deviceMask - u32 deviceRenderAreaCount - const VkRect2D* pDeviceRenderAreas -} - -@extension("VK_KHR_device_group") // 61 -class VkDeviceGroupCommandBufferBeginInfoKHR { - VkStructureType sType - const void* pNext - u32 deviceMask -} - -@extension("VK_KHR_device_group") // 61 -class VkDeviceGroupSubmitInfoKHR { - VkStructureType sType - const void* pNext - u32 waitSemaphoreCount - const u32* pWaitSemaphoreDeviceIndices - u32 commandBufferCount - const u32* pCommandBufferDeviceMasks - u32 signalSemaphoreCount - const u32* pSignalSemaphoreDeviceIndices -} - -@extension("VK_KHR_device_group") // 61 -class VkDeviceGroupBindSparseInfoKHR { - VkStructureType sType - const void* pNext - u32 resourceDeviceIndex - u32 memoryDeviceIndex -} - -@extension("VK_EXT_validation_flags") // 62 -class VkValidationFlagsEXT { - VkStructureType sType - const void* pNext - u32 disabledValidationCheckCount - const VkValidationCheckEXT* pDisabledValidationChecks -} - -@extension("VK_NN_vi_surface") // 63 -class VkViSurfaceCreateInfoNN { - VkStructureType sType - const void* pNext - VkViSurfaceCreateFlagsNN flags - void* window -} - -@extension("VK_EXT_astc_decode_mode") // 68 -class VkImageViewASTCDecodeModeEXT { - VkStructureType sType - const void* pNext - VkFormat decodeMode -} - -@extension("VK_EXT_astc_decode_mode") // 68 -class VkPhysicalDeviceASTCDecodeFeaturesEXT { - VkStructureType sType - void* pNext - VkBool32 decodeModeSharedExponent -} - -@extension("VK_KHR_device_group_creation") // 71 -class VkPhysicalDeviceGroupPropertiesKHR { - VkStructureType sType - void* pNext - u32 physicalDeviceCount - VkPhysicalDevice[VK_MAX_DEVICE_GROUP_SIZE] physicalDevices - VkBool32 subsetAllocation -} - -@extension("VK_KHR_device_group_creation") // 71 -class VkDeviceGroupDeviceCreateInfoKHR { - VkStructureType sType - const void* pNext - u32 physicalDeviceCount - const VkPhysicalDevice* pPhysicalDevices -} - -@extension("VK_KHR_external_memory_capabilities") // 72 -class VkExternalMemoryPropertiesKHR { - VkExternalMemoryFeatureFlagsKHR externalMemoryFeatures - VkExternalMemoryHandleTypeFlagsKHR exportFromImportedHandleTypes - VkExternalMemoryHandleTypeFlagsKHR compatibleHandleTypes -} - -@extension("VK_KHR_external_memory_capabilities") // 72 -class VkPhysicalDeviceExternalImageFormatInfoKHR { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagBitsKHR handleType -} - -@extension("VK_KHR_external_memory_capabilities") // 72 -class VkExternalImageFormatPropertiesKHR { - VkStructureType sType - void* pNext - VkExternalMemoryPropertiesKHR externalMemoryProperties -} - -@extension("VK_KHR_external_memory_capabilities") // 72 -class VkPhysicalDeviceExternalBufferInfoKHR { - VkStructureType sType - const void* pNext - VkBufferCreateFlags flags - VkBufferUsageFlags usage - VkExternalMemoryHandleTypeFlagBitsKHR handleType -} - -@extension("VK_KHR_external_memory_capabilities") // 72 -class VkExternalBufferPropertiesKHR { - VkStructureType sType - void* pNext - VkExternalMemoryPropertiesKHR externalMemoryProperties -} - -@extension("VK_KHR_external_memory_capabilities") // 72 -class VkPhysicalDeviceIDPropertiesKHR { - VkStructureType sType - void* pNext - u8[VK_UUID_SIZE] deviceUUID - u8[VK_UUID_SIZE] driverUUID - u8[VK_LUID_SIZE] deviceLUID - u32 deviceNodeMask - VkBool32 deviceLUIDValid -} - -@extension("VK_KHR_external_memory") // 73 -class VkExternalMemoryImageCreateInfoKHR { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagsKHR handleTypes -} - -@extension("VK_KHR_external_memory") // 73 -class VkExternalMemoryBufferCreateInfoKHR { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagsKHR handleTypes -} - -@extension("VK_KHR_external_memory") // 73 -class VkExportMemoryAllocateInfoKHR { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagsKHR handleTypes -} - -@extension("VK_KHR_external_memory_win32") // 74 -class VkImportMemoryWin32HandleInfoKHR { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagBitsKHR handleType - platform.HANDLE handle - platform.LPCWSTR name -} - -@extension("VK_KHR_external_memory_win32") // 74 -class VkExportMemoryWin32HandleInfoKHR { - VkStructureType sType - const void* pNext - const platform.SECURITY_ATTRIBUTES* pAttributes - platform.DWORD dwAccess - platform.LPCWSTR name -} - -@extension("VK_KHR_external_memory_win32") // 74 -class VkMemoryWin32HandlePropertiesKHR { - VkStructureType sType - void* pNext - u32 memoryTypeBits -} - -@extension("VK_KHR_external_memory_win32") // 74 -class VkMemoryGetWin32HandleInfoKHR { - VkStructureType sType - void* pNext - VkDeviceMemory memory - VkExternalMemoryHandleTypeFlagBitsKHR handleType -} - -@extension("VK_KHR_external_memory_fd") // 75 -class VkImportMemoryFdInfoKHR { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagBitsKHR handleType - int fd -} - -@extension("VK_KHR_external_memory_fd") // 75 -class VkMemoryFdPropertiesKHR { - VkStructureType sType - void* pNext - u32 memoryTypeBits -} - -@extension("VK_KHR_external_memory_fd") // 75 -class VkMemoryGetFdInfoKHR { - VkStructureType sType - void* pNext - VkDeviceMemory memory - VkExternalMemoryHandleTypeFlagBitsKHR handleType -} - -@extension("VK_KHR_win32_keyed_mutex") // 76 -class VkWin32KeyedMutexAcquireReleaseInfoKHR { - VkStructureType sType - const void* pNext - u32 acquireCount - const VkDeviceMemory* pAcquireSyncs - const u64* pAcquireKeys - const u32* pAcquireTimeouts - u32 releaseCount - const VkDeviceMemory* pReleaseSyncs - const u64* pReleaseKeys -} - -@extension("VK_KHR_external_semaphore_capabilities") // 77 -class VkPhysicalDeviceExternalSemaphoreInfoKHR { - VkStructureType sType - const void* pNext - VkExternalSemaphoreHandleTypeFlagBitsKHR handleType -} - -@extension("VK_KHR_external_semaphore_capabilities") // 77 -class VkExternalSemaphorePropertiesKHR { - VkStructureType sType - void* pNext - VkExternalSemaphoreHandleTypeFlagsKHR exportFromImportedHandleTypes - VkExternalSemaphoreHandleTypeFlagsKHR compatibleHandleTypes - VkExternalSemaphoreFeatureFlagsKHR externalSemaphoreFeatures -} - -@extension("VK_KHR_external_semaphore") // 78 -class VkExportSemaphoreCreateInfoKHR { - VkStructureType sType - const void* pNext - VkExternalSemaphoreHandleTypeFlagsKHR handleTypes -} - -@extension("VK_KHR_external_semaphore_win32") // 79 -class VkImportSemaphoreWin32HandleInfoKHR { - VkStructureType sType - const void* pNext - VkSemaphore semaphore - VkSemaphoreImportFlagsKHR flags - VkExternalSemaphoreHandleTypeFlagsKHR handleType - platform.HANDLE handle - platform.LPCWSTR name -} - -@extension("VK_KHR_external_semaphore_win32") // 79 -class VkExportSemaphoreWin32HandleInfoKHR { - VkStructureType sType - const void* pNext - const platform.SECURITY_ATTRIBUTES* pAttributes - platform.DWORD dwAccess - platform.LPCWSTR name -} - -@extension("VK_KHR_external_semaphore_win32") // 79 -class VkD3D12FenceSubmitInfoKHR { - VkStructureType sType - const void* pNext - u32 waitSemaphoreValuesCount - const u64* pWaitSemaphoreValues - u32 signalSemaphoreValuesCount - const u64* pSignalSemaphoreValues -} - -@extension("VK_KHR_external_semaphore_win32") // 79 -class VkSemaphoreGetWin32HandleInfoKHR { - VkStructureType sType - const void* pNext - VkSemaphore semaphore - VkExternalSemaphoreHandleTypeFlagBitsKHR handleType -} - -@extension("VK_KHR_external_semaphore_fd") // 80 -class VkImportSemaphoreFdInfoKHR { - VkStructureType sType - const void* pNext - VkSemaphore semaphore - VkSemaphoreImportFlagsKHR flags - VkExternalSemaphoreHandleTypeFlagBitsKHR handleType - s32 fd -} - -@extension("VK_KHR_external_semaphore_fd") // 80 -class VkSemaphoreGetFdInfoKHR { - VkStructureType sType - const void* pNext - VkSemaphore semaphore - VkExternalSemaphoreHandleTypeFlagBitsKHR handleType -} - -@extension("VK_KHR_push_descriptor") // 81 -class VkPhysicalDevicePushDescriptorPropertiesKHR { - VkStructureType sType - void* pNext - u32 maxPushDescriptors -} - -@extension("VK_EXT_conditional_rendering") // 82 -class VkConditionalRenderingBeginInfoEXT { - VkStructureType sType - const void* pNext - VkBuffer buffer - VkDeviceSize offset - VkConditionalRenderingFlagsEXT flags -} - -@extension("VK_EXT_conditional_rendering") // 82 -class VkPhysicalDeviceConditionalRenderingFeaturesEXT { - VkStructureType sType - void* pNext - VkBool32 conditionalRendering - VkBool32 inheritedConditionalRendering -} - -@extension("VK_EXT_conditional_rendering") // 82 -class VkCommandBufferInheritanceConditionalRenderingInfoEXT { - VkStructureType sType - const void* pNext - VkBool32 conditionalRenderingEnable -} - -@extension("VK_KHR_shader_float16_int8") // 83 -class VkPhysicalDeviceFloat16Int8FeaturesKHR { - VkStructureType sType - void* pNext - VkBool32 shaderFloat16 - VkBool32 shaderInt8 -} - -@extension("VK_KHR_16bit_storage") // 84 -class VkPhysicalDevice16BitStorageFeaturesKHR { - VkStructureType sType - void* pNext - VkBool32 storageBuffer16BitAccess - VkBool32 uniformAndStorageBuffer16BitAccess - VkBool32 storagePushConstant16 - VkBool32 storageInputOutput16 -} - -@extension("VK_KHR_incremental_present") // 85 -class VkRectLayerKHR { - VkOffset2D offset - VkExtent2D extent - u32 layer -} - -@extension("VK_KHR_incremental_present") // 85 -class VkPresentRegionKHR { - u32 rectangleCount - const VkRectLayerKHR* pRectangles -} - -@extension("VK_KHR_incremental_present") // 85 -class VkPresentRegionsKHR { - VkStructureType sType - const void* pNext - u32 swapchainCount - const VkPresentRegionKHR* pRegions -} - -@extension("VK_KHR_descriptor_update_template") // 86 -class VkDescriptorUpdateTemplateEntryKHR { - u32 dstBinding - u32 dstArrayElement - u32 descriptorCount - VkDescriptorType descriptorType - platform.size_t offset - platform.size_t stride -} - -@extension("VK_KHR_descriptor_update_template") // 86 -class VkDescriptorUpdateTemplateCreateInfoKHR { - VkStructureType sType - void* pNext - VkDescriptorUpdateTemplateCreateFlagsKHR flags - u32 descriptorUpdateEntryCount - const VkDescriptorUpdateTemplateEntryKHR* pDescriptorUpdateEntries - VkDescriptorUpdateTemplateTypeKHR templateType - VkDescriptorSetLayout descriptorSetLayout - VkPipelineBindPoint pipelineBindPoint - VkPipelineLayout pipelineLayout - u32 set -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkDeviceGeneratedCommandsFeaturesNVX { - VkStructureType sType - const void* pNext - VkBool32 computeBindingPointSupport -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkDeviceGeneratedCommandsLimitsNVX { - VkStructureType sType - const void* pNext - u32 maxIndirectCommandsLayoutTokenCount - u32 maxObjectEntryCounts - u32 minSequenceCountBufferOffsetAlignment - u32 minSequenceIndexBufferOffsetAlignment - u32 minCommandsTokenBufferOffsetAlignment -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkIndirectCommandsTokenNVX { - VkIndirectCommandsTokenTypeNVX tokenType - VkBuffer buffer - VkDeviceSize offset -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkIndirectCommandsLayoutTokenNVX { - VkIndirectCommandsTokenTypeNVX tokenType - u32 bindingUnit - u32 dynamicCount - u32 divisor -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkIndirectCommandsLayoutCreateInfoNVX { - VkStructureType sType - const void* pNext - VkPipelineBindPoint pipelineBindPoint - VkIndirectCommandsLayoutUsageFlagsNVX flags - u32 tokenCount - const VkIndirectCommandsLayoutTokenNVX* pTokens -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkCmdProcessCommandsInfoNVX { - VkStructureType sType - const void* pNext - VkObjectTableNVX objectTable - VkIndirectCommandsLayoutNVX indirectCommandsLayout - u32 indirectCommandsTokenCount - const VkIndirectCommandsTokenNVX* pIndirectCommandsTokens - u32 maxSequencesCount - VkCommandBuffer targetCommandBuffer - VkBuffer sequencesCountBuffer - VkDeviceSize sequencesCountOffset - VkBuffer sequencesIndexBuffer - VkDeviceSize sequencesIndexOffset -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkCmdReserveSpaceForCommandsInfoNVX { - VkStructureType sType - const void* pNext - VkObjectTableNVX objectTable - VkIndirectCommandsLayoutNVX indirectCommandsLayout - u32 maxSequencesCount -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkObjectTableCreateInfoNVX { - VkStructureType sType - const void* pNext - u32 objectCount - const VkObjectEntryTypeNVX* pObjectEntryTypes - const u32* pObjectEntryCounts - const VkObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags - u32 maxUniformBuffersPerDescriptor - u32 maxStorageBuffersPerDescriptor - u32 maxStorageImagesPerDescriptor - u32 maxSampledImagesPerDescriptor - u32 maxPipelineLayouts -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkObjectTableEntryNVX { - VkObjectEntryTypeNVX type - VkObjectEntryUsageFlagsNVX flags -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkObjectTablePipelineEntryNVX { - VkObjectEntryTypeNVX type - VkObjectEntryUsageFlagsNVX flags - VkPipeline pipeline -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkObjectTableDescriptorSetEntryNVX { - VkObjectEntryTypeNVX type - VkObjectEntryUsageFlagsNVX flags - VkPipelineLayout pipelineLayout - VkDescriptorSet descriptorSet -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkObjectTableVertexBufferEntryNVX { - VkObjectEntryTypeNVX type - VkObjectEntryUsageFlagsNVX flags - VkBuffer buffer -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkObjectTableIndexBufferEntryNVX { - VkObjectEntryTypeNVX type - VkObjectEntryUsageFlagsNVX flags - VkBuffer buffer - VkIndexType indexType -} - -@extension("VK_NVX_device_generated_commands") // 87 -class VkObjectTablePushConstantEntryNVX { - VkObjectEntryTypeNVX type - VkObjectEntryUsageFlagsNVX flags - VkPipelineLayout pipelineLayout - VkShaderStageFlags stageFlags -} - -@extension("VK_NV_clip_space_w_scaling") // 88 -class VkViewportWScalingNV { - f32 xcoeff - f32 ycoeff -} - -@extension("VK_NV_clip_space_w_scaling") // 88 -class VkPipelineViewportWScalingStateCreateInfoNV { - VkStructureType sType - const void* pNext - VkBool32 viewportWScalingEnable - u32 viewportCount - const VkViewportWScalingNV* pViewportWScalings -} - -@extension("VK_EXT_display_surface_counter") // 91 -class VkSurfaceCapabilities2EXT { - VkStructureType sType - void* pNext - u32 minImageCount - u32 maxImageCount - VkExtent2D currentExtent - VkExtent2D minImageExtent - VkExtent2D maxImageExtent - u32 maxImageArrayLayers - VkSurfaceTransformFlagsKHR supportedTransforms - VkSurfaceTransformFlagBitsKHR currentTransform - VkCompositeAlphaFlagsKHR supportedCompositeAlpha - VkImageUsageFlags supportedUsageFlags - VkSurfaceCounterFlagsEXT supportedSurfaceCounters -} - -@extension("VK_EXT_display_control") // 92 -class VkDisplayPowerInfoEXT { - VkStructureType sType - const void* pNext - VkDisplayPowerStateEXT powerState -} - -@extension("VK_EXT_display_control") // 92 -class VkDeviceEventInfoEXT { - VkStructureType sType - const void* pNext - VkDeviceEventTypeEXT deviceEvent -} - -@extension("VK_EXT_display_control") // 92 -class VkDisplayEventInfoEXT { - VkStructureType sType - const void* pNext - VkDisplayEventTypeEXT displayEvent -} - -@extension("VK_EXT_display_control") // 92 -class VkSwapchainCounterCreateInfoEXT { - VkStructureType sType - const void* pNext - VkSurfaceCounterFlagsEXT surfaceCounters -} - -@extension("VK_GOOGLE_display_timing") // 93 -class VkRefreshCycleDurationGOOGLE { - u64 refreshDuration -} - -@extension("VK_GOOGLE_display_timing") // 93 -class VkPastPresentationTimingGOOGLE { - u32 presentID - u64 desiredPresentTime - u64 actualPresentTime - u64 earliestPresentTime - u64 presentMargin -} - -@extension("VK_GOOGLE_display_timing") // 93 -class VkPresentTimeGOOGLE { - u32 presentID - u64 desiredPresentTime -} - -@extension("VK_GOOGLE_display_timing") // 93 -class VkPresentTimesInfoGOOGLE { - VkStructureType sType - const void* pNext - u32 swapchainCount - const VkPresentTimeGOOGLE* pTimes -} - -@extension("VK_NVX_multiview_per_view_attributes") // 98 -class VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX { - VkStructureType sType - void* pNext - VkBool32 perViewPositionAllComponents -} - -@extension("VK_NV_viewport_swizzle") // 99 -class VkViewportSwizzleNV { - VkViewportCoordinateSwizzleNV x - VkViewportCoordinateSwizzleNV y - VkViewportCoordinateSwizzleNV z - VkViewportCoordinateSwizzleNV w -} - -@extension("VK_NV_viewport_swizzle") // 99 -class VkPipelineViewportSwizzleStateCreateInfoNV { - VkStructureType sType - const void* pNext - VkPipelineViewportSwizzleStateCreateFlagsNV flags - u32 viewportCount - const VkViewportSwizzleNV* pViewportSwizzles -} - -@extension("VK_EXT_discard_rectangles") // 100 -class VkPhysicalDeviceDiscardRectanglePropertiesEXT { - VkStructureType sType - void* pNext - u32 maxDiscardRectangles -} - -@extension("VK_EXT_discard_rectangles") // 100 -class VkPipelineDiscardRectangleStateCreateInfoEXT { - VkStructureType sType - const void* pNext - VkPipelineDiscardRectangleStateCreateFlagsEXT flags - VkDiscardRectangleModeEXT discardRectangleMode - u32 discardRectangleCount - const VkRect2D* pDiscardRectangles -} - -@extension("VK_EXT_conservative_rasterization") // 102 -class VkPhysicalDeviceConservativeRasterizationPropertiesEXT { - VkStructureType sType - void* pNext - f32 primitiveOverestimationSize - f32 maxExtraPrimitiveOverestimationSize - f32 extraPrimitiveOverestimationSizeGranularity - VkBool32 primitiveUnderestimation - VkBool32 conservativePointAndLineRasterization - VkBool32 degenerateTrianglesRasterized - VkBool32 degenerateLinesRasterized - VkBool32 fullyCoveredFragmentShaderInputVariable - VkBool32 conservativeRasterizationPostDepthCoverage -} - -@extension("VK_EXT_conservative_rasterization") // 102 -class VkPipelineRasterizationConservativeStateCreateInfoEXT { - VkStructureType sType - const void* pNext - VkPipelineRasterizationConservativeStateCreateFlagsEXT flags - VkConservativeRasterizationModeEXT conservativeRasterizationMode - f32 extraPrimitiveOverestimationSize -} - -@extension("VK_EXT_hdr_metadata") // 106 -class VkXYColorEXT { - f32 x - f32 y -} - -@extension("VK_EXT_hdr_metadata") // 106 -class VkHdrMetadataEXT { - VkStructureType sType - const void* pNext - VkXYColorEXT displayPrimaryRed - VkXYColorEXT displayPrimaryGreen - VkXYColorEXT displayPrimaryBlue - VkXYColorEXT whitePoint - f32 maxLuminance - f32 minLuminance - f32 maxContentLightLevel - f32 maxFrameAverageLightLevel -} - -@extension("VK_KHR_create_renderpass2") // 110 -class VkAttachmentDescription2KHR { - VkStructureType sType - const void* pNext - VkAttachmentDescriptionFlags flags - VkFormat format - VkSampleCountFlagBits samples - VkAttachmentLoadOp loadOp - VkAttachmentStoreOp storeOp - VkAttachmentLoadOp stencilLoadOp - VkAttachmentStoreOp stencilStoreOp - VkImageLayout initialLayout - VkImageLayout finalLayout -} - -@extension("VK_KHR_create_renderpass2") // 110 -class VkAttachmentReference2KHR { - VkStructureType sType - const void* pNext - u32 attachment - VkImageLayout layout - VkImageAspectFlags aspectMask -} - -@extension("VK_KHR_create_renderpass2") // 110 -class VkSubpassDescription2KHR { - VkStructureType sType - const void* pNext - VkSubpassDescriptionFlags flags - VkPipelineBindPoint pipelineBindPoint - u32 viewMask - u32 inputAttachmentCount - const VkAttachmentReference2KHR* pInputAttachments - u32 colorAttachmentCount - const VkAttachmentReference2KHR* pColorAttachments - const VkAttachmentReference2KHR* pResolveAttachments - const VkAttachmentReference2KHR* pDepthStencilAttachment - u32 preserveAttachmentCount - const u32* pPreserveAttachments -} - -@extension("VK_KHR_create_renderpass2") // 110 -class VkSubpassDependency2KHR { - VkStructureType sType - const void* pNext - u32 srcSubpass - u32 dstSubpass - VkPipelineStageFlags srcStageMask - VkPipelineStageFlags dstStageMask - VkAccessFlags srcAccessMask - VkAccessFlags dstAccessMask - VkDependencyFlags dependencyFlags - s32 viewOffset -} - -@extension("VK_KHR_create_renderpass2") // 110 -class VkRenderPassCreateInfo2KHR { - VkStructureType sType - const void* pNext - VkRenderPassCreateFlags flags - u32 attachmentCount - const VkAttachmentDescription2KHR* pAttachments - u32 subpassCount - const VkSubpassDescription2KHR* pSubpasses - u32 dependencyCount - const VkSubpassDependency2KHR* pDependencies - u32 correlatedViewMaskCount - const u32* pCorrelatedViewMasks -} - -@extension("VK_KHR_create_renderpass2") // 110 -class VkSubpassBeginInfoKHR { - VkStructureType sType - const void* pNext - VkSubpassContents contents -} - -@extension("VK_KHR_create_renderpass2") // 110 -class VkSubpassEndInfoKHR { - VkStructureType sType - const void* pNext -} - -@extension("VK_KHR_shared_presentable_image") // 112 -class VkSharedPresentSurfaceCapabilitiesKHR { - VkStructureType sType - const void* pNext - VkImageUsageFlags sharedPresentSupportedUsageFlags -} - -@extension("VK_KHR_external_fence_capabilities") // 113 -class VkPhysicalDeviceExternalFenceInfoKHR { - VkStructureType sType - const void* pNext - VkExternalFenceHandleTypeFlagBitsKHR handleType -} - -@extension("VK_KHR_external_fence_capabilities") // 113 -class VkExternalFencePropertiesKHR { - VkStructureType sType - void* pNext - VkExternalFenceHandleTypeFlagsKHR exportFromImportedHandleTypes - VkExternalFenceHandleTypeFlagsKHR compatibleHandleTypes - VkExternalFenceFeatureFlagsKHR externalFenceFeatures -} - -@extension("VK_KHR_external_fence") // 114 -class VkExportFenceCreateInfoKHR { - VkStructureType sType - const void* pNext - VkExternalFenceHandleTypeFlagsKHR handleTypes -} - -@extension("VK_KHR_external_fence_win32") // 115 -class VkImportFenceWin32HandleInfoKHR { - VkStructureType sType - const void* pNext - VkFence fence - VkFenceImportFlagsKHR flags - VkExternalFenceHandleTypeFlagBitsKHR handleType - platform.HANDLE handle - platform.LPCWSTR name -} - -@extension("VK_KHR_external_fence_win32") // 115 -class VkExportFenceWin32HandleInfoKHR { - VkStructureType sType - const void* pNext - const platform.SECURITY_ATTRIBUTES* pAttributes - platform.DWORD dwAccess - platform.LPCWSTR name -} - -@extension("VK_KHR_external_fence_win32") // 115 -class VkFenceGetWin32HandleInfoKHR { - VkStructureType sType - const void* pNext - VkFence fence - VkExternalFenceHandleTypeFlagBitsKHR handleType -} - -@extension("VK_KHR_external_fence_fd") // 116 -class VkImportFenceFdInfoKHR { - VkStructureType sType - const void* pNext - VkFence fence - VkFenceImportFlagsKHR flags - VkExternalFenceHandleTypeFlagBitsKHR handleType - int fd -} - -@extension("VK_KHR_external_fence_fd") // 116 -class VkFenceGetFdInfoKHR { - VkStructureType sType - const void* pNext - VkFence fence - VkExternalFenceHandleTypeFlagBitsKHR handleType -} - -@extension("VK_KHR_maintenance2") // 118 -class VkPhysicalDevicePointClippingPropertiesKHR { - VkStructureType sType - void* pNext - VkPointClippingBehaviorKHR pointClippingBehavior -} - -@extension("VK_KHR_maintenance2") // 118 -class VkInputAttachmentAspectReferenceKHR { - u32 subpass - u32 inputAttachmentIndex - VkImageAspectFlags aspectMask -} - -@extension("VK_KHR_maintenance2") // 118 -class VkRenderPassInputAttachmentAspectCreateInfoKHR { - VkStructureType sType - const void* pNext - u32 aspectReferenceCount - const VkInputAttachmentAspectReferenceKHR* pAspectReferences -} - -@extension("VK_KHR_maintenance2") // 118 -class VkImageViewUsageCreateInfoKHR { - VkStructureType sType - const void* pNext - VkImageUsageFlags usage -} - -@extension("VK_KHR_maintenance2") // 118 -class VkPipelineTessellationDomainOriginStateCreateInfoKHR { - VkStructureType sType - const void* pNext - VkTessellationDomainOriginKHR domainOrigin -} - -@extension("VK_KHR_get_surface_capabilities2") // 120 -class VkPhysicalDeviceSurfaceInfo2KHR { - VkStructureType sType - const void* pNext - VkSurfaceKHR surface -} - -@extension("VK_KHR_get_surface_capabilities2") // 120 -class VkSurfaceCapabilities2KHR { - VkStructureType sType - void* pNext - VkSurfaceCapabilitiesKHR surfaceCapabilities -} - -@extension("VK_KHR_get_surface_capabilities2") // 120 -class VkSurfaceFormat2KHR { - VkStructureType sType - void* pNext - VkSurfaceFormatKHR surfaceFormat -} - -@extension("VK_KHR_variable_pointers") // 121 -class VkPhysicalDeviceVariablePointerFeaturesKHR { - VkStructureType sType - void* pNext - VkBool32 variablePointersStorageBuffer - VkBool32 variablePointers -} - -@extension("VK_KHR_display_properties2") // 122 -class VkDisplayProperties2KHR { - VkStructureType sType - void* pNext - VkDisplayPropertiesKHR displayProperties -} - -@extension("VK_KHR_display_properties2") // 122 -class VkDisplayPlaneProperties2KHR { - VkStructureType sType - void* pNext - VkDisplayPlanePropertiesKHR displayPlaneProperties -} - -@extension("VK_KHR_display_properties2") // 122 -class VkDisplayModeProperties2KHR { - VkStructureType sType - void* pNext - VkDisplayModePropertiesKHR displayModeProperties -} - -@extension("VK_KHR_display_properties2") // 122 -class VkDisplayPlaneInfo2KHR { - VkStructureType sType - const void* pNext - VkDisplayModeKHR mode - u32 planeIndex -} - -@extension("VK_KHR_display_properties2") // 122 -class VkDisplayPlaneCapabilities2KHR { - VkStructureType sType - void* pNext - VkDisplayPlaneCapabilitiesKHR capabilities -} - -@extension("VK_MVK_ios_surface") // 123 -class VkIOSSurfaceCreateInfoMVK { - VkStructureType sType - const void* pNext - VkIOSSurfaceCreateFlagsMVK flags - const void* pView -} - -@extension("VK_MVK_macos_surface") // 124 -class VkMacOSSurfaceCreateInfoMVK { - VkStructureType sType - const void* pNext - VkMacOSSurfaceCreateFlagsMVK flags - const void* pView -} - -@extension("VK_KHR_dedicated_allocation") // 128 -class VkMemoryDedicatedRequirementsKHR { - VkStructureType sType - void* pNext - VkBool32 prefersDedicatedAllocation - VkBool32 requiresDedicatedAllocation -} - -@extension("VK_KHR_dedicated_allocation") // 128 -class VkMemoryDedicatedAllocateInfoKHR { - VkStructureType sType - const void* pNext - VkImage image - VkBuffer buffer -} - -@extension("VK_EXT_debug_utils") // 129 -class VkDebugUtilsObjectNameInfoEXT { - VkStructureType sType - const void* pNext - VkObjectType objectType - u64 objectHandle - const char* pObjectName -} - -@extension("VK_EXT_debug_utils") // 129 -class VkDebugUtilsObjectTagInfoEXT { - VkStructureType sType - const void* pNext - VkObjectType objectType - u64 objectHandle - u64 tagName - platform.size_t tagSize - const void* pTag -} - -@extension("VK_EXT_debug_utils") // 129 -class VkDebugUtilsLabelEXT { - VkStructureType sType - const void* pNext - const char* pLabelName - f32[4] color -} - -@extension("VK_EXT_debug_utils") // 129 -class VkDebugUtilsMessengerCallbackDataEXT { - VkStructureType sType - const void* pNext - VkDebugUtilsMessengerCallbackDataFlagsEXT flags - const char* pMessageIdName - s32 messageIdNumber - const char* pMessage - u32 queueLabelCount - const VkDebugUtilsLabelEXT* pQueueLabels - u32 cmdBufLabelCount - const VkDebugUtilsLabelEXT* pCmdBufLabels - u32 objectCount - const VkDebugUtilsObjectNameInfoEXT* pObjects -} - -@extension("VK_EXT_debug_utils") // 129 -class VkDebugUtilsMessengerCreateInfoEXT { - VkStructureType sType - const void* pNext - VkDebugUtilsMessengerCreateFlagsEXT flags - VkDebugUtilsMessageSeverityFlagsEXT messageSeverity - VkDebugUtilsMessageTypeFlagsEXT messageTypes - PFN_vkDebugUtilsMessengerCallbackEXT pfnUserCallback - void* pUserData -} - -@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 131 -class VkAndroidHardwareBufferUsageANDROID { - VkStructureType sType - void* pNext - u64 androidHardwareBufferUsage -} - -@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130 -class VkAndroidHardwareBufferPropertiesANDROID { - VkStructureType sType - void* pNext - VkDeviceSize allocationSize - u32 memoryTypeBits -} - -@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130 -class VkAndroidHardwareBufferFormatPropertiesANDROID { - VkStructureType sType - void* pNext - VkFormat format - u64 externalFormat - VkFormatFeatureFlags formatFeatures - VkComponentMapping samplerYcbcrConversionComponents - VkSamplerYcbcrModelConversion suggestedYcbcrModel - VkSamplerYcbcrRange suggestedYcbcrRange - VkChromaLocation suggestedXChromaOffset - VkChromaLocation suggestedYChromaOffset -} - -@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130 -class VkImportAndroidHardwareBufferInfoANDROID { - VkStructureType sType - const void* pNext - platform.AHardwareBuffer* buffer -} - -@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130 -class VkMemoryGetAndroidHardwareBufferInfoANDROID { - VkStructureType sType - const void* pNext - VkDeviceMemory memory -} - -@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130 -class VkExternalFormatANDROID { - VkStructureType sType - void* pNext - u64 externalFormat -} - -@extension("VK_EXT_sampler_filter_minmax") // 131 -class VkSamplerReductionModeCreateInfoEXT { - VkStructureType sType - const void* pNext - VkSamplerReductionModeEXT reductionMode -} - -@extension("VK_EXT_sampler_filter_minmax") // 131 -class VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT { - VkStructureType sType - void* pNext - VkBool32 filterMinmaxSingleComponentFormats - VkBool32 filterMinmaxImageComponentMapping -} - -@extension("VK_EXT_inline_uniform_block") // 139 -class VkPhysicalDeviceInlineUniformBlockFeaturesEXT { - VkStructureType sType - void* pNext - VkBool32 inlineUniformBlock - VkBool32 descriptorBindingInlineUniformBlockUpdateAfterBind -} - -@extension("VK_EXT_inline_uniform_block") // 139 -class VkPhysicalDeviceInlineUniformBlockPropertiesEXT { - VkStructureType sType - void* pNext - u32 maxInlineUniformBlockSize - u32 maxPerStageDescriptorInlineUniformBlocks - u32 maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks - u32 maxDescriptorSetInlineUniformBlocks - u32 maxDescriptorSetUpdateAfterBindInlineUniformBlocks -} - -@extension("VK_EXT_inline_uniform_block") // 139 -class VkWriteDescriptorSetInlineUniformBlockEXT { - VkStructureType sType - const void* pNext - u32 dataSize - const void* pData -} - -@extension("VK_EXT_inline_uniform_block") // 139 -class VkDescriptorPoolInlineUniformBlockCreateInfoEXT { - VkStructureType sType - const void* pNext - u32 maxInlineUniformBlockBindings -} - -@extension("VK_EXT_sample_locations") // 144 -class VkSampleLocationEXT { - f32 x - f32 y -} - -@extension("VK_EXT_sample_locations") // 144 -class VkSampleLocationsInfoEXT { - VkStructureType sType - const void* pNext - VkSampleCountFlagBits sampleLocationsPerPixel - VkExtent2D sampleLocationGridSize - u32 sampleLocationsCount - const VkSampleLocationEXT* pSampleLocations -} - -@extension("VK_EXT_sample_locations") // 144 -class VkAttachmentSampleLocationsEXT { - u32 attachmentIndex - VkSampleLocationsInfoEXT sampleLocationsInfo -} - -@extension("VK_EXT_sample_locations") // 144 -class VkSubpassSampleLocationsEXT { - u32 subpassIndex - VkSampleLocationsInfoEXT sampleLocationsInfo -} - -@extension("VK_EXT_sample_locations") // 144 -class VkRenderPassSampleLocationsBeginInfoEXT { - VkStructureType sType - const void* pNext - u32 attachmentInitialSampleLocationsCount - const VkAttachmentSampleLocationsEXT* pAttachmentInitialSampleLocations - u32 postSubpassSampleLocationsCount - const VkSubpassSampleLocationsEXT* pPostSubpassSampleLocations -} - -@extension("VK_EXT_sample_locations") // 144 -class VkPipelineSampleLocationsStateCreateInfoEXT { - VkStructureType sType - const void* pNext - VkBool32 sampleLocationsEnable - VkSampleLocationsInfoEXT sampleLocationsInfo -} - -@extension("VK_EXT_sample_locations") // 144 -class VkPhysicalDeviceSampleLocationsPropertiesEXT { - VkStructureType sType - void* pNext - VkSampleCountFlags sampleLocationSampleCounts - VkExtent2D maxSampleLocationGridSize - f32[2] sampleLocationCoordinateRange - u32 sampleLocationSubPixelBits - VkBool32 variableSampleLocations -} - -@extension("VK_EXT_sample_locations") // 144 -class VkMultisamplePropertiesEXT { - VkStructureType sType - void* pNext - VkExtent2D maxSampleLocationGridSize -} - -@extension("VK_KHR_get_memory_requirements2") // 147 -class VkBufferMemoryRequirementsInfo2KHR { - VkStructureType sType - const void* pNext - VkBuffer buffer -} - -@extension("VK_KHR_get_memory_requirements2") // 147 -class VkImageMemoryRequirementsInfo2KHR { - VkStructureType sType - const void* pNext - VkImage image -} - -@extension("VK_KHR_get_memory_requirements2") // 147 -class VkImageSparseMemoryRequirementsInfo2KHR { - VkStructureType sType - const void* pNext - VkImage image -} - -@extension("VK_KHR_get_memory_requirements2") // 147 -class VkMemoryRequirements2KHR { - VkStructureType sType - void* pNext - VkMemoryRequirements memoryRequirements -} - -@extension("VK_KHR_get_memory_requirements2") // 147 -class VkSparseImageMemoryRequirements2KHR { - VkStructureType sType - void* pNext - VkSparseImageMemoryRequirements memoryRequirements -} - -@extension("VK_KHR_image_format_list") // 148 -class VkImageFormatListCreateInfoKHR { - VkStructureType sType - const void* pNext - u32 viewFormatCount - const VkFormat* pViewFormats -} - -@extension("VK_EXT_blend_operation_advanced") // 149 -class VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT { - VkStructureType sType - void* pNext - VkBool32 advancedBlendCoherentOperations -} - -@extension("VK_EXT_blend_operation_advanced") // 149 -class VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT { - VkStructureType sType - void* pNext - u32 advancedBlendMaxColorAttachments - VkBool32 advancedBlendIndependentBlend - VkBool32 advancedBlendNonPremultipliedSrcColor - VkBool32 advancedBlendNonPremultipliedDstColor - VkBool32 advancedBlendCorrelatedOverlap - VkBool32 advancedBlendAllOperations -} - -@extension("VK_EXT_blend_operation_advanced") // 149 -class VkPipelineColorBlendAdvancedStateCreateInfoEXT { - VkStructureType sType - const void* pNext - VkBool32 srcPremultiplied - VkBool32 dstPremultiplied - VkBlendOverlapEXT blendOverlap -} - -@extension("VK_NV_fragment_coverage_to_color") // 150 -class VkPipelineCoverageToColorStateCreateInfoNV { - VkStructureType sType - const void* pNext - VkPipelineCoverageToColorStateCreateFlagsNV flags - VkBool32 coverageToColorEnable - u32 coverageToColorLocation -} - -@extension("VK_NV_framebuffer_mixed_samples") // 153 -class VkPipelineCoverageModulationStateCreateInfoNV { - VkStructureType sType - const void* pNext - VkPipelineCoverageModulationStateCreateFlagsNV flags - VkCoverageModulationModeNV coverageModulationMode - VkBool32 coverageModulationTableEnable - u32 coverageModulationTableCount - const f32* pCoverageModulationTable -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -class VkSamplerYcbcrConversionCreateInfoKHR { - VkStructureType sType - const void* pNext - VkFormat format - VkSamplerYcbcrModelConversionKHR ycbcrModel - VkSamplerYcbcrRangeKHR ycbcrRange - VkComponentMapping components - VkChromaLocationKHR xChromaOffset - VkChromaLocationKHR yChromaOffset - VkFilter chromaFilter - VkBool32 forceExplicitReconstruction -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -class VkSamplerYcbcrConversionInfoKHR { - VkStructureType sType - const void* pNext - VkSamplerYcbcrConversionKHR conversion -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -class VkBindImagePlaneMemoryInfoKHR { - VkStructureType sType - const void* pNext - VkImageAspectFlagBits planeAspect -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -class VkImagePlaneMemoryRequirementsInfoKHR { - VkStructureType sType - const void* pNext - VkImageAspectFlagBits planeAspect -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -class VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR { - VkStructureType sType - void* pNext - VkBool32 samplerYcbcrConversion -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -class VkSamplerYcbcrConversionImageFormatPropertiesKHR { - VkStructureType sType - void* pNext - u32 combinedImageSamplerDescriptorCount -} - -@extension("VK_KHR_bind_memory2") // 158 -class VkBindBufferMemoryInfoKHR { - VkStructureType sType - const void* pNext - VkBuffer buffer - VkDeviceMemory memory - VkDeviceSize memoryOffset -} - -@extension("VK_KHR_bind_memory2") // 158 -class VkBindImageMemoryInfoKHR { - VkStructureType sType - const void* pNext - VkImage image - VkDeviceMemory memory - VkDeviceSize memoryOffset -} - -@extension("VK_EXT_image_drm_format_modifier") // 159 -class VkDrmFormatModifierPropertiesEXT { - u64 drmFormatModifier - u32 drmFormatModifierPlaneCount - VkFormatFeatureFlags drmFormatModifierTilingFeatures -} - -@extension("VK_EXT_image_drm_format_modifier") // 159 -class VkDrmFormatModifierPropertiesListEXT { - VkStructureType sType - void* pNext - u32 drmFormatModifierCount - VkDrmFormatModifierPropertiesEXT* pDrmFormatModifierProperties -} - -@extension("VK_EXT_image_drm_format_modifier") // 159 -class VkPhysicalDeviceImageDrmFormatModifierInfoEXT { - VkStructureType sType - const void* pNext - u64 drmFormatModifier - VkSharingMode sharingMode - u32 queueFamilyIndexCount - const u32* pQueueFamilyIndices -} - -@extension("VK_EXT_image_drm_format_modifier") // 159 -class VkImageDrmFormatModifierListCreateInfoEXT { - VkStructureType sType - const void* pNext - u32 drmFormatModifierCount - const u64* pDrmFormatModifiers -} - -@extension("VK_EXT_image_drm_format_modifier") // 159 -class VkImageDrmFormatModifierExplicitCreateInfoEXT { - VkStructureType sType - const void* pNext - u64 drmFormatModifier - u32 drmFormatModifierPlaneCount - const VkSubresourceLayout* pPlaneLayouts -} - -@extension("VK_EXT_image_drm_format_modifier") // 159 -class VkImageDrmFormatModifierPropertiesEXT { - VkStructureType sType - void* pNext - u64 drmFormatModifier -} - -@extension("VK_EXT_validation_cache") // 161 -class VkValidationCacheCreateInfoEXT { - VkStructureType sType - const void* pNext - VkValidationCacheCreateFlagsEXT flags - platform.size_t initialDataSize - const void* pInitialData -} - -@extension("VK_EXT_validation_cache") // 161 -class VkShaderModuleValidationCacheCreateInfoEXT { - VkStructureType sType - const void* pNext - VkValidationCacheEXT validationCache -} - -@extension("VK_EXT_descriptor_indexing") // 162 -class VkDescriptorSetLayoutBindingFlagsCreateInfoEXT { - VkStructureType sType - const void* pNext - u32 bindingCount - const VkDescriptorBindingFlagsEXT* pBindingFlags -} - -@extension("VK_EXT_descriptor_indexing") // 162 -class VkPhysicalDeviceDescriptorIndexingFeaturesEXT { - VkStructureType sType - void* pNext - VkBool32 shaderInputAttachmentArrayDynamicIndexing - VkBool32 shaderUniformTexelBufferArrayDynamicIndexing - VkBool32 shaderStorageTexelBufferArrayDynamicIndexing - VkBool32 shaderUniformBufferArrayNonUniformIndexing - VkBool32 shaderSampledImageArrayNonUniformIndexing - VkBool32 shaderStorageBufferArrayNonUniformIndexing - VkBool32 shaderStorageImageArrayNonUniformIndexing - VkBool32 shaderInputAttachmentArrayNonUniformIndexing - VkBool32 shaderUniformTexelBufferArrayNonUniformIndexing - VkBool32 shaderStorageTexelBufferArrayNonUniformIndexing - VkBool32 descriptorBindingUniformBufferUpdateAfterBind - VkBool32 descriptorBindingSampledImageUpdateAfterBind - VkBool32 descriptorBindingStorageImageUpdateAfterBind - VkBool32 descriptorBindingStorageBufferUpdateAfterBind - VkBool32 descriptorBindingUniformTexelBufferUpdateAfterBind - VkBool32 descriptorBindingStorageTexelBufferUpdateAfterBind - VkBool32 descriptorBindingUpdateUnusedWhilePending - VkBool32 descriptorBindingPartiallyBound - VkBool32 descriptorBindingVariableDescriptorCount - VkBool32 runtimeDescriptorArray -} - -@extension("VK_EXT_descriptor_indexing") // 162 -class VkPhysicalDeviceDescriptorIndexingPropertiesEXT { - VkStructureType sType - void* pNext - u32 maxUpdateAfterBindDescriptorsInAllPools - VkBool32 shaderUniformBufferArrayNonUniformIndexingNative - VkBool32 shaderSampledImageArrayNonUniformIndexingNative - VkBool32 shaderStorageBufferArrayNonUniformIndexingNative - VkBool32 shaderStorageImageArrayNonUniformIndexingNative - VkBool32 shaderInputAttachmentArrayNonUniformIndexingNative - VkBool32 robustBufferAccessUpdateAfterBind - VkBool32 quadDivergentImplicitLod - u32 maxPerStageDescriptorUpdateAfterBindSamplers - u32 maxPerStageDescriptorUpdateAfterBindUniformBuffers - u32 maxPerStageDescriptorUpdateAfterBindStorageBuffers - u32 maxPerStageDescriptorUpdateAfterBindSampledImages - u32 maxPerStageDescriptorUpdateAfterBindStorageImages - u32 maxPerStageDescriptorUpdateAfterBindInputAttachments - u32 maxPerStageUpdateAfterBindResources - u32 maxDescriptorSetUpdateAfterBindSamplers - u32 maxDescriptorSetUpdateAfterBindUniformBuffers - u32 maxDescriptorSetUpdateAfterBindUniformBuffersDynamic - u32 maxDescriptorSetUpdateAfterBindStorageBuffers - u32 maxDescriptorSetUpdateAfterBindStorageBuffersDynamic - u32 maxDescriptorSetUpdateAfterBindSampledImages - u32 maxDescriptorSetUpdateAfterBindStorageImages - u32 maxDescriptorSetUpdateAfterBindInputAttachments -} - -@extension("VK_EXT_descriptor_indexing") // 162 -class VkDescriptorSetVariableDescriptorCountAllocateInfoEXT { - VkStructureType sType - const void* pNext - u32 descriptorSetCount - const u32* pDescriptorCounts -} - -@extension("VK_EXT_descriptor_indexing") // 162 -class VkDescriptorSetVariableDescriptorCountLayoutSupportEXT { - VkStructureType sType - void* pNext - u32 maxVariableDescriptorCount -} - -@extension("VK_NV_shading_rate_image") // 165 -class VkShadingRatePaletteNV { - u32 shadingRatePaletteEntryCount - const VkShadingRatePaletteEntryNV* pShadingRatePaletteEntries -} - -@extension("VK_NV_shading_rate_image") // 165 -class VkPipelineViewportShadingRateImageStateCreateInfoNV { - VkStructureType sType - const void* pNext - VkBool32 shadingRateImageEnable - u32 viewportCount - const VkShadingRatePaletteNV* pShadingRatePalettes -} - -@extension("VK_NV_shading_rate_image") // 165 -class VkPhysicalDeviceShadingRateImageFeaturesNV { - VkStructureType sType - void* pNext - VkBool32 shadingRateImage - VkBool32 shadingRateCoarseSampleOrder -} - -@extension("VK_NV_shading_rate_image") // 165 -class VkPhysicalDeviceShadingRateImagePropertiesNV { - VkStructureType sType - void* pNext - VkExtent2D shadingRateTexelSize - u32 shadingRatePaletteSize - u32 shadingRateMaxCoarseSamples -} - -@extension("VK_NV_shading_rate_image") // 165 -class VkCoarseSampleLocationNV { - u32 pixelX - u32 pixelY - u32 sample -} - -@extension("VK_NV_shading_rate_image") // 165 -class VkCoarseSampleOrderCustomNV { - VkShadingRatePaletteEntryNV shadingRate - u32 sampleCount - u32 sampleLocationCount - const VkCoarseSampleLocationNV* pSampleLocations -} - -@extension("VK_NV_shading_rate_image") // 165 -class VkPipelineViewportCoarseSampleOrderStateCreateInfoNV { - VkStructureType sType - const void* pNext - VkCoarseSampleOrderTypeNV sampleOrderType - u32 customSampleOrderCount - const VkCoarseSampleOrderCustomNV* pCustomSampleOrders -} - -@extension("VK_NV_ray_tracing") // 166 -class VkRayTracingShaderGroupCreateInfoNV { - VkStructureType sType - const void* pNext - VkRayTracingShaderGroupTypeNV type - u32 generalShader - u32 closestHitShader - u32 anyHitShader - u32 intersectionShader -} - -@extension("VK_NV_ray_tracing") // 166 -class VkRayTracingPipelineCreateInfoNV { - VkStructureType sType - const void* pNext - VkPipelineCreateFlags flags - u32 stageCount - const VkPipelineShaderStageCreateInfo* pStages - u32 groupCount - const VkRayTracingShaderGroupCreateInfoNV* pGroups - u32 maxRecursionDepth - VkPipelineLayout layout - VkPipeline basePipelineHandle - s32 basePipelineIndex -} - -@extension("VK_NV_ray_tracing") // 166 -class VkGeometryTrianglesNV { - VkStructureType sType - const void* pNext - VkBuffer vertexData - VkDeviceSize vertexOffset - u32 vertexCount - VkDeviceSize vertexStride - VkFormat vertexFormat - VkBuffer indexData - VkDeviceSize indexOffset - u32 indexCount - VkIndexType indexType - VkBuffer transformData - VkDeviceSize transformOffset -} - -@extension("VK_NV_ray_tracing") // 166 -class VkGeometryAABBNV { - VkStructureType sType - const void* pNext - VkBuffer aabbData - u32 numAABBs - u32 stride - VkDeviceSize offset -} - -@extension("VK_NV_ray_tracing") // 166 -class VkGeometryDataNV { - VkGeometryTrianglesNV triangles - VkGeometryAABBNV aabbs -} - -@extension("VK_NV_ray_tracing") // 166 -class VkGeometryNV { - VkStructureType sType - const void* pNext - VkGeometryTypeNV geometryType - VkGeometryDataNV geometry - VkGeometryFlagsNV flags -} - -@extension("VK_NV_ray_tracing") // 166 -class VkAccelerationStructureInfoNV { - VkStructureType sType - const void* pNext - VkAccelerationStructureTypeNV type - VkBuildAccelerationStructureFlagsNV flags - u32 instanceCount - u32 geometryCount - const VkGeometryNV* pGeometries -} - -@extension("VK_NV_ray_tracing") // 166 -class VkAccelerationStructureCreateInfoNV { - VkStructureType sType - const void* pNext - VkDeviceSize compactedSize - VkAccelerationStructureInfoNV info -} - -@extension("VK_NV_ray_tracing") // 166 -class VkBindAccelerationStructureMemoryInfoNV { - VkStructureType sType - const void* pNext - VkAccelerationStructureNV accelerationStructure - VkDeviceMemory memory - VkDeviceSize memoryOffset - u32 deviceIndexCount - const u32* pDeviceIndices -} - -@extension("VK_NV_ray_tracing") // 166 -class VkDescriptorAccelerationStructureInfoNV { - VkStructureType sType - const void* pNext - u32 accelerationStructureCount - const VkAccelerationStructureNV* pAccelerationStructures -} - -@extension("VK_NV_ray_tracing") // 166 -class VkAccelerationStructureMemoryRequirementsInfoNV { - VkStructureType sType - const void* pNext - VkAccelerationStructureMemoryRequirementsTypeNV type - VkAccelerationStructureNV accelerationStructure -} - -@extension("VK_NV_ray_tracing") // 166 -class VkPhysicalDeviceRaytracingPropertiesNV { - VkStructureType sType - void* pNext - u32 shaderGroupHandleSize - u32 maxRecursionDepth - u32 maxShaderGroupStride - u32 shaderGroupBaseAlignment - u64 maxGeometryCount - u64 maxInstanceCount - u64 maxTriangleCount - u32 maxDescriptorSetAccelerationStructures -} - -@extension("VK_NV_representative_fragment_test") // 167 -class VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV { - VkStructureType sType - void* pNext - VkBool32 representativeFragmentTest -} - -@extension("VK_NV_representative_fragment_test") // 167 -class VkPipelineRepresentativeFragmentTestStateCreateInfoNV { - VkStructureType sType - const void* pNext - VkBool32 representativeFragmentTestEnable -} - -@extension("VK_KHR_maintenance3") // 169 -class VkPhysicalDeviceMaintenance3PropertiesKHR { - VkStructureType sType - void* pNext - u32 maxPerSetDescriptors - VkDeviceSize maxMemoryAllocationSize -} - -@extension("VK_KHR_maintenance3") // 169 -class VkDescriptorSetLayoutSupportKHR { - VkStructureType sType - void* pNext - VkBool32 supported -} - -@extension("VK_EXT_global_priority") // 175 -class VkDeviceQueueGlobalPriorityCreateInfoEXT { - VkStructureType sType - const void* pNext - VkQueueGlobalPriorityEXT globalPriority -} - -@extension("VK_KHR_8bit_storage") // 178 -class VkPhysicalDevice8BitStorageFeaturesKHR { - VkStructureType sType - void* pNext - VkBool32 storageBuffer8BitAccess - VkBool32 uniformAndStorageBuffer8BitAccess - VkBool32 storagePushConstant8 -} - -@extension("VK_EXT_external_memory_host") // 179 -class VkImportMemoryHostPointerInfoEXT { - VkStructureType sType - const void* pNext - VkExternalMemoryHandleTypeFlagBits handleType - void* pHostPointer -} - -@extension("VK_EXT_external_memory_host") // 179 -class VkMemoryHostPointerPropertiesEXT { - VkStructureType sType - void* pNext - u32 memoryTypeBits -} - -@extension("VK_EXT_external_memory_host") // 179 -class VkPhysicalDeviceExternalMemoryHostPropertiesEXT { - VkStructureType sType - void* pNext - VkDeviceSize minImportedHostPointerAlignment -} - -@extension("VK_KHR_shader_atomic_int64") // 181 -class VkPhysicalDeviceShaderAtomicInt64FeaturesKHR { - VkStructureType sType - void* pNext - VkBool32 shaderBufferInt64Atomics - VkBool32 shaderSharedInt64Atomics -} - -@extension("VK_EXT_calibrated_timestamps") // 185 -class VkCalibratedTimestampInfoEXT { - VkStructureType sType - const void* pNext - VkTimeDomainEXT timeDomain -} - -@extension("VK_AMD_shader_core_properties") // 186 -class VkPhysicalDeviceShaderCorePropertiesAMD { - VkStructureType sType - void* pNext - u32 shaderEngineCount - u32 shaderArraysPerEngineCount - u32 computeUnitsPerShaderArray - u32 simdPerComputeUnit - u32 wavefrontsPerSimd - u32 wavefrontSize - u32 sgprsPerSimd - u32 minSgprAllocation - u32 maxSgprAllocation - u32 sgprAllocationGranularity - u32 vgprsPerSimd - u32 minVgprAllocation - u32 maxVgprAllocation - u32 vgprAllocationGranularity -} - -@extension("VK_AMD_memory_overallocation_behavior") // 190 -class VkDeviceMemoryOverallocationCreateInfoAMD { - VkStructureType sType - const void* pNext - VkMemoryOverallocationBehaviorAMD overallocationBehavior -} - -@extension("VK_EXT_vertex_attribute_divisor") // 191 -class VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT { - VkStructureType sType - void* pNext - u32 maxVertexAttribDivisor -} - -@extension("VK_EXT_vertex_attribute_divisor") // 191 -class VkVertexInputBindingDivisorDescriptionEXT { - u32 binding - u32 divisor -} - -@extension("VK_EXT_vertex_attribute_divisor") // 191 -class VkPipelineVertexInputDivisorStateCreateInfoEXT { - VkStructureType sType - const void* pNext - u32 vertexBindingDivisorCount - const VkVertexInputBindingDivisorDescriptionEXT* pVertexBindingDivisors -} - -@extension("VK_EXT_vertex_attribute_divisor") // 191 -class VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT { - VkStructureType sType - void* pNext - VkBool32 vertexAttributeInstanceRateDivisor - VkBool32 vertexAttributeInstanceRateZeroDivisor -} - -@extension("VK_KHR_driver_properties") // 197 -class VkConformanceVersionKHR { - u8 major - u8 minor - u8 subminor - u8 patch -} - -@extension("VK_KHR_driver_properties") // 197 -class VkPhysicalDeviceDriverPropertiesKHR { - VkStructureType sType - void* pNext - VkDriverIdKHR driverID - char[VK_MAX_DRIVER_NAME_SIZE_KHR] driverName - char[VK_MAX_DRIVER_INFO_SIZE_KHR] driverInfo - VkConformanceVersionKHR conformanceVersion -} - -@extension("VK_KHR_shader_float_controls") // 198 -class VkPhysicalDeviceFloatControlsPropertiesKHR { - VkStructureType sType - void* pNext - VkBool32 separateDenormSettings - VkBool32 separateRoundingModeSettings - VkBool32 shaderSignedZeroInfNanPreserveFloat16 - VkBool32 shaderSignedZeroInfNanPreserveFloat32 - VkBool32 shaderSignedZeroInfNanPreserveFloat64 - VkBool32 shaderDenormPreserveFloat16 - VkBool32 shaderDenormPreserveFloat32 - VkBool32 shaderDenormPreserveFloat64 - VkBool32 shaderDenormFlushToZeroFloat16 - VkBool32 shaderDenormFlushToZeroFloat32 - VkBool32 shaderDenormFlushToZeroFloat64 - VkBool32 shaderRoundingModeRTEFloat16 - VkBool32 shaderRoundingModeRTEFloat32 - VkBool32 shaderRoundingModeRTEFloat64 - VkBool32 shaderRoundingModeRTZFloat16 - VkBool32 shaderRoundingModeRTZFloat32 - VkBool32 shaderRoundingModeRTZFloat64 -} - -@extension("VK_NV_compute_shader_derivatives") // 202 -class VkPhysicalDeviceComputeShaderDerivativesFeaturesNV { - VkStructureType sType - void* pNext - VkBool32 computeDerivativeGroupQuads - VkBool32 computeDerivativeGroupLinear -} - -@extension("VK_NV_mesh_shader") // 203 -class VkPhysicalDeviceMeshShaderFeaturesNV { - VkStructureType sType - void* pNext - VkBool32 taskShader - VkBool32 meshShader -} - -@extension("VK_NV_mesh_shader") // 203 -class VkPhysicalDeviceMeshShaderPropertiesNV { - VkStructureType sType - void* pNext - u32 maxDrawMeshTasksCount - u32 maxTaskWorkGroupInvocations - u32[3] maxTaskWorkGroupSize - u32 maxTaskTotalMemorySize - u32 maxTaskOutputCount - u32 maxMeshWorkGroupInvocations - u32[3] maxMeshWorkGroupSize - u32 maxMeshTotalMemorySize - u32 maxMeshOutputVertices - u32 maxMeshOutputPrimitives - u32 maxMeshMultiviewViewCount - u32 meshOutputPerVertexGranularity - u32 meshOutputPerPrimitiveGranularity -} - -@extension("VK_NV_mesh_shader") // 203 -class VkDrawMeshTasksIndirectCommandNV { - u32 taskCount - u32 firstTask -} - -@extension("VK_NV_fragment_shader_barycentric") // 204 -class VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV { - VkStructureType sType - void* pNext - VkBool32 fragmentShaderBarycentric -} - -@extension("VK_NV_shader_image_footprint") // 205 -class VkPhysicalDeviceShaderImageFootprintFeaturesNV { - VkStructureType sType - void* pNext - VkBool32 imageFootprint -} - -@extension("VK_NV_scissor_exclusive") // 206 -class VkPipelineViewportExclusiveScissorStateCreateInfoNV { - VkStructureType sType - const void* pNext - u32 exclusiveScissorCount - const VkRect2D* pExclusiveScissors -} - -@extension("VK_NV_scissor_exclusive") // 206 -class VkPhysicalDeviceExclusiveScissorFeaturesNV { - VkStructureType sType - void* pNext - VkBool32 exclusiveScissor -} - -@extension("VK_NV_device_diagnostic_checkpoints") // 207 -class VkQueueFamilyCheckpointPropertiesNV { - VkStructureType sType - void* pNext - VkPipelineStageFlags checkpointExecutionStageMask -} - -@extension("VK_NV_device_diagnostic_checkpoints") // 207 -class VkCheckpointDataNV { - VkStructureType sType - void* pNext - VkPipelineStageFlagBits stage - void* pCheckpointMarker -} - -@extension("VK_KHR_vulkan_memory_model") // 212 -class VkPhysicalDeviceVulkanMemoryModelFeaturesKHR { - VkStructureType sType - void* pNext - VkBool32 vulkanMemoryModel - VkBool32 vulkanMemoryModelDeviceScope -} - -@extension("VK_EXT_pci_bus_info") // 213 -class VkPhysicalDevicePCIBusInfoPropertiesEXT { - VkStructureType sType - void* pNext - u32 pciDomain - u32 pciBus - u32 pciDevice - u32 pciFunction -} - -@extension("VK_FUCHSIA_imagepipe_surface") // 215 -class VkImagePipeSurfaceCreateInfoFUCHSIA { - VkStructureType sType - const void* pNext - VkImagePipeSurfaceCreateFlagsFUCHSIA flags - platform.zx_handle_t imagePipeHandle -} - -@extension("VK_EXT_fragment_density_map") // 219 -class VkPhysicalDeviceFragmentDensityMapFeaturesEXT { - VkStructureType sType - void* pNext - VkBool32 fragmentDensityMap - VkBool32 fragmentDensityMapDynamic - VkBool32 fragmentDensityMapNonSubsampledImages -} - -@extension("VK_EXT_fragment_density_map") // 219 -class VkPhysicalDeviceFragmentDensityMapPropertiesEXT { - VkStructureType sType - void* pNext - VkExtent2D minFragmentDensityTexelSize - VkExtent2D maxFragmentDensityTexelSize - VkBool32 fragmentDensityInvocations -} - -@extension("VK_EXT_fragment_density_map") // 219 -class VkRenderPassFragmentDensityMapCreateInfoEXT { - VkStructureType sType - const void* pNext - VkAttachmentReference fragmentDensityMapAttachment -} - -@extension("VK_EXT_scalar_block_layout") // 222 -class VkPhysicalDeviceScalarBlockLayoutFeaturesEXT { - VkStructureType sType - void* pNext - VkBool32 scalarBlockLayout -} - -@extension("VK_EXT_separate_stencil_usage") // 247 -class VkImageStencilUsageCreateInfoEXT { - VkStructureType sType - const void* pNext - VkImageUsageFlags stencilUsage -} - - -//////////////// -// Commands // -//////////////// - -// Function pointers. TODO: add support for function pointers. - -@external type void* PFN_vkVoidFunction -@pfn cmd void vkVoidFunction() { -} - -@external type void* PFN_vkAllocationFunction -@pfn cmd void* vkAllocationFunction( - void* pUserData, - platform.size_t size, - platform.size_t alignment, - VkSystemAllocationScope allocationScope) { - return ? -} - -@external type void* PFN_vkReallocationFunction -@pfn cmd void* vkReallocationFunction( - void* pUserData, - void* pOriginal, - platform.size_t size, - platform.size_t alignment, - VkSystemAllocationScope allocationScope) { - return ? -} - -@external type void* PFN_vkFreeFunction -@pfn cmd void vkFreeFunction( - void* pUserData, - void* pMemory) { -} - -@external type void* PFN_vkInternalAllocationNotification -@pfn cmd void vkInternalAllocationNotification( - void* pUserData, - platform.size_t size, - VkInternalAllocationType allocationType, - VkSystemAllocationScope allocationScope) { -} - -@external type void* PFN_vkInternalFreeNotification -@pfn cmd void vkInternalFreeNotification( - void* pUserData, - platform.size_t size, - VkInternalAllocationType allocationType, - VkSystemAllocationScope allocationScope) { -} - -// Global functions - -@threadSafety("system") -cmd VkResult vkCreateInstance( - const VkInstanceCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkInstance* pInstance) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO) - - instance := ? - pInstance[0] = instance - State.Instances[instance] = new!InstanceObject() - - layers := pCreateInfo.ppEnabledLayerNames[0:pCreateInfo.enabledLayerCount] - extensions := pCreateInfo.ppEnabledExtensionNames[0:pCreateInfo.enabledExtensionCount] - - return ? -} - -@threadSafety("system") -cmd void vkDestroyInstance( - VkInstance instance, - const VkAllocationCallbacks* pAllocator) { - instanceObject := GetInstance(instance) - - State.Instances[instance] = null -} - -@threadSafety("system") -cmd VkResult vkEnumeratePhysicalDevices( - VkInstance instance, - u32* pPhysicalDeviceCount, - VkPhysicalDevice* pPhysicalDevices) { - instanceObject := GetInstance(instance) - - physicalDeviceCount := as!u32(?) - pPhysicalDeviceCount[0] = physicalDeviceCount - physicalDevices := pPhysicalDevices[0:physicalDeviceCount] - - for i in (0 .. physicalDeviceCount) { - physicalDevice := ? - physicalDevices[i] = physicalDevice - if !(physicalDevice in State.PhysicalDevices) { - State.PhysicalDevices[physicalDevice] = new!PhysicalDeviceObject(instance: instance) - } - } - - return ? -} - -cmd PFN_vkVoidFunction vkGetDeviceProcAddr( - VkDevice device, - const char* pName) { - if device != NULL_HANDLE { - device := GetDevice(device) - } - - return ? -} - -cmd PFN_vkVoidFunction vkGetInstanceProcAddr( - VkInstance instance, - const char* pName) { - if instance != NULL_HANDLE { - instanceObject := GetInstance(instance) - } - - return ? -} - -cmd void vkGetPhysicalDeviceProperties( - VkPhysicalDevice physicalDevice, - VkPhysicalDeviceProperties* pProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - properties := ? - pProperties[0] = properties -} - -cmd void vkGetPhysicalDeviceQueueFamilyProperties( - VkPhysicalDevice physicalDevice, - u32* pQueueFamilyPropertyCount, - VkQueueFamilyProperties* pQueueFamilyProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - // TODO: Figure out how to express fetch-count-or-properties - // This version fails 'apic validate' with 'fence not allowed in - // *semantic.Branch'. Other attempts have failed with the same or other - // errors. - // if pQueueFamilyProperties != null { - // queuesProperties := pQueueFamilyProperties[0:pCount[0]] - // for i in (0 .. pCount[0]) { - // queueProperties := as!VkQueueFamilyProperties(?) - // queuesProperties[i] = queueProperties - // } - // } else { - // count := ? - // pCount[0] = count - // } -} - -cmd void vkGetPhysicalDeviceMemoryProperties( - VkPhysicalDevice physicalDevice, - VkPhysicalDeviceMemoryProperties* pMemoryProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - memoryProperties := ? - pMemoryProperties[0] = memoryProperties -} - -cmd void vkGetPhysicalDeviceFeatures( - VkPhysicalDevice physicalDevice, - VkPhysicalDeviceFeatures* pFeatures) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - features := ? - pFeatures[0] = features -} - -cmd void vkGetPhysicalDeviceFormatProperties( - VkPhysicalDevice physicalDevice, - VkFormat format, - VkFormatProperties* pFormatProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - formatProperties := ? - pFormatProperties[0] = formatProperties -} - -cmd VkResult vkGetPhysicalDeviceImageFormatProperties( - VkPhysicalDevice physicalDevice, - VkFormat format, - VkImageType type, - VkImageTiling tiling, - VkImageUsageFlags usage, - VkImageCreateFlags flags, - VkImageFormatProperties* pImageFormatProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - imageFormatProperties := ? - pImageFormatProperties[0] = imageFormatProperties - - return ? -} - - -// Device functions - -@threadSafety("system") -cmd VkResult vkCreateDevice( - VkPhysicalDevice physicalDevice, - const VkDeviceCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDevice* pDevice) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO) - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - device := ? - pDevice[0] = device - State.Devices[device] = new!DeviceObject(physicalDevice: physicalDevice) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyDevice( - VkDevice device, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - - State.Devices[device] = null -} - - -// Extension discovery functions - -cmd VkResult vkEnumerateInstanceLayerProperties( - u32* pPropertyCount, - VkLayerProperties* pProperties) { - count := as!u32(?) - pPropertyCount[0] = count - - properties := pProperties[0:count] - for i in (0 .. count) { - property := ? - properties[i] = property - } - - return ? -} - -cmd VkResult vkEnumerateInstanceExtensionProperties( - const char* pLayerName, - u32* pPropertyCount, - VkExtensionProperties* pProperties) { - count := as!u32(?) - pPropertyCount[0] = count - - properties := pProperties[0:count] - for i in (0 .. count) { - property := ? - properties[i] = property - } - - return ? -} - -cmd VkResult vkEnumerateDeviceLayerProperties( - VkPhysicalDevice physicalDevice, - u32* pPropertyCount, - VkLayerProperties* pProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - count := as!u32(?) - pPropertyCount[0] = count - - properties := pProperties[0:count] - for i in (0 .. count) { - property := ? - properties[i] = property - } - - return ? -} - -cmd VkResult vkEnumerateDeviceExtensionProperties( - VkPhysicalDevice physicalDevice, - const char* pLayerName, - u32* pPropertyCount, - VkExtensionProperties* pProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - count := as!u32(?) - pPropertyCount[0] = count - - properties := pProperties[0:count] - for i in (0 .. count) { - property := ? - properties[i] = property - } - - return ? -} - - -// Queue functions - -@threadSafety("system") -cmd void vkGetDeviceQueue( - VkDevice device, - u32 queueFamilyIndex, - u32 queueIndex, - VkQueue* pQueue) { - deviceObject := GetDevice(device) - - queue := ? - pQueue[0] = queue - - if !(queue in State.Queues) { - State.Queues[queue] = new!QueueObject(device: device) - } -} - -@threadSafety("app") -cmd VkResult vkQueueSubmit( - VkQueue queue, - u32 submitCount, - const VkSubmitInfo* pSubmits, - VkFence fence) { - queueObject := GetQueue(queue) - - if fence != NULL_HANDLE { - fenceObject := GetFence(fence) - assert(fenceObject.device == queueObject.device) - } - - // commandBuffers := pcommandBuffers[0:commandBufferCount] - // for i in (0 .. commandBufferCount) { - // commandBuffer := commandBuffers[i] - // commandBufferObject := GetCommandBuffer(commandBuffer) - // assert(commandBufferObject.device == queueObject.device) - // - // validate("QueueCheck", commandBufferObject.queueFlags in queueObject.flags, - // "vkQueueSubmit: enqueued commandBuffer requires missing queue capabilities.") - // } - - return ? -} - -@threadSafety("system") -cmd VkResult vkQueueWaitIdle( - VkQueue queue) { - queueObject := GetQueue(queue) - - return ? -} - -@threadSafety("system") -cmd VkResult vkDeviceWaitIdle( - VkDevice device) { - deviceObject := GetDevice(device) - - return ? -} - - -// Memory functions - -@threadSafety("system") -cmd VkResult vkAllocateMemory( - VkDevice device, - const VkMemoryAllocateInfo* pAllocateInfo, - const VkAllocationCallbacks* pAllocator, - VkDeviceMemory* pMemory) { - assert(pAllocateInfo.sType == VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO) - deviceObject := GetDevice(device) - - memory := ? - pMemory[0] = memory - State.DeviceMemories[memory] = new!DeviceMemoryObject( - device: device, - allocationSize: pAllocateInfo[0].allocationSize) - - return ? -} - -@threadSafety("system") -cmd void vkFreeMemory( - VkDevice device, - VkDeviceMemory memory, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - memoryObject := GetDeviceMemory(memory) - assert(memoryObject.device == device) - - // Check that no objects are still bound before freeing. - validate("MemoryCheck", len(memoryObject.boundObjects) == 0, - "vkFreeMemory: objects still bound") - validate("MemoryCheck", len(memoryObject.boundCommandBuffers) == 0, - "vkFreeMemory: commandBuffers still bound") - State.DeviceMemories[memory] = null -} - -@threadSafety("app") -cmd VkResult vkMapMemory( - VkDevice device, - VkDeviceMemory memory, - VkDeviceSize offset, - VkDeviceSize size, - VkMemoryMapFlags flags, - void** ppData) { - deviceObject := GetDevice(device) - memoryObject := GetDeviceMemory(memory) - assert(memoryObject.device == device) - - assert(flags == as!VkMemoryMapFlags(0)) - assert((offset + size) <= memoryObject.allocationSize) - - return ? -} - -@threadSafety("app") -cmd void vkUnmapMemory( - VkDevice device, - VkDeviceMemory memory) { - deviceObject := GetDevice(device) - memoryObject := GetDeviceMemory(memory) - assert(memoryObject.device == device) -} - -cmd VkResult vkFlushMappedMemoryRanges( - VkDevice device, - u32 memoryRangeCount - const VkMappedMemoryRange* pMemoryRanges) { - deviceObject := GetDevice(device) - - memoryRanges := pMemoryRanges[0:memoryRangeCount] - for i in (0 .. memoryRangeCount) { - memoryRange := memoryRanges[i] - memoryObject := GetDeviceMemory(memoryRange.memory) - assert(memoryObject.device == device) - assert((memoryRange.offset + memoryRange.size) <= memoryObject.allocationSize) - } - - return ? -} - -cmd VkResult vkInvalidateMappedMemoryRanges( - VkDevice device, - u32 memoryRangeCount, - const VkMappedMemoryRange* pMemoryRanges) { - deviceObject := GetDevice(device) - - memoryRanges := pMemoryRanges[0:memoryRangeCount] - for i in (0 .. memoryRangeCount) { - memoryRange := memoryRanges[i] - memoryObject := GetDeviceMemory(memoryRange.memory) - assert(memoryObject.device == device) - assert((memoryRange.offset + memoryRange.size) <= memoryObject.allocationSize) - } - - return ? -} - - -// Memory management API functions - -cmd void vkGetDeviceMemoryCommitment( - VkDevice device, - VkDeviceMemory memory, - VkDeviceSize* pCommittedMemoryInBytes) { - deviceObject := GetDevice(device) - - if memory != NULL_HANDLE { - memoryObject := GetDeviceMemory(memory) - assert(memoryObject.device == device) - } - - committedMemoryInBytes := ? - pCommittedMemoryInBytes[0] = committedMemoryInBytes -} - -cmd void vkGetBufferMemoryRequirements( - VkDevice device, - VkBuffer buffer, - VkMemoryRequirements* pMemoryRequirements) { - deviceObject := GetDevice(device) - bufferObject := GetBuffer(buffer) - assert(bufferObject.device == device) -} - -cmd VkResult vkBindBufferMemory( - VkDevice device, - VkBuffer buffer, - VkDeviceMemory memory, - VkDeviceSize memoryOffset) { - deviceObject := GetDevice(device) - bufferObject := GetBuffer(buffer) - assert(bufferObject.device == device) - - // Unbind buffer from previous memory object, if not VK_NULL_HANDLE. - if bufferObject.memory != NULL_HANDLE { - memoryObject := GetDeviceMemory(bufferObject.memory) - memoryObject.boundObjects[as!u64(buffer)] = null - } - - // Bind buffer to given memory object, if not VK_NULL_HANDLE. - if memory != NULL_HANDLE { - memoryObject := GetDeviceMemory(memory) - assert(memoryObject.device == device) - memoryObject.boundObjects[as!u64(buffer)] = memoryOffset - } - bufferObject.memory = memory - bufferObject.memoryOffset = memoryOffset - - return ? -} - -cmd void vkGetImageMemoryRequirements( - VkDevice device, - VkImage image, - VkMemoryRequirements* pMemoryRequirements) { - deviceObject := GetDevice(device) - imageObject := GetImage(image) - assert(imageObject.device == device) -} - -cmd VkResult vkBindImageMemory( - VkDevice device, - VkImage image, - VkDeviceMemory memory, - VkDeviceSize memoryOffset) { - deviceObject := GetDevice(device) - imageObject := GetImage(image) - assert(imageObject.device == device) - - // Unbind image from previous memory object, if not VK_NULL_HANDLE. - if imageObject.memory != NULL_HANDLE { - memoryObject := GetDeviceMemory(imageObject.memory) - memoryObject.boundObjects[as!u64(image)] = null - } - - // Bind image to given memory object, if not VK_NULL_HANDLE. - if memory != NULL_HANDLE { - memoryObject := GetDeviceMemory(memory) - assert(memoryObject.device == device) - memoryObject.boundObjects[as!u64(image)] = memoryOffset - } - imageObject.memory = memory - imageObject.memoryOffset = memoryOffset - - return ? -} - -cmd void vkGetImageSparseMemoryRequirements( - VkDevice device, - VkImage image, - u32* pSparseMemoryRequirementCount, - VkSparseImageMemoryRequirements* pSparseMemoryRequirements) { - deviceObject := GetDevice(device) - imageObject := GetImage(image) - assert(imageObject.device == device) -} - -cmd void vkGetPhysicalDeviceSparseImageFormatProperties( - VkPhysicalDevice physicalDevice, - VkFormat format, - VkImageType type, - VkSampleCountFlagBits samples, - VkImageUsageFlags usage, - VkImageTiling tiling, - u32* pPropertyCount, - VkSparseImageFormatProperties* pProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) -} - -cmd VkResult vkQueueBindSparse( - VkQueue queue, - u32 bindInfoCount, - const VkBindSparseInfo* pBindInfo, - VkFence fence) { - queueObject := GetQueue(queue) - - return ? -} - - -// Fence functions - -@threadSafety("system") -cmd VkResult vkCreateFence( - VkDevice device, - const VkFenceCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkFence* pFence) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_FENCE_CREATE_INFO) - deviceObject := GetDevice(device) - - fence := ? - pFence[0] = fence - State.Fences[fence] = new!FenceObject( - device: device, signaled: (pCreateInfo.flags == as!VkFenceCreateFlags(VK_FENCE_CREATE_SIGNALED_BIT))) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyFence( - VkDevice device, - VkFence fence, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - fenceObject := GetFence(fence) - assert(fenceObject.device == device) - - State.Fences[fence] = null -} - -@threadSafety("system") -cmd VkResult vkResetFences( - VkDevice device, - u32 fenceCount, - const VkFence* pFences) { - deviceObject := GetDevice(device) - - fences := pFences[0:fenceCount] - for i in (0 .. fenceCount) { - fence := fences[i] - fenceObject := GetFence(fence) - assert(fenceObject.device == device) - fenceObject.signaled = false - } - - return ? -} - -@threadSafety("system") -cmd VkResult vkGetFenceStatus( - VkDevice device, - VkFence fence) { - deviceObject := GetDevice(device) - fenceObject := GetFence(fence) - assert(fenceObject.device == device) - - return ? -} - -@threadSafety("system") -cmd VkResult vkWaitForFences( - VkDevice device, - u32 fenceCount, - const VkFence* pFences, - VkBool32 waitAll, - u64 timeout) { /// timeout in nanoseconds - deviceObject := GetDevice(device) - - fences := pFences[0:fenceCount] - for i in (0 .. fenceCount) { - fence := fences[i] - fenceObject := GetFence(fence) - assert(fenceObject.device == device) - } - - return ? -} - - -// Queue semaphore functions - -@threadSafety("system") -cmd VkResult vkCreateSemaphore( - VkDevice device, - const VkSemaphoreCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSemaphore* pSemaphore) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO) - deviceObject := GetDevice(device) - - semaphore := ? - pSemaphore[0] = semaphore - State.Semaphores[semaphore] = new!SemaphoreObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroySemaphore( - VkDevice device, - VkSemaphore semaphore, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - semaphoreObject := GetSemaphore(semaphore) - assert(semaphoreObject.device == device) - - State.Semaphores[semaphore] = null -} - - -// Event functions - -@threadSafety("system") -cmd VkResult vkCreateEvent( - VkDevice device, - const VkEventCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkEvent* pEvent) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_EVENT_CREATE_INFO) - deviceObject := GetDevice(device) - - event := ? - pEvent[0] = event - State.Events[event] = new!EventObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyEvent( - VkDevice device, - VkEvent event, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - eventObject := GetEvent(event) - assert(eventObject.device == device) - - State.Events[event] = null -} - -@threadSafety("system") -cmd VkResult vkGetEventStatus( - VkDevice device, - VkEvent event) { - deviceObject := GetDevice(device) - eventObject := GetEvent(event) - assert(eventObject.device == device) - - return ? -} - -@threadSafety("system") -cmd VkResult vkSetEvent( - VkDevice device, - VkEvent event) { - deviceObject := GetDevice(device) - eventObject := GetEvent(event) - assert(eventObject.device == device) - - return ? -} - -@threadSafety("system") -cmd VkResult vkResetEvent( - VkDevice device, - VkEvent event) { - deviceObject := GetDevice(device) - eventObject := GetEvent(event) - assert(eventObject.device == device) - - return ? -} - - -// Query functions - -@threadSafety("system") -cmd VkResult vkCreateQueryPool( - VkDevice device, - const VkQueryPoolCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkQueryPool* pQueryPool) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO) - deviceObject := GetDevice(device) - - queryPool := ? - pQueryPool[0] = queryPool - State.QueryPools[queryPool] = new!QueryPoolObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyQueryPool( - VkDevice device, - VkQueryPool queryPool, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - queryPoolObject := GetQueryPool(queryPool) - assert(queryPoolObject.device == device) - - State.QueryPools[queryPool] = null -} - -@threadSafety("system") -cmd VkResult vkGetQueryPoolResults( - VkDevice device, - VkQueryPool queryPool, - u32 firstQuery, - u32 queryCount, - platform.size_t dataSize, - void* pData, - VkDeviceSize stride, - VkQueryResultFlags flags) { - deviceObject := GetDevice(device) - queryPoolObject := GetQueryPool(queryPool) - assert(queryPoolObject.device == device) - - data := pData[0:dataSize] - - return ? -} - -// Buffer functions - -@threadSafety("system") -cmd VkResult vkCreateBuffer( - VkDevice device, - const VkBufferCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkBuffer* pBuffer) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO) - deviceObject := GetDevice(device) - - buffer := ? - pBuffer[0] = buffer - State.Buffers[buffer] = new!BufferObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyBuffer( - VkDevice device, - VkBuffer buffer, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - bufferObject := GetBuffer(buffer) - assert(bufferObject.device == device) - - assert(bufferObject.memory == 0) - State.Buffers[buffer] = null -} - - -// Buffer view functions - -@threadSafety("system") -cmd VkResult vkCreateBufferView( - VkDevice device, - const VkBufferViewCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkBufferView* pView) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO) - deviceObject := GetDevice(device) - - bufferObject := GetBuffer(pCreateInfo.buffer) - assert(bufferObject.device == device) - - view := ? - pView[0] = view - State.BufferViews[view] = new!BufferViewObject(device: device, buffer: pCreateInfo.buffer) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyBufferView( - VkDevice device, - VkBufferView bufferView, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - bufferViewObject := GetBufferView(bufferView) - assert(bufferViewObject.device == device) - - State.BufferViews[bufferView] = null -} - - -// Image functions - -@threadSafety("system") -cmd VkResult vkCreateImage( - VkDevice device, - const VkImageCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkImage* pImage) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO) - deviceObject := GetDevice(device) - - image := ? - pImage[0] = image - State.Images[image] = new!ImageObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyImage( - VkDevice device, - VkImage image, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - imageObject := GetImage(image) - assert(imageObject.device == device) - - assert(imageObject.memory == 0) - State.Images[image] = null -} - -cmd void vkGetImageSubresourceLayout( - VkDevice device, - VkImage image, - const VkImageSubresource* pSubresource, - VkSubresourceLayout* pLayout) { - deviceObject := GetDevice(device) - imageObject := GetImage(image) - assert(imageObject.device == device) -} - - -// Image view functions - -@threadSafety("system") -cmd VkResult vkCreateImageView( - VkDevice device, - const VkImageViewCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkImageView* pView) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO) - deviceObject := GetDevice(device) - - imageObject := GetImage(pCreateInfo.image) - assert(imageObject.device == device) - - view := ? - pView[0] = view - State.ImageViews[view] = new!ImageViewObject(device: device, image: pCreateInfo.image) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyImageView( - VkDevice device, - VkImageView imageView, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - imageViewObject := GetImageView(imageView) - assert(imageViewObject.device == device) - - State.ImageViews[imageView] = null -} - - -// Shader functions - -cmd VkResult vkCreateShaderModule( - VkDevice device, - const VkShaderModuleCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkShaderModule* pShaderModule) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO) - deviceObject := GetDevice(device) - - shaderModule := ? - pShaderModule[0] = shaderModule - State.ShaderModules[shaderModule] = new!ShaderModuleObject(device: device) - - return ? -} - -cmd void vkDestroyShaderModule( - VkDevice device, - VkShaderModule shaderModule, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - shaderModuleObject := GetShaderModule(shaderModule) - assert(shaderModuleObject.device == device) - - State.ShaderModules[shaderModule] = null -} - - -// Pipeline functions - -cmd VkResult vkCreatePipelineCache( - VkDevice device, - const VkPipelineCacheCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkPipelineCache* pPipelineCache) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO) - deviceObject := GetDevice(device) - - pipelineCache := ? - pPipelineCache[0] = pipelineCache - State.PipelineCaches[pipelineCache] = new!PipelineCacheObject(device: device) - - return ? -} - -cmd void vkDestroyPipelineCache( - VkDevice device, - VkPipelineCache pipelineCache, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - pipelineCacheObject := GetPipelineCache(pipelineCache) - assert(pipelineCacheObject.device == device) - - State.PipelineCaches[pipelineCache] = null -} - -cmd VkResult vkGetPipelineCacheData( - VkDevice device, - VkPipelineCache pipelineCache, - platform.size_t* pDataSize, - void* pData) { - deviceObject := GetDevice(device) - pipelineCacheObject := GetPipelineCache(pipelineCache) - assert(pipelineCacheObject.device == device) - - return ? -} - -cmd VkResult vkMergePipelineCaches( - VkDevice device, - VkPipelineCache dstCache, - u32 srcCacheCount, - const VkPipelineCache* pSrcCaches) { - deviceObject := GetDevice(device) - dstCacheObject := GetPipelineCache(dstCache) - assert(dstCacheObject.device == device) - - srcCaches := pSrcCaches[0:srcCacheCount] - for i in (0 .. srcCacheCount) { - srcCache := srcCaches[i] - srcCacheObject := GetPipelineCache(srcCache) - assert(srcCacheObject.device == device) - } - - return ? -} - -cmd VkResult vkCreateGraphicsPipelines( - VkDevice device, - VkPipelineCache pipelineCache, - u32 createInfoCount, - const VkGraphicsPipelineCreateInfo* pCreateInfos, - const VkAllocationCallbacks* pAllocator, - VkPipeline* pPipelines) { - deviceObject := GetDevice(device) - if pipelineCache != NULL_HANDLE { - pipelineCacheObject := GetPipelineCache(pipelineCache) - assert(pipelineCacheObject.device == device) - } - - createInfos := pCreateInfos[0:createInfoCount] - pipelines := pPipelines[0:createInfoCount] - for i in (0 .. createInfoCount) { - pipeline := ? - pipelines[i] = pipeline - State.Pipelines[pipeline] = new!PipelineObject(device: device) - } - - return ? -} - -cmd VkResult vkCreateComputePipelines( - VkDevice device, - VkPipelineCache pipelineCache, - u32 createInfoCount, - const VkComputePipelineCreateInfo* pCreateInfos, - const VkAllocationCallbacks* pAllocator, - VkPipeline* pPipelines) { - deviceObject := GetDevice(device) - if pipelineCache != NULL_HANDLE { - pipelineCacheObject := GetPipelineCache(pipelineCache) - assert(pipelineCacheObject.device == device) - } - - createInfos := pCreateInfos[0:createInfoCount] - pipelines := pPipelines[0:createInfoCount] - for i in (0 .. createInfoCount) { - pipeline := ? - pipelines[i] = pipeline - State.Pipelines[pipeline] = new!PipelineObject(device: device) - } - - return ? -} - -@threadSafety("system") -cmd void vkDestroyPipeline( - VkDevice device, - VkPipeline pipeline, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - pipelineObjects := GetPipeline(pipeline) - assert(pipelineObjects.device == device) - - State.Pipelines[pipeline] = null -} - - -// Pipeline layout functions - -@threadSafety("system") -cmd VkResult vkCreatePipelineLayout( - VkDevice device, - const VkPipelineLayoutCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkPipelineLayout* pPipelineLayout) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO) - deviceObject := GetDevice(device) - - pipelineLayout := ? - pPipelineLayout[0] = pipelineLayout - State.PipelineLayouts[pipelineLayout] = new!PipelineLayoutObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyPipelineLayout( - VkDevice device, - VkPipelineLayout pipelineLayout, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - pipelineLayoutObjects := GetPipelineLayout(pipelineLayout) - assert(pipelineLayoutObjects.device == device) - - State.PipelineLayouts[pipelineLayout] = null -} - - -// Sampler functions - -@threadSafety("system") -cmd VkResult vkCreateSampler( - VkDevice device, - const VkSamplerCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSampler* pSampler) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO) - deviceObject := GetDevice(device) - - sampler := ? - pSampler[0] = sampler - State.Samplers[sampler] = new!SamplerObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroySampler( - VkDevice device, - VkSampler sampler, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - samplerObject := GetSampler(sampler) - assert(samplerObject.device == device) - - State.Samplers[sampler] = null -} - - -// Descriptor set functions - -@threadSafety("system") -cmd VkResult vkCreateDescriptorSetLayout( - VkDevice device, - const VkDescriptorSetLayoutCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDescriptorSetLayout* pSetLayout) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO) - deviceObject := GetDevice(device) - - setLayout := ? - pSetLayout[0] = setLayout - State.DescriptorSetLayouts[setLayout] = new!DescriptorSetLayoutObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyDescriptorSetLayout( - VkDevice device, - VkDescriptorSetLayout descriptorSetLayout, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - descriptorSetLayoutObject := GetDescriptorSetLayout(descriptorSetLayout) - assert(descriptorSetLayoutObject.device == device) - - State.DescriptorSetLayouts[descriptorSetLayout] = null -} - -@threadSafety("system") -cmd VkResult vkCreateDescriptorPool( - VkDevice device, - const VkDescriptorPoolCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDescriptorPool* pDescriptorPool) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO) - deviceObject := GetDevice(device) - - descriptorPool := ? - pDescriptorPool[0] = descriptorPool - State.DescriptorPools[descriptorPool] = new!DescriptorPoolObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyDescriptorPool( - VkDevice device, - VkDescriptorPool descriptorPool, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - descriptorPoolObject := GetDescriptorPool(descriptorPool) - assert(descriptorPoolObject.device == device) - - State.DescriptorPools[descriptorPool] = null -} - -@threadSafety("app") -cmd VkResult vkResetDescriptorPool( - VkDevice device, - VkDescriptorPool descriptorPool, - VkDescriptorPoolResetFlags flags) { - deviceObject := GetDevice(device) - descriptorPoolObject := GetDescriptorPool(descriptorPool) - assert(descriptorPoolObject.device == device) - - return ? -} - -@threadSafety("app") -cmd VkResult vkAllocateDescriptorSets( - VkDevice device, - const VkDescriptorSetAllocateInfo* pAllocateInfo, - VkDescriptorSet* pDescriptorSets) { - deviceObject := GetDevice(device) - allocInfo := pAllocateInfo[0] - descriptorPoolObject := GetDescriptorPool(allocInfo.descriptorPool) - - setLayouts := allocInfo.pSetLayouts[0:allocInfo.setCount] - for i in (0 .. allocInfo.setCount) { - setLayout := setLayouts[i] - setLayoutObject := GetDescriptorSetLayout(setLayout) - assert(setLayoutObject.device == device) - } - - descriptorSets := pDescriptorSets[0:allocInfo.setCount] - for i in (0 .. allocInfo.setCount) { - descriptorSet := ? - descriptorSets[i] = descriptorSet - State.DescriptorSets[descriptorSet] = new!DescriptorSetObject(device: device) - } - - return ? -} - -cmd VkResult vkFreeDescriptorSets( - VkDevice device, - VkDescriptorPool descriptorPool, - u32 descriptorSetCount, - const VkDescriptorSet* pDescriptorSets) { - deviceObject := GetDevice(device) - descriptorPoolObject := GetDescriptorPool(descriptorPool) - - descriptorSets := pDescriptorSets[0:descriptorSetCount] - for i in (0 .. descriptorSetCount) { - descriptorSet := descriptorSets[i] - descriptorSetObject := GetDescriptorSet(descriptorSet) - assert(descriptorSetObject.device == device) - State.DescriptorSets[descriptorSet] = null - } - - return ? -} - -cmd void vkUpdateDescriptorSets( - VkDevice device, - u32 descriptorWriteCount, - const VkWriteDescriptorSet* pDescriptorWrites, - u32 descriptorCopyCount, - const VkCopyDescriptorSet* pDescriptorCopies) { - deviceObject := GetDevice(device) - - descriptorWrites := pDescriptorWrites[0:descriptorWriteCount] - for i in (0 .. descriptorWriteCount) { - descriptorWrite := descriptorWrites[i] - descriptorWriteObject := GetDescriptorSet(descriptorWrite.dstSet) - assert(descriptorWriteObject.device == device) - } - - descriptorCopies := pDescriptorCopies[0:descriptorCopyCount] - for i in (0 .. descriptorCopyCount) { - descriptorCopy := descriptorCopies[i] - descriptorCopyObject := GetDescriptorSet(descriptorCopy.dstSet) - assert(descriptorCopyObject.device == device) - } -} - - -// Framebuffer functions - -@threadSafety("system") -cmd VkResult vkCreateFramebuffer( - VkDevice device, - const VkFramebufferCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkFramebuffer* pFramebuffer) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO) - deviceObject := GetDevice(device) - - framebuffer := ? - pFramebuffer[0] = framebuffer - State.Framebuffers[framebuffer] = new!FramebufferObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyFramebuffer( - VkDevice device, - VkFramebuffer framebuffer, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - framebufferObject := GetFramebuffer(framebuffer) - assert(framebufferObject.device == device) - - State.Framebuffers[framebuffer] = null -} - - -// Renderpass functions - -@threadSafety("system") -cmd VkResult vkCreateRenderPass( - VkDevice device, - const VkRenderPassCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkRenderPass* pRenderPass) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO) - deviceObject := GetDevice(device) - - renderpass := ? - pRenderPass[0] = renderpass - State.RenderPasses[renderpass] = new!RenderPassObject(device: device) - - return ? -} - -@threadSafety("system") -cmd void vkDestroyRenderPass( - VkDevice device, - VkRenderPass renderPass, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - renderPassObject := GetRenderPass(renderPass) - assert(renderPassObject.device == device) - - State.RenderPasses[renderPass] = null -} - -cmd void vkGetRenderAreaGranularity( - VkDevice device, - VkRenderPass renderPass, - VkExtent2D* pGranularity) { - deviceObject := GetDevice(device) - renderPassObject := GetRenderPass(renderPass) - - granularity := ? - pGranularity[0] = granularity -} - -// Command pool functions - -cmd VkResult vkCreateCommandPool( - VkDevice device, - const VkCommandPoolCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkCommandPool* pCommandPool) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO) - deviceObject := GetDevice(device) - - commandPool := ? - pCommandPool[0] = commandPool - State.CommandPools[commandPool] = new!CommandPoolObject(device: device) - - return ? -} - -cmd void vkDestroyCommandPool( - VkDevice device, - VkCommandPool commandPool, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - commandPoolObject := GetCommandPool(commandPool) - assert(commandPoolObject.device == device) - - State.CommandPools[commandPool] = null -} - -cmd VkResult vkResetCommandPool( - VkDevice device, - VkCommandPool commandPool, - VkCommandPoolResetFlags flags) { - deviceObject := GetDevice(device) - commandPoolObject := GetCommandPool(commandPool) - assert(commandPoolObject.device == device) - - return ? -} - -// Command buffer functions - -macro void bindCommandBuffer(VkCommandBuffer commandBuffer, any obj, VkDeviceMemory memory) { - memoryObject := GetDeviceMemory(memory) - memoryObject.boundCommandBuffers[commandBuffer] = commandBuffer - - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.boundObjects[as!u64(obj)] = memory -} - -macro void unbindCommandBuffer(VkCommandBuffer commandBuffer, any obj, VkDeviceMemory memory) { - memoryObject := GetDeviceMemory(memory) - memoryObject.boundCommandBuffers[commandBuffer] = null - - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.boundObjects[as!u64(obj)] = null -} - -@threadSafety("system") -cmd VkResult vkAllocateCommandBuffers( - VkDevice device, - const VkCommandBufferAllocateInfo* pAllocateInfo, - VkCommandBuffer* pCommandBuffers) { - assert(pAllocateInfo[0].sType == VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO) - - count := pAllocateInfo[0].commandBufferCount - commandBuffers := pCommandBuffers[0:count] - for i in (0 .. count) { - commandBuffer := ? - commandBuffers[i] = commandBuffer - State.CommandBuffers[commandBuffer] = new!CommandBufferObject(device: device) - } - - return ? -} - -@threadSafety("system") -cmd void vkFreeCommandBuffers( - VkDevice device, - VkCommandPool commandPool, - u32 commandBufferCount, - const VkCommandBuffer* pCommandBuffers) { - deviceObject := GetDevice(device) - - commandBuffers := pCommandBuffers[0:commandBufferCount] - for i in (0 .. commandBufferCount) { - commandBufferObject := GetCommandBuffer(commandBuffers[i]) - assert(commandBufferObject.device == device) - // TODO: iterate over boundObjects and clear memory bindings - State.CommandBuffers[commandBuffers[i]] = null - } -} - -@threadSafety("app") -cmd VkResult vkBeginCommandBuffer( - VkCommandBuffer commandBuffer, - const VkCommandBufferBeginInfo* pBeginInfo) { - assert(pBeginInfo.sType == VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO) - commandBufferObject := GetCommandBuffer(commandBuffer) - - // TODO: iterate over boundObjects and clear memory bindings - - return ? -} - -@threadSafety("app") -cmd VkResult vkEndCommandBuffer( - VkCommandBuffer commandBuffer) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - return ? -} - -@threadSafety("app") -cmd VkResult vkResetCommandBuffer( - VkCommandBuffer commandBuffer, - VkCommandBufferResetFlags flags) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - // TODO: iterate over boundObjects and clear memory bindings - - return ? -} - - -// Command buffer building functions - -@threadSafety("app") -cmd void vkCmdBindPipeline( - VkCommandBuffer commandBuffer, - VkPipelineBindPoint pipelineBindPoint, - VkPipeline pipeline) { - commandBufferObject := GetCommandBuffer(commandBuffer) - pipelineObject := GetPipeline(pipeline) - assert(commandBufferObject.device == pipelineObject.device) - - queue := switch (pipelineBindPoint) { - case VK_PIPELINE_BIND_POINT_COMPUTE: VK_QUEUE_COMPUTE_BIT - case VK_PIPELINE_BIND_POINT_GRAPHICS: VK_QUEUE_GRAPHICS_BIT - } - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, queue) -} - -@threadSafety("app") -cmd void vkCmdSetViewport( - VkCommandBuffer commandBuffer, - u32 firstViewport, - u32 viewportCount, - const VkViewport* pViewports) { - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdSetScissor( - VkCommandBuffer commandBuffer, - u32 firstScissor, - u32 scissorCount, - const VkRect2D* pScissors) { - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdSetLineWidth( - VkCommandBuffer commandBuffer, - f32 lineWidth) { - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdSetDepthBias( - VkCommandBuffer commandBuffer, - f32 depthBiasConstantFactor, - f32 depthBiasClamp, - f32 depthBiasSlopeFactor) { - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdSetBlendConstants( - VkCommandBuffer commandBuffer, - // TODO(jessehall): apic only supports 'const' on pointer types. Using - // an annotation as a quick hack to pass this to the template without - // having to modify the AST and semantic model. - @readonly f32[4] blendConstants) { - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdSetDepthBounds( - VkCommandBuffer commandBuffer, - f32 minDepthBounds, - f32 maxDepthBounds) { - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdSetStencilCompareMask( - VkCommandBuffer commandBuffer, - VkStencilFaceFlags faceMask, - u32 compareMask) { - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdSetStencilWriteMask( - VkCommandBuffer commandBuffer, - VkStencilFaceFlags faceMask, - u32 writeMask) { - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdSetStencilReference( - VkCommandBuffer commandBuffer, - VkStencilFaceFlags faceMask, - u32 reference) { - commandBufferObject := GetCommandBuffer(commandBuffer) - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdBindDescriptorSets( - VkCommandBuffer commandBuffer, - VkPipelineBindPoint pipelineBindPoint, - VkPipelineLayout layout, - u32 firstSet, - u32 descriptorSetCount, - const VkDescriptorSet* pDescriptorSets, - u32 dynamicOffsetCount, - const u32* pDynamicOffsets) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - descriptorSets := pDescriptorSets[0:descriptorSetCount] - for i in (0 .. descriptorSetCount) { - descriptorSet := descriptorSets[i] - descriptorSetObject := GetDescriptorSet(descriptorSet) - assert(commandBufferObject.device == descriptorSetObject.device) - } - - dynamicOffsets := pDynamicOffsets[0:dynamicOffsetCount] - for i in (0 .. dynamicOffsetCount) { - dynamicOffset := dynamicOffsets[i] - } - - queue := switch (pipelineBindPoint) { - case VK_PIPELINE_BIND_POINT_COMPUTE: VK_QUEUE_COMPUTE_BIT - case VK_PIPELINE_BIND_POINT_GRAPHICS: VK_QUEUE_GRAPHICS_BIT - } - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, queue) -} - -@threadSafety("app") -cmd void vkCmdBindIndexBuffer( - VkCommandBuffer commandBuffer, - VkBuffer buffer, - VkDeviceSize offset, - VkIndexType indexType) { - commandBufferObject := GetCommandBuffer(commandBuffer) - bufferObject := GetBuffer(buffer) - assert(commandBufferObject.device == bufferObject.device) - - bindCommandBuffer(commandBuffer, buffer, bufferObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdBindVertexBuffers( - VkCommandBuffer commandBuffer, - u32 firstBinding, - u32 bindingCount, - const VkBuffer* pBuffers, - const VkDeviceSize* pOffsets) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - // TODO: check if not [firstBinding:firstBinding+bindingCount] - buffers := pBuffers[0:bindingCount] - offsets := pOffsets[0:bindingCount] - for i in (0 .. bindingCount) { - buffer := buffers[i] - offset := offsets[i] - bufferObject := GetBuffer(buffer) - assert(commandBufferObject.device == bufferObject.device) - - bindCommandBuffer(commandBuffer, buffer, bufferObject.memory) - } - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdDraw( - VkCommandBuffer commandBuffer, - u32 vertexCount, - u32 instanceCount, - u32 firstVertex, - u32 firstInstance) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdDrawIndexed( - VkCommandBuffer commandBuffer, - u32 indexCount, - u32 instanceCount, - u32 firstIndex, - s32 vertexOffset, - u32 firstInstance) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdDrawIndirect( - VkCommandBuffer commandBuffer, - VkBuffer buffer, - VkDeviceSize offset, - u32 drawCount, - u32 stride) { - commandBufferObject := GetCommandBuffer(commandBuffer) - bufferObject := GetBuffer(buffer) - assert(commandBufferObject.device == bufferObject.device) - - bindCommandBuffer(commandBuffer, buffer, bufferObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdDrawIndexedIndirect( - VkCommandBuffer commandBuffer, - VkBuffer buffer, - VkDeviceSize offset, - u32 drawCount, - u32 stride) { - commandBufferObject := GetCommandBuffer(commandBuffer) - bufferObject := GetBuffer(buffer) - assert(commandBufferObject.device == bufferObject.device) - - bindCommandBuffer(commandBuffer, buffer, bufferObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdDispatch( - VkCommandBuffer commandBuffer, - u32 groupCountX, - u32 groupCountY, - u32 groupCountZ) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_COMPUTE_BIT) -} - -@threadSafety("app") -cmd void vkCmdDispatchIndirect( - VkCommandBuffer commandBuffer, - VkBuffer buffer, - VkDeviceSize offset) { - commandBufferObject := GetCommandBuffer(commandBuffer) - bufferObject := GetBuffer(buffer) - assert(commandBufferObject.device == bufferObject.device) - - bindCommandBuffer(commandBuffer, buffer, bufferObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_COMPUTE_BIT) -} - -@threadSafety("app") -cmd void vkCmdCopyBuffer( - VkCommandBuffer commandBuffer, - VkBuffer srcBuffer, - VkBuffer dstBuffer, - u32 regionCount, - const VkBufferCopy* pRegions) { - commandBufferObject := GetCommandBuffer(commandBuffer) - srcBufferObject := GetBuffer(srcBuffer) - dstBufferObject := GetBuffer(dstBuffer) - assert(commandBufferObject.device == srcBufferObject.device) - assert(commandBufferObject.device == dstBufferObject.device) - - regions := pRegions[0:regionCount] - for i in (0 .. regionCount) { - region := regions[i] - } - - bindCommandBuffer(commandBuffer, srcBuffer, srcBufferObject.memory) - bindCommandBuffer(commandBuffer, dstBuffer, dstBufferObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT) -} - -@threadSafety("app") -cmd void vkCmdCopyImage( - VkCommandBuffer commandBuffer, - VkImage srcImage, - VkImageLayout srcImageLayout, - VkImage dstImage, - VkImageLayout dstImageLayout, - u32 regionCount, - const VkImageCopy* pRegions) { - commandBufferObject := GetCommandBuffer(commandBuffer) - srcImageObject := GetImage(srcImage) - dstImageObject := GetImage(dstImage) - assert(commandBufferObject.device == srcImageObject.device) - assert(commandBufferObject.device == dstImageObject.device) - - regions := pRegions[0:regionCount] - for i in (0 .. regionCount) { - region := regions[i] - } - - bindCommandBuffer(commandBuffer, srcImage, srcImageObject.memory) - bindCommandBuffer(commandBuffer, dstImage, dstImageObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT) -} - -@threadSafety("app") -cmd void vkCmdBlitImage( - VkCommandBuffer commandBuffer, - VkImage srcImage, - VkImageLayout srcImageLayout, - VkImage dstImage, - VkImageLayout dstImageLayout, - u32 regionCount, - const VkImageBlit* pRegions, - VkFilter filter) { - commandBufferObject := GetCommandBuffer(commandBuffer) - srcImageObject := GetImage(srcImage) - dstImageObject := GetImage(dstImage) - assert(commandBufferObject.device == srcImageObject.device) - assert(commandBufferObject.device == dstImageObject.device) - - regions := pRegions[0:regionCount] - for i in (0 .. regionCount) { - region := regions[i] - } - - bindCommandBuffer(commandBuffer, srcImage, srcImageObject.memory) - bindCommandBuffer(commandBuffer, dstImage, dstImageObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdCopyBufferToImage( - VkCommandBuffer commandBuffer, - VkBuffer srcBuffer, - VkImage dstImage, - VkImageLayout dstImageLayout, - u32 regionCount, - const VkBufferImageCopy* pRegions) { - commandBufferObject := GetCommandBuffer(commandBuffer) - srcBufferObject := GetBuffer(srcBuffer) - dstImageObject := GetImage(dstImage) - assert(commandBufferObject.device == srcBufferObject.device) - assert(commandBufferObject.device == dstImageObject.device) - - regions := pRegions[0:regionCount] - for i in (0 .. regionCount) { - region := regions[i] - } - - bindCommandBuffer(commandBuffer, srcBuffer, srcBufferObject.memory) - bindCommandBuffer(commandBuffer, dstImage, dstImageObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT) -} - -@threadSafety("app") -cmd void vkCmdCopyImageToBuffer( - VkCommandBuffer commandBuffer, - VkImage srcImage, - VkImageLayout srcImageLayout, - VkBuffer dstBuffer, - u32 regionCount, - const VkBufferImageCopy* pRegions) { - commandBufferObject := GetCommandBuffer(commandBuffer) - srcImageObject := GetImage(srcImage) - dstBufferObject := GetBuffer(dstBuffer) - assert(commandBufferObject.device == srcImageObject.device) - assert(commandBufferObject.device == dstBufferObject.device) - - regions := pRegions[0:regionCount] - for i in (0 .. regionCount) { - region := regions[i] - } - - bindCommandBuffer(commandBuffer, srcImage, srcImageObject.memory) - bindCommandBuffer(commandBuffer, dstBuffer, dstBufferObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT) -} - -@threadSafety("app") -cmd void vkCmdUpdateBuffer( - VkCommandBuffer commandBuffer, - VkBuffer dstBuffer, - VkDeviceSize dstOffset, - VkDeviceSize dataSize, - const void* pData) { - commandBufferObject := GetCommandBuffer(commandBuffer) - dstBufferObject := GetBuffer(dstBuffer) - assert(commandBufferObject.device == dstBufferObject.device) - - data := pData[0:dataSize] - - bindCommandBuffer(commandBuffer, dstBuffer, dstBufferObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT) -} - -@threadSafety("app") -cmd void vkCmdFillBuffer( - VkCommandBuffer commandBuffer, - VkBuffer dstBuffer, - VkDeviceSize dstOffset, - VkDeviceSize size, - u32 data) { - commandBufferObject := GetCommandBuffer(commandBuffer) - dstBufferObject := GetBuffer(dstBuffer) - assert(commandBufferObject.device == dstBufferObject.device) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_TRANSFER_BIT) -} - -@threadSafety("app") -cmd void vkCmdClearColorImage( - VkCommandBuffer commandBuffer, - VkImage image, - VkImageLayout imageLayout, - const VkClearColorValue* pColor, - u32 rangeCount, - const VkImageSubresourceRange* pRanges) { - commandBufferObject := GetCommandBuffer(commandBuffer) - imageObject := GetImage(image) - assert(commandBufferObject.device == imageObject.device) - - ranges := pRanges[0:rangeCount] - for i in (0 .. rangeCount) { - range := ranges[i] - } - - bindCommandBuffer(commandBuffer, image, imageObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdClearDepthStencilImage( - VkCommandBuffer commandBuffer, - VkImage image, - VkImageLayout imageLayout, - const VkClearDepthStencilValue* pDepthStencil, - u32 rangeCount, - const VkImageSubresourceRange* pRanges) { - commandBufferObject := GetCommandBuffer(commandBuffer) - imageObject := GetImage(image) - assert(commandBufferObject.device == imageObject.device) - - ranges := pRanges[0:rangeCount] - for i in (0 .. rangeCount) { - range := ranges[i] - } - - bindCommandBuffer(commandBuffer, image, imageObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdClearAttachments( - VkCommandBuffer commandBuffer, - u32 attachmentCount, - const VkClearAttachment* pAttachments, - u32 rectCount, - const VkClearRect* pRects) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - rects := pRects[0:rectCount] - for i in (0 .. rectCount) { - rect := rects[i] - } - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdResolveImage( - VkCommandBuffer commandBuffer, - VkImage srcImage, - VkImageLayout srcImageLayout, - VkImage dstImage, - VkImageLayout dstImageLayout, - u32 regionCount, - const VkImageResolve* pRegions) { - commandBufferObject := GetCommandBuffer(commandBuffer) - srcImageObject := GetImage(srcImage) - dstImageObject := GetImage(dstImage) - assert(commandBufferObject.device == srcImageObject.device) - assert(commandBufferObject.device == dstImageObject.device) - - regions := pRegions[0:regionCount] - for i in (0 .. regionCount) { - region := regions[i] - } - - bindCommandBuffer(commandBuffer, srcImage, srcImageObject.memory) - bindCommandBuffer(commandBuffer, dstImage, dstImageObject.memory) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -@threadSafety("app") -cmd void vkCmdSetEvent( - VkCommandBuffer commandBuffer, - VkEvent event, - VkPipelineStageFlags stageMask) { - commandBufferObject := GetCommandBuffer(commandBuffer) - eventObject := GetEvent(event) - assert(commandBufferObject.device == eventObject.device) -} - -@threadSafety("app") -cmd void vkCmdResetEvent( - VkCommandBuffer commandBuffer, - VkEvent event, - VkPipelineStageFlags stageMask) { - commandBufferObject := GetCommandBuffer(commandBuffer) - eventObject := GetEvent(event) - assert(commandBufferObject.device == eventObject.device) -} - -@threadSafety("app") -cmd void vkCmdWaitEvents( - VkCommandBuffer commandBuffer, - u32 eventCount, - const VkEvent* pEvents, - VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, - u32 memoryBarrierCount, - const VkMemoryBarrier* pMemoryBarriers, - u32 bufferMemoryBarrierCount, - const VkBufferMemoryBarrier* pBufferMemoryBarriers, - u32 imageMemoryBarrierCount, - const VkImageMemoryBarrier* pImageMemoryBarriers) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - events := pEvents[0:eventCount] - for i in (0 .. eventCount) { - event := events[i] - eventObject := GetEvent(event) - assert(commandBufferObject.device == eventObject.device) - } - - memoryBarriers := pMemoryBarriers[0:memoryBarrierCount] - for i in (0 .. memoryBarrierCount) { - memoryBarrier := memoryBarriers[i] - } - bufferMemoryBarriers := pBufferMemoryBarriers[0:bufferMemoryBarrierCount] - for i in (0 .. bufferMemoryBarrierCount) { - bufferMemoryBarrier := bufferMemoryBarriers[i] - bufferObject := GetBuffer(bufferMemoryBarrier.buffer) - assert(bufferObject.device == commandBufferObject.device) - } - imageMemoryBarriers := pImageMemoryBarriers[0:imageMemoryBarrierCount] - for i in (0 .. imageMemoryBarrierCount) { - imageMemoryBarrier := imageMemoryBarriers[i] - imageObject := GetImage(imageMemoryBarrier.image) - assert(imageObject.device == commandBufferObject.device) - } -} - -@threadSafety("app") -cmd void vkCmdPipelineBarrier( - VkCommandBuffer commandBuffer, - VkPipelineStageFlags srcStageMask, - VkPipelineStageFlags dstStageMask, - VkDependencyFlags dependencyFlags, - u32 memoryBarrierCount, - const VkMemoryBarrier* pMemoryBarriers, - u32 bufferMemoryBarrierCount, - const VkBufferMemoryBarrier* pBufferMemoryBarriers, - u32 imageMemoryBarrierCount, - const VkImageMemoryBarrier* pImageMemoryBarriers) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - memoryBarriers := pMemoryBarriers[0:memoryBarrierCount] - for i in (0 .. memoryBarrierCount) { - memoryBarrier := memoryBarriers[i] - } - bufferMemoryBarriers := pBufferMemoryBarriers[0:bufferMemoryBarrierCount] - for i in (0 .. bufferMemoryBarrierCount) { - bufferMemoryBarrier := bufferMemoryBarriers[i] - bufferObject := GetBuffer(bufferMemoryBarrier.buffer) - assert(bufferObject.device == commandBufferObject.device) - } - imageMemoryBarriers := pImageMemoryBarriers[0:imageMemoryBarrierCount] - for i in (0 .. imageMemoryBarrierCount) { - imageMemoryBarrier := imageMemoryBarriers[i] - imageObject := GetImage(imageMemoryBarrier.image) - assert(imageObject.device == commandBufferObject.device) - } -} - -@threadSafety("app") -cmd void vkCmdBeginQuery( - VkCommandBuffer commandBuffer, - VkQueryPool queryPool, - u32 query, - VkQueryControlFlags flags) { - commandBufferObject := GetCommandBuffer(commandBuffer) - queryPoolObject := GetQueryPool(queryPool) - assert(commandBufferObject.device == queryPoolObject.device) -} - -@threadSafety("app") -cmd void vkCmdEndQuery( - VkCommandBuffer commandBuffer, - VkQueryPool queryPool, - u32 query) { - commandBufferObject := GetCommandBuffer(commandBuffer) - queryPoolObject := GetQueryPool(queryPool) - assert(commandBufferObject.device == queryPoolObject.device) -} - -@threadSafety("app") -cmd void vkCmdResetQueryPool( - VkCommandBuffer commandBuffer, - VkQueryPool queryPool, - u32 firstQuery, - u32 queryCount) { - commandBufferObject := GetCommandBuffer(commandBuffer) - queryPoolObject := GetQueryPool(queryPool) - assert(commandBufferObject.device == queryPoolObject.device) -} - -@threadSafety("app") -cmd void vkCmdWriteTimestamp( - VkCommandBuffer commandBuffer, - VkPipelineStageFlagBits pipelineStage, - VkQueryPool queryPool, - u32 query) { - commandBufferObject := GetCommandBuffer(commandBuffer) - queryPoolObject := GetQueryPool(queryPool) - assert(commandBufferObject.device == queryPoolObject.device) -} - -@threadSafety("app") -cmd void vkCmdCopyQueryPoolResults( - VkCommandBuffer commandBuffer, - VkQueryPool queryPool, - u32 firstQuery, - u32 queryCount, - VkBuffer dstBuffer, - VkDeviceSize dstOffset, - VkDeviceSize stride, - VkQueryResultFlags flags) { - commandBufferObject := GetCommandBuffer(commandBuffer) - queryPoolObject := GetQueryPool(queryPool) - dstBufferObject := GetBuffer(dstBuffer) - assert(commandBufferObject.device == queryPoolObject.device) - assert(commandBufferObject.device == dstBufferObject.device) -} - -cmd void vkCmdPushConstants( - VkCommandBuffer commandBuffer, - VkPipelineLayout layout, - VkShaderStageFlags stageFlags, - u32 offset, - u32 size, - const void* pValues) { - commandBufferObject := GetCommandBuffer(commandBuffer) - layoutObject := GetPipelineLayout(layout) - assert(commandBufferObject.device == layoutObject.device) -} - -@threadSafety("app") -cmd void vkCmdBeginRenderPass( - VkCommandBuffer commandBuffer, - const VkRenderPassBeginInfo* pRenderPassBegin, - VkSubpassContents contents) { - commandBufferObject := GetCommandBuffer(commandBuffer) - renderPassObject := GetRenderPass(pRenderPassBegin.renderPass) - framebufferObject := GetFramebuffer(pRenderPassBegin.framebuffer) - assert(commandBufferObject.device == renderPassObject.device) - assert(commandBufferObject.device == framebufferObject.device) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -cmd void vkCmdNextSubpass( - VkCommandBuffer commandBuffer, - VkSubpassContents contents) { - commandBufferObject := GetCommandBuffer(commandBuffer) -} - -@threadSafety("app") -cmd void vkCmdEndRenderPass( - VkCommandBuffer commandBuffer) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - commandBufferObject.queueFlags = AddQueueFlag(commandBufferObject.queueFlags, VK_QUEUE_GRAPHICS_BIT) -} - -cmd void vkCmdExecuteCommands( - VkCommandBuffer commandBuffer, - u32 commandBufferCount, - const VkCommandBuffer* pCommandBuffers) { - commandBufferObject := GetCommandBuffer(commandBuffer) - - commandBuffers := pCommandBuffers[0:commandBufferCount] - for i in (0 .. commandBufferCount) { - secondaryCommandBuffer := commandBuffers[i] - secondaryCommandBufferObject := GetCommandBuffer(secondaryCommandBuffer) - assert(commandBufferObject.device == secondaryCommandBufferObject.device) - } -} - -//@vulkan1_1 functions - -@vulkan1_1 -cmd VkResult vkEnumerateInstanceVersion( - u32* pApiVersion) { - return ? -} - -@vulkan1_1 -cmd VkResult vkBindBufferMemory2( - VkDevice device, - u32 bindInfoCount, - const VkBindBufferMemoryInfo* pBindInfos) { - return ? -} - -@vulkan1_1 -cmd VkResult vkBindImageMemory2( - VkDevice device, - u32 bindInfoCount, - const VkBindImageMemoryInfo* pBindInfos) { - return ? -} - -@vulkan1_1 -cmd void vkGetDeviceGroupPeerMemoryFeatures( - VkDevice device, - u32 heapIndex, - u32 localDeviceIndex, - u32 remoteDeviceIndex, - VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) { -} - -@vulkan1_1 -cmd void vkCmdSetDeviceMask( - VkCommandBuffer commandBuffer, - u32 deviceMask) { -} - -@vulkan1_1 -cmd void vkCmdDispatchBase( - VkCommandBuffer commandBuffer, - u32 baseGroupX, - u32 baseGroupY, - u32 baseGroupZ, - u32 groupCountX, - u32 groupCountY, - u32 groupCountZ) { -} - -@threadSafety("system") -@vulkan1_1 -cmd VkResult vkEnumeratePhysicalDeviceGroups( - VkInstance instance, - u32* pPhysicalDeviceGroupCount, - VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) { - instanceObject := GetInstance(instance) - - physicalDeviceGroupCount := as!u32(?) - pPhysicalDeviceGroupCount[0] = physicalDeviceGroupCount - physicalDevices := pPhysicalDeviceGroupProperties[0:physicalDeviceGroupCount] - - for i in (0 .. physicalDeviceGroupCount) { - physicalDevice := ? - physicalDevices[i] = physicalDevice - if !(physicalDevice in State.PhysicalDevices) { - State.PhysicalDevices[physicalDevice] = new!PhysicalDeviceObject(instance: instance) - } - } - - return ? -} - -@vulkan1_1 -cmd void vkGetImageMemoryRequirements2( - VkDevice device, - const VkImageMemoryRequirementsInfo2* pInfo, - VkMemoryRequirements2* pMemoryRequirements) { -} - -@vulkan1_1 -cmd void vkGetBufferMemoryRequirements2( - VkDevice device, - const VkBufferMemoryRequirementsInfo2* pInfo, - VkMemoryRequirements2* pMemoryRequirements) { -} - -@vulkan1_1 -cmd void vkGetImageSparseMemoryRequirements2( - VkDevice device, - const VkImageSparseMemoryRequirementsInfo2* pInfo, - u32* pSparseMemoryRequirementCount, - VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) { -} - -@vulkan1_1 -cmd void vkGetPhysicalDeviceFeatures2( - VkPhysicalDevice physicalDevice, - VkPhysicalDeviceFeatures2* pFeatures) { -} - -@vulkan1_1 -cmd void vkGetPhysicalDeviceProperties2( - VkPhysicalDevice physicalDevice, - VkPhysicalDeviceProperties2* pProperties) { -} - -@vulkan1_1 -cmd void vkGetPhysicalDeviceFormatProperties2( - VkPhysicalDevice physicalDevice, - VkFormat format, - VkFormatProperties2* pFormatProperties) { -} - -@vulkan1_1 -cmd VkResult vkGetPhysicalDeviceImageFormatProperties2( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, - VkImageFormatProperties2* pImageFormatProperties) { - return ? -} - -@vulkan1_1 -cmd void vkGetPhysicalDeviceQueueFamilyProperties2( - VkPhysicalDevice physicalDevice, - u32* pQueueFamilyPropertyCount, - VkQueueFamilyProperties2* pQueueFamilyProperties) { -} - -@vulkan1_1 -cmd void vkGetPhysicalDeviceMemoryProperties2( - VkPhysicalDevice physicalDevice, - VkPhysicalDeviceMemoryProperties2* pMemoryProperties) { -} - -@vulkan1_1 -cmd void vkGetPhysicalDeviceSparseImageFormatProperties2( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, - u32* pPropertyCount, - VkSparseImageFormatProperties2* pProperties) { -} - -@vulkan1_1 -cmd void vkTrimCommandPool( - VkDevice device, - VkCommandPool commandPool, - VkCommandPoolTrimFlags flags) { -} - - -@vulkan1_1 -cmd void vkGetDeviceQueue2( - VkDevice device, - const VkDeviceQueueInfo2* pQueueInfo, - VkQueue* pQueue) { - deviceObject := GetDevice(device) - - queue := ? - pQueue[0] = queue - - if !(queue in State.Queues) { - State.Queues[queue] = new!QueueObject(device: device) - } -} - -@vulkan1_1 -cmd VkResult vkCreateSamplerYcbcrConversion( - VkDevice device, - const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSamplerYcbcrConversion* pYcbcrConversion) { - return ? -} - -@vulkan1_1 -cmd void vkDestroySamplerYcbcrConversion( - VkDevice device, - VkSamplerYcbcrConversion ycbcrConversion, - const VkAllocationCallbacks* pAllocator) { -} - -@vulkan1_1 -cmd VkResult vkCreateDescriptorUpdateTemplate( - VkDevice device, - const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) { - return ? -} - -@vulkan1_1 -cmd void vkDestroyDescriptorUpdateTemplate( - VkDevice device, - VkDescriptorUpdateTemplate descriptorUpdateTemplate, - const VkAllocationCallbacks* pAllocator) { -} - -@vulkan1_1 -cmd void vkUpdateDescriptorSetWithTemplate( - VkDevice device, - VkDescriptorSet descriptorSet, - VkDescriptorUpdateTemplate descriptorUpdateTemplate, - const void* pData) { -} - -@vulkan1_1 -cmd void vkGetPhysicalDeviceExternalBufferProperties( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, - VkExternalBufferProperties* pExternalBufferProperties) { -} - -@vulkan1_1 -cmd void vkGetPhysicalDeviceExternalFenceProperties( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, - VkExternalFenceProperties* pExternalFenceProperties) { -} - -@vulkan1_1 -cmd void vkGetPhysicalDeviceExternalSemaphoreProperties( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, - VkExternalSemaphoreProperties* pExternalSemaphoreProperties) { -} - -@vulkan1_1 -cmd void vkGetDescriptorSetLayoutSupport( - VkDevice device, - const VkDescriptorSetLayoutCreateInfo* pCreateInfo, - VkDescriptorSetLayoutSupport* pSupport) { -} - - -@extension("VK_KHR_surface") // 1 -cmd void vkDestroySurfaceKHR( - VkInstance instance, - VkSurfaceKHR surface, - const VkAllocationCallbacks* pAllocator) { - instanceObject := GetInstance(instance) - surfaceObject := GetSurface(surface) - assert(surfaceObject.instance == instance) - - State.Surfaces[surface] = null -} - -@extension("VK_KHR_surface") // 1 -cmd VkResult vkGetPhysicalDeviceSurfaceSupportKHR( - VkPhysicalDevice physicalDevice, - u32 queueFamilyIndex, - VkSurfaceKHR surface, - VkBool32* pSupported) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - return ? -} - -@extension("VK_KHR_surface") // 1 -cmd VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR( - VkPhysicalDevice physicalDevice, - VkSurfaceKHR surface, - VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - surfaceCapabilities := ? - pSurfaceCapabilities[0] = surfaceCapabilities - - return ? -} - -@extension("VK_KHR_surface") // 1 -cmd VkResult vkGetPhysicalDeviceSurfaceFormatsKHR( - VkPhysicalDevice physicalDevice, - VkSurfaceKHR surface, - u32* pSurfaceFormatCount, - VkSurfaceFormatKHR* pSurfaceFormats) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - count := as!u32(?) - pSurfaceFormatCount[0] = count - surfaceFormats := pSurfaceFormats[0:count] - - for i in (0 .. count) { - surfaceFormat := ? - surfaceFormats[i] = surfaceFormat - } - - return ? -} - -@extension("VK_KHR_surface") // 1 -cmd VkResult vkGetPhysicalDeviceSurfacePresentModesKHR( - VkPhysicalDevice physicalDevice, - VkSurfaceKHR surface, - u32* pPresentModeCount, - VkPresentModeKHR* pPresentModes) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - - count := as!u32(?) - pPresentModeCount[0] = count - presentModes := pPresentModes[0:count] - - for i in (0 .. count) { - presentMode := ? - presentModes[i] = presentMode - } - - return ? -} - -@extension("VK_KHR_swapchain") // 2 -cmd VkResult vkCreateSwapchainKHR( - VkDevice device, - const VkSwapchainCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSwapchainKHR* pSwapchain) { - assert(pCreateInfo.sType == VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR) - deviceObject := GetDevice(device) - - swapchain := ? - pSwapchain[0] = swapchain - State.Swapchains[swapchain] = new!SwapchainObject(device: device) - - return ? -} - -@extension("VK_KHR_swapchain") // 2 -cmd void vkDestroySwapchainKHR( - VkDevice device, - VkSwapchainKHR swapchain, - const VkAllocationCallbacks* pAllocator) { - deviceObject := GetDevice(device) - swapchainObject := GetSwapchain(swapchain) - assert(swapchainObject.device == device) - - State.Swapchains[swapchain] = null -} - -@extension("VK_KHR_swapchain") // 2 -cmd VkResult vkGetSwapchainImagesKHR( - VkDevice device, - VkSwapchainKHR swapchain, - u32* pSwapchainImageCount, - VkImage* pSwapchainImages) { - deviceObject := GetDevice(device) - - count := as!u32(?) - pSwapchainImageCount[0] = count - swapchainImages := pSwapchainImages[0:count] - - for i in (0 .. count) { - swapchainImage := ? - swapchainImages[i] = swapchainImage - State.Images[swapchainImage] = new!ImageObject(device: device) - } - - return ? -} - -@extension("VK_KHR_swapchain") // 2 -cmd VkResult vkAcquireNextImageKHR( - VkDevice device, - VkSwapchainKHR swapchain, - u64 timeout, - VkSemaphore semaphore, - VkFence fence, - u32* pImageIndex) { - deviceObject := GetDevice(device) - swapchainObject := GetSwapchain(swapchain) - - imageIndex := ? - pImageIndex[0] = imageIndex - - return ? -} - -@extension("VK_KHR_swapchain") // 2 -cmd VkResult vkQueuePresentKHR( - VkQueue queue, - const VkPresentInfoKHR* pPresentInfo) { - queueObject := GetQueue(queue) - - presentInfo := ? - pPresentInfo[0] = presentInfo - - return ? -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -cmd VkResult vkGetDeviceGroupPresentCapabilitiesKHR( - VkDevice device, - VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) { - return ? -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -cmd VkResult vkGetDeviceGroupSurfacePresentModesKHR( - VkDevice device, - VkSurfaceKHR surface, - VkDeviceGroupPresentModeFlagsKHR* pModes) { - return ? -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -cmd VkResult vkGetPhysicalDevicePresentRectanglesKHR( - VkPhysicalDevice physicalDevice, - VkSurfaceKHR surface, - u32* pRectCount, - VkRect2D* pRects) { - return ? -} - -@vulkan1_1 -@extension("VK_KHR_swapchain") // 2 -cmd VkResult vkAcquireNextImage2KHR( - VkDevice device, - const VkAcquireNextImageInfoKHR* pAcquireInfo, - u32* pImageIndex) { - return ? -} - -@extension("VK_KHR_display") // 3 -cmd VkResult vkGetPhysicalDeviceDisplayPropertiesKHR( - VkPhysicalDevice physicalDevice, - u32* pPropertyCount, - VkDisplayPropertiesKHR* pProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - return ? -} - -@extension("VK_KHR_display") // 3 -cmd VkResult vkGetPhysicalDeviceDisplayPlanePropertiesKHR( - VkPhysicalDevice physicalDevice, - u32* pPropertyCount, - VkDisplayPlanePropertiesKHR* pProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - return ? -} - -@extension("VK_KHR_display") // 3 -cmd VkResult vkGetDisplayPlaneSupportedDisplaysKHR( - VkPhysicalDevice physicalDevice, - u32 planeIndex, - u32* pDisplayCount, - VkDisplayKHR* pDisplays) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - return ? -} - -@extension("VK_KHR_display") // 3 -cmd VkResult vkGetDisplayModePropertiesKHR( - VkPhysicalDevice physicalDevice, - VkDisplayKHR display, - u32* pPropertyCount, - VkDisplayModePropertiesKHR* pProperties) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - return ? -} - -@extension("VK_KHR_display") // 3 -cmd VkResult vkCreateDisplayModeKHR( - VkPhysicalDevice physicalDevice, - VkDisplayKHR display, - const VkDisplayModeCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDisplayModeKHR* pMode) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - return ? -} - -@extension("VK_KHR_display") // 3 -cmd VkResult vkGetDisplayPlaneCapabilitiesKHR( - VkPhysicalDevice physicalDevice, - VkDisplayModeKHR mode, - u32 planeIndex, - VkDisplayPlaneCapabilitiesKHR* pCapabilities) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - return ? -} - -@extension("VK_KHR_display") // 3 -cmd VkResult vkCreateDisplayPlaneSurfaceKHR( - VkInstance instance, - const VkDisplaySurfaceCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { - return ? -} - -@extension("VK_KHR_display_swapchain") // 4 -cmd VkResult vkCreateSharedSwapchainsKHR( - VkDevice device, - u32 swapchainCount, - const VkSwapchainCreateInfoKHR* pCreateInfos, - const VkAllocationCallbacks* pAllocator, - VkSwapchainKHR* pSwapchains) { - return ? -} - -@extension("VK_KHR_xlib_surface") // 5 -cmd VkResult vkCreateXlibSurfaceKHR( - VkInstance instance, - const VkXlibSurfaceCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { - instanceObject := GetInstance(instance) - return ? -} - -@extension("VK_KHR_xlib_surface") // 5 -cmd VkBool32 vkGetPhysicalDeviceXlibPresentationSupportKHR( - VkPhysicalDevice physicalDevice, - u32 queueFamilyIndex, - platform.Display* dpy, - platform.VisualID visualID) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - return ? -} - -@extension("VK_KHR_xcb_surface") // 6 -cmd VkResult vkCreateXcbSurfaceKHR( - VkInstance instance, - const VkXcbSurfaceCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { - instanceObject := GetInstance(instance) - return ? -} - -@extension("VK_KHR_xcb_surface") // 6 -cmd VkBool32 vkGetPhysicalDeviceXcbPresentationSupportKHR( - VkPhysicalDevice physicalDevice, - u32 queueFamilyIndex, - platform.xcb_connection_t* connection, - platform.xcb_visualid_t visual_id) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - return ? -} - -@extension("VK_KHR_wayland_surface") // 7 -cmd VkResult vkCreateWaylandSurfaceKHR( - VkInstance instance, - const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { - instanceObject := GetInstance(instance) - return ? -} - -@extension("VK_KHR_wayland_surface") // 7 -cmd VkBool32 vkGetPhysicalDeviceWaylandPresentationSupportKHR( - VkPhysicalDevice physicalDevice, - u32 queueFamilyIndex, - platform.wl_display* display) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - return ? -} - -@extension("VK_KHR_android_surface") // 9 -cmd VkResult vkCreateAndroidSurfaceKHR( - VkInstance instance, - const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { - instanceObject := GetInstance(instance) - return ? -} - -@extension("VK_KHR_win32_surface") // 10 -cmd VkResult vkCreateWin32SurfaceKHR( - VkInstance instance, - const VkWin32SurfaceCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { - instanceObject := GetInstance(instance) - return ? -} - -@extension("VK_KHR_win32_surface") // 10 -cmd VkResult vkGetPhysicalDeviceWin32PresentationSupportKHR( - VkPhysicalDevice physicalDevice, - u32 queueFamilyIndex) { - physicalDeviceObject := GetPhysicalDevice(physicalDevice) - return ? -} - -@extension("VK_ANDROID_native_buffer") // 11 -@optional -cmd VkResult vkGetSwapchainGrallocUsageANDROID( - VkDevice device, - VkFormat format, - VkImageUsageFlags imageUsage, - s32* grallocUsage) { - return ? -} - -@extension("VK_ANDROID_native_buffer") // 11 -@optional -cmd VkResult vkGetSwapchainGrallocUsage2ANDROID( - VkDevice device, - VkFormat format, - VkImageUsageFlags imageUsage, - VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, - u64* grallocConsumerUsage, - u64* grallocProducerUsage) { - return ? -} - -@extension("VK_ANDROID_native_buffer") // 11 -cmd VkResult vkAcquireImageANDROID( - VkDevice device, - VkImage image, - int nativeFenceFd, - VkSemaphore semaphore, - VkFence fence) { - return ? -} - -@extension("VK_ANDROID_native_buffer") // 11 -cmd VkResult vkQueueSignalReleaseImageANDROID( - VkQueue queue, - u32 waitSemaphoreCount, - const VkSemaphore* pWaitSemaphores, - VkImage image, - int* pNativeFenceFd) { - return ? -} - -@extension("VK_EXT_debug_report") // 12 -@external type void* PFN_vkDebugReportCallbackEXT -@extension("VK_EXT_debug_report") // 12 -@pfn cmd VkBool32 vkDebugReportCallbackEXT( - VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - u64 object, - platform.size_t location, - s32 messageCode, - const char* pLayerPrefix, - const char* pMessage, - void* pUserData) { - return ? -} - -@extension("VK_EXT_debug_report") // 12 -cmd VkResult vkCreateDebugReportCallbackEXT( - VkInstance instance, - const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDebugReportCallbackEXT* pCallback) { - return ? -} - -@extension("VK_EXT_debug_report") // 12 -cmd void vkDestroyDebugReportCallbackEXT( - VkInstance instance, - VkDebugReportCallbackEXT callback, - const VkAllocationCallbacks* pAllocator) { -} - -@extension("VK_EXT_debug_report") // 12 -cmd void vkDebugReportMessageEXT( - VkInstance instance, - VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objectType, - u64 object, - platform.size_t location, - s32 messageCode, - const char* pLayerPrefix, - const char* pMessage) { -} - -@extension("VK_EXT_debug_marker") // 23 -cmd VkResult vkDebugMarkerSetObjectTagEXT( - VkDevice device, - const VkDebugMarkerObjectTagInfoEXT* pTagInfo) { - return ? -} - -@extension("VK_EXT_debug_marker") // 23 -cmd VkResult vkDebugMarkerSetObjectNameEXT( - VkDevice device, - const VkDebugMarkerObjectNameInfoEXT* pNameInfo) { - return ? -} - -@extension("VK_EXT_debug_marker") // 23 -cmd void vkCmdDebugMarkerBeginEXT( - VkCommandBuffer commandBuffer, - const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { -} - -@extension("VK_EXT_debug_marker") // 23 -cmd void vkCmdDebugMarkerEndEXT( - VkCommandBuffer commandBuffer) { -} - -@extension("VK_EXT_debug_marker") // 23 -cmd void vkCmdDebugMarkerInsertEXT( - VkCommandBuffer commandBuffer, - const VkDebugMarkerMarkerInfoEXT* pMarkerInfo) { -} - -@extension("VK_EXT_transform_feedback") // 29 -cmd void vkCmdBindTransformFeedbackBuffersEXT( - VkCommandBuffer commandBuffer, - u32 firstBinding, - u32 bindingCount, - const VkBuffer* pBuffers, - const VkDeviceSize* pOffsets, - const VkDeviceSize* pSizes) { -} - -@extension("VK_EXT_transform_feedback") // 29 -cmd void vkCmdBeginTransformFeedbackEXT( - VkCommandBuffer commandBuffer, - u32 firstCounterBuffer, - u32 counterBufferCount, - const VkBuffer* pCounterBuffers, - const VkDeviceSize* pCounterBufferOffsets) { -} - -@extension("VK_EXT_transform_feedback") // 29 -cmd void vkCmdEndTransformFeedbackEXT( - VkCommandBuffer commandBuffer, - u32 firstCounterBuffer, - u32 counterBufferCount, - const VkBuffer* pCounterBuffers, - const VkDeviceSize* pCounterBufferOffsets) { -} - -@extension("VK_EXT_transform_feedback") // 29 -cmd void vkCmdBeginQueryIndexedEXT( - VkCommandBuffer commandBuffer, - VkQueryPool queryPool, - u32 query, - VkQueryControlFlags flags, - u32 index) { -} - -@extension("VK_EXT_transform_feedback") // 29 -cmd void vkCmdEndQueryIndexedEXT( - VkCommandBuffer commandBuffer, - VkQueryPool queryPool, - u32 query, - u32 index) { -} - -@extension("VK_EXT_transform_feedback") // 29 -cmd void vkCmdDrawIndirectByteCountEXT( - VkCommandBuffer commandBuffer, - u32 instanceCount, - u32 firstInstance, - VkBuffer counterBuffer, - VkDeviceSize counterBufferOffset, - u32 counterOffset, - u32 vertexStride) { -} - -@extension("VK_AMD_draw_indirect_count") // 34 -cmd void vkCmdDrawIndirectCountAMD( - VkCommandBuffer commandBuffer, - VkBuffer buffer, - VkDeviceSize offset, - VkBuffer countBuffer, - VkDeviceSize countBufferOffset, - u32 maxDrawCount, - u32 stride) { -} - -@extension("VK_AMD_draw_indirect_count") // 34 -cmd void vkCmdDrawIndexedIndirectCountAMD( - VkCommandBuffer commandBuffer, - VkBuffer buffer, - VkDeviceSize offset, - VkBuffer countBuffer, - VkDeviceSize countBufferOffset, - u32 maxDrawCount, - u32 stride) { -} - -@extension("VK_AMD_shader_info") // 43 -cmd VkResult vkGetShaderInfoAMD( - VkDevice device, - VkPipeline pipeline, - VkShaderStageFlagBits shaderStage, - VkShaderInfoTypeAMD infoType, - platform.size_t* pInfoSize, - void* pInfo) { - return ? -} - -@extension("VK_NV_external_memory_capabilities") // 56 -cmd VkResult vkGetPhysicalDeviceExternalImageFormatPropertiesNV( - VkPhysicalDevice physicalDevice, - VkFormat format, - VkImageType type, - VkImageTiling tiling, - VkImageUsageFlags usage, - VkImageCreateFlags flags, - VkExternalMemoryHandleTypeFlagsNV externalHandleType, - VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties) { - return ? -} - -@extension("VK_NV_external_memory_win32") // 58 -cmd VkResult vkGetMemoryWin32HandleNV( - VkDevice device, - VkDeviceMemory memory, - VkExternalMemoryHandleTypeFlagsNV handleType, - platform.HANDLE* pHandle) { - return ? -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -cmd void vkGetPhysicalDeviceFeatures2KHR( - VkPhysicalDevice physicalDevice, - VkPhysicalDeviceFeatures2KHR* pFeatures) { -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -cmd void vkGetPhysicalDeviceProperties2KHR( - VkPhysicalDevice physicalDevice, - VkPhysicalDeviceProperties2KHR* pProperties) { -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -cmd void vkGetPhysicalDeviceFormatProperties2KHR( - VkPhysicalDevice physicalDevice, - VkFormat format, - VkFormatProperties2KHR* pFormatProperties) { -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -cmd VkResult vkGetPhysicalDeviceImageFormatProperties2KHR( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, - VkImageFormatProperties2KHR* pImageFormatProperties) { - return ? -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -cmd void vkGetPhysicalDeviceQueueFamilyProperties2KHR( - VkPhysicalDevice physicalDevice, - u32* pQueueFamilyPropertyCount, - VkQueueFamilyProperties2KHR* pQueueFamilyProperties) { -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -cmd void vkGetPhysicalDeviceMemoryProperties2KHR( - VkPhysicalDevice physicalDevice, - VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties) { -} - -@extension("VK_KHR_get_physical_device_properties2") // 60 -cmd void vkGetPhysicalDeviceSparseImageFormatProperties2KHR( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, - u32* pPropertyCount, - VkSparseImageFormatProperties2KHR* pProperties) { -} - -@extension("VK_KHR_device_group") // 61 -cmd void vkGetDeviceGroupPeerMemoryFeaturesKHR( - VkDevice device, - u32 heapIndex, - u32 localDeviceIndex, - u32 remoteDeviceIndex, - VkPeerMemoryFeatureFlagsKHR* pPeerMemoryFeatures) { -} - -@extension("VK_KHR_device_group") // 61 -cmd void vkCmdSetDeviceMaskKHR( - VkCommandBuffer commandBuffer, - u32 deviceMask) { -} - - -@extension("VK_KHR_device_group") // 61 -cmd void vkCmdDispatchBaseKHR( - VkCommandBuffer commandBuffer, - u32 baseGroupX, - u32 baseGroupY, - u32 baseGroupZ, - u32 groupCountX, - u32 groupCountY, - u32 groupCountZ) { -} - -@extension("VK_NN_vi_surface") // 63 -cmd VkResult vkCreateViSurfaceNN( - VkInstance instance, - const VkViSurfaceCreateInfoNN* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { - return ? -} - -@extension("VK_KHR_maintenance1") // 70 -cmd void vkTrimCommandPoolKHR( - VkDevice device, - VkCommandPool commandPool, - VkCommandPoolTrimFlagsKHR flags) { -} - -@extension("VK_KHR_device_group_creation") // 71 -@threadSafety("system") -cmd VkResult vkEnumeratePhysicalDeviceGroupsKHR( - VkInstance instance, - u32* pPhysicalDeviceGroupCount, - VkPhysicalDeviceGroupPropertiesKHR* pPhysicalDeviceGroupProperties) { - instanceObject := GetInstance(instance) - - physicalDeviceGroupCount := as!u32(?) - pPhysicalDeviceGroupCount[0] = physicalDeviceGroupCount - physicalDevices := pPhysicalDeviceGroupProperties[0:physicalDeviceGroupCount] - - for i in (0 .. physicalDeviceGroupCount) { - physicalDevice := ? - physicalDevices[i] = physicalDevice - if !(physicalDevice in State.PhysicalDevices) { - State.PhysicalDevices[physicalDevice] = new!PhysicalDeviceObject(instance: instance) - } - } - - return ? -} - -@extension("VK_KHR_external_memory_capabilities") // 72 -cmd void vkGetPhysicalDeviceExternalBufferPropertiesKHR( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceExternalBufferInfoKHR* pExternalBufferInfo, - VkExternalBufferPropertiesKHR* pExternalBufferProperties) { -} - -@extension("VK_KHR_external_memory_win32") // 74 -cmd VkResult vkGetMemoryWin32HandleKHR( - VkDevice device, - const VkMemoryGetWin32HandleInfoKHR* pGetWin32HandleInfo, - platform.HANDLE* pHandle) { - return ? -} - -@extension("VK_KHR_external_memory_win32") // 74 -cmd VkResult vkGetMemoryWin32HandlePropertiesKHR( - VkDevice device, - VkExternalMemoryHandleTypeFlagBitsKHR handleType, - platform.HANDLE handle, - VkMemoryWin32HandlePropertiesKHR* pMemoryWin32HandleProperties) { - return ? -} - -@extension("VK_KHR_external_memory_fd") // 75 -cmd VkResult vkGetMemoryFdKHR( - VkDevice device, - const VkMemoryGetFdInfoKHR* pGetFdInfo, - s32* pFd) { - return ? -} - -@extension("VK_KHR_external_memory_fd") // 75 -cmd VkResult vkGetMemoryFdPropertiesKHR( - VkDevice device, - VkExternalMemoryHandleTypeFlagBitsKHR handleType, - s32 fd, - VkMemoryFdPropertiesKHR* pMemoryFdProperties) { - return ? -} - -@extension("VK_KHR_external_semaphore_capabilities") // 77 -cmd void vkGetPhysicalDeviceExternalSemaphorePropertiesKHR( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceExternalSemaphoreInfoKHR* pExternalSemaphoreInfo, - VkExternalSemaphorePropertiesKHR* pExternalSemaphoreProperties) { -} - -@extension("VK_KHR_external_semaphore_win32") // 79 -cmd VkResult vkImportSemaphoreWin32HandleKHR( - VkDevice device, - const VkImportSemaphoreWin32HandleInfoKHR* pImportSemaphoreWin32HandleInfo) { - return ? -} - -@extension("VK_KHR_external_semaphore_win32") // 79 -cmd VkResult vkGetSemaphoreWin32HandleKHR( - VkDevice device, - const VkSemaphoreGetWin32HandleInfoKHR* pGetWin32HandleInfo, - platform.HANDLE* pHandle) { - return ? -} - -@extension("VK_KHR_external_semaphore_fd") // 80 -cmd VkResult vkImportSemaphoreFdKHR( - VkDevice device, - const VkImportSemaphoreFdInfoKHR* pImportSemaphoreFdInfo) { - return ? -} - -@extension("VK_KHR_external_semaphore_fd") // 80 -cmd VkResult vkGetSemaphoreFdKHR( - VkDevice device, - const VkSemaphoreGetFdInfoKHR* pGetFdInfo, - s32* pFd) { - return ? -} - -@extension("VK_KHR_push_descriptor") // 81 -cmd void vkCmdPushDescriptorSetKHR( - VkCommandBuffer commandBuffer, - VkPipelineBindPoint pipelineBindPoint, - VkPipelineLayout layout, - u32 set, - u32 descriptorWriteCount, - const VkWriteDescriptorSet* pDescriptorWrites) { -} - -@extension("VK_EXT_conditional_rendering") // 82 -cmd void vkCmdBeginConditionalRenderingEXT( - VkCommandBuffer commandBuffer, - const VkConditionalRenderingBeginInfoEXT* pConditionalRenderingBegin) { -} - -@extension("VK_EXT_conditional_rendering") // 82 -cmd void vkCmdEndConditionalRenderingEXT( - VkCommandBuffer commandBuffer) { -} - -@extension("VK_KHR_descriptor_update_template") // 86 -cmd VkResult vkCreateDescriptorUpdateTemplateKHR( - VkDevice device, - const VkDescriptorUpdateTemplateCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDescriptorUpdateTemplateKHR* pDescriptorUpdateTemplate) { - return ? -} - -@extension("VK_KHR_descriptor_update_template") // 86 -cmd void vkDestroyDescriptorUpdateTemplateKHR( - VkDevice device, - VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, - const VkAllocationCallbacks* pAllocator) { -} - -@extension("VK_KHR_descriptor_update_template") // 86 -cmd void vkUpdateDescriptorSetWithTemplateKHR( - VkDevice device, - VkDescriptorSet descriptorSet, - VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, - const void* pData) { -} - -@extension("VK_KHR_descriptor_update_template") // 86 -cmd void vkCmdPushDescriptorSetWithTemplateKHR( - VkCommandBuffer commandBuffer, - VkDescriptorUpdateTemplateKHR descriptorUpdateTemplate, - VkPipelineLayout layout, - u32 set, - const void* pData) { -} - -@extension("VK_NVX_device_generated_commands") // 87 -cmd void vkCmdProcessCommandsNVX( - VkCommandBuffer commandBuffer, - const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo) { -} - -@extension("VK_NVX_device_generated_commands") // 87 -cmd void vkCmdReserveSpaceForCommandsNVX( - VkCommandBuffer commandBuffer, - const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo) { -} - -@extension("VK_NVX_device_generated_commands") // 87 -cmd VkResult vkCreateIndirectCommandsLayoutNVX( - VkDevice device, - const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout) { - return ? -} - -@extension("VK_NVX_device_generated_commands") // 87 -cmd void vkDestroyIndirectCommandsLayoutNVX( - VkDevice device, - VkIndirectCommandsLayoutNVX indirectCommandsLayout, - const VkAllocationCallbacks* pAllocator) { -} - -@extension("VK_NVX_device_generated_commands") // 87 -cmd VkResult vkCreateObjectTableNVX( - VkDevice device, - const VkObjectTableCreateInfoNVX* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkObjectTableNVX* pObjectTable) { - return ? -} - -@extension("VK_NVX_device_generated_commands") // 87 -cmd void vkDestroyObjectTableNVX( - VkDevice device, - VkObjectTableNVX objectTable, - const VkAllocationCallbacks* pAllocator) { -} - -@extension("VK_NVX_device_generated_commands") // 87 -cmd VkResult vkRegisterObjectsNVX( - VkDevice device, - VkObjectTableNVX objectTable, - u32 objectCount, - const VkObjectTableEntryNVX* const* ppObjectTableEntries, - const u32* pObjectIndices) { - return ? -} - -@extension("VK_NVX_device_generated_commands") // 87 -cmd VkResult vkUnregisterObjectsNVX( - VkDevice device, - VkObjectTableNVX objectTable, - u32 objectCount, - const VkObjectEntryTypeNVX* pObjectEntryTypes, - const u32* pObjectIndices) { - return ? -} - -@extension("VK_NVX_device_generated_commands") // 87 -cmd void vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX( - VkPhysicalDevice physicalDevice, - VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, - VkDeviceGeneratedCommandsLimitsNVX* pLimits) { -} - -@extension("VK_NV_clip_space_w_scaling") // 88 -cmd void vkCmdSetViewportWScalingNV( - VkCommandBuffer commandBuffer, - u32 firstViewport, - u32 viewportCount, - const VkViewportWScalingNV* pViewportWScalings) { -} - -@extension("VK_EXT_direct_mode_display") // 89 -cmd VkResult vkReleaseDisplayEXT( - VkPhysicalDevice physicalDevice, - VkDisplayKHR display) { - return ? -} - -@extension("VK_EXT_acquire_xlib_display") // 90 -cmd VkResult vkAcquireXlibDisplayEXT( - VkPhysicalDevice physicalDevice, - platform.Display* dpy, - VkDisplayKHR display) { - return ? -} - -@extension("VK_EXT_acquire_xlib_display") // 90 -cmd VkResult vkGetRandROutputDisplayEXT( - VkPhysicalDevice physicalDevice, - platform.Display* dpy, - platform.RROutput rrOutput, - VkDisplayKHR* pDisplay) { - return ? -} - -@extension("VK_EXT_display_surface_counter") // 91 -cmd VkResult vkGetPhysicalDeviceSurfaceCapabilities2EXT( - VkPhysicalDevice physicalDevice, - VkSurfaceKHR surface, - VkSurfaceCapabilities2EXT* pSurfaceCapabilities) { - return ? -} - -@extension("VK_EXT_display_control") // 92 -cmd VkResult vkDisplayPowerControlEXT( - VkDevice device, - VkDisplayKHR display, - const VkDisplayPowerInfoEXT* pDisplayPowerInfo) { - return ? -} - -@extension("VK_EXT_display_control") // 92 -cmd VkResult vkRegisterDeviceEventEXT( - VkDevice device, - const VkDeviceEventInfoEXT* pDeviceEventInfo, - const VkAllocationCallbacks* pAllocator, - VkFence* pFence) { - return ? -} - -@extension("VK_EXT_display_control") // 92 -cmd VkResult vkRegisterDisplayEventEXT( - VkDevice device, - VkDisplayKHR display, - const VkDisplayEventInfoEXT* pDisplayEventInfo, - const VkAllocationCallbacks* pAllocator, - VkFence* pFence) { - return ? -} - -@extension("VK_EXT_display_control") // 92 -cmd VkResult vkGetSwapchainCounterEXT( - VkDevice device, - VkSwapchainKHR swapchain, - VkSurfaceCounterFlagBitsEXT counter, - u64* pCounterValue) { - return ? -} - -@extension("VK_GOOGLE_display_timing") // 93 -cmd VkResult vkGetRefreshCycleDurationGOOGLE( - VkDevice device, - VkSwapchainKHR swapchain, - VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) { - deviceObject := GetDevice(device) - swapchainObject := GetSwapchain(swapchain) - - displayTimingProperties := ? - pDisplayTimingProperties[0] = displayTimingProperties - - return ? -} - -@extension("VK_GOOGLE_display_timing") // 93 -cmd VkResult vkGetPastPresentationTimingGOOGLE( - VkDevice device, - VkSwapchainKHR swapchain, - u32* pPresentationTimingCount, - VkPastPresentationTimingGOOGLE* pPresentationTimings) { - return ? -} - -@extension("VK_EXT_discard_rectangles") // 100 -cmd void vkCmdSetDiscardRectangleEXT( - VkCommandBuffer commandBuffer, - u32 firstDiscardRectangle, - u32 discardRectangleCount, - const VkRect2D* pDiscardRectangles) { -} - -@extension("VK_EXT_hdr_metadata") // 106 -cmd void vkSetHdrMetadataEXT( - VkDevice device, - u32 swapchainCount, - const VkSwapchainKHR* pSwapchains, - const VkHdrMetadataEXT* pMetadata) { -} - -@extension("VK_KHR_create_renderpass2") // 110 -cmd VkResult vkCreateRenderPass2KHR( - VkDevice device, - const VkRenderPassCreateInfo2KHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkRenderPass* pRenderPass) { - return ? -} - -@extension("VK_KHR_create_renderpass2") // 110 -cmd void vkCmdBeginRenderPass2KHR( - VkCommandBuffer commandBuffer, - const VkRenderPassBeginInfo* pRenderPassBegin, - const VkSubpassBeginInfoKHR* pSubpassBeginInfo) { -} - -@extension("VK_KHR_create_renderpass2") // 110 -cmd void vkCmdNextSubpass2KHR( - VkCommandBuffer commandBuffer, - const VkSubpassBeginInfoKHR* pSubpassBeginInfo, - const VkSubpassEndInfoKHR* pSubpassEndInfo) { -} - -@extension("VK_KHR_create_renderpass2") // 110 -cmd void vkCmdEndRenderPass2KHR( - VkCommandBuffer commandBuffer, - const VkSubpassEndInfoKHR* pSubpassEndInfo) { -} - -@extension("VK_KHR_shared_presentable_image") // 112 -cmd VkResult vkGetSwapchainStatusKHR( - VkDevice device, - VkSwapchainKHR swapchain) { - return ? -} - -@extension("VK_KHR_external_fence_capabilities") // 113 -cmd void vkGetPhysicalDeviceExternalFencePropertiesKHR( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceExternalFenceInfoKHR* pExternalFenceInfo, - VkExternalFencePropertiesKHR* pExternalFenceProperties) { -} - -@extension("VK_KHR_external_fence_win32") // 115 -cmd VkResult vkImportFenceWin32HandleKHR( - VkDevice device, - const VkImportFenceWin32HandleInfoKHR* pImportFenceWin32HandleInfo) { - return ? -} - -@extension("VK_KHR_external_fence_win32") // 115 -cmd VkResult vkGetFenceWin32HandleKHR( - VkDevice device, - const VkFenceGetWin32HandleInfoKHR* pGetWin32HandleInfo, - platform.HANDLE* pHandle) { - return ? -} - -@extension("VK_KHR_external_fence_fd") // 116 -cmd VkResult vkImportFenceFdKHR( - VkDevice device, - const VkImportFenceFdInfoKHR* pImportFenceFdInfo) { - return ? -} - -@extension("VK_KHR_external_fence_fd") // 116 -cmd VkResult vkGetFenceFdKHR( - VkDevice device, - const VkFenceGetFdInfoKHR* pGetFdInfo, - int* pFd) { - return ? -} - -@extension("VK_KHR_get_surface_capabilities2") // 120 -cmd VkResult vkGetPhysicalDeviceSurfaceCapabilities2KHR( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, - VkSurfaceCapabilities2KHR* pSurfaceCapabilities) { - return ? -} - -@extension("VK_KHR_get_surface_capabilities2") // 120 -cmd VkResult vkGetPhysicalDeviceSurfaceFormats2KHR( - VkPhysicalDevice physicalDevice, - const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, - u32* pSurfaceFormatCount, - VkSurfaceFormat2KHR* pSurfaceFormats) { - return ? -} - -@extension("VK_KHR_display_properties2") // 122 -cmd VkResult vkGetPhysicalDeviceDisplayProperties2KHR( - VkPhysicalDevice physicalDevice, - u32* pPropertyCount, - VkDisplayProperties2KHR* pProperties) { - return ? -} - -@extension("VK_KHR_display_properties2") // 122 -cmd VkResult vkGetPhysicalDeviceDisplayPlaneProperties2KHR( - VkPhysicalDevice physicalDevice, - u32* pPropertyCount, - VkDisplayPlaneProperties2KHR* pProperties) { - return ? -} - -@extension("VK_KHR_display_properties2") // 122 -cmd VkResult vkGetDisplayModeProperties2KHR( - VkPhysicalDevice physicalDevice, - VkDisplayKHR display, - u32* pPropertyCount, - VkDisplayModeProperties2KHR* pProperties) { - return ? -} - -@extension("VK_KHR_display_properties2") // 122 -cmd VkResult vkGetDisplayPlaneCapabilities2KHR( - VkPhysicalDevice physicalDevice, - const VkDisplayPlaneInfo2KHR* pDisplayPlaneInfo, - VkDisplayPlaneCapabilities2KHR* pCapabilities) { - return ? -} - -@extension("VK_MVK_ios_surface") // 123 -cmd VkResult vkCreateIOSSurfaceMVK( - VkInstance instance, - const VkIOSSurfaceCreateInfoMVK* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { - return ? -} - -@extension("VK_MVK_macos_surface") // 124 -cmd VkResult vkCreateMacOSSurfaceMVK( - VkInstance instance, - const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { - return ? -} - -@extension("VK_EXT_debug_utils") // 129 -@external type void* PFN_vkDebugUtilsMessengerCallbackEXT -@extension("VK_EXT_debug_utils") // 129 -@pfn cmd VkBool32 vkDebugUtilsMessengerCallbackEXT( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData) { - return ? -} - -@extension("VK_EXT_debug_utils") // 129 -cmd VkResult vkSetDebugUtilsObjectNameEXT( - VkDevice device, - const VkDebugUtilsObjectNameInfoEXT* pNameInfo) { - return ? -} - -@extension("VK_EXT_debug_utils") // 129 -cmd VkResult vkSetDebugUtilsObjectTagEXT( - VkDevice device, - const VkDebugUtilsObjectTagInfoEXT* pTagInfo) { - return ? -} - -@extension("VK_EXT_debug_utils") // 129 -cmd void vkQueueBeginDebugUtilsLabelEXT( - VkQueue queue, - const VkDebugUtilsLabelEXT* pLabelInfo) { -} - -@extension("VK_EXT_debug_utils") // 129 -cmd void vkQueueEndDebugUtilsLabelEXT(VkQueue queue) { -} - -@extension("VK_EXT_debug_utils") // 129 -cmd void vkQueueInsertDebugUtilsLabelEXT( - VkQueue queue, - const VkDebugUtilsLabelEXT* pLabelInfo) { -} - -@extension("VK_EXT_debug_utils") // 129 -cmd void vkCmdBeginDebugUtilsLabelEXT( - VkCommandBuffer commandBuffer, - const VkDebugUtilsLabelEXT* pLabelInfo) { -} - -@extension("VK_EXT_debug_utils") // 129 -cmd void vkCmdEndDebugUtilsLabelEXT(VkCommandBuffer commandBuffer) { -} - -@extension("VK_EXT_debug_utils") // 129 -cmd void vkCmdInsertDebugUtilsLabelEXT( - VkCommandBuffer commandBuffer, - const VkDebugUtilsLabelEXT* pLabelInfo) { -} - -@extension("VK_EXT_debug_utils") // 129 -cmd VkResult vkCreateDebugUtilsMessengerEXT( - VkInstance instance, - const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkDebugUtilsMessengerEXT* pMessenger) { - return ? -} - -@extension("VK_EXT_debug_utils") // 129 -cmd void vkDestroyDebugUtilsMessengerEXT( - VkInstance instance, - VkDebugUtilsMessengerEXT messenger, - const VkAllocationCallbacks* pAllocator) { -} - -@extension("VK_EXT_debug_utils") // 129 -cmd void vkSubmitDebugUtilsMessageEXT( - VkInstance instance, - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageTypes, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData) { -} - -@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130 -@vulkan1_1 // extension requires 1.1, and should become non-optional when 1.1 does -cmd VkResult vkGetAndroidHardwareBufferPropertiesANDROID( - VkDevice device, - const platform.AHardwareBuffer* buffer, - VkAndroidHardwareBufferPropertiesANDROID* pProperties) { - return ? -} - -@extension("VK_ANDROID_external_memory_android_hardware_buffer") // 130 -@vulkan1_1 // extension requires 1.1, and should become non-optional when 1.1 does -cmd VkResult vkGetMemoryAndroidHardwareBufferANDROID( - VkDevice device, - const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, - platform.AHardwareBuffer** pBuffer) { - return ? -} - -@extension("VK_EXT_sample_locations") // 144 -cmd void vkCmdSetSampleLocationsEXT( - VkCommandBuffer commandBuffer, - const VkSampleLocationsInfoEXT* pSampleLocationsInfo) { -} - -@extension("VK_EXT_sample_locations") // 144 -cmd void vkGetPhysicalDeviceMultisamplePropertiesEXT( - VkPhysicalDevice physicalDevice, - VkSampleCountFlagBits samples, - VkMultisamplePropertiesEXT* pMultisampleProperties) { -} - -@extension("VK_KHR_get_memory_requirements2") // 147 -cmd void vkGetImageMemoryRequirements2KHR( - VkDevice device, - const VkImageMemoryRequirementsInfo2KHR* pInfo, - VkMemoryRequirements2KHR* pMemoryRequirements) { -} - -@extension("VK_KHR_get_memory_requirements2") // 147 -cmd void vkGetBufferMemoryRequirements2KHR( - VkDevice device, - const VkBufferMemoryRequirementsInfo2KHR* pInfo, - VkMemoryRequirements2KHR* pMemoryRequirements) { -} - -@extension("VK_KHR_get_memory_requirements2") // 147 -cmd void vkGetImageSparseMemoryRequirements2KHR( - VkDevice device, - const VkImageSparseMemoryRequirementsInfo2KHR* pInfo, - u32* pSparseMemoryRequirementCount, - VkSparseImageMemoryRequirements2KHR* pSparseMemoryRequirements) { -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -cmd VkResult vkCreateSamplerYcbcrConversionKHR( - VkDevice device, - const VkSamplerYcbcrConversionCreateInfoKHR* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSamplerYcbcrConversionKHR* pYcbcrConversion) { - return ? -} - -@extension("VK_KHR_sampler_ycbcr_conversion") // 157 -cmd void vkDestroySamplerYcbcrConversionKHR( - VkDevice device, - VkSamplerYcbcrConversionKHR ycbcrConversion, - const VkAllocationCallbacks* pAllocator) { -} - -@extension("VK_KHR_bind_memory2") // 158 -cmd VkResult vkBindBufferMemory2KHR( - VkDevice device, - u32 bindInfoCount, - const VkBindBufferMemoryInfoKHR* pBindInfos) { - return ? -} - -@extension("VK_KHR_bind_memory2") // 158 -cmd VkResult vkBindImageMemory2KHR( - VkDevice device, - u32 bindInfoCount, - const VkBindImageMemoryInfoKHR* pBindInfos) { - return ? -} - -@extension("VK_EXT_image_drm_format_modifier") // 159 -cmd VkResult vkGetImageDrmFormatModifierPropertiesEXT( - VkDevice device, - VkImage image, - VkImageDrmFormatModifierPropertiesEXT* pProperties) { - return ? -} - -@extension("VK_EXT_validation_cache") // 161 -cmd VkResult vkCreateValidationCacheEXT( - VkDevice device, - const VkValidationCacheCreateInfoEXT* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkValidationCacheEXT* pValidationCache) { - return ? -} - -@extension("VK_EXT_validation_cache") // 161 -cmd void vkDestroyValidationCacheEXT( - VkDevice device, - VkValidationCacheEXT validationCache, - const VkAllocationCallbacks* pAllocator) { -} - -@extension("VK_EXT_validation_cache") // 161 -cmd VkResult vkMergeValidationCachesEXT( - VkDevice device, - VkValidationCacheEXT dstCache, - u32 srcCacheCount, - const VkValidationCacheEXT* pSrcCaches) { - return ? -} - -@extension("VK_EXT_validation_cache") // 161 -cmd VkResult vkGetValidationCacheDataEXT( - VkDevice device, - VkValidationCacheEXT validationCache, - platform.size_t* pDataSize, - void* pData) { - return ? -} - -@extension("VK_NV_shading_rate_image") // 165 -cmd void vkCmdBindShadingRateImageNV( - VkCommandBuffer commandBuffer, - VkImageView imageView, - VkImageLayout imageLayout) { -} - -@extension("VK_NV_shading_rate_image") // 165 -cmd void vkCmdSetViewportShadingRatePaletteNV( - VkCommandBuffer commandBuffer, - u32 firstViewport, - u32 viewportCount, - const VkShadingRatePaletteNV* pShadingRatePalettes) { -} - -@extension("VK_NV_shading_rate_image") // 165 -cmd void vkCmdSetCoarseSampleOrderNV( - VkCommandBuffer commandBuffer, - VkCoarseSampleOrderTypeNV sampleOrderType, - u32 customSampleOrderCount, - const VkCoarseSampleOrderCustomNV* pCustomSampleOrders) { -} - -@extension("VK_NV_ray_tracing") // 166 -cmd VkResult vkCreateAccelerationStructureNV( - VkDevice device, - const VkAccelerationStructureCreateInfoNV* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkAccelerationStructureNV* pAccelerationStructure) { - return ? -} - -@extension("VK_NV_ray_tracing") // 166 -cmd void vkDestroyAccelerationStructureNV( - VkDevice device, - VkAccelerationStructureNV accelerationStructure, - const VkAllocationCallbacks* pAllocator) { -} - -@extension("VK_NV_ray_tracing") // 166 -cmd void vkGetAccelerationStructureMemoryRequirementsNV( - VkDevice device, - const VkAccelerationStructureMemoryRequirementsInfoNV* pInfo, - VkMemoryRequirements2KHR* pMemoryRequirements) { -} - -@extension("VK_NV_ray_tracing") // 166 -cmd VkResult vkBindAccelerationStructureMemoryNV( - VkDevice device, - u32 bindInfoCount, - const VkBindAccelerationStructureMemoryInfoNV* pBindInfos) { - return ? -} - -@extension("VK_NV_ray_tracing") // 166 -cmd void vkCmdBuildAccelerationStructureNV( - VkCommandBuffer commandBuffer, - const VkAccelerationStructureInfoNV* pInfo, - VkBuffer instanceData, - VkDeviceSize instanceOffset, - VkBool32 update, - VkAccelerationStructureNV dst, - VkAccelerationStructureNV src, - VkBuffer scratch, - VkDeviceSize scratchOffset) { -} - -@extension("VK_NV_ray_tracing") // 166 -cmd void vkCmdCopyAccelerationStructureNV( - VkCommandBuffer commandBuffer, - VkAccelerationStructureNV dst, - VkAccelerationStructureNV src, - VkCopyAccelerationStructureModeNV mode) { -} - -@extension("VK_NV_ray_tracing") // 166 -cmd void vkCmdTraceRaysNV( - VkCommandBuffer commandBuffer, - VkBuffer raygenShaderBindingTableBuffer, - VkDeviceSize raygenShaderBindingOffset, - VkBuffer missShaderBindingTableBuffer, - VkDeviceSize missShaderBindingOffset, - VkDeviceSize missShaderBindingStride, - VkBuffer hitShaderBindingTableBuffer, - VkDeviceSize hitShaderBindingOffset, - VkDeviceSize hitShaderBindingStride, - VkBuffer callableShaderBindingTableBuffer, - VkDeviceSize callableShaderBindingOffset, - VkDeviceSize callableShaderBindingStride, - u32 width, - u32 height, - u32 depth) { -} - -@extension("VK_NV_ray_tracing") // 166 -cmd VkResult vkCreateRaytracingPipelinesNV( - VkDevice device, - VkPipelineCache pipelineCache, - u32 createInfoCount, - const VkRayTracingPipelineCreateInfoNV* pCreateInfos, - const VkAllocationCallbacks* pAllocator, - VkPipeline* pPipelines) { - return ? -} - -@extension("VK_NV_ray_tracing") // 166 -cmd VkResult vkGetRaytracingShaderHandlesNV( - VkDevice device, - VkPipeline pipeline, - u32 firstGroup, - u32 groupCount, - platform.size_t dataSize, - void* pData) { - return ? -} - -@extension("VK_NV_ray_tracing") // 166 -cmd VkResult vkGetAccelerationStructureHandleNV( - VkDevice device, - VkAccelerationStructureNV accelerationStructure, - platform.size_t dataSize, - void* pData) { - return ? -} - -@extension("VK_NV_ray_tracing") // 166 -cmd void vkCmdWriteAccelerationStructurePropertiesNV( - VkCommandBuffer commandBuffer, - u32 accelerationStructureCount, - const VkAccelerationStructureNV* pAccelerationStructures, - VkQueryType queryType, - VkQueryPool queryPool, - u32 firstQuery) { -} - -@extension("VK_NV_ray_tracing") // 166 -cmd VkResult vkCompileDeferredNV( - VkDevice device, - VkPipeline pipeline, - u32 shader) { - return ? -} - -@extension("VK_KHR_maintenance3") // 169 -cmd void vkGetDescriptorSetLayoutSupportKHR( - VkDevice device, - const VkDescriptorSetLayoutCreateInfo* pCreateInfo, - VkDescriptorSetLayoutSupportKHR* pSupport) { -} - -@extension("VK_KHR_draw_indirect_count") // 170 -cmd void vkCmdDrawIndirectCountKHR( - VkCommandBuffer commandBuffer, - VkBuffer buffer, - VkDeviceSize offset, - VkBuffer countBuffer, - VkDeviceSize countBufferOffset, - u32 maxDrawCount, - u32 stride) { -} - -@extension("VK_KHR_draw_indirect_count") // 170 -cmd void vkCmdDrawIndexedIndirectCountKHR( - VkCommandBuffer commandBuffer, - VkBuffer buffer, - VkDeviceSize offset, - VkBuffer countBuffer, - VkDeviceSize countBufferOffset, - u32 maxDrawCount, - u32 stride) { -} - -@extension("VK_EXT_external_memory_host") // 179 -cmd VkResult vkGetMemoryHostPointerPropertiesEXT( - VkDevice device, - VkExternalMemoryHandleTypeFlagBits handleType, - const void* pHostPointer, - VkMemoryHostPointerPropertiesEXT* pMemoryHostPointerProperties) { - return ? -} - -@extension("VK_AMD_buffer_marker") // 180 -cmd void vkCmdWriteBufferMarkerAMD( - VkCommandBuffer commandBuffer, - VkPipelineStageFlagBits pipelineStage, - VkBuffer dstBuffer, - VkDeviceSize dstOffset, - u32 marker) { -} - -@extension("VK_EXT_calibrated_timestamps") // 185 -cmd VkResult vkGetPhysicalDeviceCalibrateableTimeDomainsEXT( - VkPhysicalDevice physicalDevice, - u32* pTimeDomainCount, - VkTimeDomainEXT* pTimeDomains) { - return ? -} - -@extension("VK_EXT_calibrated_timestamps") // 185 -cmd VkResult vkGetCalibratedTimestampsEXT( - VkDevice device, - u32 timestampCount, - const VkCalibratedTimestampInfoEXT* pTimestampInfos, - u64* pTimestamps, - u64* pMaxDeviation) { - return ? -} - -@extension("VK_NV_mesh_shader") // 203 -cmd void vkCmdDrawMeshTasksNV( - VkCommandBuffer commandBuffer, - u32 taskCount, - u32 firstTask) { -} - -@extension("VK_NV_mesh_shader") // 203 -cmd void vkCmdDrawMeshTasksIndirectNV( - VkCommandBuffer commandBuffer, - VkBuffer buffer, - VkDeviceSize offset, - u32 drawCount, - u32 stride) { -} - -@extension("VK_NV_mesh_shader") // 203 -cmd void vkCmdDrawMeshTasksIndirectCountNV( - VkCommandBuffer commandBuffer, - VkBuffer buffer, - VkDeviceSize offset, - VkBuffer countBuffer, - VkDeviceSize countBufferOffset, - u32 maxDrawCount, - u32 stride) { -} - -@extension("VK_NV_scissor_exclusive") // 206 -cmd void vkCmdSetExclusiveScissorNV( - VkCommandBuffer commandBuffer, - u32 firstExclusiveScissor, - u32 exclusiveScissorCount, - const VkRect2D* pExclusiveScissors) { -} - -@extension("VK_NV_device_diagnostic_checkpoints") // 207 -cmd void vkCmdSetCheckpointNV( - VkCommandBuffer commandBuffer, - const void* pCheckpointMarker) { -} - -@extension("VK_NV_device_diagnostic_checkpoints") // 207 -cmd void vkGetQueueCheckpointDataNV( - VkQueue queue, - u32* pCheckpointDataCount, - VkCheckpointDataNV* pCheckpointData) { -} - -@extension("VK_FUCHSIA_imagepipe_surface") // 215 -cmd VkResult vkCreateImagePipeSurfaceFUCHSIA( - VkInstance instance, - const VkImagePipeSurfaceCreateInfoFUCHSIA* pCreateInfo, - const VkAllocationCallbacks* pAllocator, - VkSurfaceKHR* pSurface) { - return ? -} - - -//////////////// -// Validation // -//////////////// - -extern void validate(string layerName, bool condition, string message) - - -///////////////////////////// -// Internal State Tracking // -///////////////////////////// - -StateObject State - -@internal class StateObject { - // Dispatchable objects. - map!(VkInstance, ref!InstanceObject) Instances - map!(VkPhysicalDevice, ref!PhysicalDeviceObject) PhysicalDevices - map!(VkDevice, ref!DeviceObject) Devices - map!(VkQueue, ref!QueueObject) Queues - map!(VkCommandBuffer, ref!CommandBufferObject) CommandBuffers - - // Non-dispatchable objects. - map!(VkDeviceMemory, ref!DeviceMemoryObject) DeviceMemories - map!(VkBuffer, ref!BufferObject) Buffers - map!(VkBufferView, ref!BufferViewObject) BufferViews - map!(VkImage, ref!ImageObject) Images - map!(VkImageView, ref!ImageViewObject) ImageViews - map!(VkShaderModule, ref!ShaderModuleObject) ShaderModules - map!(VkPipeline, ref!PipelineObject) Pipelines - map!(VkPipelineLayout, ref!PipelineLayoutObject) PipelineLayouts - map!(VkSampler, ref!SamplerObject) Samplers - map!(VkDescriptorSet, ref!DescriptorSetObject) DescriptorSets - map!(VkDescriptorSetLayout, ref!DescriptorSetLayoutObject) DescriptorSetLayouts - map!(VkDescriptorPool, ref!DescriptorPoolObject) DescriptorPools - map!(VkFence, ref!FenceObject) Fences - map!(VkSemaphore, ref!SemaphoreObject) Semaphores - map!(VkEvent, ref!EventObject) Events - map!(VkQueryPool, ref!QueryPoolObject) QueryPools - map!(VkFramebuffer, ref!FramebufferObject) Framebuffers - map!(VkRenderPass, ref!RenderPassObject) RenderPasses - map!(VkPipelineCache, ref!PipelineCacheObject) PipelineCaches - map!(VkCommandPool, ref!CommandPoolObject) CommandPools - map!(VkSurfaceKHR, ref!SurfaceObject) Surfaces - map!(VkSwapchainKHR, ref!SwapchainObject) Swapchains -} - -@internal class InstanceObject { -} - -@internal class PhysicalDeviceObject { - VkInstance instance -} - -@internal class DeviceObject { - VkPhysicalDevice physicalDevice -} - -@internal class QueueObject { - VkDevice device - VkQueueFlags flags -} - -@internal class CommandBufferObject { - VkDevice device - map!(u64, VkDeviceMemory) boundObjects - VkQueueFlags queueFlags -} - -@internal class DeviceMemoryObject { - VkDevice device - VkDeviceSize allocationSize - map!(u64, VkDeviceSize) boundObjects - map!(VkCommandBuffer, VkCommandBuffer) boundCommandBuffers -} - -@internal class BufferObject { - VkDevice device - VkDeviceMemory memory - VkDeviceSize memoryOffset -} - -@internal class BufferViewObject { - VkDevice device - VkBuffer buffer -} - -@internal class ImageObject { - VkDevice device - VkDeviceMemory memory - VkDeviceSize memoryOffset -} - -@internal class ImageViewObject { - VkDevice device - VkImage image -} - -@internal class ShaderObject { - VkDevice device -} - -@internal class ShaderModuleObject { - VkDevice device -} - -@internal class PipelineObject { - VkDevice device -} - -@internal class PipelineLayoutObject { - VkDevice device -} - -@internal class SamplerObject { - VkDevice device -} - -@internal class DescriptorSetObject { - VkDevice device -} - -@internal class DescriptorSetLayoutObject { - VkDevice device -} - -@internal class DescriptorPoolObject { - VkDevice device -} - -@internal class FenceObject { - VkDevice device - bool signaled -} - -@internal class SemaphoreObject { - VkDevice device -} - -@internal class EventObject { - VkDevice device -} - -@internal class QueryPoolObject { - VkDevice device -} - -@internal class FramebufferObject { - VkDevice device -} - -@internal class RenderPassObject { - VkDevice device -} - -@internal class PipelineCacheObject { - VkDevice device -} - -@internal class CommandPoolObject { - VkDevice device -} - -@internal class SurfaceObject { - VkInstance instance -} - -@internal class SwapchainObject { - VkDevice device -} - -macro ref!InstanceObject GetInstance(VkInstance instance) { - assert(instance in State.Instances) - return State.Instances[instance] -} - -macro ref!PhysicalDeviceObject GetPhysicalDevice(VkPhysicalDevice physicalDevice) { - assert(physicalDevice in State.PhysicalDevices) - return State.PhysicalDevices[physicalDevice] -} - -macro ref!DeviceObject GetDevice(VkDevice device) { - assert(device in State.Devices) - return State.Devices[device] -} - -macro ref!QueueObject GetQueue(VkQueue queue) { - assert(queue in State.Queues) - return State.Queues[queue] -} - -macro ref!CommandBufferObject GetCommandBuffer(VkCommandBuffer commandBuffer) { - assert(commandBuffer in State.CommandBuffers) - return State.CommandBuffers[commandBuffer] -} - -macro ref!DeviceMemoryObject GetDeviceMemory(VkDeviceMemory memory) { - assert(memory in State.DeviceMemories) - return State.DeviceMemories[memory] -} - -macro ref!BufferObject GetBuffer(VkBuffer buffer) { - assert(buffer in State.Buffers) - return State.Buffers[buffer] -} - -macro ref!BufferViewObject GetBufferView(VkBufferView bufferView) { - assert(bufferView in State.BufferViews) - return State.BufferViews[bufferView] -} - -macro ref!ImageObject GetImage(VkImage image) { - assert(image in State.Images) - return State.Images[image] -} - -macro ref!ImageViewObject GetImageView(VkImageView imageView) { - assert(imageView in State.ImageViews) - return State.ImageViews[imageView] -} - -macro ref!ShaderModuleObject GetShaderModule(VkShaderModule shaderModule) { - assert(shaderModule in State.ShaderModules) - return State.ShaderModules[shaderModule] -} - -macro ref!PipelineObject GetPipeline(VkPipeline pipeline) { - assert(pipeline in State.Pipelines) - return State.Pipelines[pipeline] -} - -macro ref!PipelineLayoutObject GetPipelineLayout(VkPipelineLayout pipelineLayout) { - assert(pipelineLayout in State.PipelineLayouts) - return State.PipelineLayouts[pipelineLayout] -} - -macro ref!SamplerObject GetSampler(VkSampler sampler) { - assert(sampler in State.Samplers) - return State.Samplers[sampler] -} - -macro ref!DescriptorSetObject GetDescriptorSet(VkDescriptorSet descriptorSet) { - assert(descriptorSet in State.DescriptorSets) - return State.DescriptorSets[descriptorSet] -} - -macro ref!DescriptorSetLayoutObject GetDescriptorSetLayout(VkDescriptorSetLayout descriptorSetLayout) { - assert(descriptorSetLayout in State.DescriptorSetLayouts) - return State.DescriptorSetLayouts[descriptorSetLayout] -} - -macro ref!DescriptorPoolObject GetDescriptorPool(VkDescriptorPool descriptorPool) { - assert(descriptorPool in State.DescriptorPools) - return State.DescriptorPools[descriptorPool] -} - -macro ref!FenceObject GetFence(VkFence fence) { - assert(fence in State.Fences) - return State.Fences[fence] -} - -macro ref!SemaphoreObject GetSemaphore(VkSemaphore semaphore) { - assert(semaphore in State.Semaphores) - return State.Semaphores[semaphore] -} - -macro ref!EventObject GetEvent(VkEvent event) { - assert(event in State.Events) - return State.Events[event] -} - -macro ref!QueryPoolObject GetQueryPool(VkQueryPool queryPool) { - assert(queryPool in State.QueryPools) - return State.QueryPools[queryPool] -} - -macro ref!FramebufferObject GetFramebuffer(VkFramebuffer framebuffer) { - assert(framebuffer in State.Framebuffers) - return State.Framebuffers[framebuffer] -} - -macro ref!RenderPassObject GetRenderPass(VkRenderPass renderPass) { - assert(renderPass in State.RenderPasses) - return State.RenderPasses[renderPass] -} - -macro ref!PipelineCacheObject GetPipelineCache(VkPipelineCache pipelineCache) { - assert(pipelineCache in State.PipelineCaches) - return State.PipelineCaches[pipelineCache] -} - -macro ref!CommandPoolObject GetCommandPool(VkCommandPool commandPool) { - assert(commandPool in State.CommandPools) - return State.CommandPools[commandPool] -} - -macro ref!SurfaceObject GetSurface(VkSurfaceKHR surface) { - assert(surface in State.Surfaces) - return State.Surfaces[surface] -} - -macro ref!SwapchainObject GetSwapchain(VkSwapchainKHR swapchain) { - assert(swapchain in State.Swapchains) - return State.Swapchains[swapchain] -} - -macro VkQueueFlags AddQueueFlag(VkQueueFlags flags, VkQueueFlagBits bit) { - return as!VkQueueFlags(as!u32(flags) | as!u32(bit)) -} diff --git a/vulkan/doc/README b/vulkan/doc/README deleted file mode 100644 index d1dc2e1b7c..0000000000 --- a/vulkan/doc/README +++ /dev/null @@ -1,2 +0,0 @@ -The former contents of implementors_guide/ are now at -https://source.android.com/devices/graphics/implement-vulkan diff --git a/vulkan/include/vulkan/vk_android_native_buffer.h b/vulkan/include/vulkan/vk_android_native_buffer.h index 23006fa6be..9ffe83ba2e 100644 --- a/vulkan/include/vulkan/vk_android_native_buffer.h +++ b/vulkan/include/vulkan/vk_android_native_buffer.h @@ -62,6 +62,11 @@ typedef enum VkSwapchainImageUsageFlagBitsANDROID { typedef VkFlags VkSwapchainImageUsageFlagsANDROID; typedef struct { + uint64_t consumer; + uint64_t producer; +} VkNativeBufferUsage2ANDROID; + +typedef struct { VkStructureType sType; // must be VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID const void* pNext; @@ -73,10 +78,7 @@ typedef struct { int format; int usage; // DEPRECATED in SPEC_VERSION 6 // -- Added in SPEC_VERSION 6 -- - struct { - uint64_t consumer; - uint64_t producer; - } usage2; + VkNativeBufferUsage2ANDROID usage2; } VkNativeBufferANDROID; typedef struct { diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp index b0c4f3f95a..5686891eab 100644 --- a/vulkan/libvulkan/Android.bp +++ b/vulkan/libvulkan/Android.bp @@ -76,6 +76,7 @@ cc_library_shared { header_libs: [ "hwvulkan_headers", + "libnativeloader-headers", "vulkan_headers", ], export_header_lib_headers: ["vulkan_headers"], diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp index 71048db920..48f26e7e43 100644 --- a/vulkan/libvulkan/api.cpp +++ b/vulkan/libvulkan/api.cpp @@ -664,6 +664,12 @@ VkResult LayerChain::LoadLayer(ActiveLayer& layer, const char* name) { return VK_ERROR_LAYER_NOT_PRESENT; } + if (!layer.ref.GetGetInstanceProcAddr()) { + ALOGW("Failed to locate vkGetInstanceProcAddr in layer %s", name); + layer.ref.~LayerRef(); + return VK_ERROR_LAYER_NOT_PRESENT; + } + ALOGI("Loaded layer %s", name); return VK_SUCCESS; @@ -1166,11 +1172,20 @@ bool EnsureInitialized() { std::call_once(once_flag, []() { if (driver::OpenHAL()) { - DiscoverLayers(); initialized = true; } }); + { + static pid_t pid = getpid() + 1; + static std::mutex layer_lock; + std::lock_guard<std::mutex> lock(layer_lock); + if (pid != getpid()) { + pid = getpid(); + DiscoverLayers(); + } + } + return initialized; } diff --git a/vulkan/libvulkan/api_gen.cpp b/vulkan/libvulkan/api_gen.cpp index df86af0c3b..37b5368452 100644 --- a/vulkan/libvulkan/api_gen.cpp +++ b/vulkan/libvulkan/api_gen.cpp @@ -16,12 +16,11 @@ // WARNING: This file is generated. See ../README.md for instructions. +#include <log/log.h> #include <string.h> #include <algorithm> -#include <log/log.h> - // to catch mismatches between vulkan.h and this file #undef VK_NO_PROTOTYPES #include "api.h" @@ -55,6 +54,11 @@ namespace { // clang-format off +VKAPI_ATTR VkResult disabledCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*) { + driver::Logger(instance).Err(instance, "VK_KHR_android_surface not enabled. Exported vkCreateAndroidSurfaceKHR not executed."); + return VK_SUCCESS; +} + VKAPI_ATTR void disabledDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR, const VkAllocationCallbacks*) { driver::Logger(instance).Err(instance, "VK_KHR_surface not enabled. Exported vkDestroySurfaceKHR not executed."); } @@ -113,18 +117,13 @@ VKAPI_ATTR VkResult disabledGetDeviceGroupSurfacePresentModesKHR(VkDevice device return VK_SUCCESS; } -VKAPI_ATTR VkResult disabledGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR, uint32_t*, VkRect2D*) { - driver::Logger(physicalDevice).Err(physicalDevice, "VK_KHR_swapchain not enabled. Exported vkGetPhysicalDevicePresentRectanglesKHR not executed."); - return VK_SUCCESS; -} - VKAPI_ATTR VkResult disabledAcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR*, uint32_t*) { driver::Logger(device).Err(device, "VK_KHR_swapchain not enabled. Exported vkAcquireNextImage2KHR not executed."); return VK_SUCCESS; } -VKAPI_ATTR VkResult disabledCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR*, const VkAllocationCallbacks*, VkSurfaceKHR*) { - driver::Logger(instance).Err(instance, "VK_KHR_android_surface not enabled. Exported vkCreateAndroidSurfaceKHR not executed."); +VKAPI_ATTR VkResult disabledGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR, uint32_t*, VkRect2D*) { + driver::Logger(physicalDevice).Err(physicalDevice, "VK_KHR_swapchain not enabled. Exported vkGetPhysicalDevicePresentRectanglesKHR not executed."); return VK_SUCCESS; } @@ -162,7 +161,12 @@ bool InitDispatchTable( INIT_PROC(true, instance, CreateDevice); INIT_PROC(true, instance, EnumerateDeviceExtensionProperties); INIT_PROC(true, instance, GetPhysicalDeviceSparseImageFormatProperties); - INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups); + INIT_PROC_EXT(KHR_android_surface, true, instance, CreateAndroidSurfaceKHR); + INIT_PROC_EXT(KHR_surface, true, instance, DestroySurfaceKHR); + INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceSupportKHR); + INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceCapabilitiesKHR); + INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceFormatsKHR); + INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfacePresentModesKHR); INIT_PROC(false, instance, GetPhysicalDeviceFeatures2); INIT_PROC(false, instance, GetPhysicalDeviceProperties2); INIT_PROC(false, instance, GetPhysicalDeviceFormatProperties2); @@ -171,15 +175,10 @@ bool InitDispatchTable( INIT_PROC(false, instance, GetPhysicalDeviceMemoryProperties2); INIT_PROC(false, instance, GetPhysicalDeviceSparseImageFormatProperties2); INIT_PROC(false, instance, GetPhysicalDeviceExternalBufferProperties); - INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties); INIT_PROC(false, instance, GetPhysicalDeviceExternalSemaphoreProperties); - INIT_PROC_EXT(KHR_surface, true, instance, DestroySurfaceKHR); - INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceSupportKHR); - INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceCapabilitiesKHR); - INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfaceFormatsKHR); - INIT_PROC_EXT(KHR_surface, true, instance, GetPhysicalDeviceSurfacePresentModesKHR); + INIT_PROC(false, instance, GetPhysicalDeviceExternalFenceProperties); + INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups); INIT_PROC_EXT(KHR_swapchain, false, instance, GetPhysicalDevicePresentRectanglesKHR); - INIT_PROC_EXT(KHR_android_surface, true, instance, CreateAndroidSurfaceKHR); // clang-format on return success; @@ -314,32 +313,32 @@ bool InitDispatchTable( INIT_PROC(true, dev, CmdNextSubpass); INIT_PROC(true, dev, CmdEndRenderPass); INIT_PROC(true, dev, CmdExecuteCommands); + INIT_PROC_EXT(KHR_swapchain, true, dev, CreateSwapchainKHR); + INIT_PROC_EXT(KHR_swapchain, true, dev, DestroySwapchainKHR); + INIT_PROC_EXT(KHR_swapchain, true, dev, GetSwapchainImagesKHR); + INIT_PROC_EXT(KHR_swapchain, true, dev, AcquireNextImageKHR); + INIT_PROC_EXT(KHR_swapchain, true, dev, QueuePresentKHR); + INIT_PROC(false, dev, TrimCommandPool); + INIT_PROC(false, dev, GetDeviceGroupPeerMemoryFeatures); INIT_PROC(false, dev, BindBufferMemory2); INIT_PROC(false, dev, BindImageMemory2); - INIT_PROC(false, dev, GetDeviceGroupPeerMemoryFeatures); INIT_PROC(false, dev, CmdSetDeviceMask); + INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupPresentCapabilitiesKHR); + INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupSurfacePresentModesKHR); + INIT_PROC_EXT(KHR_swapchain, false, dev, AcquireNextImage2KHR); INIT_PROC(false, dev, CmdDispatchBase); - INIT_PROC(false, dev, GetImageMemoryRequirements2); + INIT_PROC(false, dev, CreateDescriptorUpdateTemplate); + INIT_PROC(false, dev, DestroyDescriptorUpdateTemplate); + INIT_PROC(false, dev, UpdateDescriptorSetWithTemplate); INIT_PROC(false, dev, GetBufferMemoryRequirements2); + INIT_PROC(false, dev, GetImageMemoryRequirements2); INIT_PROC(false, dev, GetImageSparseMemoryRequirements2); - INIT_PROC(false, dev, TrimCommandPool); - INIT_PROC(false, dev, GetDeviceQueue2); INIT_PROC(false, dev, CreateSamplerYcbcrConversion); INIT_PROC(false, dev, DestroySamplerYcbcrConversion); - INIT_PROC(false, dev, CreateDescriptorUpdateTemplate); - INIT_PROC(false, dev, DestroyDescriptorUpdateTemplate); - INIT_PROC(false, dev, UpdateDescriptorSetWithTemplate); + INIT_PROC(false, dev, GetDeviceQueue2); INIT_PROC(false, dev, GetDescriptorSetLayoutSupport); - INIT_PROC_EXT(KHR_swapchain, true, dev, CreateSwapchainKHR); - INIT_PROC_EXT(KHR_swapchain, true, dev, DestroySwapchainKHR); - INIT_PROC_EXT(KHR_swapchain, true, dev, GetSwapchainImagesKHR); - INIT_PROC_EXT(KHR_swapchain, true, dev, AcquireNextImageKHR); - INIT_PROC_EXT(KHR_swapchain, true, dev, QueuePresentKHR); - INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupPresentCapabilitiesKHR); - INIT_PROC_EXT(KHR_swapchain, false, dev, GetDeviceGroupSurfacePresentModesKHR); - INIT_PROC_EXT(KHR_swapchain, false, dev, AcquireNextImage2KHR); - INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, false, dev, GetAndroidHardwareBufferPropertiesANDROID); - INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, false, dev, GetMemoryAndroidHardwareBufferANDROID); + INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetAndroidHardwareBufferPropertiesANDROID); + INIT_PROC_EXT(ANDROID_external_memory_android_hardware_buffer, true, dev, GetMemoryAndroidHardwareBufferANDROID); // clang-format on return success; @@ -479,33 +478,7 @@ VKAPI_ATTR void CmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRender VKAPI_ATTR void CmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents); VKAPI_ATTR void CmdEndRenderPass(VkCommandBuffer commandBuffer); VKAPI_ATTR void CmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); -VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos); -VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos); -VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures); -VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask); -VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); -VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); -VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); -VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); -VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); -VKAPI_ATTR void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures); -VKAPI_ATTR void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties); -VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties); -VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties); -VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties); -VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties); -VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties); -VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags); -VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue); -VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion); -VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator); -VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate); -VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator); -VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData); -VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties); -VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties); -VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties); -VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport); +VKAPI_ATTR VkResult CreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); VKAPI_ATTR void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported); VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities); @@ -516,11 +489,37 @@ VKAPI_ATTR void DestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, c VKAPI_ATTR VkResult GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages); VKAPI_ATTR VkResult AcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex); VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo); +VKAPI_ATTR void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures); +VKAPI_ATTR void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties); +VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties); +VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties); +VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties); +VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties); +VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties); +VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags); +VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties); +VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties); +VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties); +VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); +VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures); +VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos); +VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos); +VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask); VKAPI_ATTR VkResult GetDeviceGroupPresentCapabilitiesKHR(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities); VKAPI_ATTR VkResult GetDeviceGroupSurfacePresentModesKHR(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR* pModes); -VKAPI_ATTR VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects); VKAPI_ATTR VkResult AcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR* pAcquireInfo, uint32_t* pImageIndex); -VKAPI_ATTR VkResult CreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); +VKAPI_ATTR VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects); +VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate); +VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData); +VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); +VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); +VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); +VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion); +VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue); +VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport); VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties); VKAPI_ATTR VkResult GetMemoryAndroidHardwareBufferANDROID(VkDevice device, const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, struct AHardwareBuffer** pBuffer); @@ -622,9 +621,9 @@ VKAPI_ATTR PFN_vkVoidFunction GetInstanceProcAddr(VkInstance instance, const cha // global functions if (instance == VK_NULL_HANDLE) { if (strcmp(pName, "vkCreateInstance") == 0) return reinterpret_cast<PFN_vkVoidFunction>(CreateInstance); + if (strcmp(pName, "vkEnumerateInstanceVersion") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceVersion); if (strcmp(pName, "vkEnumerateInstanceLayerProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceLayerProperties); if (strcmp(pName, "vkEnumerateInstanceExtensionProperties") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceExtensionProperties); - if (strcmp(pName, "vkEnumerateInstanceVersion") == 0) return reinterpret_cast<PFN_vkVoidFunction>(EnumerateInstanceVersion); ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \"%s\") call", pName); return nullptr; @@ -1313,40 +1312,48 @@ VKAPI_ATTR void CmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t comma GetData(commandBuffer).dispatch.CmdExecuteCommands(commandBuffer, commandBufferCount, pCommandBuffers); } -VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos) { - return GetData(device).dispatch.BindBufferMemory2(device, bindInfoCount, pBindInfos); +VKAPI_ATTR VkResult CreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) { + return GetData(instance).dispatch.CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface); } -VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) { - return GetData(device).dispatch.BindImageMemory2(device, bindInfoCount, pBindInfos); +VKAPI_ATTR void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator) { + GetData(instance).dispatch.DestroySurfaceKHR(instance, surface, pAllocator); } -VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) { - GetData(device).dispatch.GetDeviceGroupPeerMemoryFeatures(device, heapIndex, localDeviceIndex, remoteDeviceIndex, pPeerMemoryFeatures); +VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported) { + return GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, pSupported); } -VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask) { - GetData(commandBuffer).dispatch.CmdSetDeviceMask(commandBuffer, deviceMask); +VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) { + return GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, pSurfaceCapabilities); } -VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) { - GetData(commandBuffer).dispatch.CmdDispatchBase(commandBuffer, baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ); +VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats) { + return GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats); } -VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) { - return GetData(instance).dispatch.EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties); +VKAPI_ATTR VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes) { + return GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, pPresentModeCount, pPresentModes); } -VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) { - GetData(device).dispatch.GetImageMemoryRequirements2(device, pInfo, pMemoryRequirements); +VKAPI_ATTR VkResult CreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) { + return GetData(device).dispatch.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); } -VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) { - GetData(device).dispatch.GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements); +VKAPI_ATTR void DestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator) { + GetData(device).dispatch.DestroySwapchainKHR(device, swapchain, pAllocator); } -VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) { - GetData(device).dispatch.GetImageSparseMemoryRequirements2(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements); +VKAPI_ATTR VkResult GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) { + return GetData(device).dispatch.GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages); +} + +VKAPI_ATTR VkResult AcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) { + return GetData(device).dispatch.AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex); +} + +VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) { + return GetData(queue).dispatch.QueuePresentKHR(queue, pPresentInfo); } VKAPI_ATTR void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures) { @@ -1381,104 +1388,96 @@ VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCo GetData(device).dispatch.TrimCommandPool(device, commandPool, flags); } -VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) { - GetData(device).dispatch.GetDeviceQueue2(device, pQueueInfo, pQueue); -} - -VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) { - return GetData(device).dispatch.CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion); -} - -VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator) { - GetData(device).dispatch.DestroySamplerYcbcrConversion(device, ycbcrConversion, pAllocator); +VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties) { + GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalBufferProperties(physicalDevice, pExternalBufferInfo, pExternalBufferProperties); } -VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) { - return GetData(device).dispatch.CreateDescriptorUpdateTemplate(device, pCreateInfo, pAllocator, pDescriptorUpdateTemplate); +VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties) { + GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, pExternalSemaphoreInfo, pExternalSemaphoreProperties); } -VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator) { - GetData(device).dispatch.DestroyDescriptorUpdateTemplate(device, descriptorUpdateTemplate, pAllocator); +VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) { + GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalFenceProperties(physicalDevice, pExternalFenceInfo, pExternalFenceProperties); } -VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) { - GetData(device).dispatch.UpdateDescriptorSetWithTemplate(device, descriptorSet, descriptorUpdateTemplate, pData); +VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) { + return GetData(instance).dispatch.EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties); } -VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties) { - GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalBufferProperties(physicalDevice, pExternalBufferInfo, pExternalBufferProperties); +VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) { + GetData(device).dispatch.GetDeviceGroupPeerMemoryFeatures(device, heapIndex, localDeviceIndex, remoteDeviceIndex, pPeerMemoryFeatures); } -VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) { - GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalFenceProperties(physicalDevice, pExternalFenceInfo, pExternalFenceProperties); +VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos) { + return GetData(device).dispatch.BindBufferMemory2(device, bindInfoCount, pBindInfos); } -VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties) { - GetData(physicalDevice).dispatch.GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, pExternalSemaphoreInfo, pExternalSemaphoreProperties); +VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) { + return GetData(device).dispatch.BindImageMemory2(device, bindInfoCount, pBindInfos); } -VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) { - GetData(device).dispatch.GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport); +VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask) { + GetData(commandBuffer).dispatch.CmdSetDeviceMask(commandBuffer, deviceMask); } -VKAPI_ATTR void DestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator) { - GetData(instance).dispatch.DestroySurfaceKHR(instance, surface, pAllocator); +VKAPI_ATTR VkResult GetDeviceGroupPresentCapabilitiesKHR(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) { + return GetData(device).dispatch.GetDeviceGroupPresentCapabilitiesKHR(device, pDeviceGroupPresentCapabilities); } -VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported) { - return GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, pSupported); +VKAPI_ATTR VkResult GetDeviceGroupSurfacePresentModesKHR(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR* pModes) { + return GetData(device).dispatch.GetDeviceGroupSurfacePresentModesKHR(device, surface, pModes); } -VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) { - return GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, pSurfaceCapabilities); +VKAPI_ATTR VkResult AcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR* pAcquireInfo, uint32_t* pImageIndex) { + return GetData(device).dispatch.AcquireNextImage2KHR(device, pAcquireInfo, pImageIndex); } -VKAPI_ATTR VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats) { - return GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats); +VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) { + GetData(commandBuffer).dispatch.CmdDispatchBase(commandBuffer, baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ); } -VKAPI_ATTR VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes) { - return GetData(physicalDevice).dispatch.GetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, pPresentModeCount, pPresentModes); +VKAPI_ATTR VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects) { + return GetData(physicalDevice).dispatch.GetPhysicalDevicePresentRectanglesKHR(physicalDevice, surface, pRectCount, pRects); } -VKAPI_ATTR VkResult CreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) { - return GetData(device).dispatch.CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); +VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) { + return GetData(device).dispatch.CreateDescriptorUpdateTemplate(device, pCreateInfo, pAllocator, pDescriptorUpdateTemplate); } -VKAPI_ATTR void DestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator) { - GetData(device).dispatch.DestroySwapchainKHR(device, swapchain, pAllocator); +VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator) { + GetData(device).dispatch.DestroyDescriptorUpdateTemplate(device, descriptorUpdateTemplate, pAllocator); } -VKAPI_ATTR VkResult GetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) { - return GetData(device).dispatch.GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages); +VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) { + GetData(device).dispatch.UpdateDescriptorSetWithTemplate(device, descriptorSet, descriptorUpdateTemplate, pData); } -VKAPI_ATTR VkResult AcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) { - return GetData(device).dispatch.AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex); +VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + GetData(device).dispatch.GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements); } -VKAPI_ATTR VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) { - return GetData(queue).dispatch.QueuePresentKHR(queue, pPresentInfo); +VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + GetData(device).dispatch.GetImageMemoryRequirements2(device, pInfo, pMemoryRequirements); } -VKAPI_ATTR VkResult GetDeviceGroupPresentCapabilitiesKHR(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) { - return GetData(device).dispatch.GetDeviceGroupPresentCapabilitiesKHR(device, pDeviceGroupPresentCapabilities); +VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) { + GetData(device).dispatch.GetImageSparseMemoryRequirements2(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements); } -VKAPI_ATTR VkResult GetDeviceGroupSurfacePresentModesKHR(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR* pModes) { - return GetData(device).dispatch.GetDeviceGroupSurfacePresentModesKHR(device, surface, pModes); +VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) { + return GetData(device).dispatch.CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion); } -VKAPI_ATTR VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects) { - return GetData(physicalDevice).dispatch.GetPhysicalDevicePresentRectanglesKHR(physicalDevice, surface, pRectCount, pRects); +VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator) { + GetData(device).dispatch.DestroySamplerYcbcrConversion(device, ycbcrConversion, pAllocator); } -VKAPI_ATTR VkResult AcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR* pAcquireInfo, uint32_t* pImageIndex) { - return GetData(device).dispatch.AcquireNextImage2KHR(device, pAcquireInfo, pImageIndex); +VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) { + GetData(device).dispatch.GetDeviceQueue2(device, pQueueInfo, pQueue); } -VKAPI_ATTR VkResult CreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) { - return GetData(instance).dispatch.CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface); +VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) { + GetData(device).dispatch.GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport); } VKAPI_ATTR VkResult GetAndroidHardwareBufferPropertiesANDROID(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties) { @@ -1565,6 +1564,11 @@ VKAPI_ATTR void vkDestroyDevice(VkDevice device, const VkAllocationCallbacks* pA } __attribute__((visibility("default"))) +VKAPI_ATTR VkResult vkEnumerateInstanceVersion(uint32_t* pApiVersion) { + return vulkan::api::EnumerateInstanceVersion(pApiVersion); +} + +__attribute__((visibility("default"))) VKAPI_ATTR VkResult vkEnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties) { return vulkan::api::EnumerateInstanceLayerProperties(pPropertyCount, pProperties); } @@ -2185,53 +2189,58 @@ VKAPI_ATTR void vkCmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t com } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkEnumerateInstanceVersion(uint32_t* pApiVersion) { - return vulkan::api::EnumerateInstanceVersion(pApiVersion); +VKAPI_ATTR VkResult vkCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) { + return vulkan::api::CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkBindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos) { - return vulkan::api::BindBufferMemory2(device, bindInfoCount, pBindInfos); +VKAPI_ATTR void vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator) { + vulkan::api::DestroySurfaceKHR(instance, surface, pAllocator); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) { - return vulkan::api::BindImageMemory2(device, bindInfoCount, pBindInfos); +VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported) { + return vulkan::api::GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, pSupported); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkGetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) { - vulkan::api::GetDeviceGroupPeerMemoryFeatures(device, heapIndex, localDeviceIndex, remoteDeviceIndex, pPeerMemoryFeatures); +VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) { + return vulkan::api::GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, pSurfaceCapabilities); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkCmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask) { - vulkan::api::CmdSetDeviceMask(commandBuffer, deviceMask); +VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats) { + return vulkan::api::GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkCmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) { - vulkan::api::CmdDispatchBase(commandBuffer, baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ); +VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes) { + return vulkan::api::GetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, pPresentModeCount, pPresentModes); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkEnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) { - return vulkan::api::EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties); +VKAPI_ATTR VkResult vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) { + return vulkan::api::CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkGetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) { - vulkan::api::GetImageMemoryRequirements2(device, pInfo, pMemoryRequirements); +VKAPI_ATTR void vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator) { + vulkan::api::DestroySwapchainKHR(device, swapchain, pAllocator); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkGetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) { - vulkan::api::GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements); +VKAPI_ATTR VkResult vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) { + return vulkan::api::GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkGetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) { - vulkan::api::GetImageSparseMemoryRequirements2(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements); +VKAPI_ATTR VkResult vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) { + return vulkan::api::AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex); +} + +__attribute__((visibility("default"))) +VKAPI_ATTR VkResult vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) { + return vulkan::api::QueuePresentKHR(queue, pPresentInfo); } __attribute__((visibility("default"))) @@ -2275,128 +2284,118 @@ VKAPI_ATTR void vkTrimCommandPool(VkDevice device, VkCommandPool commandPool, Vk } __attribute__((visibility("default"))) -VKAPI_ATTR void vkGetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) { - vulkan::api::GetDeviceQueue2(device, pQueueInfo, pQueue); -} - -__attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkCreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) { - return vulkan::api::CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion); -} - -__attribute__((visibility("default"))) -VKAPI_ATTR void vkDestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator) { - vulkan::api::DestroySamplerYcbcrConversion(device, ycbcrConversion, pAllocator); +VKAPI_ATTR void vkGetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties) { + vulkan::api::GetPhysicalDeviceExternalBufferProperties(physicalDevice, pExternalBufferInfo, pExternalBufferProperties); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkCreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) { - return vulkan::api::CreateDescriptorUpdateTemplate(device, pCreateInfo, pAllocator, pDescriptorUpdateTemplate); +VKAPI_ATTR void vkGetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties) { + vulkan::api::GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, pExternalSemaphoreInfo, pExternalSemaphoreProperties); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkDestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator) { - vulkan::api::DestroyDescriptorUpdateTemplate(device, descriptorUpdateTemplate, pAllocator); +VKAPI_ATTR void vkGetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) { + vulkan::api::GetPhysicalDeviceExternalFenceProperties(physicalDevice, pExternalFenceInfo, pExternalFenceProperties); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkUpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) { - vulkan::api::UpdateDescriptorSetWithTemplate(device, descriptorSet, descriptorUpdateTemplate, pData); +VKAPI_ATTR VkResult vkEnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties) { + return vulkan::api::EnumeratePhysicalDeviceGroups(instance, pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkGetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties) { - vulkan::api::GetPhysicalDeviceExternalBufferProperties(physicalDevice, pExternalBufferInfo, pExternalBufferProperties); +VKAPI_ATTR void vkGetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures) { + vulkan::api::GetDeviceGroupPeerMemoryFeatures(device, heapIndex, localDeviceIndex, remoteDeviceIndex, pPeerMemoryFeatures); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkGetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) { - vulkan::api::GetPhysicalDeviceExternalFenceProperties(physicalDevice, pExternalFenceInfo, pExternalFenceProperties); +VKAPI_ATTR VkResult vkBindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos) { + return vulkan::api::BindBufferMemory2(device, bindInfoCount, pBindInfos); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkGetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties) { - vulkan::api::GetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, pExternalSemaphoreInfo, pExternalSemaphoreProperties); +VKAPI_ATTR VkResult vkBindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) { + return vulkan::api::BindImageMemory2(device, bindInfoCount, pBindInfos); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkGetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) { - vulkan::api::GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport); +VKAPI_ATTR void vkCmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask) { + vulkan::api::CmdSetDeviceMask(commandBuffer, deviceMask); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkDestroySurfaceKHR(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator) { - vulkan::api::DestroySurfaceKHR(instance, surface, pAllocator); +VKAPI_ATTR VkResult vkGetDeviceGroupPresentCapabilitiesKHR(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) { + return vulkan::api::GetDeviceGroupPresentCapabilitiesKHR(device, pDeviceGroupPresentCapabilities); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported) { - return vulkan::api::GetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface, pSupported); +VKAPI_ATTR VkResult vkGetDeviceGroupSurfacePresentModesKHR(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR* pModes) { + return vulkan::api::GetDeviceGroupSurfacePresentModesKHR(device, surface, pModes); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities) { - return vulkan::api::GetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, pSurfaceCapabilities); +VKAPI_ATTR VkResult vkAcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR* pAcquireInfo, uint32_t* pImageIndex) { + return vulkan::api::AcquireNextImage2KHR(device, pAcquireInfo, pImageIndex); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats) { - return vulkan::api::GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, pSurfaceFormatCount, pSurfaceFormats); +VKAPI_ATTR void vkCmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) { + vulkan::api::CmdDispatchBase(commandBuffer, baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkGetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes) { - return vulkan::api::GetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, pPresentModeCount, pPresentModes); +VKAPI_ATTR VkResult vkGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects) { + return vulkan::api::GetPhysicalDevicePresentRectanglesKHR(physicalDevice, surface, pRectCount, pRects); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain) { - return vulkan::api::CreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain); +VKAPI_ATTR VkResult vkCreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate) { + return vulkan::api::CreateDescriptorUpdateTemplate(device, pCreateInfo, pAllocator, pDescriptorUpdateTemplate); } __attribute__((visibility("default"))) -VKAPI_ATTR void vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator) { - vulkan::api::DestroySwapchainKHR(device, swapchain, pAllocator); +VKAPI_ATTR void vkDestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator) { + vulkan::api::DestroyDescriptorUpdateTemplate(device, descriptorUpdateTemplate, pAllocator); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages) { - return vulkan::api::GetSwapchainImagesKHR(device, swapchain, pSwapchainImageCount, pSwapchainImages); +VKAPI_ATTR void vkUpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData) { + vulkan::api::UpdateDescriptorSetWithTemplate(device, descriptorSet, descriptorUpdateTemplate, pData); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex) { - return vulkan::api::AcquireNextImageKHR(device, swapchain, timeout, semaphore, fence, pImageIndex); +VKAPI_ATTR void vkGetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + vulkan::api::GetBufferMemoryRequirements2(device, pInfo, pMemoryRequirements); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* pPresentInfo) { - return vulkan::api::QueuePresentKHR(queue, pPresentInfo); +VKAPI_ATTR void vkGetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements) { + vulkan::api::GetImageMemoryRequirements2(device, pInfo, pMemoryRequirements); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkGetDeviceGroupPresentCapabilitiesKHR(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) { - return vulkan::api::GetDeviceGroupPresentCapabilitiesKHR(device, pDeviceGroupPresentCapabilities); +VKAPI_ATTR void vkGetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements) { + vulkan::api::GetImageSparseMemoryRequirements2(device, pInfo, pSparseMemoryRequirementCount, pSparseMemoryRequirements); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkGetDeviceGroupSurfacePresentModesKHR(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR* pModes) { - return vulkan::api::GetDeviceGroupSurfacePresentModesKHR(device, surface, pModes); +VKAPI_ATTR VkResult vkCreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion) { + return vulkan::api::CreateSamplerYcbcrConversion(device, pCreateInfo, pAllocator, pYcbcrConversion); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkGetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects) { - return vulkan::api::GetPhysicalDevicePresentRectanglesKHR(physicalDevice, surface, pRectCount, pRects); +VKAPI_ATTR void vkDestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator) { + vulkan::api::DestroySamplerYcbcrConversion(device, ycbcrConversion, pAllocator); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkAcquireNextImage2KHR(VkDevice device, const VkAcquireNextImageInfoKHR* pAcquireInfo, uint32_t* pImageIndex) { - return vulkan::api::AcquireNextImage2KHR(device, pAcquireInfo, pImageIndex); +VKAPI_ATTR void vkGetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue) { + vulkan::api::GetDeviceQueue2(device, pQueueInfo, pQueue); } __attribute__((visibility("default"))) -VKAPI_ATTR VkResult vkCreateAndroidSurfaceKHR(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface) { - return vulkan::api::CreateAndroidSurfaceKHR(instance, pCreateInfo, pAllocator, pSurface); +VKAPI_ATTR void vkGetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport) { + vulkan::api::GetDescriptorSetLayoutSupport(device, pCreateInfo, pSupport); } __attribute__((visibility("default"))) diff --git a/vulkan/libvulkan/api_gen.h b/vulkan/libvulkan/api_gen.h index 4bedbeb16e..21958454f5 100644 --- a/vulkan/libvulkan/api_gen.h +++ b/vulkan/libvulkan/api_gen.h @@ -20,7 +20,9 @@ #define LIBVULKAN_API_GEN_H #include <vulkan/vulkan.h> + #include <bitset> + #include "driver_gen.h" namespace vulkan { @@ -40,7 +42,12 @@ struct InstanceDispatchTable { PFN_vkCreateDevice CreateDevice; PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties; PFN_vkGetPhysicalDeviceSparseImageFormatProperties GetPhysicalDeviceSparseImageFormatProperties; - PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups; + PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR; + PFN_vkDestroySurfaceKHR DestroySurfaceKHR; + PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR; PFN_vkGetPhysicalDeviceFeatures2 GetPhysicalDeviceFeatures2; PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2; PFN_vkGetPhysicalDeviceFormatProperties2 GetPhysicalDeviceFormatProperties2; @@ -49,15 +56,10 @@ struct InstanceDispatchTable { PFN_vkGetPhysicalDeviceMemoryProperties2 GetPhysicalDeviceMemoryProperties2; PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 GetPhysicalDeviceSparseImageFormatProperties2; PFN_vkGetPhysicalDeviceExternalBufferProperties GetPhysicalDeviceExternalBufferProperties; - PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties; PFN_vkGetPhysicalDeviceExternalSemaphoreProperties GetPhysicalDeviceExternalSemaphoreProperties; - PFN_vkDestroySurfaceKHR DestroySurfaceKHR; - PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR; - PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR; - PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR; - PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR; + PFN_vkGetPhysicalDeviceExternalFenceProperties GetPhysicalDeviceExternalFenceProperties; + PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups; PFN_vkGetPhysicalDevicePresentRectanglesKHR GetPhysicalDevicePresentRectanglesKHR; - PFN_vkCreateAndroidSurfaceKHR CreateAndroidSurfaceKHR; // clang-format on }; @@ -184,30 +186,30 @@ struct DeviceDispatchTable { PFN_vkCmdNextSubpass CmdNextSubpass; PFN_vkCmdEndRenderPass CmdEndRenderPass; PFN_vkCmdExecuteCommands CmdExecuteCommands; + PFN_vkCreateSwapchainKHR CreateSwapchainKHR; + PFN_vkDestroySwapchainKHR DestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR; + PFN_vkAcquireNextImageKHR AcquireNextImageKHR; + PFN_vkQueuePresentKHR QueuePresentKHR; + PFN_vkTrimCommandPool TrimCommandPool; + PFN_vkGetDeviceGroupPeerMemoryFeatures GetDeviceGroupPeerMemoryFeatures; PFN_vkBindBufferMemory2 BindBufferMemory2; PFN_vkBindImageMemory2 BindImageMemory2; - PFN_vkGetDeviceGroupPeerMemoryFeatures GetDeviceGroupPeerMemoryFeatures; PFN_vkCmdSetDeviceMask CmdSetDeviceMask; + PFN_vkGetDeviceGroupPresentCapabilitiesKHR GetDeviceGroupPresentCapabilitiesKHR; + PFN_vkGetDeviceGroupSurfacePresentModesKHR GetDeviceGroupSurfacePresentModesKHR; + PFN_vkAcquireNextImage2KHR AcquireNextImage2KHR; PFN_vkCmdDispatchBase CmdDispatchBase; - PFN_vkGetImageMemoryRequirements2 GetImageMemoryRequirements2; + PFN_vkCreateDescriptorUpdateTemplate CreateDescriptorUpdateTemplate; + PFN_vkDestroyDescriptorUpdateTemplate DestroyDescriptorUpdateTemplate; + PFN_vkUpdateDescriptorSetWithTemplate UpdateDescriptorSetWithTemplate; PFN_vkGetBufferMemoryRequirements2 GetBufferMemoryRequirements2; + PFN_vkGetImageMemoryRequirements2 GetImageMemoryRequirements2; PFN_vkGetImageSparseMemoryRequirements2 GetImageSparseMemoryRequirements2; - PFN_vkTrimCommandPool TrimCommandPool; - PFN_vkGetDeviceQueue2 GetDeviceQueue2; PFN_vkCreateSamplerYcbcrConversion CreateSamplerYcbcrConversion; PFN_vkDestroySamplerYcbcrConversion DestroySamplerYcbcrConversion; - PFN_vkCreateDescriptorUpdateTemplate CreateDescriptorUpdateTemplate; - PFN_vkDestroyDescriptorUpdateTemplate DestroyDescriptorUpdateTemplate; - PFN_vkUpdateDescriptorSetWithTemplate UpdateDescriptorSetWithTemplate; + PFN_vkGetDeviceQueue2 GetDeviceQueue2; PFN_vkGetDescriptorSetLayoutSupport GetDescriptorSetLayoutSupport; - PFN_vkCreateSwapchainKHR CreateSwapchainKHR; - PFN_vkDestroySwapchainKHR DestroySwapchainKHR; - PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR; - PFN_vkAcquireNextImageKHR AcquireNextImageKHR; - PFN_vkQueuePresentKHR QueuePresentKHR; - PFN_vkGetDeviceGroupPresentCapabilitiesKHR GetDeviceGroupPresentCapabilitiesKHR; - PFN_vkGetDeviceGroupSurfacePresentModesKHR GetDeviceGroupSurfacePresentModesKHR; - PFN_vkAcquireNextImage2KHR AcquireNextImage2KHR; PFN_vkGetAndroidHardwareBufferPropertiesANDROID GetAndroidHardwareBufferPropertiesANDROID; PFN_vkGetMemoryAndroidHardwareBufferANDROID GetMemoryAndroidHardwareBufferANDROID; // clang-format on diff --git a/vulkan/libvulkan/code-generator.tmpl b/vulkan/libvulkan/code-generator.tmpl deleted file mode 100644 index bdd3573b11..0000000000 --- a/vulkan/libvulkan/code-generator.tmpl +++ /dev/null @@ -1,1196 +0,0 @@ -{{define "Copyright"}} -/* -•* Copyright 2016 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. -•*/ -¶{{end}} - -{{Include "../api/templates/vulkan_common.tmpl"}} -{{Global "clang-format" (Strings "clang-format" "-style=file")}} -{{Macro "DefineGlobals" $}} -{{$ | Macro "api_gen.h" | Format (Global "clang-format") | Write "api_gen.h" }} -{{$ | Macro "api_gen.cpp" | Format (Global "clang-format") | Write "api_gen.cpp"}} -{{$ | Macro "driver_gen.h" | Format (Global "clang-format") | Write "driver_gen.h"}} -{{$ | Macro "driver_gen.cpp" | Format (Global "clang-format") | Write "driver_gen.cpp"}} - -{{/* -------------------------------------------------------------------------------- - api_gen.h -------------------------------------------------------------------------------- -*/}} -{{define "api_gen.h"}} -{{Macro "Copyright"}} -¶ -// WARNING: This file is generated. See ../README.md for instructions. -¶ -#ifndef LIBVULKAN_API_GEN_H -#define LIBVULKAN_API_GEN_H -¶ -#include <bitset> -#include <vulkan/vulkan.h> -#include "driver_gen.h" -¶ -namespace vulkan {« -namespace api {« -¶ -struct InstanceDispatchTable { - // clang-format off - {{range $f := AllCommands $}} - {{if (Macro "api.IsInstanceDispatchTableEntry" $f)}} - {{Macro "C++.DeclareTableEntry" $f}}; - {{end}} - {{end}} - // clang-format on -}; -¶ -struct DeviceDispatchTable { - // clang-format off - {{range $f := AllCommands $}} - {{if (Macro "api.IsDeviceDispatchTableEntry" $f)}} - {{Macro "C++.DeclareTableEntry" $f}}; - {{end}} - {{end}} - // clang-format on -}; -¶ -bool InitDispatchTable( - VkInstance instance, - PFN_vkGetInstanceProcAddr get_proc, - const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions); -bool InitDispatchTable( - VkDevice dev, - PFN_vkGetDeviceProcAddr get_proc, - const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions); -¶ -»} // namespace api -»} // namespace vulkan -¶ -#endif // LIBVULKAN_API_GEN_H -¶{{end}} - - -{{/* -------------------------------------------------------------------------------- - api_gen.cpp -------------------------------------------------------------------------------- -*/}} -{{define "api_gen.cpp"}} -{{Macro "Copyright"}} -¶ -// WARNING: This file is generated. See ../README.md for instructions. -¶ -#include <string.h> -¶ -#include <algorithm> -¶ -#include <log/log.h> -¶ -// to catch mismatches between vulkan.h and this file -#undef VK_NO_PROTOTYPES -#include "api.h" -¶ -namespace vulkan {« -namespace api {« -¶ -{{Macro "C++.DefineInitProcMacro" "dispatch"}} -¶ -{{Macro "api.C++.DefineInitProcExtMacro"}} -¶ -namespace {« -¶ -// clang-format off -¶ -{{range $f := AllCommands $}} - {{Macro "api.C++.DefineExtensionStub" $f}} -{{end}} -// clang-format on -¶ -»} // anonymous -¶ -bool InitDispatchTable( - VkInstance instance, - PFN_vkGetInstanceProcAddr get_proc, - const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions) { - auto& data = GetData(instance); - bool success = true; - ¶ - // clang-format off - {{range $f := AllCommands $}} - {{if (Macro "api.IsInstanceDispatchTableEntry" $f)}} - {{Macro "C++.InitProc" $f}} - {{end}} - {{end}} - // clang-format on - ¶ - return success; -} -¶ -bool InitDispatchTable( - VkDevice dev, - PFN_vkGetDeviceProcAddr get_proc, - const std::bitset<driver::ProcHook::EXTENSION_COUNT> &extensions) { - auto& data = GetData(dev); - bool success = true; - ¶ - // clang-format off - {{range $f := AllCommands $}} - {{if (Macro "api.IsDeviceDispatchTableEntry" $f)}} - {{Macro "C++.InitProc" $f}} - {{end}} - {{end}} - // clang-format on - ¶ - return success; -} -¶ -// clang-format off -¶ -namespace {« -¶ -// forward declarations needed by GetInstanceProcAddr and GetDeviceProcAddr -{{range $f := AllCommands $}} - {{if and (Macro "IsFunctionExported" $f) (not (Macro "api.IsIntercepted" $f))}} - VKAPI_ATTR {{Node "Type" $f.Return}} {{Macro "BaseName" $f}}({{Macro "Parameters" $f}}); - {{end}} -{{end}} -¶ -{{range $f := AllCommands $}} - {{if and (Macro "IsFunctionExported" $f) (not (Macro "api.IsIntercepted" $f))}} - VKAPI_ATTR {{Node "Type" $f.Return}} {{Macro "BaseName" $f}}({{Macro "Parameters" $f}}) { - {{ if eq $f.Name "vkGetInstanceProcAddr"}} - {{Macro "api.C++.InterceptInstanceProcAddr" $}} - {{else if eq $f.Name "vkGetDeviceProcAddr"}} - {{Macro "api.C++.InterceptDeviceProcAddr" $}} - {{end}} - - {{Macro "api.C++.Dispatch" $f}} - } - ¶ - {{end}} -{{end}} -¶ -»} // anonymous namespace -¶ -// clang-format on -¶ -»} // namespace api -»} // namespace vulkan -¶ -// clang-format off -¶ -{{range $f := AllCommands $}} - {{if (Macro "IsFunctionExported" $f)}} - __attribute__((visibility("default"))) - VKAPI_ATTR {{Node "Type" $f.Return}} {{$f.Name}}({{Macro "Parameters" $f}}) { - {{if not (IsVoid $f.Return.Type)}}return §{{end}} - vulkan::api::{{Macro "BaseName" $f}}({{Macro "Arguments" $f}}); - } - ¶ - {{end}} -{{end}} -¶ -// clang-format on -¶{{end}} - - -{{/* -------------------------------------------------------------------------------- - driver_gen.h -------------------------------------------------------------------------------- -*/}} -{{define "driver_gen.h"}} -{{Macro "Copyright"}} -¶ -// WARNING: This file is generated. See ../README.md for instructions. -¶ -#ifndef LIBVULKAN_DRIVER_GEN_H -#define LIBVULKAN_DRIVER_GEN_H -¶ -#include <bitset> -#include <vulkan/vulkan.h> -#include <vulkan/vk_android_native_buffer.h> -¶ -namespace vulkan {« -namespace driver {« -¶ -{{Macro "driver.C++.DefineProcHookType"}} -¶ -struct InstanceDriverTable { - // clang-format off - {{range $f := AllCommands $}} - {{if (Macro "driver.IsInstanceDriverTableEntry" $f)}} - {{Macro "C++.DeclareTableEntry" $f}}; - {{end}} - {{end}} - // clang-format on -}; -¶ -struct DeviceDriverTable { - // clang-format off - {{range $f := AllCommands $}} - {{if (Macro "driver.IsDeviceDriverTableEntry" $f)}} - {{Macro "C++.DeclareTableEntry" $f}}; - {{end}} - {{end}} - // clang-format on -}; -¶ -const ProcHook* GetProcHook(const char* name); -ProcHook::Extension GetProcHookExtension(const char* name); -¶ -bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc, - const std::bitset<ProcHook::EXTENSION_COUNT> &extensions); -bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc, - const std::bitset<ProcHook::EXTENSION_COUNT> &extensions); -¶ -»} // namespace driver -»} // namespace vulkan -¶ -#endif // LIBVULKAN_DRIVER_TABLE_H -¶{{end}} - - -{{/* -------------------------------------------------------------------------------- - driver_gen.cpp -------------------------------------------------------------------------------- -*/}} -{{define "driver_gen.cpp"}} -{{Macro "Copyright"}} -¶ -// WARNING: This file is generated. See ../README.md for instructions. -¶ -#include <string.h> -¶ -#include <algorithm> -¶ -#include <log/log.h> -¶ -#include "driver.h" -¶ -namespace vulkan {« -namespace driver {« -¶ -namespace {« -¶ -// clang-format off -¶ -{{range $f := AllCommands $}} - {{Macro "driver.C++.DefineProcHookStub" $f}} -{{end}} -// clang-format on -¶ -const ProcHook g_proc_hooks[] = { - // clang-format off - {{range $f := SortBy (AllCommands $) "FunctionName"}} - {{if (Macro "driver.IsIntercepted" $f)}} - {{ if (Macro "IsGloballyDispatched" $f)}} - {{Macro "driver.C++.DefineGlobalProcHook" $f}} - {{else if (Macro "IsInstanceDispatched" $f)}} - {{Macro "driver.C++.DefineInstanceProcHook" $f}} - {{else if (Macro "IsDeviceDispatched" $f)}} - {{Macro "driver.C++.DefineDeviceProcHook" $f}} - {{end}} - {{end}} - {{end}} - // clang-format on -}; -¶ -»} // anonymous -¶ -const ProcHook* GetProcHook(const char* name) { - const auto& begin = g_proc_hooks; - const auto& end = g_proc_hooks + - sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]); - const auto hook = std::lower_bound(begin, end, name, - [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; }); - return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr; -} -¶ -ProcHook::Extension GetProcHookExtension(const char* name) { - {{$exts := Strings (Macro "driver.KnownExtensions") | SplitOn "\n"}} - // clang-format off - {{range $e := $exts}} - if (strcmp(name, "{{$e}}") == 0) return ProcHook::{{TrimPrefix "VK_" $e}}; - {{end}} - // clang-format on - return ProcHook::EXTENSION_UNKNOWN; -} -¶ -{{Macro "C++.DefineInitProcMacro" "driver"}} -¶ -{{Macro "driver.C++.DefineInitProcExtMacro"}} -¶ -bool InitDriverTable(VkInstance instance, PFN_vkGetInstanceProcAddr get_proc, - const std::bitset<ProcHook::EXTENSION_COUNT> &extensions) -{ - auto& data = GetData(instance); - bool success = true; - ¶ - // clang-format off - {{range $f := AllCommands $}} - {{if (Macro "driver.IsInstanceDriverTableEntry" $f)}} - {{Macro "C++.InitProc" $f}} - {{end}} - {{end}} - // clang-format on - ¶ - return success; -} -¶ -bool InitDriverTable(VkDevice dev, PFN_vkGetDeviceProcAddr get_proc, - const std::bitset<ProcHook::EXTENSION_COUNT> &extensions) -{ - auto& data = GetData(dev); - bool success = true; - ¶ - // clang-format off - {{range $f := AllCommands $}} - {{if (Macro "driver.IsDeviceDriverTableEntry" $f)}} - {{Macro "C++.InitProc" $f}} - {{end}} - {{end}} - // clang-format on - ¶ - return success; -} -¶ -»} // namespace driver -»} // namespace vulkan -¶ -// clang-format on -¶{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits a declaration of a dispatch/driver table entry. ------------------------------------------------------------------------------- -*/}} -{{define "C++.DeclareTableEntry"}} - {{AssertType $ "Function"}} - - {{Macro "FunctionPtrName" $}} {{Macro "BaseName" $}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits INIT_PROC macro. -------------------------------------------------------------------------------- -*/}} -{{define "C++.DefineInitProcMacro"}} - #define UNLIKELY(expr) __builtin_expect((expr), 0) - ¶ - #define INIT_PROC(required, obj, proc) do { \ - data.{{$}}.proc = reinterpret_cast<PFN_vk ## proc>( \ - get_proc(obj, "vk" # proc)); \ - if (UNLIKELY(required && !data.{{$}}.proc)) { \ - ALOGE("missing " # obj " proc: vk" # proc); \ - success = false; \ - } \ - } while(0) -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits code to invoke INIT_PROC or INIT_PROC_EXT. -------------------------------------------------------------------------------- -*/}} -{{define "C++.InitProc"}} - {{AssertType $ "Function"}} - - {{$ext := GetAnnotation $ "extension"}} - {{if $ext}} - INIT_PROC_EXT({{Macro "BaseName" $ext}}, § - {{else}} - INIT_PROC(§ - {{end}} - - {{if GetAnnotation $ "optional"}}false{{else if GetAnnotation $ "vulkan1_1"}}false{{else}}true{{end}}, § - - {{if (Macro "IsInstanceDispatched" $)}} - instance, § - {{else}} - dev, § - {{end}} - - {{Macro "BaseName" $}}); -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits true if a function is exported and instance-dispatched. ------------------------------------------------------------------------------- -*/}} -{{define "api.IsInstanceDispatchTableEntry"}} - {{AssertType $ "Function"}} - - {{if and (Macro "IsFunctionExported" $) (Macro "IsInstanceDispatched" $)}} - {{/* deprecated and unused internally */}} - {{if not (eq $.Name "vkEnumerateDeviceLayerProperties")}} - true - {{end}} - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits true if a function is exported and device-dispatched. ------------------------------------------------------------------------------- -*/}} -{{define "api.IsDeviceDispatchTableEntry"}} - {{AssertType $ "Function"}} - - {{if and (Macro "IsFunctionExported" $) (Macro "IsDeviceDispatched" $)}} - true - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits true if a function is intercepted by vulkan::api. ------------------------------------------------------------------------------- -*/}} -{{define "api.IsIntercepted"}} - {{AssertType $ "Function"}} - - {{if (Macro "IsFunctionSupported" $)}} - {{/* Global functions cannot be dispatched at all */}} - {{ if (Macro "IsGloballyDispatched" $)}}true - - {{/* VkPhysicalDevice functions that manage device layers */}} - {{else if eq $.Name "vkCreateDevice"}}true - {{else if eq $.Name "vkEnumerateDeviceLayerProperties"}}true - {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true - - {{/* Destroy functions of dispatchable objects */}} - {{else if eq $.Name "vkDestroyInstance"}}true - {{else if eq $.Name "vkDestroyDevice"}}true - - {{end}} - {{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits INIT_PROC_EXT macro for vulkan::api. -------------------------------------------------------------------------------- -*/}} -{{define "api.C++.DefineInitProcExtMacro"}} - // Exported extension functions may be invoked even when their extensions - // are disabled. Dispatch to stubs when that happens. - #define INIT_PROC_EXT(ext, required, obj, proc) do { \ - if (extensions[driver::ProcHook::ext]) \ - INIT_PROC(required, obj, proc); \ - else \ - data.dispatch.proc = disabled ## proc; \ - } while(0) -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits a stub for an exported extension function. -------------------------------------------------------------------------------- -*/}} -{{define "api.C++.DefineExtensionStub"}} - {{AssertType $ "Function"}} - - {{$ext := GetAnnotation $ "extension"}} - {{if and $ext (Macro "IsFunctionExported" $)}} - {{$ext_name := index $ext.Arguments 0}} - - {{$base := (Macro "BaseName" $)}} - - {{$p0 := (index $.CallParameters 0)}} - {{$ptail := (Tail 1 $.CallParameters)}} - - {{$first_type := (Macro "Parameter" $p0)}} - {{$tail_types := (ForEach $ptail "ParameterType" | JoinWith ", ")}} - - VKAPI_ATTR {{Node "Type" $.Return}} disabled{{$base}}({{$first_type}}, {{$tail_types}}) { - driver::Logger({{$p0.Name}}).Err({{$p0.Name}}, § - "{{$ext_name}} not enabled. Exported {{$.Name}} not executed."); - {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}} - } - ¶ - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits code for vkGetInstanceProcAddr for function interception. ------------------------------------------------------------------------------- -*/}} -{{define "api.C++.InterceptInstanceProcAddr"}} - {{AssertType $ "API"}} - - // global functions - if (instance == VK_NULL_HANDLE) { - {{range $f := AllCommands $}} - {{if (Macro "IsGloballyDispatched" $f)}} - if (strcmp(pName, "{{$f.Name}}") == 0) return § - reinterpret_cast<PFN_vkVoidFunction>({{Macro "BaseName" $f}}); - {{end}} - {{end}} - ¶ - ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \"%s\") call", pName); - return nullptr; - } - ¶ - static const struct Hook { - const char* name; - PFN_vkVoidFunction proc; - } hooks[] = { - {{range $f := SortBy (AllCommands $) "FunctionName"}} - {{if (Macro "IsFunctionExported" $f)}} - {{/* hide global functions */}} - {{if (Macro "IsGloballyDispatched" $f)}} - { "{{$f.Name}}", nullptr }, - - {{/* redirect intercepted functions */}} - {{else if (Macro "api.IsIntercepted" $f)}} - { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§ - {{Macro "BaseName" $f}}) }, - - {{/* redirect vkGetInstanceProcAddr to itself */}} - {{else if eq $f.Name "vkGetInstanceProcAddr"}} - { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{Macro "BaseName" $f}}) }, - - {{/* redirect device functions to themselves as a workaround for - layers that do not intercept in their vkGetInstanceProcAddr */}} - {{else if (Macro "IsDeviceDispatched" $f)}} - { "{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>({{Macro "BaseName" $f}}) }, - - {{end}} - {{end}} - {{end}} - }; - // clang-format on - constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]); - auto hook = std::lower_bound( - hooks, hooks + count, pName, - [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; }); - if (hook < hooks + count && strcmp(hook->name, pName) == 0) { - if (!hook->proc) { - vulkan::driver::Logger(instance).Err( - instance, "invalid vkGetInstanceProcAddr(%p, \"%s\") call", - instance, pName); - } - return hook->proc; - } - // clang-format off - ¶ -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits code for vkGetDeviceProcAddr for function interception. ------------------------------------------------------------------------------- -*/}} -{{define "api.C++.InterceptDeviceProcAddr"}} - {{AssertType $ "API"}} - - if (device == VK_NULL_HANDLE) { - ALOGE("invalid vkGetDeviceProcAddr(VK_NULL_HANDLE, ...) call"); - return nullptr; - } - ¶ - static const char* const known_non_device_names[] = { - {{range $f := SortBy (AllCommands $) "FunctionName"}} - {{if (Macro "IsFunctionSupported" $f)}} - {{if not (Macro "IsDeviceDispatched" $f)}} - "{{$f.Name}}", - {{end}} - {{end}} - {{end}} - }; - // clang-format on - constexpr size_t count = sizeof(known_non_device_names) / - sizeof(known_non_device_names[0]); - if (!pName || - std::binary_search( - known_non_device_names, known_non_device_names + count, pName, - [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) { - vulkan::driver::Logger(device).Err(§ - device, "invalid vkGetDeviceProcAddr(%p, \"%s\") call", device,§ - (pName) ? pName : "(null)"); - return nullptr; - } - // clang-format off - ¶ - {{range $f := AllCommands $}} - {{if (Macro "IsDeviceDispatched" $f)}} - {{ if (Macro "api.IsIntercepted" $f)}} - if (strcmp(pName, "{{$f.Name}}") == 0) return § - reinterpret_cast<PFN_vkVoidFunction>(§ - {{Macro "BaseName" $f}}); - {{else if eq $f.Name "vkGetDeviceProcAddr"}} - if (strcmp(pName, "{{$f.Name}}") == 0) return § - reinterpret_cast<PFN_vkVoidFunction>(§ - {{Macro "BaseName" $f}}); - {{end}} - {{end}} - {{end}} - ¶ -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits code to dispatch a function. ------------------------------------------------------------------------------- -*/}} -{{define "api.C++.Dispatch"}} - {{AssertType $ "Function"}} - {{if (Macro "api.IsIntercepted" $)}} - {{Error "$.Name should not be generated"}} - {{end}} - - {{if not (IsVoid $.Return.Type)}}return §{{end}} - - {{$p0 := index $.CallParameters 0}} - GetData({{$p0.Name}}).dispatch.§ - {{Macro "BaseName" $}}({{Macro "Arguments" $}}); -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits a list of extensions intercepted by vulkan::driver. ------------------------------------------------------------------------------- -*/}} -{{define "driver.InterceptedExtensions"}} -VK_ANDROID_native_buffer -VK_EXT_debug_report -VK_EXT_hdr_metadata -VK_EXT_swapchain_colorspace -VK_GOOGLE_display_timing -VK_KHR_android_surface -VK_KHR_incremental_present -VK_KHR_shared_presentable_image -VK_KHR_surface -VK_KHR_swapchain -VK_KHR_get_surface_capabilities2 -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits a list of extensions known to vulkan::driver. ------------------------------------------------------------------------------- -*/}} -{{define "driver.KnownExtensions"}} -{{Macro "driver.InterceptedExtensions"}} -VK_KHR_get_physical_device_properties2 -VK_ANDROID_external_memory_android_hardware_buffer -VK_KHR_bind_memory2 -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits true if an extension is intercepted by vulkan::driver. ------------------------------------------------------------------------------- -*/}} -{{define "driver.IsExtensionIntercepted"}} - {{$ext_name := index $.Arguments 0}} - {{$filters := Strings (Macro "driver.InterceptedExtensions") | SplitOn "\n"}} - - {{range $f := $filters}} - {{if eq $ext_name $f}}true{{end}} - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits true if a function is intercepted by vulkan::driver. ------------------------------------------------------------------------------- -*/}} -{{define "driver.IsIntercepted"}} - {{AssertType $ "Function"}} - - {{if (Macro "IsFunctionSupported" $)}} - {{/* Create functions of dispatchable objects */}} - {{ if eq $.Name "vkCreateInstance"}}true - {{else if eq $.Name "vkCreateDevice"}}true - {{else if eq $.Name "vkEnumeratePhysicalDevices"}}true - {{else if eq $.Name "vkEnumeratePhysicalDeviceGroups"}}true - {{else if eq $.Name "vkGetDeviceQueue"}}true - {{else if eq $.Name "vkGetDeviceQueue2"}}true - {{else if eq $.Name "vkAllocateCommandBuffers"}}true - - {{/* Destroy functions of dispatchable objects */}} - {{else if eq $.Name "vkDestroyInstance"}}true - {{else if eq $.Name "vkDestroyDevice"}}true - - {{/* Enumeration of extensions */}} - {{else if eq $.Name "vkEnumerateInstanceExtensionProperties"}}true - {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true - - {{else if eq $.Name "vkGetInstanceProcAddr"}}true - {{else if eq $.Name "vkGetDeviceProcAddr"}}true - - {{/* VK_KHR_swapchain v69 requirement */}} - {{else if eq $.Name "vkBindImageMemory2"}}true - {{else if eq $.Name "vkBindImageMemory2KHR"}}true - {{end}} - - {{$ext := GetAnnotation $ "extension"}} - {{if $ext}} - {{Macro "driver.IsExtensionIntercepted" $ext}} - {{end}} - - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits true if a function needs a ProcHook stub. ------------------------------------------------------------------------------- -*/}} -{{define "driver.NeedProcHookStub"}} - {{AssertType $ "Function"}} - - {{if and (Macro "driver.IsIntercepted" $) (Macro "IsDeviceDispatched" $)}} - {{$ext := GetAnnotation $ "extension"}} - {{if $ext}} - {{if not (Macro "IsExtensionInternal" $ext)}}true{{end}} - {{end}} - {{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits definition of struct ProcHook. -------------------------------------------------------------------------------- -*/}} -{{define "driver.C++.DefineProcHookType"}} - struct ProcHook { - enum Type { - GLOBAL, - INSTANCE, - DEVICE, - }; - - enum Extension { - {{$exts := Strings (Macro "driver.KnownExtensions") | SplitOn "\n"}} - {{range $e := $exts}} - {{TrimPrefix "VK_" $e}}, - {{end}} - ¶ - EXTENSION_CORE, // valid bit - EXTENSION_COUNT, - EXTENSION_UNKNOWN, - }; - ¶ - const char* name; - Type type; - Extension extension; - ¶ - PFN_vkVoidFunction proc; - PFN_vkVoidFunction checked_proc; // always nullptr for non-device hooks - }; -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits INIT_PROC_EXT macro for vulkan::driver. -------------------------------------------------------------------------------- -*/}} -{{define "driver.C++.DefineInitProcExtMacro"}} - #define INIT_PROC_EXT(ext, required, obj, proc) do { \ - if (extensions[ProcHook::ext]) \ - INIT_PROC(required, obj, proc); \ - } while(0) -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits a stub for ProcHook::checked_proc. -------------------------------------------------------------------------------- -*/}} -{{define "driver.C++.DefineProcHookStub"}} - {{AssertType $ "Function"}} - - {{if (Macro "driver.NeedProcHookStub" $)}} - {{$ext := GetAnnotation $ "extension"}} - {{$ext_name := index $ext.Arguments 0}} - - {{$base := (Macro "BaseName" $)}} - - VKAPI_ATTR {{Node "Type" $.Return}} checked{{$base}}({{Macro "Parameters" $}}) { - {{$p0 := index $.CallParameters 0}} - {{$ext_hook := Strings ("ProcHook::") (Macro "BaseName" $ext)}} - - if (GetData({{$p0.Name}}).hook_extensions[{{$ext_hook}}]) { - {{if not (IsVoid $.Return.Type)}}return §{{end}} - {{$base}}({{Macro "Arguments" $}}); - } else { - Logger({{$p0.Name}}).Err({{$p0.Name}}, "{{$ext_name}} not enabled. {{$.Name}} not executed."); - {{if not (IsVoid $.Return.Type)}}return VK_SUCCESS;{{end}} - } - } - ¶ - {{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits definition of a global ProcHook. -------------------------------------------------------------------------------- -*/}} -{{define "driver.C++.DefineGlobalProcHook"}} - {{AssertType $ "Function"}} - - {{$base := (Macro "BaseName" $)}} - - {{$ext := GetAnnotation $ "extension"}} - {{if $ext}} - {{Error "invalid global extension"}} - {{end}} - - { - "{{$.Name}}", - ProcHook::GLOBAL, - ProcHook::EXTENSION_CORE, - reinterpret_cast<PFN_vkVoidFunction>({{$base}}), - nullptr, - }, -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits definition of an instance ProcHook. -------------------------------------------------------------------------------- -*/}} -{{define "driver.C++.DefineInstanceProcHook"}} - {{AssertType $ "Function"}} - - {{$base := (Macro "BaseName" $)}} - - { - "{{$.Name}}", - ProcHook::INSTANCE, - - {{$ext := GetAnnotation $ "extension"}} - {{if $ext}} - ProcHook::{{Macro "BaseName" $ext}}, - - {{if (Macro "IsExtensionInternal" $ext)}} - nullptr, - nullptr, - {{else}} - reinterpret_cast<PFN_vkVoidFunction>({{$base}}), - nullptr, - {{end}} - {{else}} - ProcHook::EXTENSION_CORE, - reinterpret_cast<PFN_vkVoidFunction>({{$base}}), - nullptr, - {{end}} - }, -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits definition of a device ProcHook. -------------------------------------------------------------------------------- -*/}} -{{define "driver.C++.DefineDeviceProcHook"}} - {{AssertType $ "Function"}} - - {{$base := (Macro "BaseName" $)}} - - { - "{{$.Name}}", - ProcHook::DEVICE, - - {{$ext := GetAnnotation $ "extension"}} - {{if $ext}} - ProcHook::{{Macro "BaseName" $ext}}, - - {{if (Macro "IsExtensionInternal" $ext)}} - nullptr, - nullptr, - {{else}} - reinterpret_cast<PFN_vkVoidFunction>({{$base}}), - reinterpret_cast<PFN_vkVoidFunction>(checked{{$base}}), - {{end}} - {{else}} - ProcHook::EXTENSION_CORE, - reinterpret_cast<PFN_vkVoidFunction>({{$base}}), - nullptr, - {{end}} - }, -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits true if a function is needed by vulkan::driver. -------------------------------------------------------------------------------- -*/}} -{{define "driver.IsDriverTableEntry"}} - {{AssertType $ "Function"}} - - {{if (Macro "IsFunctionSupported" $)}} - {{/* Create functions of dispatchable objects */}} - {{ if eq $.Name "vkCreateDevice"}}true - {{else if eq $.Name "vkGetDeviceQueue"}}true - {{else if eq $.Name "vkGetDeviceQueue2"}}true - {{else if eq $.Name "vkAllocateCommandBuffers"}}true - - {{/* Destroy functions of dispatchable objects */}} - {{else if eq $.Name "vkDestroyInstance"}}true - {{else if eq $.Name "vkDestroyDevice"}}true - - {{/* Enumeration of extensions */}} - {{else if eq $.Name "vkEnumerateDeviceExtensionProperties"}}true - - {{/* We cache physical devices in loader.cpp */}} - {{else if eq $.Name "vkEnumeratePhysicalDevices"}}true - {{else if eq $.Name "vkEnumeratePhysicalDeviceGroups"}}true - - {{else if eq $.Name "vkGetInstanceProcAddr"}}true - {{else if eq $.Name "vkGetDeviceProcAddr"}}true - - {{/* VK_KHR_swapchain->VK_ANDROID_native_buffer translation */}} - {{else if eq $.Name "vkCreateImage"}}true - {{else if eq $.Name "vkDestroyImage"}}true - - {{else if eq $.Name "vkGetPhysicalDeviceProperties"}}true - {{else if eq $.Name "vkGetPhysicalDeviceProperties2"}}true - {{else if eq $.Name "vkGetPhysicalDeviceProperties2KHR"}}true - - {{/* VK_KHR_swapchain v69 requirement */}} - {{else if eq $.Name "vkBindImageMemory2"}}true - {{else if eq $.Name "vkBindImageMemory2KHR"}}true - {{end}} - - {{$ext := GetAnnotation $ "extension"}} - {{if $ext}} - {{$ext_name := index $ext.Arguments 0}} - {{ if eq $ext_name "VK_ANDROID_native_buffer"}}true - {{else if eq $ext_name "VK_EXT_debug_report"}}true - {{end}} - {{end}} - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits true if an instance-dispatched function is needed by vulkan::driver. ------------------------------------------------------------------------------- -*/}} -{{define "driver.IsInstanceDriverTableEntry"}} - {{AssertType $ "Function"}} - - {{if and (Macro "driver.IsDriverTableEntry" $) (Macro "IsInstanceDispatched" $)}} - true - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits true if a device-dispatched function is needed by vulkan::driver. ------------------------------------------------------------------------------- -*/}} -{{define "driver.IsDeviceDriverTableEntry"}} - {{AssertType $ "Function"}} - - {{if and (Macro "driver.IsDriverTableEntry" $) (Macro "IsDeviceDispatched" $)}} - true - {{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits a function/extension name without the "vk"/"VK_" prefix. -------------------------------------------------------------------------------- -*/}} -{{define "BaseName"}} - {{ if IsFunction $}}{{TrimPrefix "vk" $.Name}} - {{else if eq $.Name "extension"}}{{TrimPrefix "VK_" (index $.Arguments 0)}} - {{else}}{{Error "invalid use of BaseName"}} - {{end}} -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits a comma-separated list of C parameter names for the given command. -------------------------------------------------------------------------------- -*/}} -{{define "Arguments"}} - {{AssertType $ "Function"}} - - {{ForEach $.CallParameters "ParameterName" | JoinWith ", "}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -*/}} -{{define "IsGloballyDispatched"}} - {{AssertType $ "Function"}} - {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Global")}} - true - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emit "true" for supported functions that undergo table dispatch. Only global - functions and functions handled in the loader top without calling into - lower layers are not dispatched. ------------------------------------------------------------------------------- -*/}} -{{define "IsInstanceDispatched"}} - {{AssertType $ "Function"}} - {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Instance")}} - true - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emit "true" for supported functions that can have device-specific dispatch. ------------------------------------------------------------------------------- -*/}} -{{define "IsDeviceDispatched"}} - {{AssertType $ "Function"}} - {{if and (Macro "IsFunctionSupported" $) (eq (Macro "Vtbl" $) "Device")}} - true - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emit "true" if a function is core or from a supportable extension. ------------------------------------------------------------------------------- -*/}} -{{define "IsFunctionSupported"}} - {{AssertType $ "Function"}} - {{if not (GetAnnotation $ "pfn")}} - {{$ext := GetAnnotation $ "extension"}} - {{if not $ext}}true - {{else if not (Macro "IsExtensionBlacklisted" $ext)}}true - {{end}} - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Decides whether a function should be exported from the Android Vulkan - library. Functions in the core API and in loader extensions are exported. ------------------------------------------------------------------------------- -*/}} -{{define "IsFunctionExported"}} - {{AssertType $ "Function"}} - - {{if (Macro "IsFunctionSupported" $)}} - {{$ext := GetAnnotation $ "extension"}} - {{if $ext}} - {{Macro "IsExtensionExported" $ext}} - {{else}} - true - {{end}} - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emit "true" if an extension is unsupportable on Android. ------------------------------------------------------------------------------- -*/}} -{{define "IsExtensionBlacklisted"}} - {{$ext := index $.Arguments 0}} - {{ if eq $ext "VK_KHR_display"}}true - {{else if eq $ext "VK_KHR_display_swapchain"}}true - {{else if eq $ext "VK_KHR_mir_surface"}}true - {{else if eq $ext "VK_KHR_xcb_surface"}}true - {{else if eq $ext "VK_KHR_xlib_surface"}}true - {{else if eq $ext "VK_KHR_wayland_surface"}}true - {{else if eq $ext "VK_KHR_win32_surface"}}true - {{else if eq $ext "VK_KHR_external_memory_win32"}}true - {{else if eq $ext "VK_KHR_win32_keyed_mutex"}}true - {{else if eq $ext "VK_KHR_external_semaphore_win32"}}true - {{else if eq $ext "VK_KHR_external_fence_win32"}}true - {{else if eq $ext "VK_EXT_acquire_xlib_display"}}true - {{else if eq $ext "VK_EXT_direct_mode_display"}}true - {{else if eq $ext "VK_EXT_display_surface_counter"}}true - {{else if eq $ext "VK_EXT_display_control"}}true - {{else if eq $ext "VK_FUCHSIA_imagepipe_surface"}}true - {{else if eq $ext "VK_MVK_ios_surface"}}true - {{else if eq $ext "VK_MVK_macos_surface"}}true - {{else if eq $ext "VK_NN_vi_surface"}}true - {{else if eq $ext "VK_NV_external_memory_win32"}}true - {{else if eq $ext "VK_NV_win32_keyed_mutex"}}true - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Reports whether an extension has functions exported by the loader. - E.g. applications can directly link to an extension function. ------------------------------------------------------------------------------- -*/}} -{{define "IsExtensionExported"}} - {{$ext := index $.Arguments 0}} - {{ if eq $ext "VK_KHR_surface"}}true - {{else if eq $ext "VK_KHR_swapchain"}}true - {{else if eq $ext "VK_KHR_android_surface"}}true - {{else if eq $ext "VK_ANDROID_external_memory_android_hardware_buffer"}}true - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Reports whether an extension is internal to the loader and drivers, - so the loader should not enumerate it. ------------------------------------------------------------------------------- -*/}} -{{define "IsExtensionInternal"}} - {{$ext := index $.Arguments 0}} - {{ if eq $ext "VK_ANDROID_native_buffer"}}true - {{end}} -{{end}} diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index a53bb5933b..b413ac9375 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -16,40 +16,35 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include "driver.h" + +#include <dlfcn.h> #include <malloc.h> #include <stdlib.h> #include <string.h> -#include <sys/prctl.h> - -#include <dlfcn.h> -#include <algorithm> -#include <array> -#include <new> - -#include <log/log.h> #include <android/dlext.h> #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> +#include <android-base/properties.h> #include <configstore/Utils.h> #include <cutils/properties.h> #include <graphicsenv/GraphicsEnv.h> +#include <log/log.h> +#include <nativeloader/dlext_namespaces.h> +#include <sys/prctl.h> #include <utils/Timers.h> #include <utils/Trace.h> -#include <utils/Vector.h> -#include "android-base/properties.h" +#include <algorithm> +#include <array> +#include <new> +#include <vector> -#include "driver.h" #include "stubhal.h" using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; -// TODO(b/37049319) Get this from a header once one exists -extern "C" { -android_namespace_t* android_get_exported_namespace(const char*); -} - // #define ENABLE_ALLOC_CALLSTACKS 1 #if ENABLE_ALLOC_CALLSTACKS #include <utils/CallStack.h> @@ -212,7 +207,7 @@ int LoadBuiltinDriver(const hwvulkan_module_t** module) { if (!ns) return -ENOENT; android::GraphicsEnv::getInstance().setDriverToLoad( - android::GraphicsEnv::Driver::VULKAN); + android::GpuStatsInfo::Driver::VULKAN); return LoadDriver(ns, module); } @@ -223,7 +218,7 @@ int LoadUpdatedDriver(const hwvulkan_module_t** module) { if (!ns) return -ENOENT; android::GraphicsEnv::getInstance().setDriverToLoad( - android::GraphicsEnv::Driver::VULKAN_UPDATED); + android::GpuStatsInfo::Driver::VULKAN_UPDATED); return LoadDriver(ns, module); } @@ -258,7 +253,7 @@ bool Hal::Open() { } if (result != 0) { android::GraphicsEnv::getInstance().setDriverLoaded( - android::GraphicsEnv::Api::API_VK, false, systemTime() - openTime); + android::GpuStatsInfo::Api::API_VK, false, systemTime() - openTime); ALOGV("unable to load Vulkan HAL, using stub HAL (result=%d)", result); return true; } @@ -272,7 +267,7 @@ bool Hal::Open() { ATRACE_END(); if (result != 0) { android::GraphicsEnv::getInstance().setDriverLoaded( - android::GraphicsEnv::Api::API_VK, false, systemTime() - openTime); + android::GpuStatsInfo::Api::API_VK, false, systemTime() - openTime); // Any device with a Vulkan HAL should be able to open the device. ALOGE("failed to open Vulkan HAL device: %s (%d)", strerror(-result), result); @@ -284,7 +279,7 @@ bool Hal::Open() { hal_.InitDebugReportIndex(); android::GraphicsEnv::getInstance().setDriverLoaded( - android::GraphicsEnv::Api::API_VK, true, systemTime() - openTime); + android::GpuStatsInfo::Api::API_VK, true, systemTime() - openTime); return true; } @@ -809,8 +804,7 @@ VkResult EnumerateInstanceExtensionProperties( const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties) { - - android::Vector<VkExtensionProperties> loader_extensions; + std::vector<VkExtensionProperties> loader_extensions; loader_extensions.push_back({ VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_SURFACE_SPEC_VERSION}); @@ -833,7 +827,7 @@ VkResult EnumerateInstanceExtensionProperties( uint32_t count = std::min( *pPropertyCount, static_cast<uint32_t>(loader_extensions.size())); - std::copy_n(loader_extensions.begin(), count, pProperties); + std::copy_n(loader_extensions.data(), count, pProperties); if (count < loader_extensions.size()) { *pPropertyCount = count; @@ -879,8 +873,7 @@ VkResult EnumerateInstanceExtensionProperties( bool QueryPresentationProperties( VkPhysicalDevice physicalDevice, - VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties) -{ + VkPhysicalDevicePresentationPropertiesANDROID *presentation_properties) { const InstanceData& data = GetData(physicalDevice); // GPDP2 must be present and enabled on the instance. @@ -920,7 +913,7 @@ VkResult EnumerateDeviceExtensionProperties( VkExtensionProperties* pProperties) { const InstanceData& data = GetData(physicalDevice); // extensions that are unconditionally exposed by the loader - android::Vector<VkExtensionProperties> loader_extensions; + std::vector<VkExtensionProperties> loader_extensions; loader_extensions.push_back({ VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION}); @@ -956,7 +949,7 @@ VkResult EnumerateDeviceExtensionProperties( uint32_t count = std::min( *pPropertyCount, static_cast<uint32_t>(loader_extensions.size())); - std::copy_n(loader_extensions.begin(), count, pProperties); + std::copy_n(loader_extensions.data(), count, pProperties); if (count < loader_extensions.size()) { *pPropertyCount = count; @@ -1177,7 +1170,7 @@ VkResult CreateDevice(VkPhysicalDevice physicalDevice, if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) { // Log that the app is hitting software Vulkan implementation android::GraphicsEnv::getInstance().setTargetStats( - android::GraphicsEnv::Stats::CPU_VULKAN_IN_USE); + android::GpuStatsInfo::Stats::CPU_VULKAN_IN_USE); } data->driver_device = dev; @@ -1246,11 +1239,10 @@ VkResult EnumeratePhysicalDeviceGroups( if (!device_count) return VK_INCOMPLETE; - android::Vector<VkPhysicalDevice> devices; - devices.resize(device_count); + std::vector<VkPhysicalDevice> devices(device_count); *pPhysicalDeviceGroupCount = device_count; - result = EnumeratePhysicalDevices(instance, &device_count, - devices.editArray()); + result = + EnumeratePhysicalDevices(instance, &device_count, devices.data()); if (result < 0) return result; @@ -1321,5 +1313,16 @@ AllocateCommandBuffers(VkDevice device, return result; } +VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo* pSubmits, + VkFence fence) { + ATRACE_CALL(); + + const auto& data = GetData(queue); + + return data.driver.QueueSubmit(queue, submitCount, pSubmits, fence); +} + } // namespace driver } // namespace vulkan diff --git a/vulkan/libvulkan/driver.h b/vulkan/libvulkan/driver.h index 57c956d9a4..f058c47d54 100644 --- a/vulkan/libvulkan/driver.h +++ b/vulkan/libvulkan/driver.h @@ -131,6 +131,7 @@ VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* VKAPI_ATTR void GetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue); VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue); VKAPI_ATTR VkResult AllocateCommandBuffers(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers); +VKAPI_ATTR VkResult QueueSubmit(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence); // clang-format on template <typename DispatchableType> diff --git a/vulkan/libvulkan/driver_gen.cpp b/vulkan/libvulkan/driver_gen.cpp index 574c3273d0..f676573de9 100644 --- a/vulkan/libvulkan/driver_gen.cpp +++ b/vulkan/libvulkan/driver_gen.cpp @@ -16,12 +16,11 @@ // WARNING: This file is generated. See ../README.md for instructions. +#include <log/log.h> #include <string.h> #include <algorithm> -#include <log/log.h> - #include "driver.h" namespace vulkan { @@ -75,6 +74,15 @@ VKAPI_ATTR VkResult checkedQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR } } +VKAPI_ATTR VkResult checkedBindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos) { + if (GetData(device).hook_extensions[ProcHook::KHR_bind_memory2]) { + return BindImageMemory2KHR(device, bindInfoCount, pBindInfos); + } else { + Logger(device).Err(device, "VK_KHR_bind_memory2 not enabled. vkBindImageMemory2KHR not executed."); + return VK_SUCCESS; + } +} + VKAPI_ATTR VkResult checkedGetDeviceGroupPresentCapabilitiesKHR(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities) { if (GetData(device).hook_extensions[ProcHook::KHR_swapchain]) { return GetDeviceGroupPresentCapabilitiesKHR(device, pDeviceGroupPresentCapabilities); @@ -102,24 +110,6 @@ VKAPI_ATTR VkResult checkedAcquireNextImage2KHR(VkDevice device, const VkAcquire } } -VKAPI_ATTR VkResult checkedGetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) { - if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) { - return GetRefreshCycleDurationGOOGLE(device, swapchain, pDisplayTimingProperties); - } else { - Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetRefreshCycleDurationGOOGLE not executed."); - return VK_SUCCESS; - } -} - -VKAPI_ATTR VkResult checkedGetPastPresentationTimingGOOGLE(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings) { - if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) { - return GetPastPresentationTimingGOOGLE(device, swapchain, pPresentationTimingCount, pPresentationTimings); - } else { - Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetPastPresentationTimingGOOGLE not executed."); - return VK_SUCCESS; - } -} - VKAPI_ATTR void checkedSetHdrMetadataEXT(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata) { if (GetData(device).hook_extensions[ProcHook::EXT_hdr_metadata]) { SetHdrMetadataEXT(device, swapchainCount, pSwapchains, pMetadata); @@ -137,11 +127,20 @@ VKAPI_ATTR VkResult checkedGetSwapchainStatusKHR(VkDevice device, VkSwapchainKHR } } -VKAPI_ATTR VkResult checkedBindImageMemory2KHR(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfoKHR* pBindInfos) { - if (GetData(device).hook_extensions[ProcHook::KHR_bind_memory2]) { - return BindImageMemory2KHR(device, bindInfoCount, pBindInfos); +VKAPI_ATTR VkResult checkedGetRefreshCycleDurationGOOGLE(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties) { + if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) { + return GetRefreshCycleDurationGOOGLE(device, swapchain, pDisplayTimingProperties); } else { - Logger(device).Err(device, "VK_KHR_bind_memory2 not enabled. vkBindImageMemory2KHR not executed."); + Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetRefreshCycleDurationGOOGLE not executed."); + return VK_SUCCESS; + } +} + +VKAPI_ATTR VkResult checkedGetPastPresentationTimingGOOGLE(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings) { + if (GetData(device).hook_extensions[ProcHook::GOOGLE_display_timing]) { + return GetPastPresentationTimingGOOGLE(device, swapchain, pPresentationTimingCount, pPresentationTimings); + } else { + Logger(device).Err(device, "VK_GOOGLE_display_timing not enabled. vkGetPastPresentationTimingGOOGLE not executed."); return VK_SUCCESS; } } @@ -445,6 +444,13 @@ const ProcHook g_proc_hooks[] = { nullptr, }, { + "vkQueueSubmit", + ProcHook::DEVICE, + ProcHook::EXTENSION_CORE, + reinterpret_cast<PFN_vkVoidFunction>(QueueSubmit), + nullptr, + }, + { "vkSetHdrMetadataEXT", ProcHook::DEVICE, ProcHook::EXT_hdr_metadata, @@ -474,14 +480,14 @@ ProcHook::Extension GetProcHookExtension(const char* name) { if (strcmp(name, "VK_EXT_swapchain_colorspace") == 0) return ProcHook::EXT_swapchain_colorspace; if (strcmp(name, "VK_GOOGLE_display_timing") == 0) return ProcHook::GOOGLE_display_timing; if (strcmp(name, "VK_KHR_android_surface") == 0) return ProcHook::KHR_android_surface; + if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2; if (strcmp(name, "VK_KHR_incremental_present") == 0) return ProcHook::KHR_incremental_present; if (strcmp(name, "VK_KHR_shared_presentable_image") == 0) return ProcHook::KHR_shared_presentable_image; if (strcmp(name, "VK_KHR_surface") == 0) return ProcHook::KHR_surface; if (strcmp(name, "VK_KHR_swapchain") == 0) return ProcHook::KHR_swapchain; - if (strcmp(name, "VK_KHR_get_surface_capabilities2") == 0) return ProcHook::KHR_get_surface_capabilities2; - if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2; if (strcmp(name, "VK_ANDROID_external_memory_android_hardware_buffer") == 0) return ProcHook::ANDROID_external_memory_android_hardware_buffer; if (strcmp(name, "VK_KHR_bind_memory2") == 0) return ProcHook::KHR_bind_memory2; + if (strcmp(name, "VK_KHR_get_physical_device_properties2") == 0) return ProcHook::KHR_get_physical_device_properties2; // clang-format on return ProcHook::EXTENSION_UNKNOWN; } @@ -517,12 +523,12 @@ bool InitDriverTable(VkInstance instance, INIT_PROC(true, instance, GetPhysicalDeviceProperties); INIT_PROC(true, instance, CreateDevice); INIT_PROC(true, instance, EnumerateDeviceExtensionProperties); - INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups); - INIT_PROC(false, instance, GetPhysicalDeviceProperties2); INIT_PROC_EXT(EXT_debug_report, true, instance, CreateDebugReportCallbackEXT); INIT_PROC_EXT(EXT_debug_report, true, instance, DestroyDebugReportCallbackEXT); INIT_PROC_EXT(EXT_debug_report, true, instance, DebugReportMessageEXT); + INIT_PROC(false, instance, GetPhysicalDeviceProperties2); INIT_PROC_EXT(KHR_get_physical_device_properties2, true, instance, GetPhysicalDeviceProperties2KHR); + INIT_PROC(false, instance, EnumeratePhysicalDeviceGroups); // clang-format on return success; @@ -538,16 +544,17 @@ bool InitDriverTable(VkDevice dev, INIT_PROC(true, dev, GetDeviceProcAddr); INIT_PROC(true, dev, DestroyDevice); INIT_PROC(true, dev, GetDeviceQueue); + INIT_PROC(true, dev, QueueSubmit); INIT_PROC(true, dev, CreateImage); INIT_PROC(true, dev, DestroyImage); INIT_PROC(true, dev, AllocateCommandBuffers); INIT_PROC(false, dev, BindImageMemory2); + INIT_PROC_EXT(KHR_bind_memory2, true, dev, BindImageMemory2KHR); INIT_PROC(false, dev, GetDeviceQueue2); INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsageANDROID); INIT_PROC_EXT(ANDROID_native_buffer, false, dev, GetSwapchainGrallocUsage2ANDROID); INIT_PROC_EXT(ANDROID_native_buffer, true, dev, AcquireImageANDROID); INIT_PROC_EXT(ANDROID_native_buffer, true, dev, QueueSignalReleaseImageANDROID); - INIT_PROC_EXT(KHR_bind_memory2, true, dev, BindImageMemory2KHR); // clang-format on return success; @@ -555,5 +562,3 @@ bool InitDriverTable(VkDevice dev, } // namespace driver } // namespace vulkan - -// clang-format on diff --git a/vulkan/libvulkan/driver_gen.h b/vulkan/libvulkan/driver_gen.h index 3faf6c0e32..cd7d8f82e0 100644 --- a/vulkan/libvulkan/driver_gen.h +++ b/vulkan/libvulkan/driver_gen.h @@ -21,6 +21,7 @@ #include <vulkan/vk_android_native_buffer.h> #include <vulkan/vulkan.h> + #include <bitset> namespace vulkan { @@ -39,14 +40,14 @@ struct ProcHook { EXT_swapchain_colorspace, GOOGLE_display_timing, KHR_android_surface, + KHR_get_surface_capabilities2, KHR_incremental_present, KHR_shared_presentable_image, KHR_surface, KHR_swapchain, - KHR_get_surface_capabilities2, - KHR_get_physical_device_properties2, ANDROID_external_memory_android_hardware_buffer, KHR_bind_memory2, + KHR_get_physical_device_properties2, EXTENSION_CORE, // valid bit EXTENSION_COUNT, @@ -69,12 +70,12 @@ struct InstanceDriverTable { PFN_vkGetPhysicalDeviceProperties GetPhysicalDeviceProperties; PFN_vkCreateDevice CreateDevice; PFN_vkEnumerateDeviceExtensionProperties EnumerateDeviceExtensionProperties; - PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups; - PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2; PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallbackEXT; PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT; PFN_vkDebugReportMessageEXT DebugReportMessageEXT; + PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2; PFN_vkGetPhysicalDeviceProperties2KHR GetPhysicalDeviceProperties2KHR; + PFN_vkEnumeratePhysicalDeviceGroups EnumeratePhysicalDeviceGroups; // clang-format on }; @@ -83,16 +84,17 @@ struct DeviceDriverTable { PFN_vkGetDeviceProcAddr GetDeviceProcAddr; PFN_vkDestroyDevice DestroyDevice; PFN_vkGetDeviceQueue GetDeviceQueue; + PFN_vkQueueSubmit QueueSubmit; PFN_vkCreateImage CreateImage; PFN_vkDestroyImage DestroyImage; PFN_vkAllocateCommandBuffers AllocateCommandBuffers; PFN_vkBindImageMemory2 BindImageMemory2; + PFN_vkBindImageMemory2KHR BindImageMemory2KHR; PFN_vkGetDeviceQueue2 GetDeviceQueue2; PFN_vkGetSwapchainGrallocUsageANDROID GetSwapchainGrallocUsageANDROID; PFN_vkGetSwapchainGrallocUsage2ANDROID GetSwapchainGrallocUsage2ANDROID; PFN_vkAcquireImageANDROID AcquireImageANDROID; PFN_vkQueueSignalReleaseImageANDROID QueueSignalReleaseImageANDROID; - PFN_vkBindImageMemory2KHR BindImageMemory2KHR; // clang-format on }; diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 5f4c6b1305..d60eaa7c21 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -18,13 +18,13 @@ #include <android/hardware/graphics/common/1.0/types.h> #include <grallocusage/GrallocUsageConversion.h> +#include <graphicsenv/GraphicsEnv.h> #include <log/log.h> #include <sync/sync.h> #include <system/window.h> #include <ui/BufferQueueDefs.h> #include <utils/StrongPointer.h> #include <utils/Trace.h> -#include <utils/Vector.h> #include <algorithm> #include <unordered_set> @@ -34,10 +34,6 @@ using android::hardware::graphics::common::V1_0::BufferUsage; -// TODO(jessehall): Currently we don't have a good error code for when a native -// window operation fails. Just returning INITIALIZATION_FAILED for now. Later -// versions (post SDK 0.9) of the API/extension have a better error code. -// When updating to that version, audit all error returns. namespace vulkan { namespace driver { @@ -48,29 +44,12 @@ const VkSurfaceTransformFlagsKHR kSupportedTransforms = VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR | VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR | - // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform. - // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR | - // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR | - // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR | - // VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR | + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR | + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR | + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR | + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR | VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR; -int TranslateVulkanToNativeTransform(VkSurfaceTransformFlagBitsKHR transform) { - switch (transform) { - // TODO: See TODO in TranslateNativeToVulkanTransform - case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_90; - case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_180; - case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: - return NATIVE_WINDOW_TRANSFORM_ROT_270; - case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: - case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR: - default: - return 0; - } -} - VkSurfaceTransformFlagBitsKHR TranslateNativeToVulkanTransform(int native) { // Native and Vulkan transforms are isomorphic, but are represented // differently. Vulkan transforms are built up of an optional horizontal @@ -78,27 +57,22 @@ VkSurfaceTransformFlagBitsKHR TranslateNativeToVulkanTransform(int native) { // transforms are built up from a horizontal flip, vertical flip, and // 90-degree rotation, all optional but always in that order. - // TODO(jessehall): For now, only support pure rotations, not - // flip or flip-and-rotate, until I have more time to test them and build - // sample code. As far as I know we never actually use anything besides - // pure rotations anyway. - switch (native) { - case 0: // 0x0 + case 0: return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; - // case NATIVE_WINDOW_TRANSFORM_FLIP_H: // 0x1 - // return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR; - // case NATIVE_WINDOW_TRANSFORM_FLIP_V: // 0x2 - // return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR; - case NATIVE_WINDOW_TRANSFORM_ROT_180: // FLIP_H | FLIP_V + case NATIVE_WINDOW_TRANSFORM_FLIP_H: + return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR; + case NATIVE_WINDOW_TRANSFORM_FLIP_V: + return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR; + case NATIVE_WINDOW_TRANSFORM_ROT_180: return VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR; - case NATIVE_WINDOW_TRANSFORM_ROT_90: // 0x4 + case NATIVE_WINDOW_TRANSFORM_ROT_90: return VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR; - // case NATIVE_WINDOW_TRANSFORM_FLIP_H | NATIVE_WINDOW_TRANSFORM_ROT_90: - // return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR; - // case NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_ROT_90: - // return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR; - case NATIVE_WINDOW_TRANSFORM_ROT_270: // FLIP_H | FLIP_V | ROT_90 + case NATIVE_WINDOW_TRANSFORM_FLIP_H | NATIVE_WINDOW_TRANSFORM_ROT_90: + return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR; + case NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_ROT_90: + return VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR; + case NATIVE_WINDOW_TRANSFORM_ROT_270: return VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR; case NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY: default: @@ -106,6 +80,31 @@ VkSurfaceTransformFlagBitsKHR TranslateNativeToVulkanTransform(int native) { } } +int TranslateVulkanToNativeTransform(VkSurfaceTransformFlagBitsKHR transform) { + switch (transform) { + case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_ROT_90; + case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_ROT_180; + case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_ROT_270; + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_FLIP_H; + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_FLIP_H | + NATIVE_WINDOW_TRANSFORM_ROT_90; + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_FLIP_V; + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_FLIP_V | + NATIVE_WINDOW_TRANSFORM_ROT_90; + case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: + case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR: + default: + return 0; + } +} + int InvertTransformToNative(VkSurfaceTransformFlagBitsKHR transform) { switch (transform) { case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR: @@ -114,17 +113,16 @@ int InvertTransformToNative(VkSurfaceTransformFlagBitsKHR transform) { return NATIVE_WINDOW_TRANSFORM_ROT_180; case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR: return NATIVE_WINDOW_TRANSFORM_ROT_90; - // TODO(jessehall): See TODO in TranslateNativeToVulkanTransform. - // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR: - // return NATIVE_WINDOW_TRANSFORM_FLIP_H; - // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR: - // return NATIVE_WINDOW_TRANSFORM_FLIP_H | - // NATIVE_WINDOW_TRANSFORM_ROT_90; - // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR: - // return NATIVE_WINDOW_TRANSFORM_FLIP_V; - // case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR: - // return NATIVE_WINDOW_TRANSFORM_FLIP_V | - // NATIVE_WINDOW_TRANSFORM_ROT_90; + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_FLIP_H; + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_FLIP_H | + NATIVE_WINDOW_TRANSFORM_ROT_90; + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_FLIP_V; + case VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR: + return NATIVE_WINDOW_TRANSFORM_FLIP_V | + NATIVE_WINDOW_TRANSFORM_ROT_90; case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR: case VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR: default: @@ -134,7 +132,6 @@ int InvertTransformToNative(VkSurfaceTransformFlagBitsKHR transform) { class TimingInfo { public: - TimingInfo() = default; TimingInfo(const VkPresentTimeGOOGLE* qp, uint64_t nativeFrameId) : vals_{qp->presentID, qp->desiredPresentTime, 0, 0, 0}, native_frame_id_(nativeFrameId) {} @@ -201,8 +198,6 @@ class TimingInfo { { NATIVE_WINDOW_TIMESTAMP_PENDING }; }; -// ---------------------------------------------------------------------------- - struct Surface { android::sp<ANativeWindow> window; VkSwapchainKHR swapchain_handle; @@ -270,7 +265,7 @@ struct Swapchain { bool dequeued; } images[android::BufferQueueDefs::NUM_BUFFER_SLOTS]; - android::Vector<TimingInfo> timing; + std::vector<TimingInfo> timing; }; VkSwapchainKHR HandleFromSwapchain(Swapchain* swapchain) { @@ -285,6 +280,8 @@ void ReleaseSwapchainImage(VkDevice device, ANativeWindow* window, int release_fence, Swapchain::Image& image) { + ATRACE_CALL(); + ALOG_ASSERT(release_fence == -1 || image.dequeued, "ReleaseSwapchainImage: can't provide a release fence for " "non-dequeued images"); @@ -323,7 +320,9 @@ void ReleaseSwapchainImage(VkDevice device, } if (image.image) { + ATRACE_BEGIN("DestroyImage"); GetData(device).driver.DestroyImage(device, image.image, nullptr); + ATRACE_END(); image.image = VK_NULL_HANDLE; } @@ -349,7 +348,7 @@ uint32_t get_num_ready_timings(Swapchain& swapchain) { uint32_t num_ready = 0; const size_t num_timings = swapchain.timing.size() - MIN_NUM_FRAMES_AGO + 1; for (uint32_t i = 0; i < num_timings; i++) { - TimingInfo& ti = swapchain.timing.editItemAt(i); + TimingInfo& ti = swapchain.timing[i]; if (ti.ready()) { // This TimingInfo is ready to be reported to the user. Add it // to the num_ready. @@ -371,9 +370,6 @@ uint32_t get_num_ready_timings(Swapchain& swapchain) { nullptr, //&first_composition_start_time, nullptr, //&last_composition_start_time, nullptr, //&composition_finish_time, - // TODO(ianelliott): Maybe ask if this one is - // supported, at startup time (since it may not be - // supported): &actual_present_time, nullptr, //&dequeue_ready_time, nullptr /*&reads_done_time*/); @@ -400,7 +396,6 @@ uint32_t get_num_ready_timings(Swapchain& swapchain) { return num_ready; } -// TODO(ianelliott): DEAL WITH RETURN VALUE (e.g. VK_INCOMPLETE)!!! void copy_ready_timings(Swapchain& swapchain, uint32_t* count, VkPastPresentationTimingGOOGLE* timings) { @@ -419,7 +414,7 @@ void copy_ready_timings(Swapchain& swapchain, } uint32_t num_copied = 0; - size_t num_to_remove = 0; + int32_t num_to_remove = 0; for (uint32_t i = 0; i <= last_ready && num_copied < *count; i++) { const TimingInfo& ti = swapchain.timing[i]; if (ti.ready()) { @@ -431,7 +426,8 @@ void copy_ready_timings(Swapchain& swapchain, // Discard old frames that aren't ready if newer frames are ready. // We don't expect to get the timing info for those old frames. - swapchain.timing.removeItemsAt(0, num_to_remove); + swapchain.timing.erase(swapchain.timing.begin(), + swapchain.timing.begin() + num_to_remove); *count = num_copied; } @@ -539,15 +535,12 @@ VkResult CreateAndroidSurfaceKHR( strerror(-err), err); surface->~Surface(); allocator->pfnFree(allocator->pUserData, surface); - return VK_ERROR_INITIALIZATION_FAILED; + return VK_ERROR_SURFACE_LOST_KHR; } - // TODO(jessehall): Create and use NATIVE_WINDOW_API_VULKAN. err = native_window_api_connect(surface->window.get(), NATIVE_WINDOW_API_EGL); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err), err); surface->~Surface(); @@ -656,7 +649,6 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( return VK_ERROR_SURFACE_LOST_KHR; } - // TODO(jessehall): Figure out what the min/max values should be. int max_buffer_count; err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &max_buffer_count); if (err != 0) { @@ -670,8 +662,7 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( capabilities->currentExtent = VkExtent2D{static_cast<uint32_t>(width), static_cast<uint32_t>(height)}; - // TODO(jessehall): Figure out what the max extent should be. Maximum - // texture dimension maybe? + // TODO(http://b/134182502): Figure out what the max extent should be. capabilities->minImageExtent = VkExtent2D{1, 1}; capabilities->maxImageExtent = VkExtent2D{4096, 4096}; @@ -685,11 +676,6 @@ VkResult GetPhysicalDeviceSurfaceCapabilitiesKHR( // associated with the bufferqueue. It can't be changed from here. capabilities->supportedCompositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; - // TODO(jessehall): I think these are right, but haven't thought hard about - // it. Do we need to query the driver for support of any of these? - // Currently not included: - // - VK_IMAGE_USAGE_DEPTH_STENCIL_BIT: definitely not - // - VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT: definitely not capabilities->supportedUsageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | @@ -729,8 +715,7 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, int err = native_window_get_wide_color_support(surface.window.get(), &wide_color_support); if (err) { - // Not allowed to return a more sensible error code, so do this - return VK_ERROR_OUT_OF_HOST_MEMORY; + return VK_ERROR_SURFACE_LOST_KHR; } ALOGV("wide_color_support is: %d", wide_color_support); wide_color_support = @@ -828,11 +813,10 @@ VkResult GetPhysicalDeviceSurfaceFormats2KHR( } else { // temp vector for forwarding; we'll marshal it into the pSurfaceFormats // after the call. - android::Vector<VkSurfaceFormatKHR> surface_formats; - surface_formats.resize(*pSurfaceFormatCount); + std::vector<VkSurfaceFormatKHR> surface_formats(*pSurfaceFormatCount); VkResult result = GetPhysicalDeviceSurfaceFormatsKHR( physicalDevice, pSurfaceInfo->surface, pSurfaceFormatCount, - &surface_formats.editItemAt(0)); + surface_formats.data()); if (result == VK_SUCCESS || result == VK_INCOMPLETE) { // marshal results individually due to stride difference. @@ -874,7 +858,7 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, } uint32_t max_buffer_count = static_cast<uint32_t>(query_value); - android::Vector<VkPresentModeKHR> present_modes; + std::vector<VkPresentModeKHR> present_modes; if (min_undequeued_buffers + 1 < max_buffer_count) present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); @@ -894,7 +878,7 @@ VkResult GetPhysicalDeviceSurfacePresentModesKHR(VkPhysicalDevice pdev, if (*count < num_modes) result = VK_INCOMPLETE; *count = std::min(*count, num_modes); - std::copy(present_modes.begin(), present_modes.begin() + int(*count), modes); + std::copy_n(present_modes.data(), *count, modes); } else { *count = num_modes; } @@ -978,6 +962,40 @@ VkResult GetPhysicalDevicePresentRectanglesKHR(VkPhysicalDevice, return VK_SUCCESS; } +static void DestroySwapchainInternal(VkDevice device, + VkSwapchainKHR swapchain_handle, + const VkAllocationCallbacks* allocator) { + ATRACE_CALL(); + + const auto& dispatch = GetData(device).driver; + Swapchain* swapchain = SwapchainFromHandle(swapchain_handle); + if (!swapchain) { + return; + } + + bool active = swapchain->surface.swapchain_handle == swapchain_handle; + ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr; + + if (window && swapchain->frame_timestamps_enabled) { + native_window_enable_frame_timestamps(window, false); + } + + for (uint32_t i = 0; i < swapchain->num_images; i++) { + ReleaseSwapchainImage(device, window, -1, swapchain->images[i]); + } + + if (active) { + swapchain->surface.swapchain_handle = VK_NULL_HANDLE; + } + + if (!allocator) { + allocator = &GetData(device).allocator; + } + + swapchain->~Swapchain(); + allocator->pfnFree(allocator->pUserData, swapchain); +} + VKAPI_ATTR VkResult CreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* create_info, @@ -1052,6 +1070,8 @@ VkResult CreateSwapchainKHR(VkDevice device, // non-FREE state at any given time. Disconnecting and re-connecting // orphans the previous buffers, getting us back to the state where we can // dequeue all buffers. + // + // TODO(http://b/134186185) recycle swapchain images more efficiently err = native_window_api_disconnect(surface.window.get(), NATIVE_WINDOW_API_EGL); ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)", @@ -1072,8 +1092,6 @@ VkResult CreateSwapchainKHR(VkDevice device, create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1; err = surface.window->setSwapInterval(surface.window.get(), swap_interval); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("native_window->setSwapInterval(1) failed: %s (%d)", strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; @@ -1100,8 +1118,6 @@ VkResult CreateSwapchainKHR(VkDevice device, err = native_window_set_buffers_format(surface.window.get(), native_pixel_format); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)", native_pixel_format, strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; @@ -1109,8 +1125,6 @@ VkResult CreateSwapchainKHR(VkDevice device, err = native_window_set_buffers_data_space(surface.window.get(), native_dataspace); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)", native_dataspace, strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; @@ -1120,8 +1134,6 @@ VkResult CreateSwapchainKHR(VkDevice device, surface.window.get(), static_cast<int>(create_info->imageExtent.width), static_cast<int>(create_info->imageExtent.height)); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)", create_info->imageExtent.width, create_info->imageExtent.height, strerror(-err), err); @@ -1140,8 +1152,6 @@ VkResult CreateSwapchainKHR(VkDevice device, surface.window.get(), InvertTransformToNative(create_info->preTransform)); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)", InvertTransformToNative(create_info->preTransform), strerror(-err), err); @@ -1151,8 +1161,6 @@ VkResult CreateSwapchainKHR(VkDevice device, err = native_window_set_scaling_mode( surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)", strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; @@ -1182,8 +1190,6 @@ VkResult CreateSwapchainKHR(VkDevice device, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value); if (err != 0 || query_value < 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err, query_value); return VK_ERROR_SURFACE_LOST_KHR; @@ -1201,8 +1207,6 @@ VkResult CreateSwapchainKHR(VkDevice device, // can't actually use!). err = native_window_set_buffer_count(surface.window.get(), std::max(2u, num_images)); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images, strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; @@ -1211,7 +1215,7 @@ VkResult CreateSwapchainKHR(VkDevice device, int32_t legacy_usage = 0; if (dispatch.GetSwapchainGrallocUsage2ANDROID) { uint64_t consumer_usage, producer_usage; - ATRACE_BEGIN("dispatch.GetSwapchainGrallocUsage2ANDROID"); + ATRACE_BEGIN("GetSwapchainGrallocUsage2ANDROID"); result = dispatch.GetSwapchainGrallocUsage2ANDROID( device, create_info->imageFormat, create_info->imageUsage, swapchain_image_usage, &consumer_usage, &producer_usage); @@ -1223,7 +1227,7 @@ VkResult CreateSwapchainKHR(VkDevice device, legacy_usage = android_convertGralloc1To0Usage(producer_usage, consumer_usage); } else if (dispatch.GetSwapchainGrallocUsageANDROID) { - ATRACE_BEGIN("dispatch.GetSwapchainGrallocUsageANDROID"); + ATRACE_BEGIN("GetSwapchainGrallocUsageANDROID"); result = dispatch.GetSwapchainGrallocUsageANDROID( device, create_info->imageFormat, create_info->imageUsage, &legacy_usage); @@ -1242,12 +1246,19 @@ VkResult CreateSwapchainKHR(VkDevice device, } err = native_window_set_usage(surface.window.get(), native_usage); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; } + int transform_hint; + err = surface.window->query(surface.window.get(), + NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint); + if (err != 0) { + ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)", + strerror(-err), err); + return VK_ERROR_SURFACE_LOST_KHR; + } + // -- Allocate our Swapchain object -- // After this point, we must deallocate the swapchain on error. @@ -1301,8 +1312,6 @@ VkResult CreateSwapchainKHR(VkDevice device, err = surface.window->dequeueBuffer(surface.window.get(), &buffer, &img.dequeue_fence); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate - // possible errors and translate them to valid Vulkan result codes? ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err); result = VK_ERROR_SURFACE_LOST_KHR; break; @@ -1322,7 +1331,7 @@ VkResult CreateSwapchainKHR(VkDevice device, &image_native_buffer.usage2.producer, &image_native_buffer.usage2.consumer); - ATRACE_BEGIN("dispatch.CreateImage"); + ATRACE_BEGIN("CreateImage"); result = dispatch.CreateImage(device, &image_create, nullptr, &img.image); ATRACE_END(); @@ -1335,9 +1344,6 @@ VkResult CreateSwapchainKHR(VkDevice device, // -- Cancel all buffers, returning them to the queue -- // If an error occurred before, also destroy the VkImage and release the // buffer reference. Otherwise, we retain a strong reference to the buffer. - // - // TODO(jessehall): The error path here is the same as DestroySwapchain, - // but not the non-error path. Should refactor/unify. for (uint32_t i = 0; i < num_images; i++) { Swapchain::Image& img = swapchain->images[i]; if (img.dequeued) { @@ -1348,21 +1354,20 @@ VkResult CreateSwapchainKHR(VkDevice device, img.dequeued = false; } } - if (result != VK_SUCCESS) { - if (img.image) { - ATRACE_BEGIN("dispatch.DestroyImage"); - dispatch.DestroyImage(device, img.image, nullptr); - ATRACE_END(); - } - } } if (result != VK_SUCCESS) { - swapchain->~Swapchain(); - allocator->pfnFree(allocator->pUserData, swapchain); + DestroySwapchainInternal(device, HandleFromSwapchain(swapchain), + allocator); return result; } + if (transform_hint != swapchain->pre_transform) { + // Log that the app is not doing pre-rotation. + android::GraphicsEnv::getInstance().setTargetStats( + android::GpuStatsInfo::Stats::FALSE_PREROTATION); + } + surface.swapchain_handle = HandleFromSwapchain(swapchain); *swapchain_handle = surface.swapchain_handle; return VK_SUCCESS; @@ -1374,24 +1379,7 @@ void DestroySwapchainKHR(VkDevice device, const VkAllocationCallbacks* allocator) { ATRACE_CALL(); - const auto& dispatch = GetData(device).driver; - Swapchain* swapchain = SwapchainFromHandle(swapchain_handle); - if (!swapchain) - return; - bool active = swapchain->surface.swapchain_handle == swapchain_handle; - ANativeWindow* window = active ? swapchain->surface.window.get() : nullptr; - - if (window && swapchain->frame_timestamps_enabled) { - native_window_enable_frame_timestamps(window, false); - } - for (uint32_t i = 0; i < swapchain->num_images; i++) - ReleaseSwapchainImage(device, window, -1, swapchain->images[i]); - if (active) - swapchain->surface.swapchain_handle = VK_NULL_HANDLE; - if (!allocator) - allocator = &GetData(device).allocator; - swapchain->~Swapchain(); - allocator->pfnFree(allocator->pUserData, swapchain); + DestroySwapchainInternal(device, swapchain_handle, allocator); } VKAPI_ATTR @@ -1457,8 +1445,6 @@ VkResult AcquireNextImageKHR(VkDevice device, int fence_fd; err = window->dequeueBuffer(window, &buffer, &fence_fd); if (err != 0) { - // TODO(jessehall): Improve error reporting. Can we enumerate possible - // errors and translate them to valid Vulkan result codes? ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err); return VK_ERROR_SURFACE_LOST_KHR; } @@ -1513,8 +1499,6 @@ VkResult AcquireNextImage2KHR(VkDevice device, uint32_t* pImageIndex) { ATRACE_CALL(); - // TODO: this should actually be the other way around and this function - // should handle any additional structures that get passed in return AcquireNextImageKHR(device, pAcquireInfo->swapchain, pAcquireInfo->timeout, pAcquireInfo->semaphore, pAcquireInfo->fence, pImageIndex); @@ -1673,9 +1657,9 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { // Add a new timing record with the user's presentID and // the nativeFrameId. - swapchain.timing.push_back(TimingInfo(time, nativeFrameId)); + swapchain.timing.emplace_back(time, nativeFrameId); while (swapchain.timing.size() > MAX_TIMING_INFOS) { - swapchain.timing.removeAt(0); + swapchain.timing.erase(swapchain.timing.begin()); } if (time->desiredPresentTime) { // Set the desiredPresentTime: @@ -1692,17 +1676,16 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { err = window->queueBuffer(window, img.buffer.get(), fence); // queueBuffer always closes fence, even on error if (err != 0) { - // TODO(jessehall): What now? We should probably cancel the - // buffer, I guess? ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err); swapchain_result = WorstPresentResult( swapchain_result, VK_ERROR_OUT_OF_DATE_KHR); + } else { + if (img.dequeue_fence >= 0) { + close(img.dequeue_fence); + img.dequeue_fence = -1; + } + img.dequeued = false; } - if (img.dequeue_fence >= 0) { - close(img.dequeue_fence); - img.dequeue_fence = -1; - } - img.dequeued = false; // If the swapchain is in shared mode, immediately dequeue the // buffer so it can be presented again without an intervening @@ -1729,7 +1712,6 @@ VkResult QueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* present_info) { } } if (swapchain_result != VK_SUCCESS) { - ReleaseSwapchainImage(device, window, fence, img); OrphanSwapchain(device, &swapchain); } int window_transform_hint; @@ -1787,6 +1769,10 @@ VkResult GetPastPresentationTimingGOOGLE( ATRACE_CALL(); Swapchain& swapchain = *SwapchainFromHandle(swapchain_handle); + if (swapchain.surface.swapchain_handle != swapchain_handle) { + return VK_ERROR_OUT_OF_DATE_KHR; + } + ANativeWindow* window = swapchain.surface.window.get(); VkResult result = VK_SUCCESS; @@ -1797,8 +1783,15 @@ VkResult GetPastPresentationTimingGOOGLE( } if (timings) { - // TODO(ianelliott): plumb return value (e.g. VK_INCOMPLETE) + // Get the latest ready timing count before copying, since the copied + // timing info will be erased in copy_ready_timings function. + uint32_t n = get_num_ready_timings(swapchain); copy_ready_timings(swapchain, count, timings); + // Check the *count here against the recorded ready timing count, since + // *count can be overwritten per spec describes. + if (*count < n) { + result = VK_INCOMPLETE; + } } else { *count = get_num_ready_timings(swapchain); } diff --git a/vulkan/nulldrv/null_driver.tmpl b/vulkan/nulldrv/null_driver.tmpl deleted file mode 100644 index ce155172e9..0000000000 --- a/vulkan/nulldrv/null_driver.tmpl +++ /dev/null @@ -1,210 +0,0 @@ -{{/* - * Copyright 2015 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 "../api/templates/vulkan_common.tmpl"}} -{{Global "clang-format" (Strings "clang-format" "-style=file")}} -{{Macro "DefineGlobals" $}} -{{$ | Macro "null_driver_gen.h" | Format (Global "clang-format") | Write "null_driver_gen.h" }} -{{$ | Macro "null_driver_gen.cpp" | Format (Global "clang-format") | Write "null_driver_gen.cpp"}} - - -{{/* -------------------------------------------------------------------------------- - null_driver_gen.h -------------------------------------------------------------------------------- -*/}} -{{define "null_driver_gen.h"}} -/* -•* Copyright 2015 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. -•*/ -¶ -// WARNING: This file is generated. See ../README.md for instructions. -¶ -#ifndef NULLDRV_NULL_DRIVER_H -#define NULLDRV_NULL_DRIVER_H 1 -¶ -#include <vulkan/vk_android_native_buffer.h> -#include <vulkan/vulkan.h> -¶ -namespace null_driver {« -¶ -PFN_vkVoidFunction GetGlobalProcAddr(const char* name); -PFN_vkVoidFunction GetInstanceProcAddr(const char* name); -¶ -// clang-format off - {{range $f := AllCommands $}} - {{if (Macro "IsDriverFunction" $f)}} -VKAPI_ATTR {{Node "Type" $f.Return}} {{Macro "BaseName" $f}}({{Macro "Parameters" $f}}); - {{end}} - {{end}} -VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage); -VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence); -VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd); -// clang-format on -¶ -»} // namespace null_driver -¶ -#endif // NULLDRV_NULL_DRIVER_H -¶{{end}} - - -{{/* -------------------------------------------------------------------------------- - null_driver_gen.cpp -------------------------------------------------------------------------------- -*/}} -{{define "null_driver_gen.cpp"}} -/* -•* Copyright 2015 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. -•*/ -¶ -// WARNING: This file is generated. See ../README.md for instructions. -¶ -#include "null_driver_gen.h" -#include <algorithm> -¶ -using namespace null_driver; -¶ -namespace { -¶ -struct NameProc { - const char* name; - PFN_vkVoidFunction proc; -}; -¶ -PFN_vkVoidFunction Lookup(const char* name, - const NameProc* begin, - const NameProc* end) { - const auto& entry = std::lower_bound( - begin, end, name, - [](const NameProc& e, const char* n) { return strcmp(e.name, n) < 0; }); - if (entry == end || strcmp(entry->name, name) != 0) - return nullptr; - return entry->proc; -} -¶ -template <size_t N> -PFN_vkVoidFunction Lookup(const char* name, const NameProc (&procs)[N]) { - return Lookup(name, procs, procs + N); -} -¶ -const NameProc kGlobalProcs[] = {« - // clang-format off - {{range $f := SortBy (AllCommands $) "FunctionName"}} - {{if and (Macro "IsDriverFunction" $f) (eq (Macro "Vtbl" $f) "Global")}} - {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§ - static_cast<{{Macro "FunctionPtrName" $f}}>(§ - {{Macro "BaseName" $f}}))}, - {{end}} - {{end}} - // clang-format on -»}; -¶ -const NameProc kInstanceProcs[] = {« - // clang-format off - {{range $f := SortBy (AllCommands $) "FunctionName"}} - {{if (Macro "IsDriverFunction" $f)}} - {"{{$f.Name}}", reinterpret_cast<PFN_vkVoidFunction>(§ - static_cast<{{Macro "FunctionPtrName" $f}}>(§ - {{Macro "BaseName" $f}}))}, - {{end}} - {{end}} - // clang-format on -»}; -¶ -} // namespace -¶ -namespace null_driver { -¶ -PFN_vkVoidFunction GetGlobalProcAddr(const char* name) { - return Lookup(name, kGlobalProcs); -} -¶ -PFN_vkVoidFunction GetInstanceProcAddr(const char* name) {« - return Lookup(name, kInstanceProcs); -»} -¶ -} // namespace null_driver -¶ -{{end}} - - -{{/* -------------------------------------------------------------------------------- - Emits a function name without the "vk" prefix. -------------------------------------------------------------------------------- -*/}} -{{define "BaseName"}} - {{AssertType $ "Function"}} - {{TrimPrefix "vk" $.Name}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Emits 'true' if the API function is implemented by the driver. ------------------------------------------------------------------------------- -*/}} -{{define "IsDriverFunction"}} - {{AssertType $ "Function"}} - - {{if not (GetAnnotation $ "pfn")}} - {{$ext := GetAnnotation $ "extension"}} - {{if $ext}} - {{Macro "IsDriverExtension" $ext}} - {{else}} - true - {{end}} - {{end}} -{{end}} - - -{{/* ------------------------------------------------------------------------------- - Reports whether an extension is implemented by the driver. ------------------------------------------------------------------------------- -*/}} -{{define "IsDriverExtension"}} - {{$ext := index $.Arguments 0}} - {{ if eq $ext "VK_ANDROID_native_buffer"}}true - {{else if eq $ext "VK_EXT_debug_report"}}true - {{else if eq $ext "VK_KHR_get_physical_device_properties2"}}true - {{end}} -{{end}} diff --git a/vulkan/nulldrv/null_driver_gen.cpp b/vulkan/nulldrv/null_driver_gen.cpp index 92b7468321..b8d7d2b643 100644 --- a/vulkan/nulldrv/null_driver_gen.cpp +++ b/vulkan/nulldrv/null_driver_gen.cpp @@ -17,6 +17,7 @@ // WARNING: This file is generated. See ../README.md for instructions. #include <algorithm> + #include "null_driver_gen.h" using namespace null_driver; diff --git a/vulkan/nulldrv/null_driver_gen.h b/vulkan/nulldrv/null_driver_gen.h index c6ad537cb8..0d3f68895d 100644 --- a/vulkan/nulldrv/null_driver_gen.h +++ b/vulkan/nulldrv/null_driver_gen.h @@ -41,6 +41,7 @@ VKAPI_ATTR void GetPhysicalDeviceFormatProperties(VkPhysicalDevice physicalDevic VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* pImageFormatProperties); VKAPI_ATTR VkResult CreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice); VKAPI_ATTR void DestroyDevice(VkDevice device, const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR VkResult EnumerateInstanceVersion(uint32_t* pApiVersion); VKAPI_ATTR VkResult EnumerateInstanceLayerProperties(uint32_t* pPropertyCount, VkLayerProperties* pProperties); VKAPI_ATTR VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); VKAPI_ATTR VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties); @@ -165,49 +166,45 @@ VKAPI_ATTR void CmdBeginRenderPass(VkCommandBuffer commandBuffer, const VkRender VKAPI_ATTR void CmdNextSubpass(VkCommandBuffer commandBuffer, VkSubpassContents contents); VKAPI_ATTR void CmdEndRenderPass(VkCommandBuffer commandBuffer); VKAPI_ATTR void CmdExecuteCommands(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); -VKAPI_ATTR VkResult EnumerateInstanceVersion(uint32_t* pApiVersion); -VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos); -VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos); -VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures); -VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask); -VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); -VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); -VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); -VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); -VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); +VKAPI_ATTR VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback); +VKAPI_ATTR void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR void DebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage); VKAPI_ATTR void GetPhysicalDeviceFeatures2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures); +VKAPI_ATTR void GetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures); VKAPI_ATTR void GetPhysicalDeviceProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties); +VKAPI_ATTR void GetPhysicalDeviceProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties); VKAPI_ATTR void GetPhysicalDeviceFormatProperties2(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties); +VKAPI_ATTR void GetPhysicalDeviceFormatProperties2KHR(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties); VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties); +VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties); VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties); +VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties); VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties); +VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties); VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties); +VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties); VKAPI_ATTR void TrimCommandPool(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags); -VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue); -VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion); -VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties); +VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties); +VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties); +VKAPI_ATTR VkResult EnumeratePhysicalDeviceGroups(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); +VKAPI_ATTR void GetDeviceGroupPeerMemoryFeatures(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures); +VKAPI_ATTR VkResult BindBufferMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos); +VKAPI_ATTR VkResult BindImageMemory2(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos); +VKAPI_ATTR void CmdSetDeviceMask(VkCommandBuffer commandBuffer, uint32_t deviceMask); +VKAPI_ATTR void CmdDispatchBase(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); VKAPI_ATTR VkResult CreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate); VKAPI_ATTR void DestroyDescriptorUpdateTemplate(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator); VKAPI_ATTR void UpdateDescriptorSetWithTemplate(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData); -VKAPI_ATTR void GetPhysicalDeviceExternalBufferProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties); -VKAPI_ATTR void GetPhysicalDeviceExternalFenceProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties); -VKAPI_ATTR void GetPhysicalDeviceExternalSemaphoreProperties(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties); +VKAPI_ATTR void GetBufferMemoryRequirements2(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); +VKAPI_ATTR void GetImageMemoryRequirements2(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); +VKAPI_ATTR void GetImageSparseMemoryRequirements2(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); +VKAPI_ATTR VkResult CreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion); +VKAPI_ATTR void DestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator); +VKAPI_ATTR void GetDeviceQueue2(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue); VKAPI_ATTR void GetDescriptorSetLayoutSupport(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport); -VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int32_t* grallocUsage); -VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage); -VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence); -VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd); -VKAPI_ATTR VkResult CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback); -VKAPI_ATTR void DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator); -VKAPI_ATTR void DebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage); -VKAPI_ATTR void GetPhysicalDeviceFeatures2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2KHR* pFeatures); -VKAPI_ATTR void GetPhysicalDeviceProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2KHR* pProperties); -VKAPI_ATTR void GetPhysicalDeviceFormatProperties2KHR(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2KHR* pFormatProperties); -VKAPI_ATTR VkResult GetPhysicalDeviceImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo, VkImageFormatProperties2KHR* pImageFormatProperties); -VKAPI_ATTR void GetPhysicalDeviceQueueFamilyProperties2KHR(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2KHR* pQueueFamilyProperties); -VKAPI_ATTR void GetPhysicalDeviceMemoryProperties2KHR(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2KHR* pMemoryProperties); -VKAPI_ATTR void GetPhysicalDeviceSparseImageFormatProperties2KHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2KHR* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2KHR* pProperties); VKAPI_ATTR VkResult GetSwapchainGrallocUsageANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, int* grallocUsage); +VKAPI_ATTR VkResult GetSwapchainGrallocUsage2ANDROID(VkDevice device, VkFormat format, VkImageUsageFlags imageUsage, VkSwapchainImageUsageFlagsANDROID swapchainImageUsage, uint64_t* grallocConsumerUsage, uint64_t* grallocProducerUsage); VKAPI_ATTR VkResult AcquireImageANDROID(VkDevice device, VkImage image, int nativeFenceFd, VkSemaphore semaphore, VkFence fence); VKAPI_ATTR VkResult QueueSignalReleaseImageANDROID(VkQueue queue, uint32_t waitSemaphoreCount, const VkSemaphore* pWaitSemaphores, VkImage image, int* pNativeFenceFd); // clang-format on diff --git a/vulkan/scripts/api_generator.py b/vulkan/scripts/api_generator.py new file mode 100644 index 0000000000..7c390755e1 --- /dev/null +++ b/vulkan/scripts/api_generator.py @@ -0,0 +1,410 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 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. + +"""Generates the api_gen.h and api_gen.cpp. +""" + +import os +import generator_common as gencom + +# Functions intercepted at vulkan::api level. +_INTERCEPTED_COMMANDS = [ + 'vkCreateDevice', + 'vkDestroyDevice', + 'vkDestroyInstance', + 'vkEnumerateDeviceExtensionProperties', + 'vkEnumerateDeviceLayerProperties', +] + + +def gen_h(): + """Generates the api_gen.h file. + """ + genfile = os.path.join(os.path.dirname(__file__), + '..', 'libvulkan', 'api_gen.h') + + with open(genfile, 'w') as f: + instance_dispatch_table_entries = [] + device_dispatch_table_entries = [] + + for cmd in gencom.command_list: + if cmd not in gencom.alias_dict: + if gencom.is_instance_dispatch_table_entry(cmd): + instance_dispatch_table_entries.append( + 'PFN_' + cmd + ' ' + gencom.base_name(cmd) + ';') + elif gencom.is_device_dispatch_table_entry(cmd): + device_dispatch_table_entries.append( + 'PFN_' + cmd + ' ' + gencom.base_name(cmd) + ';') + + f.write(gencom.copyright_and_warning(2016)) + + f.write("""\ +#ifndef LIBVULKAN_API_GEN_H +#define LIBVULKAN_API_GEN_H + +#include <vulkan/vulkan.h> + +#include <bitset> + +#include "driver_gen.h" + +namespace vulkan { +namespace api { + +struct InstanceDispatchTable { + // clang-format off\n""") + + for entry in instance_dispatch_table_entries: + f.write(gencom.indent(1) + entry + '\n') + + f.write("""\ + // clang-format on +}; + +struct DeviceDispatchTable { + // clang-format off\n""") + + for entry in device_dispatch_table_entries: + f.write(gencom.indent(1) + entry + '\n') + + f.write("""\ + // clang-format on +}; + +bool InitDispatchTable( + VkInstance instance, + PFN_vkGetInstanceProcAddr get_proc, + const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions); +bool InitDispatchTable( + VkDevice dev, + PFN_vkGetDeviceProcAddr get_proc, + const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions); + +} // namespace api +} // namespace vulkan + +#endif // LIBVULKAN_API_GEN_H\n""") + + f.close() + gencom.run_clang_format(genfile) + + +def _define_extension_stub(cmd, f): + """Emits a stub for an exported extension function. + + Args: + cmd: Vulkan function name. + f: Output file handle. + """ + if (cmd in gencom.extension_dict and gencom.is_function_exported(cmd)): + ext_name = gencom.extension_dict[cmd] + ret = gencom.return_type_dict[cmd] + params = gencom.param_dict[cmd] + first_param = params[0][0] + params[0][1] + tail_params = ', '.join([i[0][:-1] for i in params[1:]]) + + f.write('VKAPI_ATTR ' + ret + ' disabled' + gencom.base_name(cmd) + + '(' + first_param + ', ' + tail_params + ') {\n') + + f.write(gencom.indent(1) + 'driver::Logger(' + params[0][1] + + ').Err(' + params[0][1] + ', \"' + ext_name + + ' not enabled. Exported ' + cmd + ' not executed.\");\n') + + if gencom.return_type_dict[cmd] != 'void': + f.write(gencom.indent(1) + 'return VK_SUCCESS;\n') + + f.write('}\n\n') + + +def _is_intercepted(cmd): + """Returns true if a function is intercepted by vulkan::api. + + Args: + cmd: Vulkan function name. + """ + if gencom.is_function_supported(cmd): + if gencom.is_globally_dispatched(cmd) or cmd in _INTERCEPTED_COMMANDS: + return True + return False + + +def _intercept_instance_proc_addr(f): + """Emits code for vkGetInstanceProcAddr for function interception. + + Args: + f: Output file handle. + """ + f.write("""\ + // global functions + if (instance == VK_NULL_HANDLE) {\n""") + + for cmd in gencom.command_list: + if gencom.is_globally_dispatched(cmd): + f.write(gencom.indent(2) + + 'if (strcmp(pName, \"' + cmd + + '\") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' + + gencom.base_name(cmd) + ');\n') + + f.write(""" + ALOGE("invalid vkGetInstanceProcAddr(VK_NULL_HANDLE, \\\"%s\\\") call", pName); + return nullptr; + } + + static const struct Hook { + const char* name; + PFN_vkVoidFunction proc; + } hooks[] = {\n""") + + sorted_command_list = sorted(gencom.command_list) + for cmd in sorted_command_list: + if gencom.is_function_exported(cmd): + if gencom.is_globally_dispatched(cmd): + f.write(gencom.indent(2) + '{ \"' + cmd + '\", nullptr },\n') + elif (_is_intercepted(cmd) or + cmd == 'vkGetInstanceProcAddr' or + gencom.is_device_dispatched(cmd)): + f.write(gencom.indent(2) + '{ \"' + cmd + + '\", reinterpret_cast<PFN_vkVoidFunction>(' + + gencom.base_name(cmd) + ') },\n') + + f.write("""\ + }; + // clang-format on + constexpr size_t count = sizeof(hooks) / sizeof(hooks[0]); + auto hook = std::lower_bound( + hooks, hooks + count, pName, + [](const Hook& h, const char* n) { return strcmp(h.name, n) < 0; }); + if (hook < hooks + count && strcmp(hook->name, pName) == 0) { + if (!hook->proc) { + vulkan::driver::Logger(instance).Err( + instance, "invalid vkGetInstanceProcAddr(%p, \\\"%s\\\") call", + instance, pName); + } + return hook->proc; + } + // clang-format off\n\n""") + + +def _intercept_device_proc_addr(f): + """Emits code for vkGetDeviceProcAddr for function interception. + + Args: + f: Output file handle. + """ + f.write("""\ + if (device == VK_NULL_HANDLE) { + ALOGE("invalid vkGetDeviceProcAddr(VK_NULL_HANDLE, ...) call"); + return nullptr; + } + + static const char* const known_non_device_names[] = {\n""") + + sorted_command_list = sorted(gencom.command_list) + for cmd in sorted_command_list: + if gencom.is_function_supported(cmd): + if not gencom.is_device_dispatched(cmd): + f.write(gencom.indent(2) + '\"' + cmd + '\",\n') + + f.write("""\ + }; + // clang-format on + constexpr size_t count = + sizeof(known_non_device_names) / sizeof(known_non_device_names[0]); + if (!pName || + std::binary_search( + known_non_device_names, known_non_device_names + count, pName, + [](const char* a, const char* b) { return (strcmp(a, b) < 0); })) { + vulkan::driver::Logger(device).Err( + device, "invalid vkGetDeviceProcAddr(%p, \\\"%s\\\") call", device, + (pName) ? pName : "(null)"); + return nullptr; + } + // clang-format off\n\n""") + + for cmd in gencom.command_list: + if gencom.is_device_dispatched(cmd): + if _is_intercepted(cmd) or cmd == 'vkGetDeviceProcAddr': + f.write(gencom.indent(1) + 'if (strcmp(pName, "' + cmd + + '") == 0) return reinterpret_cast<PFN_vkVoidFunction>(' + + gencom.base_name(cmd) + ');\n') + f.write('\n') + + +def _api_dispatch(cmd, f): + """Emits code to dispatch a function. + + Args: + cmd: Vulkan function name. + f: Output file handle. + """ + assert not _is_intercepted(cmd) + + f.write(gencom.indent(1)) + if gencom.return_type_dict[cmd] != 'void': + f.write('return ') + + param_list = gencom.param_dict[cmd] + handle = param_list[0][1] + f.write('GetData(' + handle + ').dispatch.' + gencom.base_name(cmd) + + '(' + ', '.join(i[1] for i in param_list) + ');\n') + + +def gen_cpp(): + """Generates the api_gen.cpp file. + """ + genfile = os.path.join(os.path.dirname(__file__), + '..', 'libvulkan', 'api_gen.cpp') + + with open(genfile, 'w') as f: + f.write(gencom.copyright_and_warning(2016)) + + f.write("""\ +#include <log/log.h> +#include <string.h> + +#include <algorithm> + +// to catch mismatches between vulkan.h and this file +#undef VK_NO_PROTOTYPES +#include "api.h" + +namespace vulkan { +namespace api { + +#define UNLIKELY(expr) __builtin_expect((expr), 0) + +#define INIT_PROC(required, obj, proc) \\ + do { \\ + data.dispatch.proc = \\ + reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \\ + if (UNLIKELY(required && !data.dispatch.proc)) { \\ + ALOGE("missing " #obj " proc: vk" #proc); \\ + success = false; \\ + } \\ + } while (0) + +// Exported extension functions may be invoked even when their extensions +// are disabled. Dispatch to stubs when that happens. +#define INIT_PROC_EXT(ext, required, obj, proc) \\ + do { \\ + if (extensions[driver::ProcHook::ext]) \\ + INIT_PROC(required, obj, proc); \\ + else \\ + data.dispatch.proc = disabled##proc; \\ + } while (0) + +namespace { + +// clang-format off\n\n""") + + for cmd in gencom.command_list: + _define_extension_stub(cmd, f) + + f.write("""\ +// clang-format on + +} // namespace + +bool InitDispatchTable( + VkInstance instance, + PFN_vkGetInstanceProcAddr get_proc, + const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) { + auto& data = GetData(instance); + bool success = true; + + // clang-format off\n""") + + for cmd in gencom.command_list: + if gencom.is_instance_dispatch_table_entry(cmd): + gencom.init_proc(cmd, f) + + f.write("""\ + // clang-format on + + return success; +} + +bool InitDispatchTable( + VkDevice dev, + PFN_vkGetDeviceProcAddr get_proc, + const std::bitset<driver::ProcHook::EXTENSION_COUNT>& extensions) { + auto& data = GetData(dev); + bool success = true; + + // clang-format off\n""") + + for cmd in gencom.command_list: + if gencom.is_device_dispatch_table_entry(cmd): + gencom.init_proc(cmd, f) + + f.write("""\ + // clang-format on + + return success; +} + +// clang-format off + +namespace { + +// forward declarations needed by GetInstanceProcAddr and GetDeviceProcAddr +""") + + for cmd in gencom.command_list: + if gencom.is_function_exported(cmd) and not _is_intercepted(cmd): + param_list = [''.join(i) for i in gencom.param_dict[cmd]] + f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' + + gencom.base_name(cmd) + '(' + ', '.join(param_list) + ');\n') + + f.write('\n') + for cmd in gencom.command_list: + if gencom.is_function_exported(cmd) and not _is_intercepted(cmd): + param_list = [''.join(i) for i in gencom.param_dict[cmd]] + f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' + + gencom.base_name(cmd) + '(' + ', '.join(param_list) + ') {\n') + if cmd == 'vkGetInstanceProcAddr': + _intercept_instance_proc_addr(f) + elif cmd == 'vkGetDeviceProcAddr': + _intercept_device_proc_addr(f) + _api_dispatch(cmd, f) + f.write('}\n\n') + + f.write(""" +} // anonymous namespace + +// clang-format on + +} // namespace api +} // namespace vulkan + +// clang-format off\n\n""") + + for cmd in gencom.command_list: + if gencom.is_function_exported(cmd): + param_list = [''.join(i) for i in gencom.param_dict[cmd]] + f.write('__attribute__((visibility("default")))\n') + f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' + + cmd + '(' + ', '.join(param_list) + ') {\n') + f.write(gencom.indent(1)) + if gencom.return_type_dict[cmd] != 'void': + f.write('return ') + param_list = gencom.param_dict[cmd] + f.write('vulkan::api::' + gencom.base_name(cmd) + + '(' + ', '.join(i[1] for i in param_list) + ');\n}\n\n') + + f.write('// clang-format on\n') + f.close() + gencom.run_clang_format(genfile) diff --git a/vulkan/scripts/code_generator.py b/vulkan/scripts/code_generator.py new file mode 100755 index 0000000000..2a017d2259 --- /dev/null +++ b/vulkan/scripts/code_generator.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 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. + +"""Generates vulkan framework directly from the vulkan registry (vk.xml). +""" + +import api_generator +import driver_generator +import generator_common +import null_generator + +if __name__ == '__main__': + generator_common.parse_vulkan_registry() + api_generator.gen_h() + api_generator.gen_cpp() + driver_generator.gen_h() + driver_generator.gen_cpp() + null_generator.gen_h() + null_generator.gen_cpp() diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py new file mode 100644 index 0000000000..0be049186e --- /dev/null +++ b/vulkan/scripts/driver_generator.py @@ -0,0 +1,511 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 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. + +"""Generates the driver_gen.h and driver_gen.cpp. +""" + +import os +import generator_common as gencom + +# Extensions intercepted at vulkan::driver level. +_INTERCEPTED_EXTENSIONS = [ + 'VK_ANDROID_native_buffer', + 'VK_EXT_debug_report', + 'VK_EXT_hdr_metadata', + 'VK_EXT_swapchain_colorspace', + 'VK_GOOGLE_display_timing', + 'VK_KHR_android_surface', + 'VK_KHR_get_surface_capabilities2', + 'VK_KHR_incremental_present', + 'VK_KHR_shared_presentable_image', + 'VK_KHR_surface', + 'VK_KHR_swapchain', +] + +# Extensions known to vulkan::driver level. +_KNOWN_EXTENSIONS = _INTERCEPTED_EXTENSIONS + [ + 'VK_ANDROID_external_memory_android_hardware_buffer', + 'VK_KHR_bind_memory2', + 'VK_KHR_get_physical_device_properties2', +] + +# Functions needed at vulkan::driver level. +_NEEDED_COMMANDS = [ + # Create functions of dispatchable objects + 'vkCreateDevice', + 'vkGetDeviceQueue', + 'vkGetDeviceQueue2', + 'vkAllocateCommandBuffers', + + # Destroy functions of dispatchable objects + 'vkDestroyInstance', + 'vkDestroyDevice', + + # Enumeration of extensions + 'vkEnumerateDeviceExtensionProperties', + + # We cache physical devices in loader.cpp + 'vkEnumeratePhysicalDevices', + 'vkEnumeratePhysicalDeviceGroups', + + 'vkGetInstanceProcAddr', + 'vkGetDeviceProcAddr', + + 'vkQueueSubmit', + + # VK_KHR_swapchain->VK_ANDROID_native_buffer translation + 'vkCreateImage', + 'vkDestroyImage', + + 'vkGetPhysicalDeviceProperties', + 'vkGetPhysicalDeviceProperties2', + 'vkGetPhysicalDeviceProperties2KHR', + + # VK_KHR_swapchain v69 requirement + 'vkBindImageMemory2', + 'vkBindImageMemory2KHR', +] + +# Functions intercepted at vulkan::driver level. +_INTERCEPTED_COMMANDS = [ + # Create functions of dispatchable objects + 'vkCreateInstance', + 'vkCreateDevice', + 'vkEnumeratePhysicalDevices', + 'vkEnumeratePhysicalDeviceGroups', + 'vkGetDeviceQueue', + 'vkGetDeviceQueue2', + 'vkAllocateCommandBuffers', + + # Destroy functions of dispatchable objects + 'vkDestroyInstance', + 'vkDestroyDevice', + + # Enumeration of extensions + 'vkEnumerateInstanceExtensionProperties', + 'vkEnumerateDeviceExtensionProperties', + + 'vkGetInstanceProcAddr', + 'vkGetDeviceProcAddr', + + 'vkQueueSubmit', + + # VK_KHR_swapchain v69 requirement + 'vkBindImageMemory2', + 'vkBindImageMemory2KHR', +] + + +def _is_driver_table_entry(cmd): + """Returns true if a function is needed by vulkan::driver. + + Args: + cmd: Vulkan function name. + """ + if gencom.is_function_supported(cmd): + if cmd in _NEEDED_COMMANDS: + return True + if cmd in gencom.extension_dict: + if (gencom.extension_dict[cmd] == 'VK_ANDROID_native_buffer' or + gencom.extension_dict[cmd] == 'VK_EXT_debug_report'): + return True + return False + + +def _is_instance_driver_table_entry(cmd): + """Returns true if a instance-dispatched function is needed by vulkan::driver. + + Args: + cmd: Vulkan function name. + """ + return (_is_driver_table_entry(cmd) and + gencom.is_instance_dispatched(cmd)) + + +def _is_device_driver_table_entry(cmd): + """Returns true if a device-dispatched function is needed by vulkan::driver. + + Args: + cmd: Vulkan function name. + """ + return (_is_driver_table_entry(cmd) and + gencom.is_device_dispatched(cmd)) + + +def gen_h(): + """Generates the driver_gen.h file. + """ + genfile = os.path.join(os.path.dirname(__file__), + '..', 'libvulkan', 'driver_gen.h') + + with open(genfile, 'w') as f: + f.write(gencom.copyright_and_warning(2016)) + + f.write("""\ +#ifndef LIBVULKAN_DRIVER_GEN_H +#define LIBVULKAN_DRIVER_GEN_H + +#include <vulkan/vk_android_native_buffer.h> +#include <vulkan/vulkan.h> + +#include <bitset> + +namespace vulkan { +namespace driver { + +struct ProcHook { + enum Type { + GLOBAL, + INSTANCE, + DEVICE, + }; + enum Extension {\n""") + + for ext in _KNOWN_EXTENSIONS: + f.write(gencom.indent(2) + gencom.base_ext_name(ext) + ',\n') + + f.write(""" + EXTENSION_CORE, // valid bit + EXTENSION_COUNT, + EXTENSION_UNKNOWN, + }; + + const char* name; + Type type; + Extension extension; + + PFN_vkVoidFunction proc; + PFN_vkVoidFunction checked_proc; // always nullptr for non-device hooks +}; + +struct InstanceDriverTable { + // clang-format off\n""") + + for cmd in gencom.command_list: + if _is_instance_driver_table_entry(cmd): + f.write(gencom.indent(1) + 'PFN_' + cmd + ' ' + + gencom.base_name(cmd) + ';\n') + + f.write("""\ + // clang-format on +}; + +struct DeviceDriverTable { + // clang-format off\n""") + + for cmd in gencom.command_list: + if _is_device_driver_table_entry(cmd): + f.write(gencom.indent(1) + 'PFN_' + cmd + ' ' + + gencom.base_name(cmd) + ';\n') + + f.write("""\ + // clang-format on +}; + +const ProcHook* GetProcHook(const char* name); +ProcHook::Extension GetProcHookExtension(const char* name); + +bool InitDriverTable(VkInstance instance, + PFN_vkGetInstanceProcAddr get_proc, + const std::bitset<ProcHook::EXTENSION_COUNT>& extensions); +bool InitDriverTable(VkDevice dev, + PFN_vkGetDeviceProcAddr get_proc, + const std::bitset<ProcHook::EXTENSION_COUNT>& extensions); + +} // namespace driver +} // namespace vulkan + +#endif // LIBVULKAN_DRIVER_TABLE_H\n""") + + f.close() + gencom.run_clang_format(genfile) + + +def _is_intercepted(cmd): + """Returns true if a function is intercepted by vulkan::driver. + + Args: + cmd: Vulkan function name. + """ + if gencom.is_function_supported(cmd): + if cmd in _INTERCEPTED_COMMANDS: + return True + + if cmd in gencom.extension_dict: + return gencom.extension_dict[cmd] in _INTERCEPTED_EXTENSIONS + return False + + +def _need_proc_hook_stub(cmd): + """Returns true if a function needs a ProcHook stub. + + Args: + cmd: Vulkan function name. + """ + if _is_intercepted(cmd) and gencom.is_device_dispatched(cmd): + if cmd in gencom.extension_dict: + if not gencom.is_extension_internal(gencom.extension_dict[cmd]): + return True + return False + + +def _define_proc_hook_stub(cmd, f): + """Emits a stub for ProcHook::checked_proc. + + Args: + cmd: Vulkan function name. + f: Output file handle. + """ + if _need_proc_hook_stub(cmd): + return_type = gencom.return_type_dict[cmd] + ext_name = gencom.extension_dict[cmd] + ext_hook = 'ProcHook::' + ext_name[3:] + handle = gencom.param_dict[cmd][0][1] + param_types = ', '.join([''.join(i) for i in gencom.param_dict[cmd]]) + param_names = ', '.join([''.join(i[1]) for i in gencom.param_dict[cmd]]) + + f.write('VKAPI_ATTR ' + return_type + ' checked' + gencom.base_name(cmd) + + '(' + param_types + ') {\n') + f.write(gencom.indent(1) + 'if (GetData(' + handle + ').hook_extensions[' + + ext_hook + ']) {\n') + + f.write(gencom.indent(2)) + if gencom.return_type_dict[cmd] != 'void': + f.write('return ') + f.write(gencom.base_name(cmd) + '(' + param_names + ');\n') + + f.write(gencom.indent(1) + '} else {\n') + f.write(gencom.indent(2) + 'Logger(' + handle + ').Err(' + handle + ', \"' + + ext_name + ' not enabled. ' + cmd + ' not executed.\");\n') + if gencom.return_type_dict[cmd] != 'void': + f.write(gencom.indent(2) + 'return VK_SUCCESS;\n') + f.write(gencom.indent(1) + '}\n}\n\n') + + +def _define_global_proc_hook(cmd, f): + """Emits definition of a global ProcHook. + + Args: + cmd: Vulkan function name. + f: Output file handle. + """ + assert cmd not in gencom.extension_dict + + f.write(gencom.indent(1) + '{\n') + f.write(gencom.indent(2) + '\"' + cmd + '\",\n') + f.write("""\ + ProcHook::GLOBAL, + ProcHook::EXTENSION_CORE, + reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """), + nullptr, + },\n""") + + +def _define_instance_proc_hook(cmd, f): + """Emits definition of a instance ProcHook. + + Args: + cmd: Vulkan function name. + f: Output file handle. + """ + f.write(gencom.indent(1) + '{\n') + f.write(gencom.indent(2) + '\"' + cmd + '\",\n') + f.write(gencom.indent(2) + 'ProcHook::INSTANCE,\n') + + if cmd in gencom.extension_dict: + ext_name = gencom.extension_dict[cmd] + f.write(gencom.indent(2) + 'ProcHook::' + ext_name[3:] + ',\n') + + if gencom.is_extension_internal(ext_name): + f.write("""\ + nullptr, + nullptr,\n""") + else: + f.write("""\ + reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """), + nullptr,\n""") + else: + f.write("""\ + ProcHook::EXTENSION_CORE, + reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """), + nullptr,\n""") + + f.write(gencom.indent(1) + '},\n') + + +def _define_device_proc_hook(cmd, f): + """Emits definition of a device ProcHook. + + Args: + cmd: Vulkan function name. + f: Output file handle. + """ + f.write(gencom.indent(1) + '{\n') + f.write(gencom.indent(2) + '\"' + cmd + '\",\n') + f.write(gencom.indent(2) + 'ProcHook::DEVICE,\n') + + if cmd in gencom.extension_dict: + ext_name = gencom.extension_dict[cmd] + f.write(gencom.indent(2) + 'ProcHook::' + ext_name[3:] + ',\n') + + if gencom.is_extension_internal(ext_name): + f.write("""\ + nullptr, + nullptr,\n""") + else: + f.write("""\ + reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """), + reinterpret_cast<PFN_vkVoidFunction>(checked""" + + gencom.base_name(cmd) + '),\n') + + else: + f.write("""\ + ProcHook::EXTENSION_CORE, + reinterpret_cast<PFN_vkVoidFunction>(""" + gencom.base_name(cmd) + """), + nullptr,\n""") + + f.write(gencom.indent(1) + '},\n') + + +def gen_cpp(): + """Generates the driver_gen.cpp file. + """ + genfile = os.path.join(os.path.dirname(__file__), + '..', 'libvulkan', 'driver_gen.cpp') + + with open(genfile, 'w') as f: + f.write(gencom.copyright_and_warning(2016)) + f.write("""\ +#include <log/log.h> +#include <string.h> + +#include <algorithm> + +#include "driver.h" + +namespace vulkan { +namespace driver { + +namespace { + +// clang-format off\n\n""") + + for cmd in gencom.command_list: + _define_proc_hook_stub(cmd, f) + + f.write("""\ +// clang-format on + +const ProcHook g_proc_hooks[] = { + // clang-format off\n""") + + sorted_command_list = sorted(gencom.command_list) + for cmd in sorted_command_list: + if _is_intercepted(cmd): + if gencom.is_globally_dispatched(cmd): + _define_global_proc_hook(cmd, f) + elif gencom.is_instance_dispatched(cmd): + _define_instance_proc_hook(cmd, f) + elif gencom.is_device_dispatched(cmd): + _define_device_proc_hook(cmd, f) + + f.write("""\ + // clang-format on +}; + +} // namespace + +const ProcHook* GetProcHook(const char* name) { + const auto& begin = g_proc_hooks; + const auto& end = + g_proc_hooks + sizeof(g_proc_hooks) / sizeof(g_proc_hooks[0]); + const auto hook = std::lower_bound( + begin, end, name, + [](const ProcHook& e, const char* n) { return strcmp(e.name, n) < 0; }); + return (hook < end && strcmp(hook->name, name) == 0) ? hook : nullptr; +} + +ProcHook::Extension GetProcHookExtension(const char* name) { + // clang-format off\n""") + + for ext in _KNOWN_EXTENSIONS: + f.write(gencom.indent(1) + 'if (strcmp(name, \"' + ext + + '\") == 0) return ProcHook::' + gencom.base_ext_name(ext) + ';\n') + + f.write("""\ + // clang-format on + return ProcHook::EXTENSION_UNKNOWN; +} + +#define UNLIKELY(expr) __builtin_expect((expr), 0) + +#define INIT_PROC(required, obj, proc) \\ + do { \\ + data.driver.proc = \\ + reinterpret_cast<PFN_vk##proc>(get_proc(obj, "vk" #proc)); \\ + if (UNLIKELY(required && !data.driver.proc)) { \\ + ALOGE("missing " #obj " proc: vk" #proc); \\ + success = false; \\ + } \\ + } while (0) + +#define INIT_PROC_EXT(ext, required, obj, proc) \\ + do { \\ + if (extensions[ProcHook::ext]) \\ + INIT_PROC(required, obj, proc); \\ + } while (0) + +bool InitDriverTable(VkInstance instance, + PFN_vkGetInstanceProcAddr get_proc, + const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) { + auto& data = GetData(instance); + bool success = true; + + // clang-format off\n""") + + for cmd in gencom.command_list: + if _is_instance_driver_table_entry(cmd): + gencom.init_proc(cmd, f) + + f.write("""\ + // clang-format on + + return success; +} + +bool InitDriverTable(VkDevice dev, + PFN_vkGetDeviceProcAddr get_proc, + const std::bitset<ProcHook::EXTENSION_COUNT>& extensions) { + auto& data = GetData(dev); + bool success = true; + + // clang-format off\n""") + + for cmd in gencom.command_list: + if _is_device_driver_table_entry(cmd): + gencom.init_proc(cmd, f) + + f.write("""\ + // clang-format on + + return success; +} + +} // namespace driver +} // namespace vulkan\n""") + + f.close() + gencom.run_clang_format(genfile) diff --git a/vulkan/scripts/generator_common.py b/vulkan/scripts/generator_common.py new file mode 100644 index 0000000000..5bfa9ecb52 --- /dev/null +++ b/vulkan/scripts/generator_common.py @@ -0,0 +1,387 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 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. + +"""Provide the utilities for framework generation. +""" + +import os +import subprocess +import xml.etree.ElementTree as element_tree + +# Extensions unsupported on Android. +_BLACKLISTED_EXTENSIONS = [ + 'VK_EXT_acquire_xlib_display', + 'VK_EXT_direct_mode_display', + 'VK_EXT_display_control', + 'VK_EXT_display_surface_counter', + 'VK_EXT_full_screen_exclusive', + 'VK_EXT_headless_surface', + 'VK_EXT_metal_surface', + 'VK_FUCHSIA_imagepipe_surface', + 'VK_GGP_stream_descriptor_surface', + 'VK_KHR_display', + 'VK_KHR_display_swapchain', + 'VK_KHR_external_fence_win32', + 'VK_KHR_external_memory_win32', + 'VK_KHR_external_semaphore_win32', + 'VK_KHR_mir_surface', + 'VK_KHR_wayland_surface', + 'VK_KHR_win32_keyed_mutex', + 'VK_KHR_win32_surface', + 'VK_KHR_xcb_surface', + 'VK_KHR_xlib_surface', + 'VK_MVK_ios_surface', + 'VK_MVK_macos_surface', + 'VK_NN_vi_surface', + 'VK_NV_cooperative_matrix', + 'VK_NV_coverage_reduction_mode', + 'VK_NV_external_memory_win32', + 'VK_NV_win32_keyed_mutex', + 'VK_NVX_image_view_handle', +] + +# Extensions having functions exported by the loader. +_EXPORTED_EXTENSIONS = [ + 'VK_ANDROID_external_memory_android_hardware_buffer', + 'VK_KHR_android_surface', + 'VK_KHR_surface', + 'VK_KHR_swapchain', +] + +# Functions optional on Android even if extension is advertised. +_OPTIONAL_COMMANDS = [ + 'vkGetSwapchainGrallocUsageANDROID', + 'vkGetSwapchainGrallocUsage2ANDROID', +] + +# Dict for mapping dispatch table to a type. +_DISPATCH_TYPE_DICT = { + 'VkInstance ': 'Instance', + 'VkPhysicalDevice ': 'Instance', + 'VkDevice ': 'Device', + 'VkQueue ': 'Device', + 'VkCommandBuffer ': 'Device' +} + +# Dict for mapping a function to its alias. +alias_dict = {} + +# List of all the Vulkan functions. +command_list = [] + +# Dict for mapping a function to an extension. +extension_dict = {} + +# Dict for mapping a function to all its parameters. +param_dict = {} + +# Dict for mapping a function to its return type. +return_type_dict = {} + +# Dict for mapping a function to the core Vulkan API version. +version_dict = {} + + +def indent(num): + """Returns the requested indents. + + Args: + num: Number of the 4-space indents. + """ + return ' ' * num + + +def copyright_and_warning(year): + """Returns the standard copyright and warning codes. + + Args: + year: An integer year for the copyright. + """ + return """\ +/* + * Copyright """ + str(year) + """ 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. + */ + +// WARNING: This file is generated. See ../README.md for instructions. + +""" + + +def run_clang_format(args): + """Run clang format on the file. + + Args: + args: The file to be formatted. + """ + clang_call = ['clang-format', '--style', 'file', '-i', args] + subprocess.check_call(clang_call) + + +def is_extension_internal(ext): + """Returns true if an extension is internal to the loader and drivers. + + The loader should not enumerate this extension. + + Args: + ext: Vulkan extension name. + """ + return ext == 'VK_ANDROID_native_buffer' + + +def base_name(cmd): + """Returns a function name without the 'vk' prefix. + + Args: + cmd: Vulkan function name. + """ + return cmd[2:] + + +def base_ext_name(ext): + """Returns an extension name without the 'VK_' prefix. + + Args: + ext: Vulkan extension name. + """ + return ext[3:] + + +def is_function_supported(cmd): + """Returns true if a function is core or from a supportable extension. + + Args: + cmd: Vulkan function name. + """ + if cmd not in extension_dict: + return True + else: + if extension_dict[cmd] not in _BLACKLISTED_EXTENSIONS: + return True + return False + + +def get_dispatch_table_type(cmd): + """Returns the dispatch table type for a function. + + Args: + cmd: Vulkan function name. + """ + if cmd not in param_dict: + return None + + if param_dict[cmd]: + return _DISPATCH_TYPE_DICT.get(param_dict[cmd][0][0], 'Global') + return 'Global' + + +def is_globally_dispatched(cmd): + """Returns true if the function is global, which is not dispatched. + + Only global functions and functions handled in the loader top without calling + into lower layers are not dispatched. + + Args: + cmd: Vulkan function name. + """ + return is_function_supported(cmd) and get_dispatch_table_type(cmd) == 'Global' + + +def is_instance_dispatched(cmd): + """Returns true for functions that can have instance-specific dispatch. + + Args: + cmd: Vulkan function name. + """ + return (is_function_supported(cmd) and + get_dispatch_table_type(cmd) == 'Instance') + + +def is_device_dispatched(cmd): + """Returns true for functions that can have device-specific dispatch. + + Args: + cmd: Vulkan function name. + """ + return is_function_supported(cmd) and get_dispatch_table_type(cmd) == 'Device' + + +def is_extension_exported(ext): + """Returns true if an extension has functions exported by the loader. + + E.g. applications can directly link to an extension function. + + Args: + ext: Vulkan extension name. + """ + return ext in _EXPORTED_EXTENSIONS + + +def is_function_exported(cmd): + """Returns true if a function is exported from the Android Vulkan library. + + Functions in the core API and in loader extensions are exported. + + Args: + cmd: Vulkan function name. + """ + if is_function_supported(cmd): + if cmd in extension_dict: + return is_extension_exported(extension_dict[cmd]) + return True + return False + + +def is_instance_dispatch_table_entry(cmd): + """Returns true if a function is exported and instance-dispatched. + + Args: + cmd: Vulkan function name. + """ + if cmd == 'vkEnumerateDeviceLayerProperties': + # deprecated, unused internally - @dbd33bc + return False + return is_function_exported(cmd) and is_instance_dispatched(cmd) + + +def is_device_dispatch_table_entry(cmd): + """Returns true if a function is exported and device-dispatched. + + Args: + cmd: Vulkan function name. + """ + return is_function_exported(cmd) and is_device_dispatched(cmd) + + +def init_proc(name, f): + """Emits code to invoke INIT_PROC or INIT_PROC_EXT. + + Args: + name: Vulkan function name. + f: Output file handle. + """ + f.write(indent(1)) + if name in extension_dict: + f.write('INIT_PROC_EXT(' + extension_dict[name][3:] + ', ') + else: + f.write('INIT_PROC(') + + if name in version_dict and version_dict[name] == 'VK_VERSION_1_1': + f.write('false, ') + elif name in _OPTIONAL_COMMANDS: + f.write('false, ') + else: + f.write('true, ') + + if is_instance_dispatched(name): + f.write('instance, ') + else: + f.write('dev, ') + + f.write(base_name(name) + ');\n') + + +def parse_vulkan_registry(): + """Parses Vulkan registry into the below global variables. + + alias_dict + command_list + extension_dict + param_dict + return_type_dict + version_dict + """ + registry = os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', + 'external', 'vulkan-headers', 'registry', 'vk.xml') + tree = element_tree.parse(registry) + root = tree.getroot() + for commands in root.iter('commands'): + for command in commands: + if command.tag == 'command': + parameter_list = [] + protoset = False + cmd_name = '' + cmd_type = '' + if command.get('alias') is not None: + alias = command.get('alias') + cmd_name = command.get('name') + alias_dict[cmd_name] = alias + command_list.append(cmd_name) + param_dict[cmd_name] = param_dict[alias].copy() + return_type_dict[cmd_name] = return_type_dict[alias] + for params in command: + if params.tag == 'param': + param_type = '' + if params.text is not None and params.text.strip(): + param_type = params.text.strip() + ' ' + type_val = params.find('type') + param_type = param_type + type_val.text + if type_val.tail is not None: + param_type += type_val.tail.strip() + ' ' + pname = params.find('name') + param_name = pname.text + if pname.tail is not None and pname.tail.strip(): + parameter_list.append( + (param_type, param_name, pname.tail.strip())) + else: + parameter_list.append((param_type, param_name)) + if params.tag == 'proto': + for c in params: + if c.tag == 'type': + cmd_type = c.text + if c.tag == 'name': + cmd_name = c.text + protoset = True + command_list.append(cmd_name) + return_type_dict[cmd_name] = cmd_type + if protoset: + param_dict[cmd_name] = parameter_list.copy() + + for exts in root.iter('extensions'): + for extension in exts: + apiversion = '' + if extension.tag == 'extension': + extname = extension.get('name') + for req in extension: + if req.get('feature') is not None: + apiversion = req.get('feature') + for commands in req: + if commands.tag == 'command': + cmd_name = commands.get('name') + if cmd_name not in extension_dict: + extension_dict[cmd_name] = extname + if apiversion: + version_dict[cmd_name] = apiversion + + for feature in root.iter('feature'): + apiversion = feature.get('name') + for req in feature: + for command in req: + if command.tag == 'command': + cmd_name = command.get('name') + if cmd_name in command_list: + version_dict[cmd_name] = apiversion diff --git a/vulkan/scripts/null_generator.py b/vulkan/scripts/null_generator.py new file mode 100644 index 0000000000..e9faef663b --- /dev/null +++ b/vulkan/scripts/null_generator.py @@ -0,0 +1,164 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 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. + +"""Generates the null_driver_gen.h and null_driver_gen.cpp. +""" + +import os +import generator_common as gencom + +# Extensions implemented by the driver. +_DRIVER_EXTENSION_DICT = { + 'VK_ANDROID_native_buffer', + 'VK_EXT_debug_report', + 'VK_KHR_get_physical_device_properties2' +} + + +def _is_driver_function(cmd): + """Returns true if the function is implemented by the driver. + + Args: + cmd: Vulkan function name. + """ + if cmd in gencom.extension_dict: + return gencom.extension_dict[cmd] in _DRIVER_EXTENSION_DICT + return True + + +def gen_h(): + """Generates the null_driver_gen.h file. + """ + genfile = os.path.join(os.path.dirname(__file__), + '..', 'nulldrv', 'null_driver_gen.h') + + with open(genfile, 'w') as f: + f.write(gencom.copyright_and_warning(2015)) + + f.write("""\ +#ifndef NULLDRV_NULL_DRIVER_H +#define NULLDRV_NULL_DRIVER_H 1 + +#include <vulkan/vk_android_native_buffer.h> +#include <vulkan/vulkan.h> + +namespace null_driver { + +PFN_vkVoidFunction GetGlobalProcAddr(const char* name); +PFN_vkVoidFunction GetInstanceProcAddr(const char* name); + +// clang-format off\n""") + + for cmd in gencom.command_list: + if _is_driver_function(cmd): + param_list = [''.join(i) for i in gencom.param_dict[cmd]] + f.write('VKAPI_ATTR ' + gencom.return_type_dict[cmd] + ' ' + + gencom.base_name(cmd) + '(' + ', '.join(param_list) + ');\n') + + f.write("""\ +// clang-format on + +} // namespace null_driver + +#endif // NULLDRV_NULL_DRIVER_H\n""") + + f.close() + gencom.run_clang_format(genfile) + + +def gen_cpp(): + """Generates the null_driver_gen.cpp file. + """ + genfile = os.path.join(os.path.dirname(__file__), + '..', 'nulldrv', 'null_driver_gen.cpp') + + with open(genfile, 'w') as f: + f.write(gencom.copyright_and_warning(2015)) + + f.write("""\ +#include <algorithm> + +#include "null_driver_gen.h" + +using namespace null_driver; + +namespace { + +struct NameProc { + const char* name; + PFN_vkVoidFunction proc; +}; + +PFN_vkVoidFunction Lookup(const char* name, + const NameProc* begin, + const NameProc* end) { + const auto& entry = std::lower_bound( + begin, end, name, + [](const NameProc& e, const char* n) { return strcmp(e.name, n) < 0; }); + if (entry == end || strcmp(entry->name, name) != 0) + return nullptr; + return entry->proc; +} + +template <size_t N> +PFN_vkVoidFunction Lookup(const char* name, const NameProc (&procs)[N]) { + return Lookup(name, procs, procs + N); +} + +const NameProc kGlobalProcs[] = { + // clang-format off\n""") + + sorted_command_list = sorted(gencom.command_list) + for cmd in sorted_command_list: + if (_is_driver_function(cmd) and + gencom.get_dispatch_table_type(cmd) == 'Global'): + f.write(gencom.indent(1) + '{\"' + cmd + + '\", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_' + + cmd + '>(' + gencom.base_name(cmd) + '))},\n') + + f.write("""\ + // clang-format on +}; + +const NameProc kInstanceProcs[] = { + // clang-format off\n""") + + for cmd in sorted_command_list: + if _is_driver_function(cmd): + f.write(gencom.indent(1) + '{\"' + cmd + + '\", reinterpret_cast<PFN_vkVoidFunction>(static_cast<PFN_' + + cmd + '>(' + gencom.base_name(cmd) + '))},\n') + + f.write("""\ + // clang-format on +}; + +} // namespace + +namespace null_driver { + +PFN_vkVoidFunction GetGlobalProcAddr(const char* name) { + return Lookup(name, kGlobalProcs); +} + +PFN_vkVoidFunction GetInstanceProcAddr(const char* name) { + return Lookup(name, kInstanceProcs); +} + +} // namespace null_driver\n""") + + f.close() + gencom.run_clang_format(genfile) diff --git a/vulkan/tools/Android.bp b/vulkan/tools/Android.bp deleted file mode 100644 index 2514094a15..0000000000 --- a/vulkan/tools/Android.bp +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2015 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. - -cc_binary { - name: "vkinfo", - - clang: true, - cflags: [ - "-fvisibility=hidden", - "-fstrict-aliasing", - - "-DLOG_TAG=\"vkinfo\"", - - "-Weverything", - "-Werror", - "-Wno-padded", - "-Wno-undef", - "-Wno-switch-enum", - ], - cppflags: [ - "-Wno-c++98-compat-pedantic", - "-Wno-c99-extensions", - "-Wno-old-style-cast", - ], - - srcs: ["vkinfo.cpp"], - - shared_libs: [ - "libvulkan", - "liblog", - ], -} diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp deleted file mode 100644 index 89bc926aa6..0000000000 --- a/vulkan/tools/vkinfo.cpp +++ /dev/null @@ -1,634 +0,0 @@ -/* - * Copyright 2015 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 <inttypes.h> -#include <stdlib.h> -#include <unistd.h> - -#include <algorithm> -#include <array> -#include <sstream> -#include <vector> - -#include <vulkan/vulkan.h> - -namespace { - -struct Options { - bool layer_description; - bool layer_extensions; - bool unsupported_features; - bool validate; -}; - -struct GpuInfo { - VkPhysicalDeviceProperties properties; - VkPhysicalDeviceMemoryProperties memory; - VkPhysicalDeviceFeatures features; - std::vector<VkQueueFamilyProperties> queue_families; - std::vector<VkExtensionProperties> extensions; - std::vector<VkLayerProperties> layers; - std::vector<std::vector<VkExtensionProperties>> layer_extensions; -}; -struct VulkanInfo { - std::vector<VkExtensionProperties> extensions; - std::vector<VkLayerProperties> layers; - std::vector<std::vector<VkExtensionProperties>> layer_extensions; - std::vector<GpuInfo> gpus; -}; - -// ---------------------------------------------------------------------------- - -[[noreturn]] void die(const char* proc, VkResult result) { - const char* result_str; - switch (result) { - // clang-format off - case VK_SUCCESS: result_str = "VK_SUCCESS"; break; - case VK_NOT_READY: result_str = "VK_NOT_READY"; break; - case VK_TIMEOUT: result_str = "VK_TIMEOUT"; break; - case VK_EVENT_SET: result_str = "VK_EVENT_SET"; break; - case VK_EVENT_RESET: result_str = "VK_EVENT_RESET"; break; - case VK_INCOMPLETE: result_str = "VK_INCOMPLETE"; break; - case VK_ERROR_OUT_OF_HOST_MEMORY: result_str = "VK_ERROR_OUT_OF_HOST_MEMORY"; break; - case VK_ERROR_OUT_OF_DEVICE_MEMORY: result_str = "VK_ERROR_OUT_OF_DEVICE_MEMORY"; break; - case VK_ERROR_INITIALIZATION_FAILED: result_str = "VK_ERROR_INITIALIZATION_FAILED"; break; - case VK_ERROR_DEVICE_LOST: result_str = "VK_ERROR_DEVICE_LOST"; break; - case VK_ERROR_MEMORY_MAP_FAILED: result_str = "VK_ERROR_MEMORY_MAP_FAILED"; break; - case VK_ERROR_LAYER_NOT_PRESENT: result_str = "VK_ERROR_LAYER_NOT_PRESENT"; break; - case VK_ERROR_EXTENSION_NOT_PRESENT: result_str = "VK_ERROR_EXTENSION_NOT_PRESENT"; break; - case VK_ERROR_INCOMPATIBLE_DRIVER: result_str = "VK_ERROR_INCOMPATIBLE_DRIVER"; break; - default: result_str = "<unknown VkResult>"; break; - // clang-format on - } - fprintf(stderr, "%s failed: %s (%d)\n", proc, result_str, result); - exit(1); -} - -bool HasExtension(const std::vector<VkExtensionProperties>& extensions, - const char* name) { - return std::find_if(extensions.cbegin(), extensions.cend(), - [=](const VkExtensionProperties& prop) { - return strcmp(prop.extensionName, name) == 0; - }) != extensions.end(); -} - -void EnumerateInstanceExtensions( - const char* layer_name, - std::vector<VkExtensionProperties>* extensions) { - VkResult result; - uint32_t count; - result = - vkEnumerateInstanceExtensionProperties(layer_name, &count, nullptr); - if (result != VK_SUCCESS) - die("vkEnumerateInstanceExtensionProperties (count)", result); - do { - extensions->resize(count); - result = vkEnumerateInstanceExtensionProperties(layer_name, &count, - extensions->data()); - } while (result == VK_INCOMPLETE); - if (result != VK_SUCCESS) - die("vkEnumerateInstanceExtensionProperties (data)", result); -} - -void EnumerateDeviceExtensions(VkPhysicalDevice gpu, - const char* layer_name, - std::vector<VkExtensionProperties>* extensions) { - VkResult result; - uint32_t count; - result = - vkEnumerateDeviceExtensionProperties(gpu, layer_name, &count, nullptr); - if (result != VK_SUCCESS) - die("vkEnumerateDeviceExtensionProperties (count)", result); - do { - extensions->resize(count); - result = vkEnumerateDeviceExtensionProperties(gpu, layer_name, &count, - extensions->data()); - } while (result == VK_INCOMPLETE); - if (result != VK_SUCCESS) - die("vkEnumerateDeviceExtensionProperties (data)", result); -} - -void GatherGpuInfo(VkPhysicalDevice gpu, - const Options &options, - GpuInfo& info) { - VkResult result; - uint32_t count; - - vkGetPhysicalDeviceProperties(gpu, &info.properties); - vkGetPhysicalDeviceMemoryProperties(gpu, &info.memory); - vkGetPhysicalDeviceFeatures(gpu, &info.features); - - vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count, nullptr); - info.queue_families.resize(count); - vkGetPhysicalDeviceQueueFamilyProperties(gpu, &count, - info.queue_families.data()); - - result = vkEnumerateDeviceLayerProperties(gpu, &count, nullptr); - if (result != VK_SUCCESS) - die("vkEnumerateDeviceLayerProperties (count)", result); - do { - info.layers.resize(count); - result = - vkEnumerateDeviceLayerProperties(gpu, &count, info.layers.data()); - } while (result == VK_INCOMPLETE); - if (result != VK_SUCCESS) - die("vkEnumerateDeviceLayerProperties (data)", result); - info.layer_extensions.resize(info.layers.size()); - - EnumerateDeviceExtensions(gpu, nullptr, &info.extensions); - for (size_t i = 0; i < info.layers.size(); i++) { - EnumerateDeviceExtensions(gpu, info.layers[i].layerName, - &info.layer_extensions[i]); - } - - const std::array<const char*, 1> kDesiredExtensions = { - {VK_KHR_SWAPCHAIN_EXTENSION_NAME}, - }; - const char* extensions[kDesiredExtensions.size()]; - uint32_t num_extensions = 0; - for (const auto& desired_ext : kDesiredExtensions) { - bool available = HasExtension(info.extensions, desired_ext); - if (options.validate) { - for (size_t i = 0; !available && i < info.layer_extensions.size(); - i++) - available = HasExtension(info.layer_extensions[i], desired_ext); - } - if (available) - extensions[num_extensions++] = desired_ext; - } - - VkDevice device; - float queue_priorities[] = {0.0}; - const VkDeviceQueueCreateInfo queue_create_info = { - .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, - .queueFamilyIndex = 0, - .queueCount = 1, - .pQueuePriorities = queue_priorities - }; - // clang-format off - const char *kValidationLayers[] = { - "VK_LAYER_GOOGLE_threading", - "VK_LAYER_LUNARG_parameter_validation", - "VK_LAYER_LUNARG_device_limits", - "VK_LAYER_LUNARG_object_tracker", - "VK_LAYER_LUNARG_image", - "VK_LAYER_LUNARG_core_validation", - "VK_LAYER_LUNARG_swapchain", - "VK_LAYER_GOOGLE_unique_objects" - }; - // clang-format on - uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*); - const VkDeviceCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, - .queueCreateInfoCount = 1, - .pQueueCreateInfos = &queue_create_info, - .enabledExtensionCount = num_extensions, - .ppEnabledExtensionNames = extensions, - .enabledLayerCount = (options.validate) ? num_layers : 0, - .ppEnabledLayerNames = kValidationLayers, - .pEnabledFeatures = &info.features, - }; - result = vkCreateDevice(gpu, &create_info, nullptr, &device); - if (result != VK_SUCCESS) - die("vkCreateDevice", result); - vkDestroyDevice(device, nullptr); -} - -void GatherInfo(VulkanInfo* info, const Options& options) { - VkResult result; - uint32_t count; - - result = vkEnumerateInstanceLayerProperties(&count, nullptr); - if (result != VK_SUCCESS) - die("vkEnumerateInstanceLayerProperties (count)", result); - do { - info->layers.resize(count); - result = - vkEnumerateInstanceLayerProperties(&count, info->layers.data()); - } while (result == VK_INCOMPLETE); - if (result != VK_SUCCESS) - die("vkEnumerateInstanceLayerProperties (data)", result); - info->layer_extensions.resize(info->layers.size()); - - EnumerateInstanceExtensions(nullptr, &info->extensions); - for (size_t i = 0; i < info->layers.size(); i++) { - EnumerateInstanceExtensions(info->layers[i].layerName, - &info->layer_extensions[i]); - } - - const char* kDesiredExtensions[] = { - VK_EXT_DEBUG_REPORT_EXTENSION_NAME, - }; - const char* - extensions[sizeof(kDesiredExtensions) / sizeof(kDesiredExtensions[0])]; - uint32_t num_extensions = 0; - for (const auto& desired_ext : kDesiredExtensions) { - bool available = HasExtension(info->extensions, desired_ext); - if (options.validate) { - for (size_t i = 0; !available && i < info->layer_extensions.size(); - i++) - available = - HasExtension(info->layer_extensions[i], desired_ext); - } - if (available) - extensions[num_extensions++] = desired_ext; - } - - // clang-format off - const char *kValidationLayers[] = { - "VK_LAYER_GOOGLE_threading", - "VK_LAYER_LUNARG_parameter_validation", - "VK_LAYER_LUNARG_device_limits", - "VK_LAYER_LUNARG_object_tracker", - "VK_LAYER_LUNARG_image", - "VK_LAYER_LUNARG_core_validation", - "VK_LAYER_LUNARG_swapchain", - "VK_LAYER_GOOGLE_unique_objects" - }; - // clang-format on - uint32_t num_layers = sizeof(kValidationLayers) / sizeof(char*); - - const VkApplicationInfo application_info = { - .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, - .pApplicationName = "vkinfo", - .applicationVersion = 0, - .pEngineName = "vkinfo", - .engineVersion = 0, - .apiVersion = VK_API_VERSION_1_0, - }; - const VkInstanceCreateInfo create_info = { - .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, - .pApplicationInfo = &application_info, - .enabledExtensionCount = num_extensions, - .ppEnabledExtensionNames = extensions, - .enabledLayerCount = (options.validate) ? num_layers : 0, - .ppEnabledLayerNames = kValidationLayers, - }; - VkInstance instance; - result = vkCreateInstance(&create_info, nullptr, &instance); - if (result != VK_SUCCESS) - die("vkCreateInstance", result); - - uint32_t num_gpus; - result = vkEnumeratePhysicalDevices(instance, &num_gpus, nullptr); - if (result != VK_SUCCESS) - die("vkEnumeratePhysicalDevices (count)", result); - std::vector<VkPhysicalDevice> gpus(num_gpus, VK_NULL_HANDLE); - do { - gpus.resize(num_gpus, VK_NULL_HANDLE); - result = vkEnumeratePhysicalDevices(instance, &num_gpus, gpus.data()); - } while (result == VK_INCOMPLETE); - if (result != VK_SUCCESS) - die("vkEnumeratePhysicalDevices (data)", result); - - info->gpus.resize(num_gpus); - for (size_t i = 0; i < gpus.size(); i++) - GatherGpuInfo(gpus[i], options, info->gpus.at(i)); - - vkDestroyInstance(instance, nullptr); -} - -// ---------------------------------------------------------------------------- - -const size_t kMaxIndent = 8; -const size_t kIndentSize = 3; -std::array<char, kMaxIndent * kIndentSize + 1> kIndent; -const char* Indent(size_t n) { - static bool initialized = false; - if (!initialized) { - kIndent.fill(' '); - kIndent.back() = '\0'; - initialized = true; - } - return kIndent.data() + - (kIndent.size() - (kIndentSize * std::min(n, kMaxIndent) + 1)); -} - -const char* VkPhysicalDeviceTypeStr(VkPhysicalDeviceType type) { - switch (type) { - case VK_PHYSICAL_DEVICE_TYPE_OTHER: - return "OTHER"; - case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: - return "INTEGRATED_GPU"; - case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: - return "DISCRETE_GPU"; - case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: - return "VIRTUAL_GPU"; - case VK_PHYSICAL_DEVICE_TYPE_CPU: - return "CPU"; - default: - return "<UNKNOWN>"; - } -} - -void PrintExtensions(const std::vector<VkExtensionProperties>& extensions, - const Options& /*options*/, - size_t indent) { - for (const auto& e : extensions) - printf("%s%s (v%u)\n", Indent(indent), e.extensionName, e.specVersion); -} - -void PrintLayers( - const std::vector<VkLayerProperties>& layers, - const std::vector<std::vector<VkExtensionProperties>> extensions, - const Options& options, - size_t indent) { - for (size_t i = 0; i < layers.size(); i++) { - printf("%s%s %u.%u.%u/%u\n", Indent(indent), layers[i].layerName, - VK_VERSION_MAJOR(layers[i].specVersion), - VK_VERSION_MINOR(layers[i].specVersion), - VK_VERSION_PATCH(layers[i].specVersion), - layers[i].implementationVersion); - if (options.layer_description) - printf("%s%s\n", Indent(indent + 1), layers[i].description); - if (options.layer_extensions && !extensions[i].empty()) { - if (!extensions[i].empty()) { - printf("%sExtensions [%zu]:\n", Indent(indent + 1), - extensions[i].size()); - PrintExtensions(extensions[i], options, indent + 2); - } - } - } -} - -void PrintAllFeatures(const char* indent, - const VkPhysicalDeviceFeatures& features) { - // clang-format off - printf("%srobustBufferAccess: %s\n", indent, features.robustBufferAccess ? "YES" : "NO"); - printf("%sfullDrawIndexUint32: %s\n", indent, features.fullDrawIndexUint32 ? "YES" : "NO"); - printf("%simageCubeArray: %s\n", indent, features.imageCubeArray ? "YES" : "NO"); - printf("%sindependentBlend: %s\n", indent, features.independentBlend ? "YES" : "NO"); - printf("%sgeometryShader: %s\n", indent, features.geometryShader ? "YES" : "NO"); - printf("%stessellationShader: %s\n", indent, features.tessellationShader ? "YES" : "NO"); - printf("%ssampleRateShading: %s\n", indent, features.sampleRateShading ? "YES" : "NO"); - printf("%sdualSrcBlend: %s\n", indent, features.dualSrcBlend ? "YES" : "NO"); - printf("%slogicOp: %s\n", indent, features.logicOp ? "YES" : "NO"); - printf("%smultiDrawIndirect: %s\n", indent, features.multiDrawIndirect ? "YES" : "NO"); - printf("%sdrawIndirectFirstInstance: %s\n", indent, features.drawIndirectFirstInstance ? "YES" : "NO"); - printf("%sdepthClamp: %s\n", indent, features.depthClamp ? "YES" : "NO"); - printf("%sdepthBiasClamp: %s\n", indent, features.depthBiasClamp ? "YES" : "NO"); - printf("%sfillModeNonSolid: %s\n", indent, features.fillModeNonSolid ? "YES" : "NO"); - printf("%sdepthBounds: %s\n", indent, features.depthBounds ? "YES" : "NO"); - printf("%swideLines: %s\n", indent, features.wideLines ? "YES" : "NO"); - printf("%slargePoints: %s\n", indent, features.largePoints ? "YES" : "NO"); - printf("%salphaToOne: %s\n", indent, features.alphaToOne ? "YES" : "NO"); - printf("%smultiViewport: %s\n", indent, features.multiViewport ? "YES" : "NO"); - printf("%ssamplerAnisotropy: %s\n", indent, features.samplerAnisotropy ? "YES" : "NO"); - printf("%stextureCompressionETC2: %s\n", indent, features.textureCompressionETC2 ? "YES" : "NO"); - printf("%stextureCompressionASTC_LDR: %s\n", indent, features.textureCompressionASTC_LDR ? "YES" : "NO"); - printf("%stextureCompressionBC: %s\n", indent, features.textureCompressionBC ? "YES" : "NO"); - printf("%socclusionQueryPrecise: %s\n", indent, features.occlusionQueryPrecise ? "YES" : "NO"); - printf("%spipelineStatisticsQuery: %s\n", indent, features.pipelineStatisticsQuery ? "YES" : "NO"); - printf("%svertexPipelineStoresAndAtomics: %s\n", indent, features.vertexPipelineStoresAndAtomics ? "YES" : "NO"); - printf("%sfragmentStoresAndAtomics: %s\n", indent, features.fragmentStoresAndAtomics ? "YES" : "NO"); - printf("%sshaderTessellationAndGeometryPointSize: %s\n", indent, features.shaderTessellationAndGeometryPointSize ? "YES" : "NO"); - printf("%sshaderImageGatherExtended: %s\n", indent, features.shaderImageGatherExtended ? "YES" : "NO"); - printf("%sshaderStorageImageExtendedFormats: %s\n", indent, features.shaderStorageImageExtendedFormats ? "YES" : "NO"); - printf("%sshaderStorageImageMultisample: %s\n", indent, features.shaderStorageImageMultisample ? "YES" : "NO"); - printf("%sshaderStorageImageReadWithoutFormat: %s\n", indent, features.shaderStorageImageReadWithoutFormat ? "YES" : "NO"); - printf("%sshaderStorageImageWriteWithoutFormat: %s\n", indent, features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO"); - printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", indent, features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO"); - printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", indent, features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO"); - printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", indent, features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO"); - printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", indent, features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO"); - printf("%sshaderClipDistance: %s\n", indent, features.shaderClipDistance ? "YES" : "NO"); - printf("%sshaderCullDistance: %s\n", indent, features.shaderCullDistance ? "YES" : "NO"); - printf("%sshaderFloat64: %s\n", indent, features.shaderFloat64 ? "YES" : "NO"); - printf("%sshaderInt64: %s\n", indent, features.shaderInt64 ? "YES" : "NO"); - printf("%sshaderInt16: %s\n", indent, features.shaderInt16 ? "YES" : "NO"); - printf("%sshaderResourceResidency: %s\n", indent, features.shaderResourceResidency ? "YES" : "NO"); - printf("%sshaderResourceMinLod: %s\n", indent, features.shaderResourceMinLod ? "YES" : "NO"); - printf("%ssparseBinding: %s\n", indent, features.sparseBinding ? "YES" : "NO"); - printf("%ssparseResidencyBuffer: %s\n", indent, features.sparseResidencyBuffer ? "YES" : "NO"); - printf("%ssparseResidencyImage2D: %s\n", indent, features.sparseResidencyImage2D ? "YES" : "NO"); - printf("%ssparseResidencyImage3D: %s\n", indent, features.sparseResidencyImage3D ? "YES" : "NO"); - printf("%ssparseResidency2Samples: %s\n", indent, features.sparseResidency2Samples ? "YES" : "NO"); - printf("%ssparseResidency4Samples: %s\n", indent, features.sparseResidency4Samples ? "YES" : "NO"); - printf("%ssparseResidency8Samples: %s\n", indent, features.sparseResidency8Samples ? "YES" : "NO"); - printf("%ssparseResidency16Samples: %s\n", indent, features.sparseResidency16Samples ? "YES" : "NO"); - printf("%ssparseResidencyAliased: %s\n", indent, features.sparseResidencyAliased ? "YES" : "NO"); - printf("%svariableMultisampleRate: %s\n", indent, features.variableMultisampleRate ? "YES" : "NO"); - printf("%sinheritedQueries: %s\n", indent, features.inheritedQueries ? "YES" : "NO"); - // clang-format on -} - -void PrintSupportedFeatures(const char* indent, - const VkPhysicalDeviceFeatures& features) { - // clang-format off - if (features.robustBufferAccess) printf("%srobustBufferAccess\n", indent); - if (features.fullDrawIndexUint32) printf("%sfullDrawIndexUint32\n", indent); - if (features.imageCubeArray) printf("%simageCubeArray\n", indent); - if (features.independentBlend) printf("%sindependentBlend\n", indent); - if (features.geometryShader) printf("%sgeometryShader\n", indent); - if (features.tessellationShader) printf("%stessellationShader\n", indent); - if (features.sampleRateShading) printf("%ssampleRateShading\n", indent); - if (features.dualSrcBlend) printf("%sdualSrcBlend\n", indent); - if (features.logicOp) printf("%slogicOp\n", indent); - if (features.multiDrawIndirect) printf("%smultiDrawIndirect\n", indent); - if (features.drawIndirectFirstInstance) printf("%sdrawIndirectFirstInstance\n", indent); - if (features.depthClamp) printf("%sdepthClamp\n", indent); - if (features.depthBiasClamp) printf("%sdepthBiasClamp\n", indent); - if (features.fillModeNonSolid) printf("%sfillModeNonSolid\n", indent); - if (features.depthBounds) printf("%sdepthBounds\n", indent); - if (features.wideLines) printf("%swideLines\n", indent); - if (features.largePoints) printf("%slargePoints\n", indent); - if (features.alphaToOne) printf("%salphaToOne\n", indent); - if (features.multiViewport) printf("%smultiViewport\n", indent); - if (features.samplerAnisotropy) printf("%ssamplerAnisotropy\n", indent); - if (features.textureCompressionETC2) printf("%stextureCompressionETC2\n", indent); - if (features.textureCompressionASTC_LDR) printf("%stextureCompressionASTC_LDR\n", indent); - if (features.textureCompressionBC) printf("%stextureCompressionBC\n", indent); - if (features.occlusionQueryPrecise) printf("%socclusionQueryPrecise\n", indent); - if (features.pipelineStatisticsQuery) printf("%spipelineStatisticsQuery\n", indent); - if (features.vertexPipelineStoresAndAtomics) printf("%svertexPipelineStoresAndAtomics\n", indent); - if (features.fragmentStoresAndAtomics) printf("%sfragmentStoresAndAtomics\n", indent); - if (features.shaderTessellationAndGeometryPointSize) printf("%sshaderTessellationAndGeometryPointSize\n", indent); - if (features.shaderImageGatherExtended) printf("%sshaderImageGatherExtended\n", indent); - if (features.shaderStorageImageExtendedFormats) printf("%sshaderStorageImageExtendedFormats\n", indent); - if (features.shaderStorageImageMultisample) printf("%sshaderStorageImageMultisample\n", indent); - if (features.shaderStorageImageReadWithoutFormat) printf("%sshaderStorageImageReadWithoutFormat\n", indent); - if (features.shaderStorageImageWriteWithoutFormat) printf("%sshaderStorageImageWriteWithoutFormat\n", indent); - if (features.shaderUniformBufferArrayDynamicIndexing) printf("%sshaderUniformBufferArrayDynamicIndexing\n", indent); - if (features.shaderSampledImageArrayDynamicIndexing) printf("%sshaderSampledImageArrayDynamicIndexing\n", indent); - if (features.shaderStorageBufferArrayDynamicIndexing) printf("%sshaderStorageBufferArrayDynamicIndexing\n", indent); - if (features.shaderStorageImageArrayDynamicIndexing) printf("%sshaderStorageImageArrayDynamicIndexing\n", indent); - if (features.shaderClipDistance) printf("%sshaderClipDistance\n", indent); - if (features.shaderCullDistance) printf("%sshaderCullDistance\n", indent); - if (features.shaderFloat64) printf("%sshaderFloat64\n", indent); - if (features.shaderInt64) printf("%sshaderInt64\n", indent); - if (features.shaderInt16) printf("%sshaderInt16\n", indent); - if (features.shaderResourceResidency) printf("%sshaderResourceResidency\n", indent); - if (features.shaderResourceMinLod) printf("%sshaderResourceMinLod\n", indent); - if (features.sparseBinding) printf("%ssparseBinding\n", indent); - if (features.sparseResidencyBuffer) printf("%ssparseResidencyBuffer\n", indent); - if (features.sparseResidencyImage2D) printf("%ssparseResidencyImage2D\n", indent); - if (features.sparseResidencyImage3D) printf("%ssparseResidencyImage3D\n", indent); - if (features.sparseResidency2Samples) printf("%ssparseResidency2Samples\n", indent); - if (features.sparseResidency4Samples) printf("%ssparseResidency4Samples\n", indent); - if (features.sparseResidency8Samples) printf("%ssparseResidency8Samples\n", indent); - if (features.sparseResidency16Samples) printf("%ssparseResidency16Samples\n", indent); - if (features.sparseResidencyAliased) printf("%ssparseResidencyAliased\n", indent); - if (features.variableMultisampleRate) printf("%svariableMultisampleRate\n", indent); - if (features.inheritedQueries) printf("%sinheritedQueries\n", indent); - // clang-format on -} - -void PrintGpuInfo(const GpuInfo& info, const Options& options, size_t indent) { - VkResult result; - std::ostringstream strbuf; - - printf("%s\"%s\" (%s) %u.%u.%u/%#x [%04x:%04x]\n", Indent(indent), - info.properties.deviceName, - VkPhysicalDeviceTypeStr(info.properties.deviceType), - VK_VERSION_MAJOR(info.properties.apiVersion), - VK_VERSION_MINOR(info.properties.apiVersion), - VK_VERSION_PATCH(info.properties.apiVersion), - info.properties.driverVersion, info.properties.vendorID, - info.properties.deviceID); - - for (uint32_t heap = 0; heap < info.memory.memoryHeapCount; heap++) { - if ((info.memory.memoryHeaps[heap].flags & - VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0) - strbuf << "DEVICE_LOCAL"; - printf("%sHeap %u: %" PRIu64 " MiB (0x%" PRIx64 " B) %s\n", - Indent(indent + 1), heap, - info.memory.memoryHeaps[heap].size / 0x100000, - info.memory.memoryHeaps[heap].size, strbuf.str().c_str()); - strbuf.str(std::string()); - - for (uint32_t type = 0; type < info.memory.memoryTypeCount; type++) { - if (info.memory.memoryTypes[type].heapIndex != heap) - continue; - VkMemoryPropertyFlags flags = - info.memory.memoryTypes[type].propertyFlags; - if ((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) - strbuf << " DEVICE_LOCAL"; - if ((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) - strbuf << " HOST_VISIBLE"; - if ((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0) - strbuf << " COHERENT"; - if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0) - strbuf << " CACHED"; - if ((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0) - strbuf << " LAZILY_ALLOCATED"; - printf("%sType %u:%s\n", Indent(indent + 2), type, - strbuf.str().c_str()); - strbuf.str(std::string()); - } - } - - for (uint32_t family = 0; family < info.queue_families.size(); family++) { - const VkQueueFamilyProperties& qprops = info.queue_families[family]; - VkQueueFlags flags = qprops.queueFlags; - char flags_str[5]; - flags_str[0] = (flags & VK_QUEUE_GRAPHICS_BIT) ? 'G' : '_'; - flags_str[1] = (flags & VK_QUEUE_COMPUTE_BIT) ? 'C' : '_'; - flags_str[2] = (flags & VK_QUEUE_TRANSFER_BIT) ? 'T' : '_'; - flags_str[3] = (flags & VK_QUEUE_SPARSE_BINDING_BIT) ? 'S' : '_'; - flags_str[4] = '\0'; - printf( - "%sQueue Family %u: %ux %s\n" - "%stimestampValidBits: %ub\n" - "%sminImageTransferGranularity: (%u,%u,%u)\n", - Indent(indent + 1), family, qprops.queueCount, flags_str, - Indent(indent + 2), qprops.timestampValidBits, Indent(indent + 2), - qprops.minImageTransferGranularity.width, - qprops.minImageTransferGranularity.height, - qprops.minImageTransferGranularity.depth); - } - - printf("%sFeatures:\n", Indent(indent + 1)); - if (options.unsupported_features) { - PrintAllFeatures(Indent(indent + 2), info.features); - } else { - PrintSupportedFeatures(Indent(indent + 2), info.features); - } - - printf("%sExtensions [%zu]:\n", Indent(indent + 1), info.extensions.size()); - if (!info.extensions.empty()) - PrintExtensions(info.extensions, options, indent + 2); - printf("%sLayers [%zu]:\n", Indent(indent + 1), info.layers.size()); - if (!info.layers.empty()) - PrintLayers(info.layers, info.layer_extensions, options, indent + 2); -} - -void PrintInfo(const VulkanInfo& info, const Options& options) { - std::ostringstream strbuf; - size_t indent = 0; - - printf("%sInstance Extensions [%zu]:\n", Indent(indent), - info.extensions.size()); - PrintExtensions(info.extensions, options, indent + 1); - printf("%sInstance Layers [%zu]:\n", Indent(indent), info.layers.size()); - if (!info.layers.empty()) - PrintLayers(info.layers, info.layer_extensions, options, indent + 1); - - printf("%sPhysicalDevices [%zu]:\n", Indent(indent), info.gpus.size()); - for (const auto& gpu : info.gpus) - PrintGpuInfo(gpu, options, indent + 1); -} - -const char kUsageString[] = - "usage: vkinfo [options]\n" - " -v enable all the following verbose options\n" - " -layer_description print layer description strings\n" - " -layer_extensions print extensions supported by each layer\n" - " -unsupported_features print all physical device features\n" - " -validate enable validation layers if present\n" - " -debug_pause pause at start until resumed via debugger\n"; - -} // namespace - -// ---------------------------------------------------------------------------- - -int main(int argc, char const* argv[]) { - static volatile bool startup_pause = false; - Options options = { - .layer_description = false, .layer_extensions = false, - .unsupported_features = false, - .validate = false, - }; - for (int argi = 1; argi < argc; argi++) { - if (strcmp(argv[argi], "-h") == 0) { - fputs(kUsageString, stdout); - return 0; - } - if (strcmp(argv[argi], "-v") == 0) { - options.layer_description = true; - options.layer_extensions = true; - options.unsupported_features = true; - } else if (strcmp(argv[argi], "-layer_description") == 0) { - options.layer_description = true; - } else if (strcmp(argv[argi], "-layer_extensions") == 0) { - options.layer_extensions = true; - } else if (strcmp(argv[argi], "-unsupported_features") == 0) { - options.unsupported_features = true; - } else if (strcmp(argv[argi], "-validate") == 0) { - options.validate = true; - } else if (strcmp(argv[argi], "-debug_pause") == 0) { - startup_pause = true; - } - } - - while (startup_pause) { - sleep(0); - } - - VulkanInfo info; - GatherInfo(&info, options); - PrintInfo(info, options); - return 0; -} diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc index 3da4336069..8f714d89d8 100644 --- a/vulkan/vkjson/vkjson.cc +++ b/vulkan/vkjson/vkjson.cc @@ -21,11 +21,14 @@ #include "vkjson.h" #include <assert.h> -#include <string.h> #include <stdlib.h> +#include <string.h> -#include <cmath> +#include <json/json.h> + +#include <algorithm> #include <cinttypes> +#include <cmath> #include <cstdio> #include <limits> #include <memory> @@ -33,8 +36,6 @@ #include <type_traits> #include <utility> -#include <json/json.h> - namespace { inline bool IsIntegral(double value) { @@ -46,6 +47,14 @@ inline bool IsIntegral(double value) { #endif } +// Floating point fields of Vulkan structure use single precision. The string +// output of max double value in c++ will be larger than Java double's infinity +// value. Below fake double max/min values are only to serve the safe json text +// parsing in between C++ and Java, becasue Java json library simply cannot +// handle infinity. +static const double SAFE_DOUBLE_MAX = 0.99 * std::numeric_limits<double>::max(); +static const double SAFE_DOUBLE_MIN = -SAFE_DOUBLE_MAX; + template <typename T> struct EnumTraits; template <> struct EnumTraits<VkPhysicalDeviceType> { static uint32_t min() { return VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE; } @@ -851,7 +860,8 @@ Json::Value ToJsonValue(const T& value); template <typename T, typename = EnableForArithmetic<T>> inline Json::Value ToJsonValue(const T& value) { - return Json::Value(static_cast<double>(value)); + return Json::Value( + std::clamp(static_cast<double>(value), SAFE_DOUBLE_MIN, SAFE_DOUBLE_MAX)); } inline Json::Value ToJsonValue(const uint64_t& value) { |