layers: Add sampler unnormalized coords checks

Adds checks for VUIDs:
    VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01072
    VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01073
    VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01074
    VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01075
    VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01077
and tests them with:
    VkLayerTest.UnnormalizedCoordinatesEnabled
diff --git a/layers/parameter_validation_utils.cpp b/layers/parameter_validation_utils.cpp
index 295f1db..17f95bb 100644
--- a/layers/parameter_validation_utils.cpp
+++ b/layers/parameter_validation_utils.cpp
@@ -2021,6 +2021,7 @@
     if (pCreateInfo != nullptr) {
         const auto &features = device_data->physical_device_features;
         const auto &limits = device_data->device_limits;
+
         if (pCreateInfo->anisotropyEnable == VK_TRUE) {
             if (!in_inclusive_range(pCreateInfo->maxAnisotropy, 1.0F, limits.maxSamplerAnisotropy)) {
                 skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
@@ -2037,14 +2038,54 @@
                                 "vkCreateSampler(): Anisotropic sampling feature is not enabled, %s must be VK_FALSE.",
                                 "pCreateInfo->anisotropyEnable");
             }
+        }
 
-            // Anistropy and unnormalized coordinates cannot be enabled simultaneously
-            if (pCreateInfo->unnormalizedCoordinates == VK_TRUE) {
+        if (pCreateInfo->unnormalizedCoordinates == VK_TRUE) {
+            if (pCreateInfo->minFilter != pCreateInfo->magFilter) {
+                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                "VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01072",
+                                "vkCreateSampler(): when pCreateInfo->unnormalizedCoordinates is VK_TRUE, "
+                                "pCreateInfo->minFilter (%s) and pCreateInfo->magFilter (%s) must be equal.",
+                                string_VkFilter(pCreateInfo->minFilter), string_VkFilter(pCreateInfo->magFilter));
+            }
+            if (pCreateInfo->mipmapMode != VK_SAMPLER_MIPMAP_MODE_NEAREST) {
+                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                "VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01073",
+                                "vkCreateSampler(): when pCreateInfo->unnormalizedCoordinates is VK_TRUE, "
+                                "pCreateInfo->mipmapMode (%s) must be VK_SAMPLER_MIPMAP_MODE_NEAREST.",
+                                string_VkSamplerMipmapMode(pCreateInfo->mipmapMode));
+            }
+            if (pCreateInfo->minLod != 0.0f || pCreateInfo->maxLod != 0.0f) {
+                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                "VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01074",
+                                "vkCreateSampler(): when pCreateInfo->unnormalizedCoordinates is VK_TRUE, "
+                                "pCreateInfo->minLod (%f) and pCreateInfo->maxLod (%f) must both be zero.",
+                                pCreateInfo->minLod, pCreateInfo->maxLod);
+            }
+            if ((pCreateInfo->addressModeU != VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE &&
+                 pCreateInfo->addressModeU != VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER) ||
+                (pCreateInfo->addressModeV != VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE &&
+                 pCreateInfo->addressModeV != VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER)) {
+                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                "VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01075",
+                                "vkCreateSampler(): when pCreateInfo->unnormalizedCoordinates is VK_TRUE, "
+                                "pCreateInfo->addressModeU (%s) and pCreateInfo->addressModeV (%s) must both be "
+                                "VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE or VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER.",
+                                string_VkSamplerAddressMode(pCreateInfo->addressModeU),
+                                string_VkSamplerAddressMode(pCreateInfo->addressModeV));
+            }
+            if (pCreateInfo->anisotropyEnable == VK_TRUE) {
                 skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
                                 "VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01076",
                                 "vkCreateSampler(): pCreateInfo->anisotropyEnable and pCreateInfo->unnormalizedCoordinates must "
                                 "not both be VK_TRUE.");
             }
+            if (pCreateInfo->compareEnable == VK_TRUE) {
+                skip |= log_msg(report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, 0,
+                                "VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01077",
+                                "vkCreateSampler(): pCreateInfo->compareEnable and pCreateInfo->unnormalizedCoordinates must "
+                                "not both be VK_TRUE.");
+            }
         }
 
         // If compareEnable is VK_TRUE, compareOp must be a valid VkCompareOp value
diff --git a/layers/vk_validation_error_database.txt b/layers/vk_validation_error_database.txt
index 63e5da8..2c30bf7 100644
--- a/layers/vk_validation_error_database.txt
+++ b/layers/vk_validation_error_database.txt
@@ -1304,12 +1304,12 @@
 VALIDATION_ERROR_1260085a~^~N~^~Unknown~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-mipLodBias-01069~^~core~^~The spec valid usage text states 'The absolute value of mipLodBias must be less than or equal to VkPhysicalDeviceLimits::maxSamplerLodBias' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-mipLodBias-01069)~^~
 VALIDATION_ERROR_1260085c~^~Y~^~AnisotropyFeatureDisabled~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-anisotropyEnable-01070~^~core~^~The spec valid usage text states 'If the anisotropic sampling feature is not enabled, anisotropyEnable must be VK_FALSE' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-anisotropyEnable-01070)~^~
 VALIDATION_ERROR_1260085e~^~Y~^~AnisotropyFeatureEnabled~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-anisotropyEnable-01071~^~core~^~The spec valid usage text states 'If anisotropyEnable is VK_TRUE, maxAnisotropy must be between 1.0 and VkPhysicalDeviceLimits::maxSamplerAnisotropy, inclusive' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-anisotropyEnable-01071)~^~
-VALIDATION_ERROR_12600860~^~N~^~Unknown~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01072~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, minFilter and magFilter must be equal' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01072)~^~
-VALIDATION_ERROR_12600862~^~N~^~Unknown~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01073~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, mipmapMode must be VK_SAMPLER_MIPMAP_MODE_NEAREST' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01073)~^~
-VALIDATION_ERROR_12600864~^~N~^~Unknown~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01074~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, minLod and maxLod must be zero' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01074)~^~
-VALIDATION_ERROR_12600866~^~N~^~Unknown~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01075~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, addressModeU and addressModeV must each be either VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE or VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01075)~^~
+VALIDATION_ERROR_12600860~^~Y~^~UnnormalizedCoordinatesEnabled~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01072~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, minFilter and magFilter must be equal' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01072)~^~
+VALIDATION_ERROR_12600862~^~Y~^~UnnormalizedCoordinatesEnabled~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01073~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, mipmapMode must be VK_SAMPLER_MIPMAP_MODE_NEAREST' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01073)~^~
+VALIDATION_ERROR_12600864~^~Y~^~UnnormalizedCoordinatesEnabled~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01074~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, minLod and maxLod must be zero' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01074)~^~
+VALIDATION_ERROR_12600866~^~Y~^~UnnormalizedCoordinatesEnabled~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01075~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, addressModeU and addressModeV must each be either VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE or VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01075)~^~
 VALIDATION_ERROR_12600868~^~Y~^~AnisotropyFeatureEnabled~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01076~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, anisotropyEnable must be VK_FALSE' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01076)~^~
-VALIDATION_ERROR_1260086a~^~N~^~Unknown~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01077~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, compareEnable must be VK_FALSE' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01077)~^~
+VALIDATION_ERROR_1260086a~^~Y~^~UnnormalizedCoordinatesEnabled~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01077~^~core~^~The spec valid usage text states 'If unnormalizedCoordinates is VK_TRUE, compareEnable must be VK_FALSE' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01077)~^~
 VALIDATION_ERROR_1260086c~^~Y~^~Unknown~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-addressModeU-01078~^~core~^~The spec valid usage text states 'If any of addressModeU, addressModeV or addressModeW are VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, borderColor must be a valid VkBorderColor value' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-addressModeU-01078)~^~
 VALIDATION_ERROR_1260086e~^~Y~^~MirrorClampToEdgeNotEnabled~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-addressModeU-01079~^~core~^~The spec valid usage text states 'If the VK_KHR_sampler_mirror_clamp_to_edge extension is not enabled, addressModeU, addressModeV and addressModeW must not be VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-addressModeU-01079)~^~
 VALIDATION_ERROR_12600870~^~Y~^~Unknown~^~VkSamplerCreateInfo~^~VUID-VkSamplerCreateInfo-compareEnable-01080~^~core~^~The spec valid usage text states 'If compareEnable is VK_TRUE, compareOp must be a valid VkCompareOp value' (https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkSamplerCreateInfo-compareEnable-01080)~^~
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index 3f6638b..9e02540 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -197,13 +197,13 @@
     sampler_create_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
     sampler_create_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
     sampler_create_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
-    sampler_create_info.mipLodBias = 1.0;
+    sampler_create_info.mipLodBias = 0.0;
     sampler_create_info.anisotropyEnable = VK_FALSE;
     sampler_create_info.maxAnisotropy = 1.0;
     sampler_create_info.compareEnable = VK_FALSE;
     sampler_create_info.compareOp = VK_COMPARE_OP_NEVER;
-    sampler_create_info.minLod = 1.0;
-    sampler_create_info.maxLod = 1.0;
+    sampler_create_info.minLod = 0.0;
+    sampler_create_info.maxLod = 16.0;
     sampler_create_info.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
     sampler_create_info.unnormalizedCoordinates = VK_FALSE;
 
@@ -1585,7 +1585,7 @@
     ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
     ASSERT_NO_FATAL_FAILURE(GetPhysicalDeviceFeatures(&device_features));
 
-    // These tests require that the device support sparse residency for 2D images
+    // These tests require that the device support anisotropic filtering
     if (VK_TRUE != device_features.samplerAnisotropy) {
         printf("%s Test requires unsupported samplerAnisotropy feature. Skipped.\n", kSkipPrefix);
         return;
@@ -1642,6 +1642,78 @@
     }
 }
 
+TEST_F(VkLayerTest, UnnormalizedCoordinatesEnabled) {
+    TEST_DESCRIPTION("Validate restrictions on sampler parameters when unnormalizedCoordinates is true.");
+
+    ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
+    VkSamplerCreateInfo sampler_info_ref = SafeSaneSamplerCreateInfo();
+    sampler_info_ref.unnormalizedCoordinates = VK_TRUE;
+    sampler_info_ref.minLod = 0.0f;
+    sampler_info_ref.maxLod = 0.0f;
+    VkSamplerCreateInfo sampler_info = sampler_info_ref;
+    ASSERT_NO_FATAL_FAILURE(InitState());
+
+    auto do_test = [this](std::string code, const VkSamplerCreateInfo *pCreateInfo) -> void {
+        VkResult err;
+        VkSampler sampler = VK_NULL_HANDLE;
+        m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, code);
+        err = vkCreateSampler(m_device->device(), pCreateInfo, NULL, &sampler);
+        m_errorMonitor->VerifyFound();
+        if (VK_SUCCESS == err) {
+            vkDestroySampler(m_device->device(), sampler, NULL);
+        }
+    };
+
+    // min and mag filters must be the same
+    sampler_info.minFilter = VK_FILTER_NEAREST;
+    sampler_info.magFilter = VK_FILTER_LINEAR;
+    do_test("VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01072", &sampler_info);
+    std::swap(sampler_info.minFilter, sampler_info.magFilter);
+    do_test("VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01072", &sampler_info);
+    sampler_info = sampler_info_ref;
+
+    // mipmapMode must be NEAREST
+    sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+    do_test("VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01073", &sampler_info);
+    sampler_info = sampler_info_ref;
+
+    // minlod and maxlod must be zero
+    sampler_info.maxLod = 3.14159f;
+    do_test("VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01074", &sampler_info);
+    sampler_info.minLod = 2.71828f;
+    do_test("VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01074", &sampler_info);
+    sampler_info = sampler_info_ref;
+
+    // addressModeU and addressModeV must both be CLAMP_TO_EDGE or CLAMP_TO_BORDER
+    // checks all 12 invalid combinations out of 16 total combinations
+    const std::array<VkSamplerAddressMode, 4> kAddressModes = {{
+        VK_SAMPLER_ADDRESS_MODE_REPEAT,
+        VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
+        VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
+        VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER,
+    }};
+    for (const auto umode : kAddressModes) {
+        for (const auto vmode : kAddressModes) {
+            if ((umode != VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE && umode != VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER) ||
+                (vmode != VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE && vmode != VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER)) {
+                sampler_info.addressModeU = umode;
+                sampler_info.addressModeV = vmode;
+                do_test("VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01075", &sampler_info);
+            }
+        }
+    }
+    sampler_info = sampler_info_ref;
+
+    // VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01076 is tested in AnisotropyFeatureEnabled above
+    // Since it requires checking/enabling the anisotropic filtering feature, it's easier to do it
+    // with the other anisotropic tests.
+
+    // compareEnable must be VK_FALSE
+    sampler_info.compareEnable = VK_TRUE;
+    do_test("VUID-VkSamplerCreateInfo-unnormalizedCoordinates-01077", &sampler_info);
+    sampler_info = sampler_info_ref;
+}
+
 TEST_F(VkLayerTest, UnrecognizedValueMaxEnum) {
     ASSERT_NO_FATAL_FAILURE(Init());
 
@@ -30118,7 +30190,7 @@
     m_commandBuffer->end();
     m_errorMonitor->VerifyNotFound();
 
-#if 0  
+#if 0
     // Copy to/from buffer
     VkBufferCreateInfo bci = {};
     bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;