WSI Validation: Initial WSI "swapchain" validation layer.
authorIan Elliott <ian@lunarg.com>
Tue, 22 Sep 2015 16:51:24 +0000 (10:51 -0600)
committerIan Elliott <ian@lunarg.com>
Fri, 25 Sep 2015 17:10:18 +0000 (11:10 -0600)
demos/cube.c
layers/CMakeLists.txt
layers/linux/swapchain.json [new file with mode: 0644]
layers/swapchain.cpp [new file with mode: 0644]
layers/swapchain.h [new file with mode: 0644]
layers/vk_layer_settings.txt
layers/windows/swapchain.json [new file with mode: 0644]
loader/wsi_swapchain.c
vk-generate.py

index 884c2b05096a09409af1b6d300caf97117eadc78..86393deba5a13bf095e10626270759dcfb84fdbb 100644 (file)
@@ -2032,6 +2032,7 @@ static void demo_init_vk(struct demo *demo)
         "DrawState",
         "ParamChecker",
         "ShaderChecker",
+        "Swapchain",
         "DeviceLimits",
         "Image",
     };
@@ -2043,6 +2044,7 @@ static void demo_init_vk(struct demo *demo)
         "DrawState",
         "ParamChecker",
         "ShaderChecker",
+        "Swapchain",
         "DeviceLimits",
         "Image",
     };
index c7ab0082cea477d01646efcc0b64ed72a0a532bc..77177b017f2fb0492c01a46e9878f9506400f0cb 100644 (file)
@@ -26,6 +26,7 @@ set(LAYER_JSON_FILES
     param_checker
     screenshot
     shader_checker
+    swapchain
     threading
     device_limits
     )
@@ -146,6 +147,7 @@ add_vk_layer(ShaderChecker shader_checker.cpp vk_layer_table.cpp)
 add_vk_layer(Image image.cpp vk_layer_table.cpp)
 add_vk_layer(ParamChecker param_checker.cpp vk_layer_debug_marker_table.cpp vk_layer_table.cpp)
 add_vk_layer(ScreenShot screenshot.cpp vk_layer_table.cpp)
+add_vk_layer(Swapchain swapchain.cpp vk_layer_table.cpp)
 # generated
 add_vk_layer(Generic generic_layer.cpp vk_layer_table.cpp)
 add_vk_layer(APIDump api_dump.cpp vk_layer_table.cpp)
diff --git a/layers/linux/swapchain.json b/layers/linux/swapchain.json
new file mode 100644 (file)
index 0000000..65a5a7f
--- /dev/null
@@ -0,0 +1,11 @@
+{
+    "file_format_version" : "0.9.0",
+    "layer" : {
+        "name": "Swapchain",
+        "type": "GLOBAL",
+        "library_path": "./libVKLayerSwapchain.so",
+        "abi_versions": "1.0.0",
+        "implementation_version": "1.0.0",
+        "description": "LunarG Validation Layer"
+    }
+}
diff --git a/layers/swapchain.cpp b/layers/swapchain.cpp
new file mode 100644 (file)
index 0000000..0028657
--- /dev/null
@@ -0,0 +1,1172 @@
+/*
+ * 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.
+ *
+ * Authors:
+ *   Ian Elliott <ian@lunarg.com>
+ */
+
+#include "swapchain.h"
+
+// FIXME/TODO: Make sure this layer is thread-safe!
+
+// The following is for logging error messages:
+static layer_data mydata;
+
+
+static const VkLayerProperties globalLayerProps[] = {
+    {
+        "Swapchain",
+        VK_API_VERSION,                 // specVersion
+        VK_MAKE_VERSION(0, 1, 0),       // implVersion
+        "layer: Swapchain",
+    }
+};
+
+static const VkLayerProperties deviceLayerProps[] = {
+    {
+        "Swapchain",
+        VK_API_VERSION,                 // specVersion
+        VK_MAKE_VERSION(0, 1, 0),       // implVersion
+        "layer: Swapchain",
+    }
+};
+
+
+static LOADER_PLATFORM_THREAD_ONCE_DECLARATION(initOnce);
+
+// NOTE: The following are for keeping track of info that is used for
+// validating the WSI extensions.
+static std::unordered_map<void *, SwpInstance>       instanceMap;
+static std::unordered_map<void *, SwpPhysicalDevice> physicalDeviceMap;
+static std::unordered_map<void *, SwpDevice>         deviceMap;
+static std::unordered_map<uint64_t, SwpSwapchain>    swapchainMap;
+
+
+static void createDeviceRegisterExtensions(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, VkDevice device)
+{
+    uint32_t i;
+    VkLayerDispatchTable *pDisp  = device_dispatch_table(device);
+    PFN_vkGetDeviceProcAddr gpa = pDisp->GetDeviceProcAddr;
+    pDisp->GetSurfacePropertiesKHR = (PFN_vkGetSurfacePropertiesKHR) gpa(device, "vkGetSurfacePropertiesKHR");
+    pDisp->GetSurfaceFormatsKHR = (PFN_vkGetSurfaceFormatsKHR) gpa(device, "vkGetSurfaceFormatsKHR");
+    pDisp->GetSurfacePresentModesKHR = (PFN_vkGetSurfacePresentModesKHR) gpa(device, "vkGetSurfacePresentModesKHR");
+    pDisp->CreateSwapchainKHR = (PFN_vkCreateSwapchainKHR) gpa(device, "vkCreateSwapchainKHR");
+    pDisp->DestroySwapchainKHR = (PFN_vkDestroySwapchainKHR) gpa(device, "vkDestroySwapchainKHR");
+    pDisp->GetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR) gpa(device, "vkGetSwapchainImagesKHR");
+    pDisp->AcquireNextImageKHR = (PFN_vkAcquireNextImageKHR) gpa(device, "vkAcquireNextImageKHR");
+    pDisp->QueuePresentKHR = (PFN_vkQueuePresentKHR) gpa(device, "vkQueuePresentKHR");
+
+    SwpPhysicalDevice *pPhysicalDevice = &physicalDeviceMap[physicalDevice];
+    if (pPhysicalDevice) {
+        deviceMap[device].pPhysicalDevice = pPhysicalDevice;
+        pPhysicalDevice->pDevice = &deviceMap[device];
+    } else {
+        LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_PHYSICAL_DEVICE,
+                                physicalDevice,
+                                "VkPhysicalDevice");
+    }
+    deviceMap[device].device = device;
+    deviceMap[device].deviceSwapchainExtensionEnabled = false;
+    deviceMap[device].gotSurfaceProperties = false;
+    deviceMap[device].surfaceFormatCount = 0;
+    deviceMap[device].pSurfaceFormats = NULL;
+    deviceMap[device].presentModeCount = 0;
+    deviceMap[device].pPresentModes = NULL;
+
+    // Record whether the WSI device extension was enabled for this VkDevice.
+    // No need to check if the extension was advertised by
+    // vkEnumerateDeviceExtensionProperties(), since the loader handles that.
+    for (i = 0; i < pCreateInfo->extensionCount; i++) {
+        if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME) == 0) {
+
+            deviceMap[device].deviceSwapchainExtensionEnabled = true;
+        }
+    }
+}
+
+static void createInstanceRegisterExtensions(const VkInstanceCreateInfo* pCreateInfo, VkInstance instance)
+{
+    uint32_t i;
+    VkLayerInstanceDispatchTable *pDisp  = instance_dispatch_table(instance);
+    PFN_vkGetInstanceProcAddr gpa = pDisp->GetInstanceProcAddr;
+    pDisp->GetPhysicalDeviceSurfaceSupportKHR = (PFN_vkGetPhysicalDeviceSurfaceSupportKHR) gpa(instance, "vkGetPhysicalDeviceSurfaceSupportKHR");
+
+    // Remember this instance, and whether the VK_EXT_KHR_swapchain extension
+    // was enabled for it:
+    instanceMap[instance].instance = instance;
+    instanceMap[instance].swapchainExtensionEnabled = false;
+
+    // Record whether the WSI instance extension was enabled for this
+    // VkInstance.  No need to check if the extension was advertised by
+    // vkEnumerateInstanceExtensionProperties(), since the loader handles that.
+    for (i = 0; i < pCreateInfo->extensionCount; i++) {
+        if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_EXT_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) {
+
+            instanceMap[instance].swapchainExtensionEnabled = true;
+        }
+    }
+}
+
+
+#include "vk_dispatch_table_helper.h"
+static void initSwapchain(void)
+{
+    uint32_t report_flags = 0;
+    uint32_t debug_action = 0;
+    FILE *log_output = NULL;
+    const char *option_str;
+
+    // Initialize Swapchain options:
+    report_flags = getLayerOptionFlags("SwapchainReportFlags", 0);
+    getLayerOptionEnum("SwapchainDebugAction", (uint32_t *) &debug_action);
+
+    if (debug_action & VK_DBG_LAYER_ACTION_LOG_MSG)
+    {
+        // Turn on logging, since it was requested:
+        option_str = getLayerOption("SwapchainLogFilename");
+        log_output = getLayerLogOutput(option_str, "Swapchain");
+        layer_create_msg_callback(&mydata.report_data, report_flags,
+                                  log_callback, (void *) log_output,
+                                  &mydata.logging_callback);
+    }
+}
+
+static const char *surfaceTransformStr(VkSurfaceTransformKHR value)
+{
+    static std::string surfaceTransformStrings[] = {
+        "VK_SURFACE_TRANSFORM_NONE_KHR",
+        "VK_SURFACE_TRANSFORM_ROT90_KHR",
+        "VK_SURFACE_TRANSFORM_ROT180_KHR",
+        "VK_SURFACE_TRANSFORM_ROT270_KHR",
+        "VK_SURFACE_TRANSFORM_HMIRROR_KHR",
+        "VK_SURFACE_TRANSFORM_HMIRROR_ROT90_KHR",
+        "VK_SURFACE_TRANSFORM_HMIRROR_ROT180_KHR",
+        "VK_SURFACE_TRANSFORM_HMIRROR_ROT270_KHR",
+        "Out-of-Range Value"};
+
+    // Deal with a out-of-range value:
+    switch (value) {
+    case VK_SURFACE_TRANSFORM_NONE_KHR:
+    case VK_SURFACE_TRANSFORM_ROT90_KHR:
+    case VK_SURFACE_TRANSFORM_ROT180_KHR:
+    case VK_SURFACE_TRANSFORM_ROT270_KHR:
+    case VK_SURFACE_TRANSFORM_HMIRROR_KHR:
+    case VK_SURFACE_TRANSFORM_HMIRROR_ROT90_KHR:
+    case VK_SURFACE_TRANSFORM_HMIRROR_ROT180_KHR:
+    case VK_SURFACE_TRANSFORM_HMIRROR_ROT270_KHR:
+        break;
+    default:
+        value =
+            (VkSurfaceTransformKHR) (VK_SURFACE_TRANSFORM_HMIRROR_ROT270_KHR + 1);
+        break;
+    }
+
+    // Return a string corresponding to the value:
+    return surfaceTransformStrings[value].c_str();
+}
+
+static const char *presentModeStr(VkPresentModeKHR value)
+{
+    static std::string presentModeStrings[] = {
+        "VK_PRESENT_MODE_IMMEDIATE_KHR",
+        "VK_PRESENT_MODE_MAILBOX_KHR",
+        "VK_PRESENT_MODE_FIFO_KHR",
+        "Out-of-Range Value"};
+
+    // Deal with a out-of-range value:
+    switch (value) {
+    case VK_PRESENT_MODE_IMMEDIATE_KHR:
+    case VK_PRESENT_MODE_MAILBOX_KHR:
+    case VK_PRESENT_MODE_FIFO_KHR:
+        break;
+    default:
+        value = (VkPresentModeKHR) (VK_PRESENT_MODE_FIFO_KHR + 1);
+        break;
+    }
+
+    // Return a string corresponding to the value:
+    return presentModeStrings[value].c_str();
+}
+
+
+VK_LAYER_EXPORT VkResult VKAPI vkCreateInstance(const VkInstanceCreateInfo* pCreateInfo, VkInstance* pInstance)
+{
+    // Call down the call chain:
+    VkResult result = instance_dispatch_table(*pInstance)->CreateInstance(pCreateInfo, pInstance);
+    if (result == VK_SUCCESS) {
+        // Since it succeeded, do layer-specific work:
+        createInstanceRegisterExtensions(pCreateInfo, *pInstance);
+    }
+    return result;
+}
+
+VK_LAYER_EXPORT void VKAPI vkDestroyInstance(VkInstance instance)
+{
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkInstance was used:
+    SwpInstance *pInstance = &instanceMap[instance];
+    if (!pInstance) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_INSTANCE,
+                                            instance,
+                                            "VkInstance");
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        dispatch_key key = get_dispatch_key(instance);
+        VkLayerInstanceDispatchTable *pDisp = instance_dispatch_table(instance);
+        pDisp->DestroyInstance(instance);
+        destroy_instance_dispatch_table(key);
+    }
+
+    // Regardless of skipCall value, do some internal cleanup:
+    if (pInstance) {
+        // Delete all of the SwpPhysicalDevice's and the SwpInstance associated
+        // with this instance:
+        for (auto it = pInstance->physicalDevices.begin() ;
+             it != pInstance->physicalDevices.end() ; it++) {
+            // Erase the SwpPhysicalDevice's from the physicalDeviceMap (which
+            // are simply pointed to by the SwpInstance):
+            physicalDeviceMap.erase(it->second->physicalDevice);
+        }
+        instanceMap.erase(instance);
+    }
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkEnumeratePhysicalDevices(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices)
+{
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkInstance was used:
+    SwpInstance *pInstance = &instanceMap[instance];
+    if (!pInstance) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_INSTANCE,
+                                            instance,
+                                            "VkInstance");
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        result = instance_dispatch_table(instance)->EnumeratePhysicalDevices(
+                instance, pPhysicalDeviceCount, pPhysicalDevices);
+
+        if ((result == VK_SUCCESS) && pInstance && pPhysicalDevices &&
+            (*pPhysicalDeviceCount > 0)) {
+            // Record the VkPhysicalDevices returned by the ICD:
+            SwpInstance *pInstance = &instanceMap[instance];
+            for (int i = 0; i < *pPhysicalDeviceCount; i++) {
+                physicalDeviceMap[pPhysicalDevices[i]].physicalDevice =
+                    pPhysicalDevices[i];
+                physicalDeviceMap[pPhysicalDevices[i]].pInstance = pInstance;
+                physicalDeviceMap[pPhysicalDevices[i]].pDevice = NULL;
+                // Point to the associated SwpInstance:
+                pInstance->physicalDevices[pPhysicalDevices[i]] =
+                    &physicalDeviceMap[pPhysicalDevices[i]];
+            }
+        }
+
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkCreateDevice(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, VkDevice* pDevice)
+{
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkPhysicalDevice was used:
+    SwpPhysicalDevice *pPhysicalDevice = &physicalDeviceMap[physicalDevice];
+    if (!pPhysicalDevice) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_PHYSICAL_DEVICE,
+                                            physicalDevice,
+                                            "VkPhysicalDevice");
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        result = device_dispatch_table(*pDevice)->CreateDevice(
+                physicalDevice, pCreateInfo, pDevice);
+        if (result == VK_SUCCESS) {
+            // Since it succeeded, do layer-specific work:
+            createDeviceRegisterExtensions(physicalDevice, pCreateInfo,
+                                           *pDevice);
+        }
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+VK_LAYER_EXPORT void VKAPI vkDestroyDevice(VkDevice device)
+{
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkDevice was used:
+    SwpDevice *pDevice = &deviceMap[device];
+    if (!pDevice) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_DEVICE,
+                                            device,
+                                            "VkDevice");
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        dispatch_key key = get_dispatch_key(device);
+        VkLayerDispatchTable *pDisp  =  device_dispatch_table(device);
+        pDisp->DestroyDevice(device);
+        destroy_device_dispatch_table(key);
+    }
+
+    // Regardless of skipCall value, do some internal cleanup:
+    if (pDevice) {
+        // Delete the SwpDevice associated with this device:
+        if (pDevice->pPhysicalDevice) {
+            pDevice->pPhysicalDevice->pDevice = NULL;
+        }
+        if (deviceMap[device].pSurfaceFormats) {
+            free(deviceMap[device].pSurfaceFormats);
+        }
+        if (deviceMap[device].pPresentModes) {
+            free(deviceMap[device].pPresentModes);
+        }
+        deviceMap.erase(device);
+        if (!pDevice->swapchains.empty()) {
+            LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                      "%s() called before all of its associated "
+                      "VkSwapchainKHRs were destroyed.",
+                      __FUNCTION__);
+            // Empty and then delete all SwpSwapchain's
+            for (auto it = pDevice->swapchains.begin() ;
+                 it != pDevice->swapchains.end() ; it++) {
+                // Delete all SwpImage's
+                it->second->images.clear();
+            }
+            pDevice->swapchains.clear();
+        }
+    }
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetPhysicalDeviceSurfaceSupportKHR(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, const VkSurfaceDescriptionKHR* pSurfaceDescription, VkBool32* pSupported)
+{
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkPhysicalDevice was used, and that the instance
+    // extension was enabled:
+    SwpPhysicalDevice *pPhysicalDevice = &physicalDeviceMap[physicalDevice];
+    if (!pPhysicalDevice || !pPhysicalDevice->pInstance) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_PHYSICAL_DEVICE,
+                                            physicalDevice,
+                                            "VkPhysicalDevice");
+    } else if (!pPhysicalDevice->pInstance->swapchainExtensionEnabled) {
+        skipCall |= LOG_ERROR(VK_OBJECT_TYPE_INSTANCE,
+                              pPhysicalDevice->pInstance,
+                              "VkInstance",
+                              "%s() called even though the "
+                              VK_EXT_KHR_SWAPCHAIN_EXTENSION_NAME,
+                              "extension was not enabled for this VkInstance.",
+                              __FUNCTION__);
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        result = instance_dispatch_table(physicalDevice)->GetPhysicalDeviceSurfaceSupportKHR(
+                physicalDevice, queueFamilyIndex, pSurfaceDescription,
+                pSupported);
+
+        if ((result == VK_SUCCESS) && pSupported && pPhysicalDevice) {
+            // Record the result of this query:
+            pPhysicalDevice->queueFamilyIndexSupport[queueFamilyIndex] =
+                *pSupported;
+            // TODO: We need to compare this with the actual queue used for
+            // presentation, to ensure it was advertised to the application as
+            // supported for presentation.
+        }
+
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetSurfacePropertiesKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, VkSurfacePropertiesKHR* pSurfaceProperties)
+{
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkDevice was used, and that the device
+    // extension was enabled:
+    SwpDevice *pDevice = &deviceMap[device];
+    if (!pDevice) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_DEVICE,
+                                            device,
+                                            "VkDevice");
+    } else if (!pDevice->deviceSwapchainExtensionEnabled) {
+        skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                              "%s() called even though the "
+                              VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME,
+                              "extension was not enabled for this VkDevice.",
+                              __FUNCTION__);
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        result = device_dispatch_table(device)->GetSurfacePropertiesKHR(
+                device, pSurfaceDescription, pSurfaceProperties);
+
+        if ((result == VK_SUCCESS) && pDevice) {
+            pDevice->gotSurfaceProperties = true;
+            pDevice->surfaceProperties = *pSurfaceProperties;
+        }
+
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetSurfaceFormatsKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, uint32_t* pCount, VkSurfaceFormatKHR* pSurfaceFormats)
+{
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkDevice was used, and that the device
+    // extension was enabled:
+    SwpDevice *pDevice = &deviceMap[device];
+    if (!pDevice) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_DEVICE,
+                                            device,
+                                            "VkDevice");
+    } else if (!pDevice->deviceSwapchainExtensionEnabled) {
+        skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                              "%s() called even though the "
+                              VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME,
+                              "extension was not enabled for this VkDevice.",
+                              __FUNCTION__);
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        result = device_dispatch_table(device)->GetSurfaceFormatsKHR(
+                device, pSurfaceDescription, pCount, pSurfaceFormats);
+
+        if ((result == VK_SUCCESS) && pDevice && pSurfaceFormats && pCount &&
+            (*pCount > 0)) {
+            pDevice->surfaceFormatCount = *pCount;
+            pDevice->pSurfaceFormats = (VkSurfaceFormatKHR *)
+                malloc(*pCount * sizeof(VkSurfaceFormatKHR));
+            if (pDevice->pSurfaceFormats) {
+                for (int i = 0 ; i < *pCount ; i++) {
+                    pDevice->pSurfaceFormats[i] = pSurfaceFormats[i];
+                }
+            } else {
+                pDevice->surfaceFormatCount = 0;
+            }
+        }
+
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetSurfacePresentModesKHR(VkDevice device, const VkSurfaceDescriptionKHR* pSurfaceDescription, uint32_t* pCount, VkPresentModeKHR* pPresentModes)
+{
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkDevice was used, and that the device
+    // extension was enabled:
+    SwpDevice *pDevice = &deviceMap[device];
+    if (!pDevice) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_DEVICE,
+                                            device,
+                                            "VkDevice");
+    } else if (!pDevice->deviceSwapchainExtensionEnabled) {
+        skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                              "%s() called even though the "
+                              VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME,
+                              "extension was not enabled for this VkDevice.",
+                              __FUNCTION__);
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        result = device_dispatch_table(device)->GetSurfacePresentModesKHR(
+                device, pSurfaceDescription, pCount, pPresentModes);
+
+        if ((result == VK_SUCCESS) && pDevice && pPresentModes && pCount &&
+            (*pCount > 0)) {
+            pDevice->presentModeCount = *pCount;
+            pDevice->pPresentModes = (VkPresentModeKHR *)
+                malloc(*pCount * sizeof(VkPresentModeKHR));
+            if (pDevice->pSurfaceFormats) {
+                for (int i = 0 ; i < *pCount ; i++) {
+                    pDevice->pPresentModes[i] = pPresentModes[i];
+                }
+            } else {
+                pDevice->presentModeCount = 0;
+            }
+        }
+
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+// This function does the up-front validation work for vkCreateSwapchainKHR(),
+// and returns VK_TRUE if a logging callback indicates that the call down the
+// chain should be skipped:
+static VkBool32 validateCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, VkSwapchainKHR* pSwapchain)
+{
+// TODO: Validate cases of re-creating a swapchain (the current code
+// assumes a new swapchain is being created).
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = VK_FALSE;
+    char fn[] = "vkCreateSwapchainKHR";
+
+    // Validate that a valid VkDevice was used, and that the device
+    // extension was enabled:
+    SwpDevice *pDevice = &deviceMap[device];
+    if (!pDevice) {
+        return LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                         "%s() called with a non-valid %s.",
+                         fn, "VkDevice");
+
+    } else if (!pDevice->deviceSwapchainExtensionEnabled) {
+        return LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                         "%s() called even though the "
+                         VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME,
+                         "extension was not enabled for this VkDevice.",
+                         fn);
+    }
+
+    // Validate pCreateInfo with the results for previous queries:
+    if (!pDevice->gotSurfaceProperties) {
+        skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                              "%s() called before calling "
+                              "vkGetSurfacePropertiesKHR().",
+                              fn);
+    } else {
+        // Validate pCreateInfo->minImageCount against
+        // VkSurfacePropertiesKHR::{min|max}ImageCount:
+        VkSurfacePropertiesKHR *pProps = &pDevice->surfaceProperties;
+        if ((pCreateInfo->minImageCount < pProps->minImageCount) ||
+            ((pProps->maxImageCount > 0) &&
+             (pCreateInfo->minImageCount > pProps->maxImageCount))) {
+            skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                                  "%s() called with pCreateInfo->minImageCount "
+                                  "= %d, which is outside the bounds returned "
+                                  "by vkGetSurfacePropertiesKHR() (i.e. "
+                                  "minImageCount = %d, maxImageCount = %d).",
+                                  fn,
+                                  pCreateInfo->minImageCount,
+                                  pProps->minImageCount,
+                                  pProps->maxImageCount);
+        }
+        // Validate pCreateInfo->imageExtent against
+        // VkSurfacePropertiesKHR::{current|min|max}ImageExtent:
+        if ((pProps->currentExtent.width == -1) &&
+            ((pCreateInfo->imageExtent.width < pProps->minImageExtent.width) ||
+             (pCreateInfo->imageExtent.width > pProps->maxImageExtent.width) ||
+             (pCreateInfo->imageExtent.height < pProps->minImageExtent.height) ||
+             (pCreateInfo->imageExtent.height > pProps->maxImageExtent.height))) {
+            skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                                  "%s() called with pCreateInfo->imageExtent = "
+                                  "(%d,%d), which is outside the bounds "
+                                  "returned by vkGetSurfacePropertiesKHR(): "
+                                  "currentExtent = (%d,%d), minImageExtent = "
+                                  "(%d,%d), maxImageExtent = (%d,%d).",
+                                  fn,
+                                  pCreateInfo->imageExtent.width,
+                                  pCreateInfo->imageExtent.height,
+                                  pProps->currentExtent.width,
+                                  pProps->currentExtent.height,
+                                  pProps->minImageExtent.width,
+                                  pProps->minImageExtent.height,
+                                  pProps->maxImageExtent.width,
+                                  pProps->maxImageExtent.height);
+        }
+        if ((pProps->currentExtent.width != -1) &&
+            ((pCreateInfo->imageExtent.width != pProps->currentExtent.width) ||
+             (pCreateInfo->imageExtent.height != pProps->currentExtent.height))) {
+            skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                                  "%s() called with pCreateInfo->imageExtent = "
+                                  "(%d,%d), which is not equal to the "
+                                  "currentExtent = (%d,%d) returned by "
+                                  "vkGetSurfacePropertiesKHR().",
+                                  fn,
+                                  pCreateInfo->imageExtent.width,
+                                  pCreateInfo->imageExtent.height,
+                                  pProps->currentExtent.width,
+                                  pProps->currentExtent.height);
+        }
+        // Validate pCreateInfo->preTransform against
+        // VkSurfacePropertiesKHR::supportedTransforms:
+        if (!((1 << pCreateInfo->preTransform) & pProps->supportedTransforms)) {
+            // This is an error situation; one for which we'd like to give
+            // the developer a helpful, multi-line error message.  Build it
+            // up a little at a time, and then log it:
+            std::string errorString = "";
+            char str[1024];
+            // Here's the first part of the message:
+            sprintf(str, "%s() called with a non-supported "
+                    "pCreateInfo->preTransform (i.e. %s).  "
+                    "Supported values are:\n",
+                    fn,
+                    surfaceTransformStr(pCreateInfo->preTransform));
+            errorString += str;
+            for (int i = VK_SURFACE_TRANSFORM_NONE_KHR ;
+                 i < VK_SURFACE_TRANSFORM_INHERIT_KHR ; i++) {
+                // Build up the rest of the message:
+                if ((1 << i) & pProps->supportedTransforms) {
+                    const char *newStr =
+                        surfaceTransformStr((VkSurfaceTransformKHR) (1 << i));
+                    sprintf(str, "    %s\n", newStr);
+                    errorString += str;
+                }
+            }
+            // Log the message that we've built up:
+            skipCall |= debug_report_log_msg(&mydata.report_data,
+                                             VK_DBG_REPORT_ERROR_BIT,
+                                             VK_OBJECT_TYPE_DEVICE,
+                                             (uint64_t) device,
+                                             0, 0, LAYER_NAME,
+                                             errorString.c_str());
+        }
+        // Validate pCreateInfo->imageArraySize against
+        // VkSurfacePropertiesKHR::maxImageArraySize:
+        if (pCreateInfo->imageArraySize <= pProps->maxImageArraySize) {
+            skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                                  "%s() called with a non-supported "
+                                  "pCreateInfo->imageArraySize (i.e. %d).  "
+                                  "Maximum value is %d.",
+                                  fn,
+                                  pCreateInfo->imageArraySize,
+                                  pProps->maxImageArraySize);
+        }
+        // Validate pCreateInfo->imageUsageFlags against
+        // VkSurfacePropertiesKHR::supportedUsageFlags:
+        if (pCreateInfo->imageUsageFlags &&
+            (pCreateInfo->imageUsageFlags !=
+             (pCreateInfo->imageUsageFlags & pProps->supportedUsageFlags))) {
+            skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                                  "%s() called with a non-supported "
+                                  "pCreateInfo->imageUsageFlags (i.e. 0x%08x)."
+                                  "  Supported flag bits are 0x%08x.",
+                                  fn,
+                                  pCreateInfo->imageUsageFlags,
+                                  pProps->supportedUsageFlags);
+        }
+    }
+    if (!pDevice->surfaceFormatCount) {
+        skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                              "%s() called before calling "
+                              "vkGetSurfaceFormatsKHR().",
+                              fn);
+    } else {
+        // Validate pCreateInfo->imageFormat against
+        // VkSurfaceFormatKHR::format:
+        bool foundFormat = false;
+        bool foundColorSpace = false;
+        bool foundMatch = false;
+        for (int i = 0 ; i < pDevice->surfaceFormatCount ; i++) {
+            if (pCreateInfo->imageFormat == pDevice->pSurfaceFormats[i].format) {
+                // Validate pCreateInfo->imageColorSpace against
+                // VkSurfaceFormatKHR::colorSpace:
+                foundFormat = true;
+                if (pCreateInfo->imageColorSpace == pDevice->pSurfaceFormats[i].colorSpace) {
+                    foundMatch = true;
+                    break;
+                }
+            } else {
+                if (pCreateInfo->imageColorSpace == pDevice->pSurfaceFormats[i].colorSpace) {
+                    foundColorSpace = true;
+                }
+            }
+        }
+        if (!foundMatch) {
+            if (!foundFormat) {
+                if (!foundColorSpace) {
+                    skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device,
+                                          "VkDevice",
+                                          "%s() called with neither a "
+                                          "supported pCreateInfo->imageFormat "
+                                          "(i.e. %d) nor a supported "
+                                          "pCreateInfo->imageColorSpace "
+                                          "(i.e. %d).",
+                                          fn,
+                                          pCreateInfo->imageFormat,
+                                          pCreateInfo->imageColorSpace);
+                } else {
+                    skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device,
+                                              "VkDevice",
+                                          "%s() called with a non-supported "
+                                          "pCreateInfo->imageFormat (i.e. %d).",
+                                          fn, pCreateInfo->imageFormat);
+                }
+            } else if (!foundColorSpace) {
+                skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                                      "%s() called with a non-supported "
+                                      "pCreateInfo->imageColorSpace (i.e. %d).",
+                                      fn, pCreateInfo->imageColorSpace);
+            }
+        }
+    }
+    if (!pDevice->presentModeCount) {
+        skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                              "%s() called before calling "
+                              "vkGetSurfacePresentModesKHR().",
+                              fn);
+    } else {
+        // Validate pCreateInfo->presentMode against
+        // vkGetSurfacePresentModesKHR():
+        bool foundMatch = false;
+        for (int i = 0 ; i < pDevice->presentModeCount ; i++) {
+            if (pDevice->pPresentModes[i] == pCreateInfo->presentMode) {
+                foundMatch = true;
+                break;
+            }
+        }
+        if (!foundMatch) {
+            skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                                  "%s() called with a non-supported "
+                                  "pCreateInfo->presentMode (i.e. %s).",
+                                  fn,
+                                  presentModeStr(pCreateInfo->presentMode));
+        }
+    }
+
+    // TODO: Validate the following values:
+    // - pCreateInfo->sharingMode
+    // - pCreateInfo->queueFamilyCount
+    // - pCreateInfo->pQueueFamilyIndices
+    // - pCreateInfo->oldSwapchain
+
+    return skipCall;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkCreateSwapchainKHR(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, VkSwapchainKHR* pSwapchain)
+{
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = validateCreateSwapchainKHR(device, pCreateInfo,
+                                                   pSwapchain);
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        result = device_dispatch_table(device)->CreateSwapchainKHR(
+                device, pCreateInfo, pSwapchain);
+
+        if (result == VK_SUCCESS) {
+            // Remember the swapchain's handle, and link it to the device:
+            SwpDevice *pDevice = &deviceMap[device];
+
+            swapchainMap[pSwapchain->handle].swapchain = *pSwapchain;
+            pDevice->swapchains[pSwapchain->handle] =
+                &swapchainMap[pSwapchain->handle];
+            swapchainMap[pSwapchain->handle].pDevice = pDevice;
+            swapchainMap[pSwapchain->handle].imageCount = 0;
+        }
+
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkDestroySwapchainKHR(VkDevice device, VkSwapchainKHR swapchain)
+{
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkDevice was used, and that the device
+    // extension was enabled:
+    SwpDevice *pDevice = &deviceMap[device];
+    if (!pDevice) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_DEVICE,
+                                            device,
+                                            "VkDevice");
+    } else if (!pDevice->deviceSwapchainExtensionEnabled) {
+        skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                              "%s() called even though the "
+                              VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME,
+                              "extension was not enabled for this VkDevice.",
+                              __FUNCTION__);
+    }
+
+    // Regardless of skipCall value, do some internal cleanup:
+    SwpSwapchain *pSwapchain = &swapchainMap[swapchain.handle];
+    if (pSwapchain) {
+        // Delete the SwpSwapchain associated with this swapchain:
+        if (pSwapchain->pDevice) {
+            pSwapchain->pDevice->swapchains.erase(swapchain.handle);
+            if (device != pSwapchain->pDevice->device) {
+                LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                          "%s() called with a different VkDevice than the "
+                          "VkSwapchainKHR was created with.",
+                          __FUNCTION__);
+            }
+        }
+        if (pSwapchain->imageCount) {
+            pSwapchain->images.clear();
+        }
+        swapchainMap.erase(swapchain.handle);
+    } else {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_SWAPCHAIN_KHR,
+                                            swapchain.handle,
+                                            "VkSwapchainKHR");
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        VkResult result = device_dispatch_table(device)->DestroySwapchainKHR(device, swapchain);
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkGetSwapchainImagesKHR(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pCount, VkImage* pSwapchainImages)
+{
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkDevice was used, and that the device
+    // extension was enabled:
+    SwpDevice *pDevice = &deviceMap[device];
+    if (!pDevice) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_DEVICE,
+                                            device,
+                                            "VkDevice");
+    } else if (!pDevice->deviceSwapchainExtensionEnabled) {
+        skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                              "%s() called even though the "
+                              VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME,
+                              "extension was not enabled for this VkDevice.",
+                              __FUNCTION__);
+    }
+    SwpSwapchain *pSwapchain = &swapchainMap[swapchain.handle];
+    if (!pSwapchain) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_SWAPCHAIN_KHR,
+                                            swapchain.handle,
+                                            "VkSwapchainKHR");
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        result = device_dispatch_table(device)->GetSwapchainImagesKHR(
+                device, swapchain, pCount, pSwapchainImages);
+
+        if ((result == VK_SUCCESS) && pSwapchain &&pSwapchainImages &&
+            pCount && (*pCount > 0)) {
+            // Record the images and their state:
+            if (pSwapchain) {
+                pSwapchain->imageCount = *pCount;
+                for (int i = 0 ; i < *pCount ; i++) {
+                    pSwapchain->images[i].image = pSwapchainImages[i];
+                    pSwapchain->images[i].pSwapchain = pSwapchain;
+                    pSwapchain->images[i].ownedByApp = false;
+                }
+            }
+        }
+
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkAcquireNextImageKHR(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, uint32_t* pImageIndex)
+{
+// TODO: Record/update the state of the swapchain, in case an error occurs
+// (e.g. VK_ERROR_OUT_OF_DATE_KHR).
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = VK_FALSE;
+
+    // Validate that a valid VkDevice was used, and that the device
+    // extension was enabled:
+    SwpDevice *pDevice = &deviceMap[device];
+    if (!pDevice) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_DEVICE,
+                                            device,
+                                            "VkDevice");
+    } else if (!pDevice->deviceSwapchainExtensionEnabled) {
+        skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE, device, "VkDevice",
+                              "%s() called even though the "
+                              VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME,
+                              "extension was not enabled for this VkDevice.",
+                              __FUNCTION__);
+    }
+    // Validate that a valid VkSwapchainKHR was used:
+    SwpSwapchain *pSwapchain = &swapchainMap[swapchain.handle];
+    if (!pSwapchain) {
+        skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_SWAPCHAIN_KHR,
+                                            swapchain.handle,
+                                            "VkSwapchainKHR");
+    } else {
+        // Look to see if the application is trying to own too many images at
+        // the same time (i.e. not leave any to display):
+        int imagesOwnedByApp = 0;
+        for (int i = 0 ; i < pSwapchain->imageCount ; i++) {
+            if (pSwapchain->images[i].ownedByApp) {
+                imagesOwnedByApp++;
+            }
+        }
+        if (imagesOwnedByApp >= (pSwapchain->imageCount - 1)) {
+            skipCall |= LOG_ERROR(VK_OBJECT_TYPE_SWAPCHAIN_KHR, swapchain,
+                                  "VkSwapchainKHR",
+                                  "%s() called when the application already "
+                                  "owns all presentable images in this "
+                                  "swapchain except for the image currently "
+                                  "being displayed.  This call to %s() cannot "
+                                  "succeed unless another thread calls the "
+                                  "vkQueuePresentKHR() function in order to "
+                                  "release ownership of one of the presentable "
+                                  "images of this swapchain.",
+                                  __FUNCTION__, __FUNCTION__);
+        }
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        result = device_dispatch_table(device)->AcquireNextImageKHR(
+                device, swapchain, timeout, semaphore, pImageIndex);
+
+        if (((result == VK_SUCCESS) || (result == VK_SUBOPTIMAL_KHR)) &&
+            pSwapchain) {
+            if (*pImageIndex >= pSwapchain->imageCount) {
+                LOG_ERROR(VK_OBJECT_TYPE_SWAPCHAIN_KHR, swapchain,
+                          "VkSwapchainKHR",
+                          "%s() returned an index that's too large (i.e. %d).  "
+                          "There are only %d images in this VkSwapchainKHR.",
+                          __FUNCTION__, *pImageIndex, pSwapchain->imageCount);
+            }
+            if (pSwapchain->images[*pImageIndex].ownedByApp) {
+                LOG_ERROR(VK_OBJECT_TYPE_SWAPCHAIN_KHR, swapchain,
+                          "VkSwapchainKHR",
+                          "%s() returned an index (i.e. %d) for an image that "
+                          "is already owned by the application.\n",
+                          __FUNCTION__, *pImageIndex);
+            }
+            // Change the state of the image (now owned by the application):
+            pSwapchain->images[*pImageIndex].ownedByApp = true;
+        }
+
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkQueuePresentKHR(VkQueue queue, VkPresentInfoKHR* pPresentInfo)
+{
+// TODOs:
+//
+// - Ensure that the queue is active, and is one of the queueFamilyIndex's
+//   that was returned by a previuos query.
+// - Record/update the state of the swapchain, in case an error occurs
+//   (e.g. VK_ERROR_OUT_OF_DATE_KHR).
+    VkResult result = VK_SUCCESS;
+    VkBool32 skipCall = VK_FALSE;
+
+    for (int i = 0; i < pPresentInfo->swapchainCount ; i++) {
+        int index = pPresentInfo->imageIndices[i];
+        SwpSwapchain *pSwapchain =
+            &swapchainMap[pPresentInfo->swapchains[i].handle];
+        if (pSwapchain) {
+            if (!pSwapchain->pDevice->deviceSwapchainExtensionEnabled) {
+                skipCall |= LOG_ERROR(VK_OBJECT_TYPE_DEVICE,
+                                      pSwapchain->pDevice, "VkDevice",
+                                      "%s() called even though the "
+                                      VK_EXT_KHR_DEVICE_SWAPCHAIN_EXTENSION_NAME,
+                                      "extension was not enabled for this "
+                                      "VkDevice.",
+                                      __FUNCTION__);
+            }
+            if (index >= pSwapchain->imageCount) {
+                skipCall |= LOG_ERROR(VK_OBJECT_TYPE_SWAPCHAIN_KHR,
+                                      pPresentInfo->swapchains[i].handle,
+                                      "VkSwapchainKHR",
+                                      "%s() called for an index that is too "
+                                      "large (i.e. %d).  There are only %d "
+                                      "images in this VkSwapchainKHR.\n",
+                                      __FUNCTION__, index,
+                                      pSwapchain->imageCount);
+            } else {
+                if (!pSwapchain->images[index].ownedByApp) {
+                    skipCall |= LOG_ERROR(VK_OBJECT_TYPE_SWAPCHAIN_KHR,
+                                          pPresentInfo->swapchains[i].handle,
+                                          "VkSwapchainKHR",
+                                          "%s() returned an index (i.e. %d) "
+                                          "for an image that is not owned by "
+                                          "the application.",
+                                          __FUNCTION__, index);
+                }
+            }
+        } else {
+            skipCall |= LOG_ERROR_NON_VALID_OBJ(VK_OBJECT_TYPE_SWAPCHAIN_KHR,
+                                                pPresentInfo->swapchains[i].handle,
+                                                "VkSwapchainKHR");
+        }
+    }
+
+    if (VK_FALSE == skipCall) {
+        // Call down the call chain:
+        result = device_dispatch_table(queue)->QueuePresentKHR(queue,
+                                                               pPresentInfo);
+
+        if ((result == VK_SUCCESS) || (result == VK_SUBOPTIMAL_KHR)) {
+            for (int i = 0; i < pPresentInfo->swapchainCount ; i++) {
+                int index = pPresentInfo->imageIndices[i];
+                SwpSwapchain *pSwapchain =
+                    &swapchainMap[pPresentInfo->swapchains[i].handle];
+                if (pSwapchain) {
+                    // Change the state of the image (no longer owned by the
+                    // application):
+                    pSwapchain->images[index].ownedByApp = false;
+                }
+            }
+        }
+
+        return result;
+    }
+    return VK_ERROR_VALIDATION_FAILED;
+}
+
+static inline PFN_vkVoidFunction layer_intercept_proc(const char *name)
+{
+    if (!name || name[0] != 'v' || name[1] != 'k')
+        return NULL;
+
+    name += 2;
+    if (!strcmp(name, "CreateInstance"))
+        return (PFN_vkVoidFunction) vkCreateInstance;
+    if (!strcmp(name, "DestroyInstance"))
+        return (PFN_vkVoidFunction) vkDestroyInstance;
+    if (!strcmp(name, "EnumeratePhysicalDevices"))
+        return (PFN_vkVoidFunction) vkEnumeratePhysicalDevices;
+    if (!strcmp(name, "CreateDevice"))
+        return (PFN_vkVoidFunction) vkCreateDevice;
+    if (!strcmp(name, "DestroyDevice"))
+        return (PFN_vkVoidFunction) vkDestroyDevice;
+
+    return NULL;
+}
+static inline PFN_vkVoidFunction layer_intercept_instance_proc(const char *name)
+{
+    if (!name || name[0] != 'v' || name[1] != 'k')
+        return NULL;
+
+    name += 2;
+    if (!strcmp(name, "CreateInstance"))
+        return (PFN_vkVoidFunction) vkCreateInstance;
+    if (!strcmp(name, "DestroyInstance"))
+        return (PFN_vkVoidFunction) vkDestroyInstance;
+    if (!strcmp(name, "EnumeratePhysicalDevices"))
+        return (PFN_vkVoidFunction) vkEnumeratePhysicalDevices;
+
+    return NULL;
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkDbgCreateMsgCallback(VkInstance instance, VkFlags msgFlags, const PFN_vkDbgMsgCallback pfnMsgCallback, void* pUserData, VkDbgMsgCallback* pMsgCallback)
+{
+    return layer_create_msg_callback(&mydata.report_data, msgFlags, pfnMsgCallback, pUserData, pMsgCallback);
+}
+
+VK_LAYER_EXPORT VkResult VKAPI vkDbgDestroyMsgCallback(VkInstance instance, VkDbgMsgCallback msgCallback)
+{
+    layer_destroy_msg_callback(&mydata.report_data, msgCallback);
+    return VK_SUCCESS;
+}
+
+VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI vkGetDeviceProcAddr(VkDevice device, const char* funcName)
+{
+    PFN_vkVoidFunction addr;
+    if (device == VK_NULL_HANDLE) {
+        return NULL;
+    }
+    loader_platform_thread_once(&initOnce, initSwapchain);
+
+    /* loader uses this to force layer initialization; device object is wrapped */
+    if (!strcmp("vkGetDeviceProcAddr", funcName)) {
+        initDeviceTable((const VkBaseLayerObject *) device);
+        return (PFN_vkVoidFunction) vkGetDeviceProcAddr;
+    }
+
+    addr = layer_intercept_proc(funcName);
+    if (addr)
+        return addr;
+
+    VkLayerDispatchTable *pDisp =  device_dispatch_table(device);
+    if (deviceMap.size() != 0 &&
+        deviceMap[pDisp].deviceSwapchainExtensionEnabled)
+    {
+        if (!strcmp("vkGetSurfacePropertiesKHR", funcName))
+            return reinterpret_cast<PFN_vkVoidFunction>(vkGetSurfacePropertiesKHR);
+        if (!strcmp("vkGetSurfaceFormatsKHR", funcName))
+            return reinterpret_cast<PFN_vkVoidFunction>(vkGetSurfaceFormatsKHR);
+        if (!strcmp("vkGetSurfacePresentModesKHR", funcName))
+            return reinterpret_cast<PFN_vkVoidFunction>(vkGetSurfacePresentModesKHR);
+        if (!strcmp("vkCreateSwapchainKHR", funcName))
+            return reinterpret_cast<PFN_vkVoidFunction>(vkCreateSwapchainKHR);
+        if (!strcmp("vkDestroySwapchainKHR", funcName))
+            return reinterpret_cast<PFN_vkVoidFunction>(vkDestroySwapchainKHR);
+        if (!strcmp("vkGetSwapchainImagesKHR", funcName))
+            return reinterpret_cast<PFN_vkVoidFunction>(vkGetSwapchainImagesKHR);
+        if (!strcmp("vkAcquireNextImageKHR", funcName))
+            return reinterpret_cast<PFN_vkVoidFunction>(vkAcquireNextImageKHR);
+        if (!strcmp("vkQueuePresentKHR", funcName))
+            return reinterpret_cast<PFN_vkVoidFunction>(vkQueuePresentKHR);
+    }
+    {
+        if (pDisp->GetDeviceProcAddr == NULL)
+            return NULL;
+        return pDisp->GetDeviceProcAddr(device, funcName);
+    }
+}
+
+VK_LAYER_EXPORT PFN_vkVoidFunction VKAPI vkGetInstanceProcAddr(VkInstance instance, const char* funcName)
+{
+    PFN_vkVoidFunction addr;
+    if (instance == VK_NULL_HANDLE) {
+        return NULL;
+    }
+    loader_platform_thread_once(&initOnce, initSwapchain);
+
+    /* loader uses this to force layer initialization; instance object is wrapped */
+    if (!strcmp("vkGetInstanceProcAddr", funcName)) {
+        initInstanceTable((const VkBaseLayerObject *) instance);
+        return (PFN_vkVoidFunction) vkGetInstanceProcAddr;
+    }
+
+    addr = layer_intercept_instance_proc(funcName);
+    if (addr)
+        return addr;
+
+    VkLayerInstanceDispatchTable* pTable = instance_dispatch_table(instance);
+    if (instanceMap.size() != 0 &&
+        instanceMap[instance].swapchainExtensionEnabled)
+    {
+        if (!strcmp("vkGetPhysicalDeviceSurfaceSupportKHR", funcName))
+            return reinterpret_cast<PFN_vkVoidFunction>(vkGetPhysicalDeviceSurfaceSupportKHR);
+    }
+
+    if (pTable->GetInstanceProcAddr == NULL)
+        return NULL;
+    return pTable->GetInstanceProcAddr(instance, funcName);
+}
+
diff --git a/layers/swapchain.h b/layers/swapchain.h
new file mode 100644 (file)
index 0000000..1f99b6f
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ *
+ * Authors:
+ *   Ian Elliott <ian@lunarg.com>
+ */
+
+#ifndef SWAPCHAIN_H
+#define SWAPCHAIN_H
+
+#include <stdio.h>
+#include <string.h>
+#include <unordered_map>
+#include "vk_loader_platform.h"
+#include "vk_layer.h"
+#include "vk_layer_config.h"
+#include "vk_layer_logging.h"
+#include "vk_layer_extension_utils.h"
+
+using namespace std;
+
+// The following is for logging error messages:
+typedef struct _layer_data {
+    debug_report_data report_data;
+    VkDbgMsgCallback logging_callback;
+} layer_data;
+#define LAYER_NAME (char *) "Swapchain"
+#define LOG_ERROR_NON_VALID_OBJ(objType, type, obj)                     \
+    log_msg(&mydata.report_data, VK_DBG_REPORT_ERROR_BIT, (objType),    \
+            (uint64_t) (obj), 0, 0, LAYER_NAME,                         \
+            "%s() called with a non-valid %s.", __FUNCTION__, (obj))
+
+#define LOG_ERROR(objType, type, obj, fmt, ...)                         \
+    log_msg(&mydata.report_data, VK_DBG_REPORT_ERROR_BIT, (objType),    \
+            (uint64_t) (obj), 0, 0, LAYER_NAME, (fmt), __VA_ARGS__)
+
+
+// NOTE: The following struct's/typedef's are for keeping track of
+// info that is used for validating the WSI extensions.
+
+// Forward declarations:
+struct _SwpInstance;
+struct _SwpPhysicalDevice;
+struct _SwpDevice;
+struct _SwpSwapchain;
+struct _SwpImage;
+
+typedef _SwpInstance SwpInstance;
+typedef _SwpPhysicalDevice SwpPhysicalDevice;
+typedef _SwpDevice SwpDevice;
+typedef _SwpSwapchain SwpSwapchain;
+typedef _SwpImage SwpImage;
+
+// Create one of these for each VkInstance:
+struct _SwpInstance {
+    // The actual handle for this VkInstance:
+    VkInstance instance;
+
+    // When vkEnumeratePhysicalDevices is called, the VkPhysicalDevice's are
+    // remembered:
+    unordered_map<const void*, SwpPhysicalDevice*> physicalDevices;
+
+    // Set to true if "VK_EXT_KHR_swapchain" was enabled for this VkInstance:
+    bool swapchainExtensionEnabled;
+
+    // TODO: Add additional booleans for platform-specific extensions:
+};
+
+// Create one of these for each VkPhysicalDevice within a VkInstance:
+struct _SwpPhysicalDevice {
+    // The actual handle for this VkPhysicalDevice:
+    VkPhysicalDevice physicalDevice;
+
+    // Corresponding VkDevice (and info) to this VkPhysicalDevice:
+    SwpDevice *pDevice;
+
+    // VkInstance that this VkPhysicalDevice is associated with:
+    SwpInstance *pInstance;
+
+    // Which queueFamilyIndices support presenting with WSI swapchains:
+    unordered_map<uint32_t, VkBool32> queueFamilyIndexSupport;
+};
+
+// Create one of these for each VkDevice within a VkInstance:
+struct _SwpDevice {
+    // The actual handle for this VkDevice:
+    VkDevice device;
+
+    // Corresponding VkPhysicalDevice (and info) to this VkDevice:
+    SwpPhysicalDevice *pPhysicalDevice;
+
+    // Set to true if "VK_EXT_KHR_device_swapchain" was enabled:
+    bool deviceSwapchainExtensionEnabled;
+
+// TODO: Record/use this info per-surface, not per-device, once a
+// non-dispatchable surface object is added to WSI:
+    // Results of vkGetSurfacePropertiesKHR():
+    bool gotSurfaceProperties;
+    VkSurfacePropertiesKHR surfaceProperties;
+
+// TODO: Record/use this info per-surface, not per-device, once a
+// non-dispatchable surface object is added to WSI:
+    // Count and VkSurfaceFormatKHR's returned by vkGetSurfaceFormatsKHR():
+    uint32_t surfaceFormatCount;
+    VkSurfaceFormatKHR* pSurfaceFormats;
+
+// TODO: Record/use this info per-surface, not per-device, once a
+// non-dispatchable surface object is added to WSI:
+    // Count and VkPresentModeKHR's returned by vkGetSurfacePresentModesKHR():
+    uint32_t presentModeCount;
+    VkPresentModeKHR* pPresentModes;
+
+    // When vkCreateSwapchainKHR is called, the VkSwapchainKHR's are
+    // remembered:
+    unordered_map<uint64_t, SwpSwapchain*> swapchains;
+};
+
+// Create one of these for each VkImage within a VkSwapchainKHR:
+struct _SwpImage {
+    // The actual handle for this VkImage:
+    VkImage image;
+
+    // Corresponding VkSwapchainKHR (and info) to this VkImage:
+    SwpSwapchain *pSwapchain;
+
+    // true if application got this image from vkAcquireNextImageKHR(), and
+    // hasn't yet called vkQueuePresentKHR() for it; otherwise false:
+    bool ownedByApp;
+};
+
+// Create one of these for each VkSwapchainKHR within a VkDevice:
+struct _SwpSwapchain {
+    // The actual handle for this VkSwapchainKHR:
+    VkSwapchainKHR swapchain;
+
+    // Corresponding VkDevice (and info) to this VkSwapchainKHR:
+    SwpDevice *pDevice;
+
+    // When vkGetSwapchainImagesKHR is called, the VkImage's are
+    // remembered:
+    uint32_t imageCount;
+    unordered_map<int, SwpImage> images;
+};
+
+#endif // SWAPCHAIN_H
index 7251ff9e8a9d67035a66e5d0c7f85b915f257b43..233877116b28f435a4406df6eed0f65e3caf0c35 100644 (file)
@@ -68,6 +68,11 @@ ShaderCheckerDebugAction = VK_DBG_LAYER_ACTION_LOG_MSG
 ShaderCheckerReportFlags = error,warn,perf
 ShaderCheckerLogFilename = stdout
 
+# Swapchain Settings
+SwapchainDebugAction = VK_DBG_LAYER_ACTION_LOG_MSG
+SwapchainReportFlags = error,warn,perf
+SwapchainLogFilename = stdout
+
 # Threading Settings
 ThreadingDebugAction = VK_DBG_LAYER_ACTION_LOG_MSG
 ThreadingReportFlags = error,warn,perf
diff --git a/layers/windows/swapchain.json b/layers/windows/swapchain.json
new file mode 100644 (file)
index 0000000..7ae89db
--- /dev/null
@@ -0,0 +1,11 @@
+{
+    "file_format_version" : "0.9.0",
+    "layer" : {
+        "name": "Swapchain",
+        "type": "GLOBAL",
+        "library_path": ".\\VKLayerSwapchain.dll",
+        "abi_versions": "1.0.0",
+        "implementation_version": "1.0.0",
+        "description": "LunarG Validation Layer"
+    }
+}
index e6f2387f9b8a840443d39a0c2fd8f5044401942b..fc2fc99560637492740a2191ab4cd22a01276269 100644 (file)
@@ -73,13 +73,13 @@ VkResult wsi_swapchain_GetPhysicalDeviceSurfaceSupportKHR(
     const VkLayerInstanceDispatchTable *disp;
 // TBD/TODO: DO WE NEED TO DO LOCKING FOR THIS FUNCTION?
     disp = loader_get_instance_dispatch(physicalDevice);
-    loader_platform_thread_lock_mutex(&loader_lock);
+//    loader_platform_thread_lock_mutex(&loader_lock);
     VkResult res = disp->GetPhysicalDeviceSurfaceSupportKHR(
                                                       physicalDevice,
                                                       queueNodeIndex,
                                                       pSurfaceDescription,
                                                       pSupported);
-    loader_platform_thread_unlock_mutex(&loader_lock);
+//    loader_platform_thread_unlock_mutex(&loader_lock);
     return res;
 }
 
index 2d235ef6d6c0e353aa353576e5a64c97c35b5a62..f5c2319482434e8771cbf4aa01b76138c9ae9a27 100755 (executable)
@@ -314,7 +314,8 @@ class WinDefFileSubcommand(Subcommand):
         body.append("EXPORTS")
 
         for proto in self.exports:
-            body.append( proto)
+            if self.library != "VKLayerSwapchain" or proto != "vkEnumerateInstanceExtensionProperties" and proto != "vkEnumerateInstanceLayerProperties":
+                body.append( proto)
 
         return "\n".join(body)