Merge "Send max duration when bugreport is finished so Shell can update metrics." into nyc-dev
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index b553390..0471fc0 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -598,7 +598,6 @@
     dump_file("BUDDYINFO", "/proc/buddyinfo");
     dump_file("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
 
-    dump_file("KERNEL WAKELOCKS", "/proc/wakelocks");
     dump_file("KERNEL WAKE SOURCES", "/d/wakeup_sources");
     dump_file("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
     dump_file("KERNEL SYNC", "/d/sync");
diff --git a/include/binder/IProcessInfoService.h b/include/binder/IProcessInfoService.h
index 34ce0f0..69dc9a7 100644
--- a/include/binder/IProcessInfoService.h
+++ b/include/binder/IProcessInfoService.h
@@ -44,16 +44,6 @@
 
 // ----------------------------------------------------------------------
 
-class BnProcessInfoService : public BnInterface<IProcessInfoService> {
-public:
-    virtual status_t    onTransact( uint32_t code,
-                                    const Parcel& data,
-                                    Parcel* reply,
-                                    uint32_t flags = 0);
-};
-
-// ----------------------------------------------------------------------
-
 }; // namespace android
 
 #endif // ANDROID_I_PROCESS_INFO_SERVICE_H
diff --git a/include/gui/ConsumerBase.h b/include/gui/ConsumerBase.h
index 9307a26..1b63552 100644
--- a/include/gui/ConsumerBase.h
+++ b/include/gui/ConsumerBase.h
@@ -26,6 +26,8 @@
 #include <utils/threads.h>
 #include <gui/IConsumerListener.h>
 
+#include <queue>
+
 namespace android {
 // ----------------------------------------------------------------------------
 
@@ -108,18 +110,18 @@
     // from the derived class.
     virtual void onLastStrongRef(const void* id);
 
-    // Implementation of the IConsumerListener interface.  These
-    // calls are used to notify the ConsumerBase of asynchronous events in the
-    // BufferQueue.  The onFrameAvailable, onFrameReplaced, and
-    // onBuffersReleased methods should not need to be overridden by derived
-    // classes, but if they are overridden the ConsumerBase implementation must
-    // be called from the derived class. The ConsumerBase version of
-    // onSidebandStreamChanged does nothing and can be overriden by derived
-    // classes if they want the notification.
-    virtual void onFrameAvailable(const BufferItem& item) override;
-    virtual void onFrameReplaced(const BufferItem& item) override;
-    virtual void onBuffersReleased() override;
-    virtual void onSidebandStreamChanged() override;
+    // Handlers for the IConsumerListener interface, these will be called from
+    // the message queue thread. These calls are used to notify the ConsumerBase
+    // of asynchronous events in the BufferQueue.  The onFrameAvailableHandler,
+    // onFrameReplacedHandler, and onBuffersReleasedHandler methods should not
+    // need to be overridden by derived classes, but if they are overridden the
+    // ConsumerBase implementation must be called from the derived class. The
+    // ConsumerBase version of onSidebandStreamChangedHandler does nothing and
+    // can be overriden by derived classes if they want the notification.
+    virtual void onFrameAvailableHandler(const BufferItem& item);
+    virtual void onFrameReplacedHandler(const BufferItem& item);
+    virtual void onBuffersReleasedHandler();
+    virtual void onSidebandStreamChangedHandler();
 
     // freeBufferLocked frees up the given buffer slot.  If the slot has been
     // initialized this will release the reference to the GraphicBuffer in that
@@ -244,6 +246,35 @@
     //
     // This mutex is intended to be locked by derived classes.
     mutable Mutex mMutex;
+
+    // Implements the ConsumerListener interface
+    virtual void onFrameAvailable(const BufferItem& item) override;
+    virtual void onFrameReplaced(const BufferItem& item) override;
+    virtual void onBuffersReleased() override;
+    virtual void onSidebandStreamChanged() override;
+
+    enum MessageType {
+        ON_FRAME_AVAILABLE,
+        ON_FRAME_REPLACED,
+        ON_BUFFERS_RELEASED,
+        ON_SIDEBAND_STREAM_CHANGED,
+        EXIT,
+    };
+
+    mutable Mutex mMessageQueueLock;
+    Condition mMessageAvailable;
+    std::queue<std::pair<MessageType, BufferItem>> mMessageQueue;
+
+    class MessageThread : public Thread {
+    public:
+        MessageThread(ConsumerBase* consumerBase) :
+            mConsumerBase(consumerBase) {};
+    protected:
+        virtual bool threadLoop() override;
+        ConsumerBase* mConsumerBase;
+    };
+
+    sp<MessageThread> mMessageThread;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/binder/IProcessInfoService.cpp b/libs/binder/IProcessInfoService.cpp
index c37920d..76508b8 100644
--- a/libs/binder/IProcessInfoService.cpp
+++ b/libs/binder/IProcessInfoService.cpp
@@ -88,72 +88,4 @@
 
 // ----------------------------------------------------------------------
 
-status_t BnProcessInfoService::onTransact( uint32_t code, const Parcel& data, Parcel* reply,
-        uint32_t flags) {
-    switch(code) {
-        case GET_PROCESS_STATES_FROM_PIDS: {
-            CHECK_INTERFACE(IProcessInfoService, data, reply);
-            int32_t arrayLen = data.readInt32();
-            if (arrayLen <= 0) {
-                reply->writeNoException();
-                reply->writeInt32(0);
-                reply->writeInt32(NOT_ENOUGH_DATA);
-                return NO_ERROR;
-            }
-
-            size_t len = static_cast<size_t>(arrayLen);
-            int32_t pids[len];
-            status_t res = data.read(pids, len * sizeof(*pids));
-
-            // Ignore output array length returned in the parcel here, as the states array must
-            // always be the same length as the input PIDs array.
-            int32_t states[len];
-            for (size_t i = 0; i < len; i++) states[i] = -1;
-            if (res == NO_ERROR) {
-                res = getProcessStatesFromPids(len, /*in*/ pids, /*out*/ states);
-            }
-            reply->writeNoException();
-            reply->writeInt32Array(len, states);
-            reply->writeInt32(res);
-            return NO_ERROR;
-        } break;
-        case GET_PROCESS_STATES_AND_OOM_SCORES_FROM_PIDS: {
-            CHECK_INTERFACE(IProcessInfoService, data, reply);
-            int32_t arrayLen = data.readInt32();
-            if (arrayLen <= 0) {
-                reply->writeNoException();
-                reply->writeInt32(0);
-                reply->writeInt32(NOT_ENOUGH_DATA);
-                return NO_ERROR;
-            }
-
-            size_t len = static_cast<size_t>(arrayLen);
-            int32_t pids[len];
-            status_t res = data.read(pids, len * sizeof(*pids));
-
-            // Ignore output array length returned in the parcel here, as the
-            // states array must always be the same length as the input PIDs array.
-            int32_t states[len];
-            int32_t scores[len];
-            for (size_t i = 0; i < len; i++) {
-                states[i] = -1;
-                scores[i] = -10000;
-            }
-            if (res == NO_ERROR) {
-                res = getProcessStatesAndOomScoresFromPids(
-                        len, /*in*/ pids, /*out*/ states, /*out*/ scores);
-            }
-            reply->writeNoException();
-            reply->writeInt32Array(len, states);
-            reply->writeInt32Array(len, scores);
-            reply->writeInt32(res);
-            return NO_ERROR;
-        } break;
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
-// ----------------------------------------------------------------------
-
 }; // namespace android
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 8466aaa..4029496 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -724,6 +724,7 @@
             "android.permission.DUMP"), pid, uid)) {
         result.appendFormat("Permission Denial: can't dump BufferQueueConsumer "
                 "from pid=%d, uid=%d\n", pid, uid);
+        android_errorWriteWithInfoLog(0x534e4554, "27046057", uid, NULL, 0);
     } else {
         mCore->dump(result, prefix);
     }
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index d01187f..a22b81b 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -74,12 +74,26 @@
     } else {
         mConsumer->setConsumerName(mName);
     }
+
+    mMessageThread = new MessageThread(this);
+    mMessageThread->run();
 }
 
 ConsumerBase::~ConsumerBase() {
     CB_LOGV("~ConsumerBase");
-    Mutex::Autolock lock(mMutex);
 
+    mMessageThread->requestExit();
+    {
+        Mutex::Autolock lock(mMessageQueueLock);
+        mMessageQueue.emplace(std::piecewise_construct,
+                std::forward_as_tuple(EXIT),
+                std::forward_as_tuple());
+        mMessageAvailable.signal();
+    }
+
+    mMessageThread->join();
+
+    Mutex::Autolock lock(mMutex);
     // Verify that abandon() has been called before we get here.  This should
     // be done by ConsumerBase::onLastStrongRef(), but it's possible for a
     // derived class to override that method and not call
@@ -100,6 +114,13 @@
 }
 
 void ConsumerBase::onFrameAvailable(const BufferItem& item) {
+    Mutex::Autolock lock(mMessageQueueLock);
+    mMessageQueue.emplace(std::piecewise_construct,
+            std::forward_as_tuple(ON_FRAME_AVAILABLE),
+            std::forward_as_tuple(item));
+    mMessageAvailable.signal();
+}
+void ConsumerBase::onFrameAvailableHandler(const BufferItem& item) {
     CB_LOGV("onFrameAvailable");
 
     sp<FrameAvailableListener> listener;
@@ -115,6 +136,14 @@
 }
 
 void ConsumerBase::onFrameReplaced(const BufferItem &item) {
+    Mutex::Autolock lock(mMessageQueueLock);
+    mMessageQueue.emplace(std::piecewise_construct,
+            std::forward_as_tuple(ON_FRAME_REPLACED),
+            std::forward_as_tuple(item));
+    mMessageAvailable.signal();
+}
+
+void ConsumerBase::onFrameReplacedHandler(const BufferItem &item) {
     CB_LOGV("onFrameReplaced");
 
     sp<FrameAvailableListener> listener;
@@ -130,6 +159,14 @@
 }
 
 void ConsumerBase::onBuffersReleased() {
+    Mutex::Autolock lock(mMessageQueueLock);
+    mMessageQueue.emplace(std::piecewise_construct,
+            std::forward_as_tuple(ON_BUFFERS_RELEASED),
+            std::forward_as_tuple());
+    mMessageAvailable.signal();
+}
+
+void ConsumerBase::onBuffersReleasedHandler() {
     Mutex::Autolock lock(mMutex);
 
     CB_LOGV("onBuffersReleased");
@@ -149,6 +186,45 @@
 }
 
 void ConsumerBase::onSidebandStreamChanged() {
+    Mutex::Autolock lock(mMessageQueueLock);
+    mMessageQueue.emplace(std::piecewise_construct,
+            std::forward_as_tuple(ON_SIDEBAND_STREAM_CHANGED),
+            std::forward_as_tuple());
+    mMessageAvailable.signal();
+}
+
+void ConsumerBase::onSidebandStreamChangedHandler() {
+}
+
+bool ConsumerBase::MessageThread::threadLoop() {
+    Mutex::Autolock lock(mConsumerBase->mMessageQueueLock);
+
+    if (mConsumerBase->mMessageQueue.empty()) {
+        mConsumerBase->mMessageAvailable.wait(mConsumerBase->mMessageQueueLock);
+    }
+
+    while (!mConsumerBase->mMessageQueue.empty()) {
+        auto nextMessage = mConsumerBase->mMessageQueue.front();
+
+        switch (nextMessage.first) {
+            case ON_FRAME_AVAILABLE:
+                mConsumerBase->onFrameAvailableHandler(nextMessage.second);
+                break;
+            case ON_FRAME_REPLACED:
+                mConsumerBase->onFrameReplacedHandler(nextMessage.second);
+                break;
+            case ON_BUFFERS_RELEASED:
+                mConsumerBase->onBuffersReleasedHandler();
+                break;
+            case ON_SIDEBAND_STREAM_CHANGED:
+                mConsumerBase->onSidebandStreamChangedHandler();
+                break;
+            case EXIT:
+                break;
+        }
+        mConsumerBase->mMessageQueue.pop();
+    }
+    return true;
 }
 
 void ConsumerBase::abandon() {
diff --git a/libs/gui/tests/SurfaceTextureGLToGL_test.cpp b/libs/gui/tests/SurfaceTextureGLToGL_test.cpp
index c28b4d1..b8a7a90 100644
--- a/libs/gui/tests/SurfaceTextureGLToGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureGLToGL_test.cpp
@@ -192,6 +192,10 @@
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
     mProducerEglSurface = EGL_NO_SURFACE;
 
+    // sleep for 10ms to allow any asynchronous operations to complete before
+    // checking the reference counts
+    usleep(10000);
+
     // This test should have the only reference to buffer 0.
     EXPECT_EQ(1, buffers[0]->getStrongCount());
 
diff --git a/services/sensorservice/Android.mk b/services/sensorservice/Android.mk
index d84ce42..ef17630 100644
--- a/services/sensorservice/Android.mk
+++ b/services/sensorservice/Android.mk
@@ -20,6 +20,8 @@
 
 LOCAL_CFLAGS:= -DLOG_TAG=\"SensorService\"
 
+LOCAL_CFLAGS += -Wall -Werror
+
 LOCAL_CFLAGS += -fvisibility=hidden
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/services/sensorservice/Fusion.cpp b/services/sensorservice/Fusion.cpp
index 359d289..e92b23d 100644
--- a/services/sensorservice/Fusion.cpp
+++ b/services/sensorservice/Fusion.cpp
@@ -72,8 +72,6 @@
  */
 static const float NOMINAL_GRAVITY = 9.81f;
 static const float FREE_FALL_THRESHOLD = 0.1f * (NOMINAL_GRAVITY);
-static const float FREE_FALL_THRESHOLD_SQ =
-        FREE_FALL_THRESHOLD*FREE_FALL_THRESHOLD;
 
 /*
  * The geomagnetic-field should be between 30uT and 60uT.
@@ -104,7 +102,6 @@
 static const float MIN_VALID_CROSS_PRODUCT_MAG_SQ =
     MIN_VALID_CROSS_PRODUCT_MAG*MIN_VALID_CROSS_PRODUCT_MAG;
 
-static const float W_EPS = 1e-4f;
 static const float SQRT_3 = 1.732f;
 static const float WVEC_EPS = 1e-4f/SQRT_3;
 // -----------------------------------------------------------------------
diff --git a/services/sensorservice/MostRecentEventLogger.cpp b/services/sensorservice/MostRecentEventLogger.cpp
index 0bd0e17..a5d8456 100644
--- a/services/sensorservice/MostRecentEventLogger.cpp
+++ b/services/sensorservice/MostRecentEventLogger.cpp
@@ -16,10 +16,12 @@
 
 #include "MostRecentEventLogger.h"
 
+#include <inttypes.h>
+
 namespace android {
 
 SensorService::MostRecentEventLogger::MostRecentEventLogger(int sensorType) :
-        mSensorType(sensorType), mNextInd(0) {
+        mNextInd(0), mSensorType(sensorType) {
 
     mBufSize = (sensorType == SENSOR_TYPE_STEP_COUNTER ||
                 sensorType == SENSOR_TYPE_SIGNIFICANT_MOTION ||
@@ -61,13 +63,13 @@
         }
         result.appendFormat("%d) ", eventNum++);
         if (mSensorType == SENSOR_TYPE_STEP_COUNTER) {
-            result.appendFormat("%llu,", mTrimmedSensorEventArr[i]->mStepCounter);
+            result.appendFormat("%" PRIu64 ",", mTrimmedSensorEventArr[i]->mStepCounter);
         } else {
             for (int j = 0; j < numData; ++j) {
                 result.appendFormat("%5.1f,", mTrimmedSensorEventArr[i]->mData[j]);
             }
         }
-        result.appendFormat("%lld %02d:%02d:%02d ", mTrimmedSensorEventArr[i]->mTimestamp,
+        result.appendFormat("%" PRId64 " %02d:%02d:%02d ", mTrimmedSensorEventArr[i]->mTimestamp,
                 mTrimmedSensorEventArr[i]->mHour, mTrimmedSensorEventArr[i]->mMin,
                 mTrimmedSensorEventArr[i]->mSec);
         i = (i + 1) % mBufSize;
diff --git a/vulkan/tools/vkinfo.cpp b/vulkan/tools/vkinfo.cpp
index 3dc6653..e650a27 100644
--- a/vulkan/tools/vkinfo.cpp
+++ b/vulkan/tools/vkinfo.cpp
@@ -32,6 +32,7 @@
 struct Options {
     bool layer_description;
     bool layer_extensions;
+    bool unsupported_features;
     bool validate;
 };
 
@@ -361,6 +362,128 @@
     }
 }
 
+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;
@@ -425,64 +548,12 @@
             qprops.minImageTransferGranularity.depth);
     }
 
-    // clang-format off
     printf("%sFeatures:\n", Indent(indent + 1));
-    printf("%srobustBufferAccess: %s\n", Indent(indent + 2), info.features.robustBufferAccess ? "YES" : "NO");
-    printf("%sfullDrawIndexUint32: %s\n", Indent(indent + 2), info.features.fullDrawIndexUint32 ? "YES" : "NO");
-    printf("%simageCubeArray: %s\n", Indent(indent + 2), info.features.imageCubeArray ? "YES" : "NO");
-    printf("%sindependentBlend: %s\n", Indent(indent + 2), info.features.independentBlend ? "YES" : "NO");
-    printf("%sgeometryShader: %s\n", Indent(indent + 2), info.features.geometryShader ? "YES" : "NO");
-    printf("%stessellationShader: %s\n", Indent(indent + 2), info.features.tessellationShader ? "YES" : "NO");
-    printf("%ssampleRateShading: %s\n", Indent(indent + 2), info.features.sampleRateShading ? "YES" : "NO");
-    printf("%sdualSrcBlend: %s\n", Indent(indent + 2), info.features.dualSrcBlend ? "YES" : "NO");
-    printf("%slogicOp: %s\n", Indent(indent + 2), info.features.logicOp ? "YES" : "NO");
-    printf("%smultiDrawIndirect: %s\n", Indent(indent + 2), info.features.multiDrawIndirect ? "YES" : "NO");
-    printf("%sdrawIndirectFirstInstance: %s\n", Indent(indent + 2), info.features.drawIndirectFirstInstance ? "YES" : "NO");
-    printf("%sdepthClamp: %s\n", Indent(indent + 2), info.features.depthClamp ? "YES" : "NO");
-    printf("%sdepthBiasClamp: %s\n", Indent(indent + 2), info.features.depthBiasClamp ? "YES" : "NO");
-    printf("%sfillModeNonSolid: %s\n", Indent(indent + 2), info.features.fillModeNonSolid ? "YES" : "NO");
-    printf("%sdepthBounds: %s\n", Indent(indent + 2), info.features.depthBounds ? "YES" : "NO");
-    printf("%swideLines: %s\n", Indent(indent + 2), info.features.wideLines ? "YES" : "NO");
-    printf("%slargePoints: %s\n", Indent(indent + 2), info.features.largePoints ? "YES" : "NO");
-    printf("%salphaToOne: %s\n", Indent(indent + 2), info.features.alphaToOne ? "YES" : "NO");
-    printf("%smultiViewport: %s\n", Indent(indent + 2), info.features.multiViewport ? "YES" : "NO");
-    printf("%ssamplerAnisotropy: %s\n", Indent(indent + 2), info.features.samplerAnisotropy ? "YES" : "NO");
-    printf("%stextureCompressionETC2: %s\n", Indent(indent + 2), info.features.textureCompressionETC2 ? "YES" : "NO");
-    printf("%stextureCompressionASTC_LDR: %s\n", Indent(indent + 2), info.features.textureCompressionASTC_LDR ? "YES" : "NO");
-    printf("%stextureCompressionBC: %s\n", Indent(indent + 2), info.features.textureCompressionBC ? "YES" : "NO");
-    printf("%socclusionQueryPrecise: %s\n", Indent(indent + 2), info.features.occlusionQueryPrecise ? "YES" : "NO");
-    printf("%spipelineStatisticsQuery: %s\n", Indent(indent + 2), info.features.pipelineStatisticsQuery ? "YES" : "NO");
-    printf("%svertexPipelineStoresAndAtomics: %s\n", Indent(indent + 2), info.features.vertexPipelineStoresAndAtomics ? "YES" : "NO");
-    printf("%sfragmentStoresAndAtomics: %s\n", Indent(indent + 2), info.features.fragmentStoresAndAtomics ? "YES" : "NO");
-    printf("%sshaderTessellationAndGeometryPointSize: %s\n", Indent(indent + 2), info.features.shaderTessellationAndGeometryPointSize ? "YES" : "NO");
-    printf("%sshaderImageGatherExtended: %s\n", Indent(indent + 2), info.features.shaderImageGatherExtended ? "YES" : "NO");
-    printf("%sshaderStorageImageExtendedFormats: %s\n", Indent(indent + 2), info.features.shaderStorageImageExtendedFormats ? "YES" : "NO");
-    printf("%sshaderStorageImageMultisample: %s\n", Indent(indent + 2), info.features.shaderStorageImageMultisample ? "YES" : "NO");
-    printf("%sshaderStorageImageReadWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageReadWithoutFormat ? "YES" : "NO");
-    printf("%sshaderStorageImageWriteWithoutFormat: %s\n", Indent(indent + 2), info.features.shaderStorageImageWriteWithoutFormat ? "YES" : "NO");
-    printf("%sshaderUniformBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderUniformBufferArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderSampledImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderSampledImageArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderStorageBufferArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageBufferArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderStorageImageArrayDynamicIndexing: %s\n", Indent(indent + 2), info.features.shaderStorageImageArrayDynamicIndexing ? "YES" : "NO");
-    printf("%sshaderClipDistance: %s\n", Indent(indent + 2), info.features.shaderClipDistance ? "YES" : "NO");
-    printf("%sshaderCullDistance: %s\n", Indent(indent + 2), info.features.shaderCullDistance ? "YES" : "NO");
-    printf("%sshaderFloat64: %s\n", Indent(indent + 2), info.features.shaderFloat64 ? "YES" : "NO");
-    printf("%sshaderInt64: %s\n", Indent(indent + 2), info.features.shaderInt64 ? "YES" : "NO");
-    printf("%sshaderInt16: %s\n", Indent(indent + 2), info.features.shaderInt16 ? "YES" : "NO");
-    printf("%sshaderResourceResidency: %s\n", Indent(indent + 2), info.features.shaderResourceResidency ? "YES" : "NO");
-    printf("%sshaderResourceMinLod: %s\n", Indent(indent + 2), info.features.shaderResourceMinLod ? "YES" : "NO");
-    printf("%ssparseBinding: %s\n", Indent(indent + 2), info.features.sparseBinding ? "YES" : "NO");
-    printf("%ssparseResidencyBuffer: %s\n", Indent(indent + 2), info.features.sparseResidencyBuffer ? "YES" : "NO");
-    printf("%ssparseResidencyImage2D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage2D ? "YES" : "NO");
-    printf("%ssparseResidencyImage3D: %s\n", Indent(indent + 2), info.features.sparseResidencyImage3D ? "YES" : "NO");
-    printf("%ssparseResidency2Samples: %s\n", Indent(indent + 2), info.features.sparseResidency2Samples ? "YES" : "NO");
-    printf("%ssparseResidency4Samples: %s\n", Indent(indent + 2), info.features.sparseResidency4Samples ? "YES" : "NO");
-    printf("%ssparseResidency8Samples: %s\n", Indent(indent + 2), info.features.sparseResidency8Samples ? "YES" : "NO");
-    printf("%ssparseResidency16Samples: %s\n", Indent(indent + 2), info.features.sparseResidency16Samples ? "YES" : "NO");
-    printf("%ssparseResidencyAliased: %s\n", Indent(indent + 2), info.features.sparseResidencyAliased ? "YES" : "NO");
-    printf("%svariableMultisampleRate: %s\n", Indent(indent + 2), info.features.variableMultisampleRate ? "YES" : "NO");
-    printf("%sinheritedQueries: %s\n", Indent(indent + 2), info.features.inheritedQueries ? "YES" : "NO");
-    // clang-format on
+    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())
@@ -508,6 +579,15 @@
         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
 
 // ----------------------------------------------------------------------------
@@ -516,16 +596,24 @@
     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) {