blob: 8a6418e083b482c634f6019b5f5d80059ad3aca8 [file] [log] [blame]
#include <vulkan.h>
#include <vkDbg.h>
#include "gtest-1.7.0/include/gtest/gtest.h"
#include "vkrenderframework.h"
#include "layers_config.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,
VkObject srcObject,
size_t location,
int32_t msgCode,
const char* pMsg,
void* pUserData);
class ErrorMonitor {
public:
ErrorMonitor()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutex_init(&m_mutex, &attr);
pthread_mutex_lock(&m_mutex);
m_msgType = VK_DBG_MSG_UNKNOWN;
m_bailout = NULL;
pthread_mutex_unlock(&m_mutex);
}
void ClearState()
{
pthread_mutex_lock(&m_mutex);
m_msgType = VK_DBG_MSG_UNKNOWN;
m_msgString.clear();
pthread_mutex_unlock(&m_mutex);
}
VK_DBG_MSG_TYPE GetState(std::string *msgString)
{
pthread_mutex_lock(&m_mutex);
*msgString = m_msgString;
pthread_mutex_unlock(&m_mutex);
return m_msgType;
}
void SetState(VK_DBG_MSG_TYPE msgType, const char *msgString)
{
pthread_mutex_lock(&m_mutex);
if (m_bailout != NULL) {
*m_bailout = true;
}
m_msgType = msgType;
m_msgString.reserve(strlen(msgString));
m_msgString = msgString;
pthread_mutex_unlock(&m_mutex);
}
void SetBailout(bool *bailout)
{
m_bailout = bailout;
}
private:
VK_DBG_MSG_TYPE m_msgType;
std::string m_msgString;
pthread_mutex_t m_mutex;
bool* m_bailout;
};
void VKAPI myDbgFunc(
VK_DBG_MSG_TYPE msgType,
VkValidationLevel validationLevel,
VkObject srcObject,
size_t location,
int32_t msgCode,
const char* pMsg,
void* pUserData)
{
if (msgType == VK_DBG_MSG_WARNING || msgType == VK_DBG_MSG_ERROR) {
ErrorMonitor *errMonitor = (ErrorMonitor *)pUserData;
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;
ErrorMonitor *m_errorMonitor;
virtual void SetUp() {
const char *extension_names[] = {"MemTracker", "ObjectTracker", "Threading"};
const std::vector<const char *> extensions(extension_names,
extension_names + sizeof(extension_names)/sizeof(extension_names[0]));
size_t extSize = sizeof(uint32_t);
uint32_t extCount = 0;
VkResult U_ASSERT_ONLY err;
err = vkGetGlobalExtensionInfo(VK_EXTENSION_INFO_TYPE_COUNT, 0, &extSize, &extCount);
assert(!err);
VkExtensionProperties extProp;
extSize = sizeof(VkExtensionProperties);
bool32_t extFound;
for (uint32_t i = 0; i < extensions.size(); i++) {
extFound = 0;
for (uint32_t j = 0; j < extCount; j++) {
err = vkGetGlobalExtensionInfo(VK_EXTENSION_INFO_TYPE_PROPERTIES, j, &extSize, &extProp);
assert(!err);
if (!strcmp(extensions[i], extProp.extName)) {
extFound = 1;
break;
}
}
ASSERT_EQ(extFound, 1) << "ERROR: Cannot find extension named " << extensions[i] << " which is necessary to pass this test";
}
// Force layer output level to be >= WARNING so that we catch those messages but ignore others
setLayerOptionEnum("MemTrackerReportLevel", "VK_DBG_LAYER_LEVEL_WARNING");
setLayerOptionEnum("ObjectTrackerReportLevel", "VK_DBG_LAYER_LEVEL_WARNING");
setLayerOptionEnum("ThreadingReportLevel", "VK_DBG_LAYER_LEVEL_WARNING");
this->app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
this->app_info.pNext = NULL;
this->app_info.pAppName = "layer_tests";
this->app_info.appVersion = 1;
this->app_info.pEngineName = "unittest";
this->app_info.engineVersion = 1;
this->app_info.apiVersion = VK_API_VERSION;
m_errorMonitor = new ErrorMonitor;
InitFramework(extensions, myDbgFunc, m_errorMonitor);
}
virtual void TearDown() {
// Clean up resources before we reset
ShutdownFramework();
delete m_errorMonitor;
}
};
VkResult VkLayerTest::BeginCommandBuffer(VkCommandBufferObj &cmdBuffer)
{
VkResult result;
result = cmdBuffer.BeginCommandBuffer();
/*
* For render test all drawing happens in a single render pass
* on a single command buffer.
*/
if (VK_SUCCESS == result) {
cmdBuffer.BeginRenderPass(renderPass(), framebuffer());
}
return result;
}
VkResult VkLayerTest::EndCommandBuffer(VkCommandBufferObj &cmdBuffer)
{
VkResult result;
cmdBuffer.EndRenderPass(renderPass());
result = cmdBuffer.EndCommandBuffer();
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;
VK_DBG_MSG_TYPE msgType;
std::string msgString;
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.pNext = NULL;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
ASSERT_NO_FATAL_FAILURE(InitState());
ASSERT_NO_FATAL_FAILURE(InitViewport());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkCommandBufferObj cmdBuffer(m_device);
cmdBuffer.AddRenderTarget(m_renderTargets[0]);
BeginCommandBuffer(cmdBuffer);
cmdBuffer.ClearAllBuffers(m_clear_color, m_depth_clear_color, m_stencil_clear_color, NULL);
EndCommandBuffer(cmdBuffer);
testFence.init(*m_device, fenceInfo);
m_errorMonitor->ClearState();
cmdBuffer.QueueCommandBuffer(testFence.obj());
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'";
}
}
TEST_F(VkLayerTest, ResetUnsignaledFence)
{
vk_testing::Fence testFence;
VK_DBG_MSG_TYPE msgType;
std::string msgString;
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.pNext = NULL;
ASSERT_NO_FATAL_FAILURE(InitState());
testFence.init(*m_device, fenceInfo);
m_errorMonitor->ClearState();
VkFence fences[1] = {testFence.obj()};
vkResetFences(m_device->device(), 1, fences);
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'";
}
}
TEST_F(VkLayerTest, WaitForUnsubmittedFence)
{
vk_testing::Fence testFence;
VK_DBG_MSG_TYPE msgType;
std::string msgString;
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.pNext = NULL;
ASSERT_NO_FATAL_FAILURE(InitState());
testFence.init(*m_device, fenceInfo);
m_errorMonitor->ClearState();
vkGetFenceStatus(m_device->device(),testFence.obj());
msgType = m_errorMonitor->GetState(&msgString);
ASSERT_EQ(msgType, VK_DBG_MSG_ERROR) << "Did not receive an error asking for status of unsubmitted fence";
if (!strstr(msgString.c_str(),"Status Requested for Unsubmitted Fence")) {
FAIL() << "Error received was not Status Requested for Unsubmitted Fence";
}
VkFence fences[1] = {testFence.obj()};
m_errorMonitor->ClearState();
vkWaitForFences(m_device->device(), 1, fences, VK_TRUE, 0);
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'";
}
}
TEST_F(VkLayerTest, GetObjectInfoMismatchedType)
{
VkEventCreateInfo event_info;
VkEvent event;
VkMemoryRequirements mem_req;
size_t data_size = sizeof(mem_req);
VK_DBG_MSG_TYPE msgType;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
memset(&event_info, 0, sizeof(event_info));
event_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
err = vkCreateEvent(device(), &event_info, &event);
ASSERT_VK_SUCCESS(err);
m_errorMonitor->ClearState();
err = vkGetObjectInfo(device(), VK_OBJECT_TYPE_IMAGE, event, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS,
&data_size, &mem_req);
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 '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
struct thread_data_struct {
VkCmdBuffer cmdBuffer;
VkEvent event;
bool bailout;
};
extern "C" void *AddToCommandBuffer(void *arg)
{
struct thread_data_struct *data = (struct thread_data_struct *) arg;
std::string msgString;
for (int i = 0; i<10000; i++) {
vkCmdSetEvent(data->cmdBuffer, data->event, VK_PIPE_EVENT_COMMANDS_COMPLETE);
if (data->bailout) {
break;
}
}
return NULL;
}
TEST_F(VkLayerTest, ThreadCmdBufferCollision)
{
VK_DBG_MSG_TYPE msgType;
std::string msgString;
pthread_t thread;
pthread_attr_t thread_attr;
ASSERT_NO_FATAL_FAILURE(InitState());
ASSERT_NO_FATAL_FAILURE(InitViewport());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
VkCommandBufferObj cmdBuffer(m_device);
m_errorMonitor->ClearState();
pthread_attr_init(&thread_attr);
BeginCommandBuffer(cmdBuffer);
VkEventCreateInfo event_info;
VkEvent event;
VkMemoryRequirements mem_req;
size_t data_size = sizeof(mem_req);
VkResult err;
memset(&event_info, 0, sizeof(event_info));
event_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
err = vkCreateEvent(device(), &event_info, &event);
ASSERT_VK_SUCCESS(err);
err = vkGetObjectInfo(device(), VK_OBJECT_TYPE_EVENT, event, VK_OBJECT_INFO_TYPE_MEMORY_REQUIREMENTS,
&data_size, &mem_req);
ASSERT_VK_SUCCESS(err);
VkMemoryAllocInfo mem_info;
VkDeviceMemory event_mem;
ASSERT_NE(0, mem_req.size) << "vkGetObjectInfo (Event): Failed - expect events to require memory";
memset(&mem_info, 0, sizeof(mem_info));
mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO;
mem_info.allocationSize = mem_req.size;
mem_info.memProps = VK_MEMORY_PROPERTY_SHAREABLE_BIT;
mem_info.memPriority = VK_MEMORY_PRIORITY_NORMAL;
err = vkAllocMemory(device(), &mem_info, &event_mem);
ASSERT_VK_SUCCESS(err);
err = vkBindObjectMemory(device(), VK_OBJECT_TYPE_EVENT, event, 0, event_mem, 0);
ASSERT_VK_SUCCESS(err);
err = vkResetEvent(device(), event);
ASSERT_VK_SUCCESS(err);
struct thread_data_struct data;
data.cmdBuffer = cmdBuffer.obj();
data.event = event;
data.bailout = false;
m_errorMonitor->SetBailout(&data.bailout);
// Add many entries to command buffer from another thread.
pthread_create(&thread, &thread_attr, AddToCommandBuffer, (void *)&data);
// Add many entries to command buffer from this thread at the same time.
AddToCommandBuffer(&data);
pthread_join(thread, NULL);
EndCommandBuffer(cmdBuffer);
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'";
}
}
#endif
int main(int argc, char **argv) {
int result;
::testing::InitGoogleTest(&argc, argv);
VkTestFramework::InitArgs(&argc, argv);
::testing::AddGlobalTestEnvironment(new TestEnvironment);
result = RUN_ALL_TESTS();
VkTestFramework::Finish();
return result;
}