tests: Expand layer validation test coverage

Added layer validation tests for several ObjectTracker and
MemTracker validation cases. Also improved error handling
in object tracker layer to handle validation failures that
could cause unrecoverable driver errors.
diff --git a/layers/mem_tracker.cpp b/layers/mem_tracker.cpp
index 3d3a996..98fdb64 100644
--- a/layers/mem_tracker.cpp
+++ b/layers/mem_tracker.cpp
@@ -482,7 +482,7 @@
         char str[1024];
         sprintf(str, "Attempting to free memory object %p which still contains %lu references",
             pMemObjInfo->mem, (cmdBufRefCount + objRefCount));
-        layerCbMsg(VK_DBG_MSG_ERROR, VK_VALIDATION_LEVEL_0, pMemObjInfo->mem, 0, MEMTRACK_INTERNAL_ERROR, "MEM", str);
+        layerCbMsg(VK_DBG_MSG_UNKNOWN, VK_VALIDATION_LEVEL_0, pMemObjInfo->mem, 0, MEMTRACK_INTERNAL_ERROR, "MEM", str);
     }
 
     if (cmdBufRefCount > 0 && pMemObjInfo->pCmdBufferBindings.size() > 0) {
@@ -504,6 +504,7 @@
         // Clear the list of hanging references
         pMemObjInfo->pObjBindings.clear();
     }
+
 }
 
 static void deleteMemObjInfo(
@@ -583,10 +584,6 @@
 
             // Now verify that no references to this mem obj remain
             if (0 != pInfo->refCount) {
-                // If references remain, report the error and can search CB list to find references
-                char str[1024];
-                sprintf(str, "Freeing mem obj %p while it still has references", (void*)mem);
-                layerCbMsg(VK_DBG_MSG_ERROR, VK_VALIDATION_LEVEL_0, mem, 0, MEMTRACK_FREED_MEM_REF, "MEM", str);
                 reportMemReferencesAndCleanUp(pInfo);
                 result = VK_FALSE;
             }
@@ -668,6 +665,7 @@
         if (!pInfo) {
             sprintf(str, "While trying to bind mem for obj %p, couldn't find info for mem obj %p", (void*)object, (void*)mem);
             layerCbMsg(VK_DBG_MSG_ERROR, VK_VALIDATION_LEVEL_0, mem, 0, MEMTRACK_INVALID_MEM_OBJ, "MEM", str);
+            return VK_FALSE;
         } else {
             // Search for object in memory object's binding list
             bool32_t found  = VK_FALSE;
@@ -930,7 +928,7 @@
     size_t              *pDataSize,
     void                *pData)
 {
-    /* This entrypoint is NOT going to init it's own dispatch table since loader calls here early */
+    // This entrypoint is NOT going to init its own dispatch table since loader calls here early
     VkExtensionProperties *ext_props;
     uint32_t *count;
 
@@ -1055,14 +1053,16 @@
      * all API objects referencing it and that it is not referenced by any queued command buffers
      */
     loader_platform_thread_lock_mutex(&globalLock);
-    if (VK_FALSE == freeMemObjInfo(mem, false)) {
-        char str[1024];
-        sprintf(str, "Issue while freeing mem obj %p", (void*)mem);
-        layerCbMsg(VK_DBG_MSG_ERROR, VK_VALIDATION_LEVEL_0, mem, 0, MEMTRACK_FREE_MEM_ERROR, "MEM", str);
-    }
+    bool32_t noerror = freeMemObjInfo(mem, false);
     printMemList();
     printObjList();
     printCBList();
+    // Output an warning message for proper error/warning handling
+    if (noerror == VK_FALSE) {
+        char str[1024];
+        sprintf(str, "Freeing memory object while it still has references: mem obj %p", (void*)mem);
+        layerCbMsg(VK_DBG_MSG_WARNING, VK_VALIDATION_LEVEL_0, mem, 0, MEMTRACK_FREED_MEM_REF, "MEM", str);
+    }
     loader_platform_thread_unlock_mutex(&globalLock);
     VkResult result = nextTable.FreeMemory(device, mem);
     return result;
@@ -1092,7 +1092,7 @@
     MT_MEM_OBJ_INFO *pMemObj = getMemObjInfo(mem);
     if ((pMemObj->allocInfo.memProps & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) {
         char str[1024];
-        sprintf(str, "Mapping Memory (%p) without VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT set", (void*)mem);
+        sprintf(str, "Mapping Memory without VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT set: mem obj %p", (void*)mem);
         layerCbMsg(VK_DBG_MSG_ERROR, VK_VALIDATION_LEVEL_0, mem, 0, MEMTRACK_INVALID_STATE, "MEM", str);
     }
     loader_platform_thread_unlock_mutex(&globalLock);
diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp
index f00a465..c4d569c 100644
--- a/tests/layer_validation_tests.cpp
+++ b/tests/layer_validation_tests.cpp
@@ -3,6 +3,52 @@
 #include "gtest-1.7.0/include/gtest/gtest.h"
 #include "vkrenderframework.h"
 
+#define GLM_FORCE_RADIANS
+#include "glm/glm.hpp"
+#include <glm/gtc/matrix_transform.hpp>
+
+//--------------------------------------------------------------------------------------
+// Mesh and VertexFormat Data
+//--------------------------------------------------------------------------------------
+struct Vertex
+{
+    float posX, posY, posZ, posW;    // Position data
+    float r, g, b, a;                // Color
+};
+
+#define XYZ1(_x_, _y_, _z_)         (_x_), (_y_), (_z_), 1.f
+
+typedef enum _BsoFailSelect {
+    BsoFailNone                     = 0x00000000,
+    BsoFailRaster                   = 0x00000001,
+    BsoFailViewport                 = 0x00000002,
+    BsoFailColorBlend               = 0x00000004,
+    BsoFailDepthStencil             = 0x00000008,
+} BsoFailSelect;
+
+struct vktriangle_vs_uniform {
+    // Must start with MVP
+    float   mvp[4][4];
+    float   position[3][4];
+    float   color[3][4];
+};
+
+static const char *bindStateVertShaderText =
+        "#version 130\n"
+        "vec2 vertices[3];\n"
+        "void main() {\n"
+        "      vertices[0] = vec2(-1.0, -1.0);\n"
+        "      vertices[1] = vec2( 1.0, -1.0);\n"
+        "      vertices[2] = vec2( 0.0,  1.0);\n"
+        "   gl_Position = vec4(vertices[gl_VertexID % 3], 0.0, 1.0);\n"
+        "}\n";
+
+static const char *bindStateFragShaderText =
+   "#version 130\n"
+   "void main() {\n"
+   "   gl_FragColor = vec4(0,1,0,1);\n"
+   "}\n";
+
 void VKAPI myDbgFunc(
     VK_DBG_MSG_TYPE     msgType,
     VkValidationLevel validationLevel,
@@ -60,6 +106,7 @@
     pthread_mutex_t        m_mutex;
     bool*                  m_bailout;
 };
+
 void VKAPI myDbgFunc(
     VK_DBG_MSG_TYPE      msgType,
     VkValidationLevel    validationLevel,
@@ -74,11 +121,14 @@
         errMonitor->SetState(msgType, pMsg);
     }
 }
+
 class VkLayerTest : public VkRenderFramework
 {
 public:
     VkResult BeginCommandBuffer(VkCommandBufferObj &cmdBuffer);
     VkResult EndCommandBuffer(VkCommandBufferObj &cmdBuffer);
+    void VKTriangleTest(const char *vertShaderText, const char *fragShaderText, BsoFailSelect failMask);
+    void GenericDrawPreparation(VkCommandBufferObj *cmdBuffer, VkPipelineObj &pipelineobj, VkDescriptorSetObj &descriptorSet, BsoFailSelect failMask);
 
 protected:
         VkMemoryRefManager         m_memoryRefManager;
@@ -131,6 +181,7 @@
         delete m_errorMonitor;
     }
 };
+
 VkResult VkLayerTest::BeginCommandBuffer(VkCommandBufferObj &cmdBuffer)
 {
     VkResult result;
@@ -159,6 +210,445 @@
     return result;
 }
 
+void VkLayerTest::VKTriangleTest(const char *vertShaderText, const char *fragShaderText, BsoFailSelect failMask)
+{
+    // Create identity matrix
+    int i;
+    struct vktriangle_vs_uniform data;
+
+    glm::mat4 Projection = glm::mat4(1.0f);
+    glm::mat4 View       = glm::mat4(1.0f);
+    glm::mat4 Model      = glm::mat4(1.0f);
+    glm::mat4 MVP        = Projection * View * Model;
+    const int matrixSize = sizeof(MVP);
+    const int bufSize    = sizeof(vktriangle_vs_uniform) / sizeof(float);
+
+    memcpy(&data.mvp, &MVP[0][0], matrixSize);
+
+    static const Vertex tri_data[] =
+    {
+        { XYZ1( -1, -1, 0 ), XYZ1( 1.f, 0.f, 0.f ) },
+        { XYZ1(  1, -1, 0 ), XYZ1( 0.f, 1.f, 0.f ) },
+        { XYZ1(  0,  1, 0 ), XYZ1( 0.f, 0.f, 1.f ) },
+    };
+
+    for (i=0; i<3; i++) {
+        data.position[i][0] = tri_data[i].posX;
+        data.position[i][1] = tri_data[i].posY;
+        data.position[i][2] = tri_data[i].posZ;
+        data.position[i][3] = tri_data[i].posW;
+        data.color[i][0]    = tri_data[i].r;
+        data.color[i][1]    = tri_data[i].g;
+        data.color[i][2]    = tri_data[i].b;
+        data.color[i][3]    = tri_data[i].a;
+    }
+
+    ASSERT_NO_FATAL_FAILURE(InitState());
+    ASSERT_NO_FATAL_FAILURE(InitViewport());
+
+    VkConstantBufferObj constantBuffer(m_device, bufSize*2, sizeof(float), (const void*) &data);
+
+    VkShaderObj vs(m_device,vertShaderText,VK_SHADER_STAGE_VERTEX, this);
+    VkShaderObj ps(m_device,fragShaderText, VK_SHADER_STAGE_FRAGMENT, this);
+
+    VkPipelineObj pipelineobj(m_device);
+    pipelineobj.AddShader(&vs);
+    pipelineobj.AddShader(&ps);
+
+    VkDescriptorSetObj descriptorSet(m_device);
+    descriptorSet.AppendBuffer(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, constantBuffer);
+
+    ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
+    VkCommandBufferObj cmdBuffer(m_device);
+    cmdBuffer.AddRenderTarget(m_renderTargets[0]);
+
+    ASSERT_VK_SUCCESS(BeginCommandBuffer(cmdBuffer));
+
+    GenericDrawPreparation(&cmdBuffer, pipelineobj, descriptorSet, failMask);
+
+    // render triangle
+    cmdBuffer.Draw(0, 3, 0, 1);
+
+    // finalize recording of the command buffer
+    EndCommandBuffer(cmdBuffer);
+
+    cmdBuffer.QueueCommandBuffer();
+}
+
+void VkLayerTest::GenericDrawPreparation(VkCommandBufferObj *cmdBuffer, VkPipelineObj &pipelineobj, VkDescriptorSetObj &descriptorSet, BsoFailSelect failMask)
+{
+    if (m_depthStencil->Initialized()) {
+        cmdBuffer->ClearAllBuffers(m_clear_color, m_depth_clear_color, m_stencil_clear_color, m_depthStencil);
+    } else {
+        cmdBuffer->ClearAllBuffers(m_clear_color, m_depth_clear_color, m_stencil_clear_color, NULL);
+    }
+
+    cmdBuffer->PrepareAttachments();
+    if ((failMask & BsoFailRaster) != BsoFailRaster) {
+        cmdBuffer->BindStateObject(VK_STATE_BIND_POINT_RASTER, m_stateRaster);
+    }
+    if ((failMask & BsoFailViewport) != BsoFailViewport) {
+        cmdBuffer->BindStateObject(VK_STATE_BIND_POINT_VIEWPORT, m_stateViewport);
+    }
+    if ((failMask & BsoFailColorBlend) != BsoFailColorBlend) {
+        cmdBuffer->BindStateObject(VK_STATE_BIND_POINT_COLOR_BLEND, m_colorBlend);
+    }
+    if ((failMask & BsoFailDepthStencil) != BsoFailDepthStencil) {
+        cmdBuffer->BindStateObject(VK_STATE_BIND_POINT_DEPTH_STENCIL, m_stateDepthStencil);
+    }
+    descriptorSet.CreateVKDescriptorSet(cmdBuffer);
+    pipelineobj.CreateVKPipeline(descriptorSet);
+    cmdBuffer->BindPipeline(pipelineobj);
+    cmdBuffer->BindDescriptorSet(descriptorSet);
+}
+
+// ********************************************************************************************************************
+// ********************************************************************************************************************
+// ********************************************************************************************************************
+// ********************************************************************************************************************
+
+TEST_F(VkLayerTest, MapMemWithoutHostVisibleBit)
+{
+    VK_DBG_MSG_TYPE msgType;
+    std::string     msgString;
+    VkResult        err;
+
+    ASSERT_NO_FATAL_FAILURE(InitState());
+    m_errorMonitor->ClearState();
+
+    // Create an image, allocate memory, free it, and then try to bind it
+    VkImage               image;
+    VkDeviceMemory       *mem;
+    VkMemoryRequirements *mem_reqs;
+
+    const VkFormat tex_format      = VK_FORMAT_B8G8R8A8_UNORM;
+    const int32_t  tex_width       = 32;
+    const int32_t  tex_height      = 32;
+    size_t         mem_reqs_size   = sizeof(VkMemoryRequirements);
+    uint32_t       num_allocations = 0;
+    size_t         num_alloc_size  = sizeof(num_allocations);
+
+    const VkImageCreateInfo image_create_info = {
+        .sType          = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+        .pNext          = NULL,
+        .imageType      = VK_IMAGE_TYPE_2D,
+        .format         = tex_format,
+        .extent         = { tex_width, tex_height, 1 },
+        .mipLevels      = 1,
+        .arraySize      = 1,
+        .samples        = 1,
+        .tiling         = VK_IMAGE_TILING_LINEAR,
+        .usage          = VK_IMAGE_USAGE_SAMPLED_BIT,
+        .flags          = 0,
+    };
+    VkMemoryAllocInfo mem_alloc = {
+        .sType          = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO,
+        .pNext          = NULL,
+        .allocationSize = 0,
+        // Introduce failure, do NOT set memProps to VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
+        .memProps       = 0,
+        .memPriority    = VK_MEMORY_PRIORITY_NORMAL,
+    };
+
+    err = vkCreateImage(m_device->device(), &image_create_info, &image);
+    ASSERT_VK_SUCCESS(err);
+
+    err = vkGetObjectInfo(m_device->device(),
+                          VK_OBJECT_TYPE_IMAGE,
+                          image,
+                          VK_OBJECT_INFO_TYPE_MEMORY_ALLOCATION_COUNT,
+                          &num_alloc_size,
+                          &num_allocations);
+    ASSERT_VK_SUCCESS(err);
+
+    mem_reqs = new VkMemoryRequirements[num_allocations];
+    mem      = new VkDeviceMemory[num_allocations];
+
+    err = vkGetObjectInfo(m_device->device(),
+                          VK_OBJECT_TYPE_IMAGE,
+                          image,
+                          VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS,
+                          &mem_reqs_size,
+                          mem_reqs);
+    ASSERT_VK_SUCCESS(err);
+
+    mem_alloc.allocationSize = mem_reqs[0].size;
+
+    // allocate memory
+    err = vkAllocMemory(m_device->device(), &mem_alloc, &(mem[0]));
+    ASSERT_VK_SUCCESS(err);
+
+    // Try to bind free memory that has been freed
+    err = vkBindObjectMemory(m_device->device(), VK_OBJECT_TYPE_IMAGE, image, 0, mem[0], 0);
+    ASSERT_VK_SUCCESS(err);
+
+    // Map memory as if to initialize the image
+    void *mappedAddress = NULL;
+    err = vkMapMemory(m_device->device(), mem[0], 0, 0, 0, &mappedAddress);
+
+    msgType = m_errorMonitor->GetState(&msgString);
+    ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error while tring to map memory not visible to CPU";
+    if (!strstr(msgString.c_str(),"Mapping Memory without VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT")) {
+        FAIL() << "Error received did not match expected error message from vkMapMemory in MemTracker";
+    }
+}
+
+TEST_F(VkLayerTest, BindInvalidMemory)
+{
+    VK_DBG_MSG_TYPE msgType;
+    std::string     msgString;
+    VkResult        err;
+
+    ASSERT_NO_FATAL_FAILURE(InitState());
+    m_errorMonitor->ClearState();
+
+    // Create an image, allocate memory, free it, and then try to bind it
+    VkImage               image;
+    VkDeviceMemory       *mem;
+    VkMemoryRequirements *mem_reqs;
+
+    const VkFormat tex_format      = VK_FORMAT_B8G8R8A8_UNORM;
+    const int32_t  tex_width       = 32;
+    const int32_t  tex_height      = 32;
+    size_t         mem_reqs_size   = sizeof(VkMemoryRequirements);
+    uint32_t       num_allocations = 0;
+    size_t         num_alloc_size  = sizeof(num_allocations);
+
+    const VkImageCreateInfo image_create_info = {
+        .sType          = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+        .pNext          = NULL,
+        .imageType      = VK_IMAGE_TYPE_2D,
+        .format         = tex_format,
+        .extent         = { tex_width, tex_height, 1 },
+        .mipLevels      = 1,
+        .arraySize      = 1,
+        .samples        = 1,
+        .tiling         = VK_IMAGE_TILING_LINEAR,
+        .usage          = VK_IMAGE_USAGE_SAMPLED_BIT,
+        .flags          = 0,
+    };
+    VkMemoryAllocInfo mem_alloc = {
+        .sType          = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO,
+        .pNext          = NULL,
+        .allocationSize = 0,
+        .memProps       = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
+        .memPriority    = VK_MEMORY_PRIORITY_NORMAL,
+    };
+
+    err = vkCreateImage(m_device->device(), &image_create_info, &image);
+    ASSERT_VK_SUCCESS(err);
+
+    err = vkGetObjectInfo(m_device->device(),
+                          VK_OBJECT_TYPE_IMAGE,
+                          image,
+                          VK_OBJECT_INFO_TYPE_MEMORY_ALLOCATION_COUNT,
+                          &num_alloc_size,
+                          &num_allocations);
+    ASSERT_VK_SUCCESS(err);
+
+    mem_reqs = new VkMemoryRequirements[num_allocations];
+    mem      = new VkDeviceMemory[num_allocations];
+
+    err = vkGetObjectInfo(m_device->device(),
+                          VK_OBJECT_TYPE_IMAGE,
+                          image,
+                          VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS,
+                          &mem_reqs_size,
+                          mem_reqs);
+    ASSERT_VK_SUCCESS(err);
+
+    mem_alloc.allocationSize = mem_reqs[0].size;
+
+    // allocate memory
+    err = vkAllocMemory(m_device->device(), &mem_alloc, &(mem[0]));
+    ASSERT_VK_SUCCESS(err);
+
+    // Introduce validation failure, free memory before binding
+    vkFreeMemory(m_device->device(), mem[0]);
+    ASSERT_VK_SUCCESS(err);
+
+    // Try to bind free memory that has been freed
+    err = vkBindObjectMemory(m_device->device(), VK_OBJECT_TYPE_IMAGE, image, 0, mem[0], 0);
+    ASSERT_VK_SUCCESS(err);
+
+    msgType = m_errorMonitor->GetState(&msgString);
+    ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error while tring to bind a freed memory object";
+    if (!strstr(msgString.c_str(),"Unable to set object")) {
+        FAIL() << "Error received did not match expected error message from BindObjectMemory in MemTracker";
+    }
+}
+
+TEST_F(VkLayerTest, FreeBoundMemory)
+{
+    VK_DBG_MSG_TYPE msgType;
+    std::string     msgString;
+    VkResult        err;
+
+    ASSERT_NO_FATAL_FAILURE(InitState());
+    m_errorMonitor->ClearState();
+
+    // Create an image, allocate memory, free it, and then try to bind it
+    VkImage               image;
+    VkDeviceMemory       *mem;
+    VkMemoryRequirements *mem_reqs;
+
+    const VkFormat tex_format      = VK_FORMAT_B8G8R8A8_UNORM;
+    const int32_t  tex_width       = 32;
+    const int32_t  tex_height      = 32;
+    size_t         mem_reqs_size   = sizeof(VkMemoryRequirements);
+    uint32_t       num_allocations = 0;
+    size_t         num_alloc_size  = sizeof(num_allocations);
+
+    const VkImageCreateInfo image_create_info = {
+        .sType          = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+        .pNext          = NULL,
+        .imageType      = VK_IMAGE_TYPE_2D,
+        .format         = tex_format,
+        .extent         = { tex_width, tex_height, 1 },
+        .mipLevels      = 1,
+        .arraySize      = 1,
+        .samples        = 1,
+        .tiling         = VK_IMAGE_TILING_LINEAR,
+        .usage          = VK_IMAGE_USAGE_SAMPLED_BIT,
+        .flags          = 0,
+    };
+    VkMemoryAllocInfo mem_alloc = {
+        .sType          = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO,
+        .pNext          = NULL,
+        .allocationSize = 0,
+        .memProps       = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
+        .memPriority    = VK_MEMORY_PRIORITY_NORMAL,
+    };
+
+    err = vkCreateImage(m_device->device(), &image_create_info, &image);
+    ASSERT_VK_SUCCESS(err);
+
+    err = vkGetObjectInfo(m_device->device(),
+                          VK_OBJECT_TYPE_IMAGE,
+                          image,
+                          VK_OBJECT_INFO_TYPE_MEMORY_ALLOCATION_COUNT,
+                          &num_alloc_size,
+                          &num_allocations);
+    ASSERT_VK_SUCCESS(err);
+
+    mem_reqs = new VkMemoryRequirements[num_allocations];
+    mem      = new VkDeviceMemory[num_allocations];
+
+    err = vkGetObjectInfo(m_device->device(),
+                          VK_OBJECT_TYPE_IMAGE,
+                          image,
+                          VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS,
+                          &mem_reqs_size,
+                          mem_reqs);
+    ASSERT_VK_SUCCESS(err);
+
+    mem_alloc.allocationSize = mem_reqs[0].size;
+
+    // allocate memory
+    err = vkAllocMemory(m_device->device(), &mem_alloc, &(mem[0]));
+    ASSERT_VK_SUCCESS(err);
+
+    // Bind memory to Image object
+    err = vkBindObjectMemory(m_device->device(), VK_OBJECT_TYPE_IMAGE, image, 0, mem[0], 0);
+    ASSERT_VK_SUCCESS(err);
+
+    // Introduce validation failure, free memory while still bound to object
+    vkFreeMemory(m_device->device(), mem[0]);
+    ASSERT_VK_SUCCESS(err);
+
+    msgType = m_errorMonitor->GetState(&msgString);
+    ASSERT_EQ(msgType, VK_DBG_MSG_WARNING) << "Did not receive an warning while tring to free bound memory";
+    if (!strstr(msgString.c_str(),"Freeing memory object while it still has references")) {
+        FAIL() << "Warning received did not match expected message from freeMemObjInfo  in MemTracker";
+    }
+}
+
+
+TEST_F(VkLayerTest, BindMemoryToDestroyedObject)
+{
+    VK_DBG_MSG_TYPE msgType;
+    std::string     msgString;
+    VkResult        err;
+
+    ASSERT_NO_FATAL_FAILURE(InitState());
+    m_errorMonitor->ClearState();
+
+    // Create an image object, allocate memory, destroy the object and then try to bind it
+    VkImage               image;
+    VkDeviceMemory       *mem;
+    VkMemoryRequirements *mem_reqs;
+
+    const VkFormat tex_format      = VK_FORMAT_B8G8R8A8_UNORM;
+    const int32_t  tex_width       = 32;
+    const int32_t  tex_height      = 32;
+    size_t         mem_reqs_size   = sizeof(VkMemoryRequirements);
+    uint32_t       num_allocations = 0;
+    size_t         num_alloc_size  = sizeof(num_allocations);
+
+    const VkImageCreateInfo image_create_info = {
+        .sType          = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+        .pNext          = NULL,
+        .imageType      = VK_IMAGE_TYPE_2D,
+        .format         = tex_format,
+        .extent         = { tex_width, tex_height, 1 },
+        .mipLevels      = 1,
+        .arraySize      = 1,
+        .samples        = 1,
+        .tiling         = VK_IMAGE_TILING_LINEAR,
+        .usage          = VK_IMAGE_USAGE_SAMPLED_BIT,
+        .flags          = 0,
+    };
+    VkMemoryAllocInfo mem_alloc = {
+        .sType          = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO,
+        .pNext          = NULL,
+        .allocationSize = 0,
+        .memProps       = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
+        .memPriority    = VK_MEMORY_PRIORITY_NORMAL,
+    };
+
+    err = vkCreateImage(m_device->device(), &image_create_info, &image);
+    ASSERT_VK_SUCCESS(err);
+
+    err = vkGetObjectInfo(m_device->device(),
+                          VK_OBJECT_TYPE_IMAGE,
+                          image,
+                          VK_OBJECT_INFO_TYPE_MEMORY_ALLOCATION_COUNT,
+                          &num_alloc_size,
+                          &num_allocations);
+    ASSERT_VK_SUCCESS(err);
+
+    mem_reqs = new VkMemoryRequirements[num_allocations];
+    mem      = new VkDeviceMemory[num_allocations];
+
+    err = vkGetObjectInfo(m_device->device(),
+                          VK_OBJECT_TYPE_IMAGE,
+                          image,
+                          VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS,
+                          &mem_reqs_size,
+                          mem_reqs);
+    ASSERT_VK_SUCCESS(err);
+
+    mem_alloc.allocationSize = mem_reqs[0].size;
+
+    // Allocate memory
+    err = vkAllocMemory(m_device->device(), &mem_alloc, &(mem[0]));
+    ASSERT_VK_SUCCESS(err);
+
+    // Introduce validation failure, destroy Image object before binding
+    vkDestroyObject(m_device->device(), VK_OBJECT_TYPE_IMAGE, image);
+    ASSERT_VK_SUCCESS(err);
+
+    // Now Try to bind memory to this destroyted object
+    err = vkBindObjectMemory(m_device->device(), VK_OBJECT_TYPE_IMAGE, image, 0, mem[0], 0);
+    ASSERT_VK_SUCCESS(err);
+
+    msgType = m_errorMonitor->GetState(&msgString);
+    ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error while binding memory to a destroyed object";
+    if (!strstr(msgString.c_str(),"Unable to set object")) {
+        FAIL() << "Error received did not match expected error message from updateObjectBinding in MemTracker";
+    }
+}
+
 TEST_F(VkLayerTest, SubmitSignaledFence)
 {
     vk_testing::Fence testFence;
@@ -187,7 +677,7 @@
     msgType = m_errorMonitor->GetState(&msgString);
     ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an err from using a fence in SIGNALED state in call to vkQueueSubmit";
     if (!strstr(msgString.c_str(),"submitted in SIGNALED state.  Fences must be reset before being submitted")) {
-        FAIL() << "Error received was not VkQueueSubmit with fence in SIGNALED_STATE";
+        FAIL() << "Error received was not 'VkQueueSubmit with fence in SIGNALED_STATE'";
     }
 
 }
@@ -209,7 +699,7 @@
     msgType = m_errorMonitor->GetState(&msgString);
     ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error from submitting fence with UNSIGNALED state to vkResetFences";
     if (!strstr(msgString.c_str(),"submitted to VkResetFences in UNSIGNALED STATE")) {
-        FAIL() << "Error received was not VkResetFences with fence in UNSIGNALED_STATE";
+        FAIL() << "Error received was not 'VkResetFences with fence in UNSIGNALED_STATE'";
     }
 
 }
@@ -239,7 +729,7 @@
     msgType = m_errorMonitor->GetState(&msgString);
     ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error for waiting for unsubmitted fence";
     if (!strstr(msgString.c_str(),"Waiting for Unsubmitted Fence")) {
-        FAIL() << "Error received was not Waiting for Unsubmitted Fence";
+        FAIL() << "Error received was not 'Waiting for Unsubmitted Fence'";
     }
 }
 
@@ -265,9 +755,71 @@
     msgType = m_errorMonitor->GetState(&msgString);
     ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error from mismatched types in vkGetObjectInfo";
     if (!strstr(msgString.c_str(),"does not match designated type")) {
-        FAIL() << "Error received was not event does not match designated type image";
+        FAIL() << "Error received was not 'does not match designated type'";
     }
+}
 
+TEST_F(VkLayerTest, RasterStateNotBound)
+{
+    VK_DBG_MSG_TYPE msgType;
+    std::string msgString;
+
+    TEST_DESCRIPTION("Simple Draw Call that validates failure when a raster state object is not bound beforehand");
+
+    VKTriangleTest(bindStateVertShaderText, bindStateFragShaderText, BsoFailRaster);
+
+    msgType = m_errorMonitor->GetState(&msgString);
+    ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error from Not Binding a Raster State Object";
+    if (!strstr(msgString.c_str(),"Raster object not bound to this command buffer")) {
+        FAIL() << "Error received was not 'Raster object not bound to this command buffer'";
+    }
+}
+
+TEST_F(VkLayerTest, ViewportStateNotBound)
+{
+    VK_DBG_MSG_TYPE msgType;
+    std::string msgString;
+    TEST_DESCRIPTION("Simple Draw Call that validates failure when a viewport state object is not bound beforehand");
+
+    VKTriangleTest(bindStateVertShaderText, bindStateFragShaderText, BsoFailViewport);
+
+    msgType = m_errorMonitor->GetState(&msgString);
+    ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error from Not Binding a Viewport State Object";
+    if (!strstr(msgString.c_str(),"Viewport object not bound to this command buffer")) {
+        FAIL() << "Error received was not 'Viewport object not bound to this command buffer'";
+    }
+}
+
+TEST_F(VkLayerTest, ColorBlendStateNotBound)
+{
+    VK_DBG_MSG_TYPE msgType;
+    std::string msgString;
+
+    TEST_DESCRIPTION("Simple Draw Call that validates failure when a color-blend state object is not bound beforehand");
+
+    VKTriangleTest(bindStateVertShaderText, bindStateFragShaderText, BsoFailColorBlend);
+
+    msgType = m_errorMonitor->GetState(&msgString);
+    ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error from Not Binding a ColorBlend State Object";
+    if (!strstr(msgString.c_str(),"Color-blend object not bound to this command buffer")) {
+        FAIL() << "Error received was not 'Color-blend object not bound to this command buffer'";
+    }
+}
+
+TEST_F(VkLayerTest, DepthStencilStateNotBound)
+{
+    VK_DBG_MSG_TYPE msgType;
+    std::string msgString;
+
+    TEST_DESCRIPTION("Simple Draw Call that validates failure when a depth-stencil state object is not bound beforehand");
+
+    VKTriangleTest(bindStateVertShaderText, bindStateFragShaderText, BsoFailDepthStencil);
+
+    msgType = m_errorMonitor->GetState(&msgString);
+    ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error from Not Binding a DepthStencil State Object";
+    if (!strstr(msgString.c_str(),"Depth-stencil object not bound to this command buffer")) {
+        FAIL() << "Error received was not 'Depth-stencil object not bound to this command buffer'";
+    }
 }
 
 #if GTEST_IS_THREADSAFE
@@ -358,7 +910,7 @@
     msgType = m_errorMonitor->GetState(&msgString);
     ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an err from using one VkCommandBufferObj in two threads";
     if (!strstr(msgString.c_str(),"THREADING ERROR")) {
-        FAIL() << "Error received was not THREADING ERROR";
+        FAIL() << "Error received was not 'THREADING ERROR'";
     }
 
 }
diff --git a/vk-layer-generate.py b/vk-layer-generate.py
index 369d63b..5c43a94 100755
--- a/vk-layer-generate.py
+++ b/vk-layer-generate.py
@@ -1139,11 +1139,13 @@
         header_txt.append('    return VK_FALSE;')
         header_txt.append('}')
         header_txt.append('')
-        header_txt.append('static void validate_draw_state_flags(VkObject vkObj) {')
-        header_txt.append('    validate_status(vkObj, VK_OBJECT_TYPE_COMMAND_BUFFER, OBJSTATUS_VIEWPORT_BOUND,      OBJSTATUS_VIEWPORT_BOUND,      VK_DBG_MSG_ERROR,    OBJTRACK_VIEWPORT_NOT_BOUND,      "Viewport object not bound to this command buffer");')
-        header_txt.append('    validate_status(vkObj, VK_OBJECT_TYPE_COMMAND_BUFFER, OBJSTATUS_RASTER_BOUND,        OBJSTATUS_RASTER_BOUND,        VK_DBG_MSG_ERROR,    OBJTRACK_RASTER_NOT_BOUND,        "Raster object not bound to this command buffer");')
-        header_txt.append('    validate_status(vkObj, VK_OBJECT_TYPE_COMMAND_BUFFER, OBJSTATUS_COLOR_BLEND_BOUND,   OBJSTATUS_COLOR_BLEND_BOUND,   VK_DBG_MSG_UNKNOWN,  OBJTRACK_COLOR_BLEND_NOT_BOUND,   "Color-blend object not bound to this command buffer");')
-        header_txt.append('    validate_status(vkObj, VK_OBJECT_TYPE_COMMAND_BUFFER, OBJSTATUS_DEPTH_STENCIL_BOUND, OBJSTATUS_DEPTH_STENCIL_BOUND, VK_DBG_MSG_UNKNOWN,  OBJTRACK_DEPTH_STENCIL_NOT_BOUND, "Depth-stencil object not bound to this command buffer");')
+        header_txt.append('static bool32_t validate_draw_state_flags(VkObject vkObj) {')
+        header_txt.append('    bool32_t result1, result2, result3, result4;')
+        header_txt.append('    result1 = validate_status(vkObj, VK_OBJECT_TYPE_COMMAND_BUFFER, OBJSTATUS_VIEWPORT_BOUND,      OBJSTATUS_VIEWPORT_BOUND,      VK_DBG_MSG_ERROR,  OBJTRACK_VIEWPORT_NOT_BOUND,      "Viewport object not bound to this command buffer");')
+        header_txt.append('    result2 = validate_status(vkObj, VK_OBJECT_TYPE_COMMAND_BUFFER, OBJSTATUS_RASTER_BOUND,        OBJSTATUS_RASTER_BOUND,        VK_DBG_MSG_ERROR,  OBJTRACK_RASTER_NOT_BOUND,        "Raster object not bound to this command buffer");')
+        header_txt.append('    result3 = validate_status(vkObj, VK_OBJECT_TYPE_COMMAND_BUFFER, OBJSTATUS_COLOR_BLEND_BOUND,   OBJSTATUS_COLOR_BLEND_BOUND,   VK_DBG_MSG_ERROR,  OBJTRACK_COLOR_BLEND_NOT_BOUND,   "Color-blend object not bound to this command buffer");')
+        header_txt.append('    result4 = validate_status(vkObj, VK_OBJECT_TYPE_COMMAND_BUFFER, OBJSTATUS_DEPTH_STENCIL_BOUND, OBJSTATUS_DEPTH_STENCIL_BOUND, VK_DBG_MSG_ERROR,  OBJTRACK_DEPTH_STENCIL_NOT_BOUND, "Depth-stencil object not bound to this command buffer");')
+        header_txt.append('    return ((result1 == VK_TRUE) && (result2 == VK_TRUE) && (result3 == VK_TRUE) && (result4 == VK_TRUE));')
         header_txt.append('}')
         header_txt.append('')
         return "\n".join(header_txt)
@@ -1212,8 +1214,9 @@
             using_line += '    track_object_status(cmdBuffer, stateBindPoint);\n'
             using_line += '    loader_platform_thread_unlock_mutex(&objLock);\n'
         elif 'CmdDraw' in proto.name:
-            using_line  = '    loader_platform_thread_lock_mutex(&objLock);\n'
-            using_line += '    validate_draw_state_flags(cmdBuffer);\n'
+            using_line  = '    bool32_t valid;\n'
+            using_line += '    loader_platform_thread_lock_mutex(&objLock);\n'
+            using_line += '    valid = validate_draw_state_flags(cmdBuffer);\n'
             using_line += '    loader_platform_thread_unlock_mutex(&objLock);\n'
         elif 'MapMemory' in proto.name:
             using_line  = '    loader_platform_thread_lock_mutex(&objLock);\n'
@@ -1295,7 +1298,6 @@
                      '    }\n'
                          '}' % (qual, decl, self.layer_name, ret_val, proto.c_call(), create_line, destroy_line, stmt, self.layer_name))
         elif 'GetPhysicalDeviceInfo' in proto.name:
-
             gpu_state  = '    if (infoType == VK_PHYSICAL_DEVICE_INFO_TYPE_QUEUE_PROPERTIES) {\n'
             gpu_state += '        if (pData != NULL) {\n'
             gpu_state += '            loader_platform_thread_lock_mutex(&objLock);\n'
@@ -1312,6 +1314,15 @@
                      '%s'
                      '%s'
                      '}' % (qual, decl, self.layer_name, ret_val, proto.c_call(), create_line, destroy_line, gpu_state, stmt))
+        elif 'CmdDraw' in proto.name:
+            funcs.append('%s%s\n'
+                     '{\n'
+                     '%s'
+                     '    if (valid == VK_TRUE) {\n'
+                     '        nextTable.%s;\n'
+                     '    }\n'
+                     '%s'
+                     '}' % (qual, decl, using_line, proto.c_call(), stmt))
         else:
             funcs.append('%s%s\n'
                      '{\n'