layers: Initial framework for DeviceLimits layer

This includes many initial entrypoints and setting up function intercepts with only a few actual checks to begin. DeviceLimits layer is intended to capture two broad categories of errors:
1. Incorrect use of APIs to query device capabilities
2. Attempt to use API functionality beyond the capability of the underlying device

DeviceLimits stores its own internal record of underlying device capabilities and flag errors if requests are made beyond those limits.

Initial checks and documentation include verification of EnumeratePhysicalDevices call and some basic queue create/get validation.
diff --git a/layers/CMakeLists.txt b/layers/CMakeLists.txt
index f82c312..500f2dc 100644
--- a/layers/CMakeLists.txt
+++ b/layers/CMakeLists.txt
@@ -27,6 +27,7 @@
     screenshot
     shader_checker
     threading
+    device_limits
     )
 
 set(VK_LAYER_RPATH /usr/lib/x86_64-linux-gnu/vulkan/layer:/usr/lib/i386-linux-gnu/vulkan/layer)
@@ -139,6 +140,7 @@
 add_vk_layer(Basic basic.cpp vk_layer_table.cpp)
 add_vk_layer(Multi multi.cpp vk_layer_table.cpp)
 add_vk_layer(DrawState draw_state.cpp vk_layer_debug_marker_table.cpp vk_layer_table.cpp)
+add_vk_layer(DeviceLimits device_limits.cpp vk_layer_debug_marker_table.cpp vk_layer_table.cpp)
 add_vk_layer(MemTracker mem_tracker.cpp vk_layer_table.cpp)
 add_vk_layer(ShaderChecker shader_checker.cpp vk_layer_table.cpp)
 add_vk_layer(Image image.cpp vk_layer_table.cpp)
diff --git a/layers/device_limits.cpp b/layers/device_limits.cpp
new file mode 100644
index 0000000..f2e2878
--- /dev/null
+++ b/layers/device_limits.cpp
@@ -0,0 +1,718 @@
+/*
+ * Vulkan
+ *
+ * Copyright (C) 2015 LunarG, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is 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 Software.
+ *
+ * THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unordered_map>
+#include <memory>
+
+#include "vk_loader_platform.h"
+#include "vk_dispatch_table_helper.h"
+#include "vk_struct_string_helper_cpp.h"
+#if defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wwrite-strings"
+#endif
+#if defined(__GNUC__)
+#pragma GCC diagnostic warning "-Wwrite-strings"
+#endif
+#include "vk_struct_size_helper.h"
+#include "device_limits.h"
+#include "vk_layer_config.h"
+#include "vk_debug_marker_layer.h"
+// The following is #included again to catch certain OS-specific functions
+// being used:
+#include "vk_loader_platform.h"
+#include "vk_layer_msg.h"
+#include "vk_layer_table.h"
+#include "vk_layer_debug_marker_table.h"
+#include "vk_layer_data.h"
+#include "vk_layer_logging.h"
+#include "vk_layer_extension_utils.h"
+
+typedef struct _layer_data {
+    debug_report_data *report_data;
+    // TODO: put instance data here
+    VkDbgMsgCallback logging_callback;
+} layer_data;
+
+static std::unordered_map<void *, layer_data *> layer_data_map;
+static device_table_map device_limits_device_table_map;
+static instance_table_map device_limits_instance_table_map;
+
+// Track state of each instance
+unordered_map<VkInstance, unique_ptr<INSTANCE_STATE>> instanceMap;
+unordered_map<VkPhysicalDevice, unique_ptr<PHYSICAL_DEVICE_STATE>> physicalDeviceMap;
+unordered_map<VkPhysicalDevice, unordered_map<uint32_t, unique_ptr<VkQueueFamilyProperties>>> queueFamilyPropertiesMap;
+unordered_map<VkPhysicalDevice, unique_ptr<VkPhysicalDeviceFeatures>> physicalDeviceFeaturesMap;
+unordered_map<VkDevice, VkPhysicalDevice> deviceMap;
+
+struct devExts {
+    bool debug_marker_enabled;
+};
+
+static std::unordered_map<void *, struct devExts> deviceExtMap;
+
+static LOADER_PLATFORM_THREAD_ONCE_DECLARATION(g_initOnce);
+
+// TODO : This can be much smarter, using separate locks for separate global data
+static int globalLockInitialized = 0;
+static loader_platform_thread_mutex globalLock;
+
+template layer_data *get_my_data_ptr<layer_data>(
+        void *data_key,
+        std::unordered_map<void *, layer_data *> &data_map);
+
+debug_report_data *mdd(void* object)
+{
+    dispatch_key key = get_dispatch_key(object);
+    layer_data *my_data = get_my_data_ptr(key, layer_data_map);
+#if DISPATCH_MAP_DEBUG
+    fprintf(stderr, "MDD: map: %p, object: %p, key: %p, data: %p\n", &layer_data_map, object, key, my_data);
+#endif
+    return my_data->report_data;
+}
+
+debug_report_data *mid(VkInstance object)
+{
+    dispatch_key key = get_dispatch_key(object);
+    layer_data *my_data = get_my_data_ptr(key, layer_data_map);
+#if DISPATCH_MAP_DEBUG
+    fprintf(stderr, "MID: map: %p, object: %p, key: %p, data: %p\n", &layer_data_map, object, key, my_data);
+#endif
+    return my_data->report_data;
+}
+
+static void init_device_limits(layer_data *my_data)
+{
+    uint32_t report_flags = 0;
+    uint32_t debug_action = 0;
+    FILE *log_output = NULL;
+    const char *option_str;
+    // initialize DeviceLimits options
+    report_flags = getLayerOptionFlags("DeviceLimitsReportFlags", 0);
+    getLayerOptionEnum("DeviceLimitsDebugAction", (uint32_t *) &debug_action);
+
+    if (debug_action & VK_DBG_LAYER_ACTION_LOG_MSG)
+    {
+        option_str = getLayerOption("DeviceLimitsLogFilename");
+        if (option_str)
+        {
+            log_output = fopen(option_str, "w");
+        }
+        if (log_output == NULL)
+            log_output = stdout;
+
+        layer_create_msg_callback(my_data->report_data, report_flags, log_callback, (void *) log_output, &my_data->logging_callback);
+    }
+
+    if (!globalLockInitialized)
+    {
+        // TODO/TBD: Need to delete this mutex sometime.  How???  One
+        // suggestion is to call this during vkCreateInstance(), and then we
+        // can clean it up during vkDestroyInstance().  However, that requires
+        // that the layer have per-instance locks.  We need to come back and
+        // address this soon.
+        loader_platform_thread_create_mutex(&globalLock);
+        globalLockInitialized = 1;
+    }
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, VkInstance* pInstance)
+{
+    VkLayerInstanceDispatchTable *pTable = get_dispatch_table(device_limits_instance_table_map,*pInstance);
+    VkResult result = pTable->CreateInstance(pCreateInfo, pInstance);
+
+    if (result == VK_SUCCESS) {
+        layer_data *my_data = get_my_data_ptr(get_dispatch_key(*pInstance), layer_data_map);
+        my_data->report_data = debug_report_create_instance(
+                                   pTable,
+                                   *pInstance,
+                                   pCreateInfo->extensionCount,
+                                   pCreateInfo->ppEnabledExtensionNames);
+
+        init_device_limits(my_data);
+        instanceMap[*pInstance] = unique_ptr<INSTANCE_STATE>(new INSTANCE_STATE());
+    }
+    return result;
+}
+
+/* hook DestroyInstance to remove tableInstanceMap entry */
+VK_LAYER_EXPORT VkResult VKAPI vkDestroyInstance(VkInstance instance)
+{
+    dispatch_key key = get_dispatch_key(instance);
+    VkLayerInstanceDispatchTable *pTable = get_dispatch_table(device_limits_instance_table_map, instance);
+    VkResult res = pTable->DestroyInstance(instance);
+
+    // Clean up logging callback, if any
+    layer_data *my_data = get_my_data_ptr(key, layer_data_map);
+    if (my_data->logging_callback) {
+        layer_destroy_msg_callback(my_data->report_data, my_data->logging_callback);
+    }
+
+    layer_debug_report_destroy_instance(my_data->report_data);
+    layer_data_map.erase(pTable);
+    instanceMap.erase(instance);
+    device_limits_instance_table_map.erase(key);
+    return res;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkEnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices)
+{
+    auto it = instanceMap.find(instance);
+    if (it != instanceMap.end()) {
+        // For this instance, flag when vkEnumeratePhysicalDevices goes to QUERY_COUNT and then QUERY_DETAILS
+        if (NULL == pPhysicalDevices) {
+            it->second->vkEnumeratePhysicalDevicesState = QUERY_COUNT;
+        } else {
+            if (UNCALLED == it->second->vkEnumeratePhysicalDevicesState) {
+                // Flag error here, shouldn't be calling this without having queried count
+                log_msg(mid(instance), VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_INSTANCE, 0, 0, DEVLIMITS_MUST_QUERY_COUNT, "DL",
+                    "Invalid call sequence to vkEnumeratePhysicalDevices() w/ non-NULL pPhysicalDevices. You should first call vkEnumeratePhysicalDevices() w/ NULL pPhysicalDevices to query pPhysicalDeviceCount.");
+            } // TODO : Could also flag a warning if re-calling this function in QUERY_DETAILS state
+            else if (it->second->physicalDevicesCount != *pPhysicalDeviceCount) {
+                log_msg(mid(instance), VK_DBG_REPORT_WARN_BIT, VK_OBJECT_TYPE_PHYSICAL_DEVICE, 0, 0, DEVLIMITS_COUNT_MISMATCH, "DL",
+                    "Call to vkEnumeratePhysicalDevices() w/ pPhysicalDeviceCount value %u, but actual count supported by this instance is %u.", *pPhysicalDeviceCount, it->second->physicalDevicesCount);
+            }
+            it->second->vkEnumeratePhysicalDevicesState = QUERY_DETAILS;
+        }
+        VkResult result = get_dispatch_table(device_limits_instance_table_map,instance)->EnumeratePhysicalDevices(instance, pPhysicalDeviceCount, pPhysicalDevices);
+        if (NULL == pPhysicalDevices) {
+            it->second->physicalDevicesCount = *pPhysicalDeviceCount;
+        } else { // Save physical devices
+            for (uint32_t i=0; i < *pPhysicalDeviceCount; i++) {
+                physicalDeviceMap[pPhysicalDevices[i]] = unique_ptr<PHYSICAL_DEVICE_STATE>(new PHYSICAL_DEVICE_STATE());
+            }
+        }
+        return result;
+    } else {
+        log_msg(mid(instance), VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_INSTANCE, 0, 0, DEVLIMITS_INVALID_INSTANCE, "DL",
+            "Invalid instance (%#" PRIxLEAST64 ") passed into vkEnumeratePhysicalDevices().", instance);
+    }
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetPhysicalDeviceFeatures(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures)
+{
+    auto it = physicalDeviceMap.find(physicalDevice);
+    if (it != physicalDeviceMap.end()) {
+        VkResult result = get_dispatch_table(device_limits_instance_table_map, physicalDevice)->GetPhysicalDeviceFeatures(physicalDevice, pFeatures);
+        // Save Features
+        physicalDeviceFeaturesMap[physicalDevice] = unique_ptr<VkPhysicalDeviceFeatures>(new VkPhysicalDeviceFeatures(*pFeatures));
+        return result;
+    }
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetPhysicalDeviceFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties* pFormatProperties)
+{
+    VkResult result = get_dispatch_table(device_limits_instance_table_map, physicalDevice)->GetPhysicalDeviceFormatProperties(physicalDevice, format, pFormatProperties);
+    return result;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetPhysicalDeviceImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageFormatProperties* pImageFormatProperties)
+{
+    VkResult result = get_dispatch_table(device_limits_instance_table_map, physicalDevice)->GetPhysicalDeviceImageFormatProperties(physicalDevice, format, type, tiling, usage, pImageFormatProperties);
+    return result;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties)
+{
+    VkResult result = get_dispatch_table(device_limits_instance_table_map, physicalDevice)->GetPhysicalDeviceProperties(physicalDevice, pProperties);
+    return result;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetPhysicalDeviceQueueFamilyProperties(VkPhysicalDevice physicalDevice, uint32_t* pCount, VkQueueFamilyProperties* pQueueFamilyProperties)
+{
+    auto it = physicalDeviceMap.find(physicalDevice);
+    if (it != physicalDeviceMap.end()) {
+        if (NULL == pQueueFamilyProperties) {
+            it->second->vkGetPhysicalDeviceQueueFamilyPropertiesState = QUERY_COUNT;
+        } else {
+            // Verify that for each physical device, this function is called first with NULL pQueueFamilyProperties ptr in order to get count
+            if (UNCALLED == it->second->vkGetPhysicalDeviceQueueFamilyPropertiesState) {
+                log_msg(mdd(physicalDevice), VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_PHYSICAL_DEVICE, 0, 0, DEVLIMITS_MUST_QUERY_COUNT, "DL",
+                    "Invalid call sequence to vkGetPhysicalDeviceQueueFamilyProperties() w/ non-NULL pQueueFamilyProperties. You should first call vkGetPhysicalDeviceQueueFamilyProperties() w/ NULL pQueueFamilyProperties to query pCount.");
+            }
+            if (it->second->queueFamilyPropertiesCount != *pCount) {
+                log_msg(mdd(physicalDevice), VK_DBG_REPORT_WARN_BIT, VK_OBJECT_TYPE_PHYSICAL_DEVICE, 0, 0, DEVLIMITS_COUNT_MISMATCH, "DL",
+                    "Call to vkGetPhysicalDeviceQueueFamilyProperties() w/ pCount value %u, but actual count supported by this physicalDevice is %u.", *pCount, it->second->queueFamilyPropertiesCount);
+            }
+            it->second->vkGetPhysicalDeviceQueueFamilyPropertiesState = QUERY_DETAILS;
+        }
+        // Then verify that pCount that is passed in on second call matches what was returned
+        VkResult result = get_dispatch_table(device_limits_instance_table_map, physicalDevice)->GetPhysicalDeviceQueueFamilyProperties(physicalDevice, pCount, pQueueFamilyProperties);
+        if (NULL == pQueueFamilyProperties) {
+            it->second->queueFamilyPropertiesCount = *pCount;
+        } else { // Save queue family properties
+            for (uint32_t i=0; i < *pCount; i++) {
+                queueFamilyPropertiesMap[physicalDevice][i] = unique_ptr<VkQueueFamilyProperties>(new VkQueueFamilyProperties(pQueueFamilyProperties[i]));
+            }
+        }
+        return result;
+    } else {
+        log_msg(mdd(physicalDevice), VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_PHYSICAL_DEVICE, 0, 0, DEVLIMITS_INVALID_PHYSICAL_DEVICE, "DL",
+            "Invalid physicalDevice (%#" PRIxLEAST64 ") passed into vkGetPhysicalDeviceQueueFamilyProperties().", physicalDevice);
+    }
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties)
+{
+    VkResult result = get_dispatch_table(device_limits_instance_table_map, physicalDevice)->GetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
+    return result;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetPhysicalDeviceSparseImageFormatProperties(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, uint32_t samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t* pNumProperties, VkSparseImageFormatProperties* pProperties)
+{
+    VkResult result = get_dispatch_table(device_limits_instance_table_map, physicalDevice)->GetPhysicalDeviceSparseImageFormatProperties(physicalDevice, format, type, samples, usage, tiling, pNumProperties, pProperties);
+    return result;
+}
+
+static void createDeviceRegisterExtensions(const VkDeviceCreateInfo* pCreateInfo, VkDevice device)
+{
+    uint32_t i;
+    VkLayerDispatchTable *pDisp =  get_dispatch_table(device_limits_device_table_map, device);
+    deviceExtMap[pDisp].debug_marker_enabled = false;
+
+    for (i = 0; i < pCreateInfo->extensionCount; i++) {
+        if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], DEBUG_MARKER_EXTENSION_NAME) == 0) {
+            /* Found a matching extension name, mark it enabled and init dispatch table*/
+            initDebugMarkerTable(device);
+            deviceExtMap[pDisp].debug_marker_enabled = true;
+        }
+
+    }
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkCreateDevice(VkPhysicalDevice gpu, const VkDeviceCreateInfo* pCreateInfo, VkDevice* pDevice)
+{
+    // Check that the requested queue properties are valid
+    for (uint32_t i=0; i<pCreateInfo->queueRecordCount; i++) {
+        uint32_t requestedIndex = pCreateInfo->pRequestedQueues[i].queueFamilyIndex;
+        auto qfp_it = queueFamilyPropertiesMap[gpu].find(requestedIndex);
+        if (qfp_it == queueFamilyPropertiesMap[gpu].end()) { // requested index is out of bounds for this physical device
+            log_msg(mdd(gpu), VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_PHYSICAL_DEVICE, 0, 0, DEVLIMITS_INVALID_QUEUE_CREATE_REQUEST, "DL",
+                "Invalid queue create request in vkCreateDevice(). Invalid queueFamilyIndex %u requested.", requestedIndex);
+        } else if (pCreateInfo->pRequestedQueues[i].queueCount > qfp_it->second->queueCount) {
+            log_msg(mdd(gpu), VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_PHYSICAL_DEVICE, 0, 0, DEVLIMITS_INVALID_QUEUE_CREATE_REQUEST, "DL",
+                "Invalid queue create request in vkCreateDevice(). QueueFamilyIndex %u only has %u queues, but requested queueCount is %u.", requestedIndex, qfp_it->second->queueCount, pCreateInfo->pRequestedQueues[i].queueCount);
+        }
+    }
+    VkLayerDispatchTable *pDeviceTable = get_dispatch_table(device_limits_device_table_map, *pDevice);
+    VkResult result = pDeviceTable->CreateDevice(gpu, pCreateInfo, pDevice);
+    if (result == VK_SUCCESS) {
+        layer_data *my_instance_data = get_my_data_ptr(get_dispatch_key(gpu), layer_data_map);
+        VkLayerDispatchTable *pTable = get_dispatch_table(device_limits_device_table_map, *pDevice);
+        layer_data *my_device_data = get_my_data_ptr(get_dispatch_key(*pDevice), layer_data_map);
+        my_device_data->report_data = layer_debug_report_create_device(my_instance_data->report_data, *pDevice);
+        createDeviceRegisterExtensions(pCreateInfo, *pDevice);
+        deviceMap[*pDevice] = gpu;
+    }
+    return result;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkDestroyDevice(VkDevice device)
+{
+    // Free device lifetime allocations
+    dispatch_key key = get_dispatch_key(device);
+    VkLayerDispatchTable *pDisp =  get_dispatch_table(device_limits_device_table_map, device);
+    VkResult result = pDisp->DestroyDevice(device);
+    deviceExtMap.erase(pDisp);
+    device_limits_device_table_map.erase(key);
+    tableDebugMarkerMap.erase(pDisp);
+    return result;
+}
+
+static const VkLayerProperties ds_global_layers[] = {
+    {
+        "DeviceLimits",
+        VK_API_VERSION,
+        VK_MAKE_VERSION(0, 1, 0),
+        "Validation layer: DeviceLimits",
+    }
+};
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetGlobalExtensionProperties(
+        const char *pLayerName,
+        uint32_t *pCount,
+        VkExtensionProperties* pProperties)
+{
+    /* DeviceLimits does not have any global extensions */
+    return util_GetExtensionProperties(0, NULL, pCount, pProperties);
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetGlobalLayerProperties(
+        uint32_t *pCount,
+        VkLayerProperties*    pProperties)
+{
+    return util_GetLayerProperties(ARRAY_SIZE(ds_global_layers),
+                                   ds_global_layers,
+                                   pCount, pProperties);
+}
+
+static const VkExtensionProperties ds_device_extensions[] = {
+    {
+        DEBUG_MARKER_EXTENSION_NAME,
+        VK_MAKE_VERSION(0, 1, 0),
+    }
+};
+
+static const VkLayerProperties ds_device_layers[] = {
+    {
+        "DeviceLimits",
+        VK_API_VERSION,
+        VK_MAKE_VERSION(0, 1, 0),
+        "Validation layer: DeviceLimits",
+    }
+};
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetPhysicalDeviceExtensionProperties(
+        VkPhysicalDevice                            physicalDevice,
+        const char*                                 pLayerName,
+        uint32_t*                                   pCount,
+        VkExtensionProperties*                      pProperties)
+{
+    /* Device Limits does not have any physical device extensions */
+    return util_GetExtensionProperties(ARRAY_SIZE(ds_device_extensions), ds_device_extensions,
+                                       pCount, pProperties);
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetPhysicalDeviceLayerProperties(
+        VkPhysicalDevice                            physicalDevice,
+        uint32_t*                                   pCount,
+        VkLayerProperties*                          pProperties)
+{
+    /* Device Limits' physical device layers are the same as global */
+    return util_GetLayerProperties(ARRAY_SIZE(ds_device_layers), ds_device_layers,
+                                   pCount, pProperties);
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetDeviceQueue(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue)
+{
+    VkPhysicalDevice gpu = deviceMap[device];
+    auto qfp_it = queueFamilyPropertiesMap[gpu].find(queueFamilyIndex);
+    if (qfp_it == queueFamilyPropertiesMap[gpu].end()) { // requested index is out of bounds for this physical device
+        log_msg(mdd(gpu), VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_PHYSICAL_DEVICE, 0, 0, DEVLIMITS_INVALID_QUEUE_CREATE_REQUEST, "DL",
+            "Invalid queueFamilyIndex %u requested in vkGetDeviceQueue().", queueFamilyIndex);
+    } else if (queueIndex >= qfp_it->second->queueCount) {
+        log_msg(mdd(gpu), VK_DBG_REPORT_ERROR_BIT, VK_OBJECT_TYPE_PHYSICAL_DEVICE, 0, 0, DEVLIMITS_INVALID_QUEUE_CREATE_REQUEST, "DL",
+            "Invalid queue request in vkGetDeviceQueue(). QueueFamilyIndex %u only has %u queues, but requested queueIndex is %u.", queueFamilyIndex, qfp_it->second->queueCount, queueIndex);
+    }
+    VkResult result = get_dispatch_table(device_limits_device_table_map, device)->GetDeviceQueue(device, queueFamilyIndex, queueIndex, pQueue);
+    return result;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkDbgCreateMsgCallback(
+    VkInstance                          instance,
+    VkFlags                             msgFlags,
+    const PFN_vkDbgMsgCallback          pfnMsgCallback,
+    void*                               pUserData,
+    VkDbgMsgCallback*                   pMsgCallback)
+{
+    VkLayerInstanceDispatchTable *pTable = get_dispatch_table(device_limits_instance_table_map, instance);
+    VkResult res = pTable->DbgCreateMsgCallback(instance, msgFlags, pfnMsgCallback, pUserData, pMsgCallback);
+    if (VK_SUCCESS == res) {
+        layer_data *my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map);
+        res = layer_create_msg_callback(my_data->report_data, msgFlags, pfnMsgCallback, pUserData, pMsgCallback);
+    }
+    return res;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkDbgDestroyMsgCallback(
+    VkInstance                          instance,
+    VkDbgMsgCallback                    msgCallback)
+{
+    VkLayerInstanceDispatchTable *pTable = get_dispatch_table(device_limits_instance_table_map, instance);
+    VkResult res = pTable->DbgDestroyMsgCallback(instance, msgCallback);
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map);
+    layer_destroy_msg_callback(my_data->report_data, msgCallback);
+    return res;
+}
+
+VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI vkGetDeviceProcAddr(VkDevice dev, const char* funcName)
+{
+    if (dev == NULL)
+        return NULL;
+
+    /* loader uses this to force layer initialization; device object is wrapped */
+    if (!strcmp(funcName, "vkGetDeviceProcAddr")) {
+        initDeviceTable(device_limits_device_table_map, (const VkBaseLayerObject *) dev);
+        return (PFN_vkVoidFunction) vkGetDeviceProcAddr;
+    }
+    if (!strcmp(funcName, "vkCreateDevice"))
+        return (PFN_vkVoidFunction) vkCreateDevice;
+    if (!strcmp(funcName, "vkDestroyDevice"))
+        return (PFN_vkVoidFunction) vkDestroyDevice;
+    /*if (!strcmp(funcName, "vkQueueSubmit"))
+        return (PFN_vkVoidFunction) vkQueueSubmit;*/
+    if (!strcmp(funcName, "vkGetDeviceQueue"))
+        return (PFN_vkVoidFunction) vkGetDeviceQueue;
+/*    if (!strcmp(funcName, "vkDestroyFence"))
+        return (PFN_vkVoidFunction) vkDestroyFence;
+    if (!strcmp(funcName, "vkDestroySemaphore"))
+        return (PFN_vkVoidFunction) vkDestroySemaphore;
+    if (!strcmp(funcName, "vkDestroyEvent"))
+        return (PFN_vkVoidFunction) vkDestroyEvent;
+    if (!strcmp(funcName, "vkDestroyQueryPool"))
+        return (PFN_vkVoidFunction) vkDestroyQueryPool;
+    if (!strcmp(funcName, "vkDestroyBuffer"))
+        return (PFN_vkVoidFunction) vkDestroyBuffer;
+    if (!strcmp(funcName, "vkDestroyBufferView"))
+        return (PFN_vkVoidFunction) vkDestroyBufferView;
+    if (!strcmp(funcName, "vkDestroyImage"))
+        return (PFN_vkVoidFunction) vkDestroyImage;
+    if (!strcmp(funcName, "vkDestroyImageView"))
+        return (PFN_vkVoidFunction) vkDestroyImageView;
+    if (!strcmp(funcName, "vkDestroyShaderModule"))
+        return (PFN_vkVoidFunction) vkDestroyShaderModule;
+    if (!strcmp(funcName, "vkDestroyShader"))
+        return (PFN_vkVoidFunction) vkDestroyShader;
+    if (!strcmp(funcName, "vkDestroyPipeline"))
+        return (PFN_vkVoidFunction) vkDestroyPipeline;
+    if (!strcmp(funcName, "vkDestroyPipelineLayout"))
+        return (PFN_vkVoidFunction) vkDestroyPipelineLayout;
+    if (!strcmp(funcName, "vkDestroySampler"))
+        return (PFN_vkVoidFunction) vkDestroySampler;
+    if (!strcmp(funcName, "vkDestroyDescriptorSetLayout"))
+        return (PFN_vkVoidFunction) vkDestroyDescriptorSetLayout;
+    if (!strcmp(funcName, "vkDestroyDescriptorPool"))
+        return (PFN_vkVoidFunction) vkDestroyDescriptorPool;
+    if (!strcmp(funcName, "vkDestroyDynamicViewportState"))
+        return (PFN_vkVoidFunction) vkDestroyDynamicViewportState;
+    if (!strcmp(funcName, "vkDestroyDynamicLineWidthState"))
+        return (PFN_vkVoidFunction) vkDestroyDynamicLineWidthState;
+    if (!strcmp(funcName, "vkDestroyDynamicDepthBiasState"))
+        return (PFN_vkVoidFunction) vkDestroyDynamicDepthBiasState;
+    if (!strcmp(funcName, "vkDestroyDynamicBlendState"))
+        return (PFN_vkVoidFunction) vkDestroyDynamicBlendState;
+    if (!strcmp(funcName, "vkDestroyDynamicDepthBoundsState"))
+        return (PFN_vkVoidFunction) vkDestroyDynamicDepthBoundsState;
+    if (!strcmp(funcName, "vkDestroyDynamicStencilState"))
+        return (PFN_vkVoidFunction) vkDestroyDynamicStencilState;
+    if (!strcmp(funcName, "vkDestroyCommandBuffer"))
+        return (PFN_vkVoidFunction) vkDestroyCommandBuffer;
+    if (!strcmp(funcName, "vkDestroyFramebuffer"))
+        return (PFN_vkVoidFunction) vkDestroyFramebuffer;
+    if (!strcmp(funcName, "vkDestroyRenderPass"))
+        return (PFN_vkVoidFunction) vkDestroyRenderPass;
+    if (!strcmp(funcName, "vkCreateBufferView"))
+        return (PFN_vkVoidFunction) vkCreateBufferView;
+    if (!strcmp(funcName, "vkCreateImageView"))
+        return (PFN_vkVoidFunction) vkCreateImageView;
+    if (!strcmp(funcName, "CreatePipelineCache"))
+        return (PFN_vkVoidFunction) vkCreatePipelineCache;
+    if (!strcmp(funcName, "DestroyPipelineCache"))
+        return (PFN_vkVoidFunction) vkDestroyPipelineCache;
+    if (!strcmp(funcName, "GetPipelineCacheSize"))
+        return (PFN_vkVoidFunction) vkGetPipelineCacheSize;
+    if (!strcmp(funcName, "GetPipelineCacheData"))
+        return (PFN_vkVoidFunction) vkGetPipelineCacheData;
+    if (!strcmp(funcName, "MergePipelineCaches"))
+        return (PFN_vkVoidFunction) vkMergePipelineCaches;
+    if (!strcmp(funcName, "vkCreateGraphicsPipelines"))
+        return (PFN_vkVoidFunction) vkCreateGraphicsPipelines;
+    if (!strcmp(funcName, "vkCreateSampler"))
+        return (PFN_vkVoidFunction) vkCreateSampler;
+    if (!strcmp(funcName, "vkCreateDescriptorSetLayout"))
+        return (PFN_vkVoidFunction) vkCreateDescriptorSetLayout;
+    if (!strcmp(funcName, "vkCreatePipelineLayout"))
+        return (PFN_vkVoidFunction) vkCreatePipelineLayout;
+    if (!strcmp(funcName, "vkCreateDescriptorPool"))
+        return (PFN_vkVoidFunction) vkCreateDescriptorPool;
+    if (!strcmp(funcName, "vkResetDescriptorPool"))
+        return (PFN_vkVoidFunction) vkResetDescriptorPool;
+    if (!strcmp(funcName, "vkAllocDescriptorSets"))
+        return (PFN_vkVoidFunction) vkAllocDescriptorSets;
+    if (!strcmp(funcName, "vkUpdateDescriptorSets"))
+        return (PFN_vkVoidFunction) vkUpdateDescriptorSets;
+    if (!strcmp(funcName, "vkCreateDynamicViewportState"))
+        return (PFN_vkVoidFunction) vkCreateDynamicViewportState;
+    if (!strcmp(funcName, "vkCreateDynamicLineWidthState"))
+        return (PFN_vkVoidFunction) vkCreateDynamicLineWidthState;
+    if (!strcmp(funcName, "vkCreateDynamicDepthBiasState"))
+        return (PFN_vkVoidFunction) vkCreateDynamicDepthBiasState;
+    if (!strcmp(funcName, "vkCreateDynamicBlendState"))
+        return (PFN_vkVoidFunction) vkCreateDynamicBlendState;
+    if (!strcmp(funcName, "vkCreateDynamicDepthBoundsState"))
+        return (PFN_vkVoidFunction) vkCreateDynamicDepthBoundsState;
+    if (!strcmp(funcName, "vkCreateDynamicStencilState"))
+        return (PFN_vkVoidFunction) vkCreateDynamicStencilState;
+    if (!strcmp(funcName, "vkCreateCommandBuffer"))
+        return (PFN_vkVoidFunction) vkCreateCommandBuffer;
+    if (!strcmp(funcName, "vkBeginCommandBuffer"))
+        return (PFN_vkVoidFunction) vkBeginCommandBuffer;
+    if (!strcmp(funcName, "vkEndCommandBuffer"))
+        return (PFN_vkVoidFunction) vkEndCommandBuffer;
+    if (!strcmp(funcName, "vkResetCommandBuffer"))
+        return (PFN_vkVoidFunction) vkResetCommandBuffer;
+    if (!strcmp(funcName, "vkCmdBindPipeline"))
+        return (PFN_vkVoidFunction) vkCmdBindPipeline;
+    if (!strcmp(funcName, "vkCmdBindDynamicViewportState"))
+        return (PFN_vkVoidFunction) vkCmdBindDynamicViewportState;
+    if (!strcmp(funcName, "vkCmdBindDynamicLineWidthState"))
+        return (PFN_vkVoidFunction) vkCmdBindDynamicLineWidthState;
+    if (!strcmp(funcName, "vkCmdBindDynamicDepthBiasState"))
+        return (PFN_vkVoidFunction) vkCmdBindDynamicDepthBiasState;
+    if (!strcmp(funcName, "vkCmdBindDynamicBlendState"))
+        return (PFN_vkVoidFunction) vkCmdBindDynamicBlendState;
+    if (!strcmp(funcName, "vkCmdBindDynamicDepthBoundsState"))
+        return (PFN_vkVoidFunction) vkCmdBindDynamicDepthBoundsState;
+    if (!strcmp(funcName, "vkCmdBindDynamicStencilState"))
+        return (PFN_vkVoidFunction) vkCmdBindDynamicStencilState;
+    if (!strcmp(funcName, "vkCmdBindDescriptorSets"))
+        return (PFN_vkVoidFunction) vkCmdBindDescriptorSets;
+    if (!strcmp(funcName, "vkCmdBindVertexBuffers"))
+        return (PFN_vkVoidFunction) vkCmdBindVertexBuffers;
+    if (!strcmp(funcName, "vkCmdBindIndexBuffer"))
+        return (PFN_vkVoidFunction) vkCmdBindIndexBuffer;
+    if (!strcmp(funcName, "vkCmdDraw"))
+        return (PFN_vkVoidFunction) vkCmdDraw;
+    if (!strcmp(funcName, "vkCmdDrawIndexed"))
+        return (PFN_vkVoidFunction) vkCmdDrawIndexed;
+    if (!strcmp(funcName, "vkCmdDrawIndirect"))
+        return (PFN_vkVoidFunction) vkCmdDrawIndirect;
+    if (!strcmp(funcName, "vkCmdDrawIndexedIndirect"))
+        return (PFN_vkVoidFunction) vkCmdDrawIndexedIndirect;
+    if (!strcmp(funcName, "vkCmdDispatch"))
+        return (PFN_vkVoidFunction) vkCmdDispatch;
+    if (!strcmp(funcName, "vkCmdDispatchIndirect"))
+        return (PFN_vkVoidFunction) vkCmdDispatchIndirect;
+    if (!strcmp(funcName, "vkCmdCopyBuffer"))
+        return (PFN_vkVoidFunction) vkCmdCopyBuffer;
+    if (!strcmp(funcName, "vkCmdCopyImage"))
+        return (PFN_vkVoidFunction) vkCmdCopyImage;
+    if (!strcmp(funcName, "vkCmdCopyBufferToImage"))
+        return (PFN_vkVoidFunction) vkCmdCopyBufferToImage;
+    if (!strcmp(funcName, "vkCmdCopyImageToBuffer"))
+        return (PFN_vkVoidFunction) vkCmdCopyImageToBuffer;
+    if (!strcmp(funcName, "vkCmdUpdateBuffer"))
+        return (PFN_vkVoidFunction) vkCmdUpdateBuffer;
+    if (!strcmp(funcName, "vkCmdFillBuffer"))
+        return (PFN_vkVoidFunction) vkCmdFillBuffer;
+    if (!strcmp(funcName, "vkCmdClearColorImage"))
+        return (PFN_vkVoidFunction) vkCmdClearColorImage;
+    if (!strcmp(funcName, "vkCmdClearDepthStencilImage"))
+        return (PFN_vkVoidFunction) vkCmdClearDepthStencilImage;
+    if (!strcmp(funcName, "vkCmdClearColorAttachment"))
+        return (PFN_vkVoidFunction) vkCmdClearColorAttachment;
+    if (!strcmp(funcName, "vkCmdClearDepthStencilAttachment"))
+        return (PFN_vkVoidFunction) vkCmdClearDepthStencilAttachment;
+    if (!strcmp(funcName, "vkCmdResolveImage"))
+        return (PFN_vkVoidFunction) vkCmdResolveImage;
+    if (!strcmp(funcName, "vkCmdSetEvent"))
+        return (PFN_vkVoidFunction) vkCmdSetEvent;
+    if (!strcmp(funcName, "vkCmdResetEvent"))
+        return (PFN_vkVoidFunction) vkCmdResetEvent;
+    if (!strcmp(funcName, "vkCmdWaitEvents"))
+        return (PFN_vkVoidFunction) vkCmdWaitEvents;
+    if (!strcmp(funcName, "vkCmdPipelineBarrier"))
+        return (PFN_vkVoidFunction) vkCmdPipelineBarrier;
+    if (!strcmp(funcName, "vkCmdBeginQuery"))
+        return (PFN_vkVoidFunction) vkCmdBeginQuery;
+    if (!strcmp(funcName, "vkCmdEndQuery"))
+        return (PFN_vkVoidFunction) vkCmdEndQuery;
+    if (!strcmp(funcName, "vkCmdResetQueryPool"))
+        return (PFN_vkVoidFunction) vkCmdResetQueryPool;
+    if (!strcmp(funcName, "vkCmdWriteTimestamp"))
+        return (PFN_vkVoidFunction) vkCmdWriteTimestamp;
+    if (!strcmp(funcName, "vkCreateFramebuffer"))
+        return (PFN_vkVoidFunction) vkCreateFramebuffer;
+    if (!strcmp(funcName, "vkCreateRenderPass"))
+        return (PFN_vkVoidFunction) vkCreateRenderPass;
+    if (!strcmp(funcName, "vkCmdBeginRenderPass"))
+        return (PFN_vkVoidFunction) vkCmdBeginRenderPass;
+    if (!strcmp(funcName, "vkCmdNextSubpass"))
+        return (PFN_vkVoidFunction) vkCmdNextSubpass;
+    if (!strcmp(funcName, "vkCmdEndRenderPass"))
+        return (PFN_vkVoidFunction) vkCmdEndRenderPass;*/
+
+    VkLayerDispatchTable* pTable = get_dispatch_table(device_limits_device_table_map, dev);
+    if (deviceExtMap.size() == 0 || deviceExtMap[pTable].debug_marker_enabled)
+    {
+//        if (!strcmp(funcName, "vkCmdDbgMarkerBegin"))
+//            return (PFN_vkVoidFunction) vkCmdDbgMarkerBegin;
+//        if (!strcmp(funcName, "vkCmdDbgMarkerEnd"))
+//            return (PFN_vkVoidFunction) vkCmdDbgMarkerEnd;
+//        if (!strcmp(funcName, "vkDbgSetObjectTag"))
+//            return (void*) vkDbgSetObjectTag;
+//        if (!strcmp(funcName, "vkDbgSetObjectName"))
+//            return (void*) vkDbgSetObjectName;
+    }
+    {
+        if (pTable->GetDeviceProcAddr == NULL)
+            return NULL;
+        return pTable->GetDeviceProcAddr(dev, funcName);
+    }
+}
+
+VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI vkGetInstanceProcAddr(VkInstance instance, const char* funcName)
+{
+    PFN_vkVoidFunction fptr;
+    if (instance == NULL)
+        return NULL;
+
+    /* loader uses this to force layer initialization; instance object is wrapped */
+    if (!strcmp(funcName, "vkGetInstanceProcAddr")) {
+        initInstanceTable(device_limits_instance_table_map, (const VkBaseLayerObject *) instance);
+        return (PFN_vkVoidFunction) vkGetInstanceProcAddr;
+    }
+    if (!strcmp(funcName, "vkCreateInstance"))
+        return (PFN_vkVoidFunction) vkCreateInstance;
+    if (!strcmp(funcName, "vkDestroyInstance"))
+        return (PFN_vkVoidFunction) vkDestroyInstance;
+    if (!strcmp(funcName, "vkEnumeratePhysicalDevices"))
+        return (PFN_vkVoidFunction) vkEnumeratePhysicalDevices;
+    if (!strcmp(funcName, "vkGetPhysicalDeviceFeatures"))
+        return (PFN_vkVoidFunction) vkGetPhysicalDeviceFeatures;
+    if (!strcmp(funcName, "vkGetPhysicalDeviceFormatProperties"))
+        return (PFN_vkVoidFunction) vkGetPhysicalDeviceFormatProperties;
+    if (!strcmp(funcName, "vkGetPhysicalDeviceImageFormatProperties"))
+        return (PFN_vkVoidFunction) vkGetPhysicalDeviceImageFormatProperties;
+    if (!strcmp(funcName, "vkGetPhysicalDeviceProperties"))
+        return (PFN_vkVoidFunction) vkGetPhysicalDeviceProperties;
+    if (!strcmp(funcName, "vkGetPhysicalDeviceQueueFamilyProperties"))
+        return (PFN_vkVoidFunction) vkGetPhysicalDeviceQueueFamilyProperties;
+    if (!strcmp(funcName, "vkGetPhysicalDeviceMemoryProperties"))
+        return (PFN_vkVoidFunction) vkGetPhysicalDeviceMemoryProperties;
+    if (!strcmp(funcName, "vkGetPhysicalDeviceLayerProperties"))
+        return (PFN_vkVoidFunction) vkGetPhysicalDeviceLayerProperties;
+    if (!strcmp(funcName, "vkGetPhysicalDeviceExtensionProperties"))
+        return (PFN_vkVoidFunction) vkGetPhysicalDeviceExtensionProperties;
+    if (!strcmp(funcName, "vkGetPhysicalDeviceSparseImageFormatProperties"))
+        return (PFN_vkVoidFunction) vkGetPhysicalDeviceSparseImageFormatProperties;
+    if (!strcmp(funcName, "vkGetGlobalLayerProperties"))
+        return (PFN_vkVoidFunction) vkGetGlobalLayerProperties;
+    if (!strcmp(funcName, "vkGetGlobalExtensionProperties"))
+        return (PFN_vkVoidFunction) vkGetGlobalExtensionProperties;
+
+    layer_data *my_data = get_my_data_ptr(get_dispatch_key(instance), layer_data_map);
+    fptr = debug_report_get_instance_proc_addr(my_data->report_data, funcName);
+    if (fptr)
+        return fptr;
+
+    {
+        VkLayerInstanceDispatchTable* pTable = get_dispatch_table(device_limits_instance_table_map, instance);
+        if (pTable->GetInstanceProcAddr == NULL)
+            return NULL;
+        return pTable->GetInstanceProcAddr(instance, funcName);
+    }
+}
diff --git a/layers/device_limits.h b/layers/device_limits.h
new file mode 100644
index 0000000..d963664
--- /dev/null
+++ b/layers/device_limits.h
@@ -0,0 +1,63 @@
+/*
+ * Vulkan
+ *
+ * Copyright (C) 2015 LunarG, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is 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 Software.
+ *
+ * THE SOFTWARE IS 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 SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#include "vk_layer.h"
+#include <vector>
+#include "layer_common.h"
+
+using namespace std;
+
+// Device Limits ERROR codes
+typedef enum _DEV_LIMITS_ERROR
+{
+    DEVLIMITS_NONE,                             // Used for INFO & other non-error messages
+    DEVLIMITS_INVALID_INSTANCE,                 // Invalid instance used
+    DEVLIMITS_INVALID_PHYSICAL_DEVICE,          // Invalid physical device used
+    DEVLIMITS_MUST_QUERY_COUNT,                 // Failed to make initial call to an API to query the count
+    DEVLIMITS_COUNT_MISMATCH,                   // App requesting a count value different than actual value
+    DEVLIMITS_INVALID_QUEUE_CREATE_REQUEST,     // Invalid queue requested based on queue family properties
+} DEV_LIMITS_ERROR;
+
+typedef enum _CALL_STATE
+{
+    UNCALLED,       // Function has not been called
+    QUERY_COUNT,    // Function called once to query a count
+    QUERY_DETAILS,  // Function called w/ a count to query details
+} CALL_STATE;
+
+typedef struct _INSTANCE_STATE
+{
+    // Track the call state and array size for physical devices
+    CALL_STATE vkEnumeratePhysicalDevicesState;
+    uint32_t physicalDevicesCount;
+    _INSTANCE_STATE():vkEnumeratePhysicalDevicesState(UNCALLED), physicalDevicesCount(0) {};
+} INSTANCE_STATE;
+
+typedef struct _PHYSICAL_DEVICE_STATE
+{
+    // Track the call state and array sizes for various query functions
+    CALL_STATE vkGetPhysicalDeviceQueueFamilyPropertiesState;
+    uint32_t queueFamilyPropertiesCount;
+    _PHYSICAL_DEVICE_STATE():vkGetPhysicalDeviceQueueFamilyPropertiesState(UNCALLED), queueFamilyPropertiesCount(0) {};
+} PHYSICAL_DEVICE_STATE;
+
diff --git a/layers/vk_validation_layer_details.md b/layers/vk_validation_layer_details.md
index 3a6b186..82f8a45 100644
--- a/layers/vk_validation_layer_details.md
+++ b/layers/vk_validation_layer_details.md
@@ -250,18 +250,27 @@
 ### Threading Pending Work
 Additional work to be done
 
-## General Pending Work
-A place to capture general validation work to be done. This includes new checks that don't clearly fit into the above layers.
-
- 1. For Upcoming Dynamic State overhaul (if approved): If dynamic state value that is consumed is never set prior to consumption, flag an error
- 2. For Upcoming Dynamic State overhaul (if approved): If dynamic state that was bound as "static" in current PSO is attempted to be set with vkCmdSet* flag an error
- 3. Need a WSI validation layer(s) to validate correct usage of WSI API. One issue that has already come up is correct UsageFlags for WSI SwapChains and SurfaceProperties. Tons of other stuff including semaphore and synchronization validation.
-
 ## Device Limitations
 
 ### Device Limitations Overview
 
-This layer does not yet exist. The general idea is that at the beginning of time this layer would query device limitations in terms of memory size, format and feature support, and so on. This entails making a complete set of vkGetPhysicalDevice* calls and storing the results. If, later on, the app violates these limitations, then this layer would flag those violations.
+This layer is a work in progress. DeviceLimits layer is intended to capture two broad categories of errors:
+ 1. Incorrect use of APIs to query device capabilities
+ 2. Attempt to use API functionality beyond the capability of the underlying device
+
+For the first category, the layer tracks which calls are made and flags errors if calls are excluded that should not be, or if call sequencing is incorrect. An example is an app that assumes attempts to Query and use queues without ever having called vkGetPhysicalDeviceQueueFamilyProperties(). Also, if an app is calling vkGetPhysicalDeviceQueueFamilyProperties() to retrieve properties with some assumed count for array size instead of first calling vkGetPhysicalDeviceQueueFamilyProperties() w/ a NULL pQueueFamilyProperties parameter in order to query the actual count.
+For the second category of errors, DeviceLimits stores its own internal record of underlying device capabilities and flags errors if requests are made beyond those limits. Most (all?) of the limits are queried via vkGetPhysicalDevice* calls.
+
+### Device Limitations Details Table
+
+| Check | Overview | ENUM DEVLIMITS_* | Relevant API | Testname | Notes/TODO |
+| ----- | -------- | ---------------- | ---------------- | -------- | ---------- |
+| Valid instance | If an invalid instance is used, this error will be flagged | INVALID_INSTANCE | vkEnumeratePhysicalDevices | NA | ObjectTracker should also catch this so if we made sure ObjectTracker was always on top, we could avoid this check |
+| Valid physical device | Enum used for informational messages | INVALID_PHYSICAL_DEVICE | vkEnumeratePhysicalDevices | NA | ObjectTracker should also catch this so if we made sure ObjectTracker was always on top, we could avoid this check |
+| Querying array counts | For API calls where an array count should be queried with an initial call and a NULL array pointer, verify that such a call was made before making a call with non-null array pointer. | MUST_QUERY_COUNT | vkEnumeratePhysicalDevices vkGetPhysicalDeviceQueueFamilyProperties | NA | Create focused test |
+| Array count value | For API calls where an array of details is queried, verify that the size of the requested array matches the size of the array supported by the device. | COUNT_MISMATCH | vkEnumeratePhysicalDevices vkGetPhysicalDeviceQueueFamilyProperties | NA | Create focused test |
+| Queue Creation | When creating/requesting queues, make sure that QueueFamilyPropertiesIndex and index/count within that queue family are valid. | INVALID_QUEUE_CREATE_REQUEST | vkGetDeviceQueue vkCreateDevice | NA | Create focused test |
+| NA | Enum used for informational messages | NONE | | NA | None |
 
 ### Device Limitations Pending Work
 
@@ -276,3 +285,12 @@
 ### APIDump Pending Work
 
  1. vkAllocDescriptorSets does not correctly print out all of the created DescriptorSets (no array printing following main API txt)
+
+## General Pending Work
+A place to capture general validation work to be done. This includes new checks that don't clearly fit into the above layers.
+
+ 1. For Upcoming Dynamic State overhaul (if approved): If dynamic state value that is consumed is never set prior to consumption, flag an error
+ 2. For Upcoming Dynamic State overhaul (if approved): If dynamic state that was bound as "static" in current PSO is attempted to be set with vkCmdSet* flag an error
+ 3. Need a WSI validation layer(s) to validate correct usage of WSI API. One issue that has already come up is correct UsageFlags for WSI SwapChains and SurfaceProperties. Tons of other stuff including semaphore and synchronization validation.
+
+
diff --git a/vk_layer_documentation_generate.py b/vk_layer_documentation_generate.py
index 48eba17..d058181 100755
--- a/vk_layer_documentation_generate.py
+++ b/vk_layer_documentation_generate.py
@@ -73,6 +73,10 @@
                                 'source' : 'dbuild/layers/object_track.cpp',
                                 'generated' : True,
                                 'error_enum' : 'OBJECT_TRACK_ERROR',},
+                 'device_limits' : {'header' : 'layers/device_limits.h',
+                                'source' : 'dbuild/layers/device_limits.cpp',
+                                'generated' : False,
+                                'error_enum' : 'DEV_LIMITS_ERROR',},
     }
 
 builtin_headers = [layer_inputs[ln]['header'] for ln in layer_inputs]