blob: 6efb6cf724d501a2ab9e6bd7aeb807068fc16224 [file] [log] [blame]
#include <vulkan.h>
#include "vk_debug_report_lunarg.h"
#include "gtest-1.7.0/include/gtest/gtest.h"
#include "vkrenderframework.h"
#include "vk_layer_config.h"
#define GLM_FORCE_RADIANS
#include "glm/glm.hpp"
#include <glm/gtc/matrix_transform.hpp>
#define MEM_TRACKER_TESTS 1
#define OBJ_TRACKER_TESTS 1
#define DRAW_STATE_TESTS 1
#define THREADING_TESTS 1
#define SHADER_CHECKER_TESTS 1
//--------------------------------------------------------------------------------------
// 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 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location = 0) out vec4 uFragColor;\n"
"void main(){\n"
" uFragColor = vec4(0,1,0,1);\n"
"}\n";
static void myDbgFunc(
VkFlags msgFlags,
VkObjectType objType,
VkObject srcObject,
size_t location,
int32_t msgCode,
const char* pLayerPrefix,
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_msgFlags = VK_DBG_REPORT_INFO_BIT;
m_bailout = NULL;
pthread_mutex_unlock(&m_mutex);
}
void ClearState()
{
pthread_mutex_lock(&m_mutex);
m_msgFlags = VK_DBG_REPORT_INFO_BIT;
m_msgString.clear();
pthread_mutex_unlock(&m_mutex);
}
VkFlags GetState(std::string *msgString)
{
pthread_mutex_lock(&m_mutex);
*msgString = m_msgString;
pthread_mutex_unlock(&m_mutex);
return m_msgFlags;
}
void SetState(VkFlags msgFlags, const char *msgString)
{
pthread_mutex_lock(&m_mutex);
if (m_bailout != NULL) {
*m_bailout = true;
}
m_msgFlags = msgFlags;
m_msgString.reserve(strlen(msgString));
m_msgString = msgString;
pthread_mutex_unlock(&m_mutex);
}
void SetBailout(bool *bailout)
{
m_bailout = bailout;
}
private:
VkFlags m_msgFlags;
std::string m_msgString;
pthread_mutex_t m_mutex;
bool* m_bailout;
};
static void myDbgFunc(
VkFlags msgFlags,
VkObjectType objType,
VkObject srcObject,
size_t location,
int32_t msgCode,
const char* pLayerPrefix,
const char* pMsg,
void* pUserData)
{
if (msgFlags & (VK_DBG_REPORT_WARN_BIT | VK_DBG_REPORT_ERROR_BIT)) {
ErrorMonitor *errMonitor = (ErrorMonitor *)pUserData;
errMonitor->SetState(msgFlags, 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() {
std::vector<const char *> instance_layer_names;
std::vector<const char *> device_layer_names;
std::vector<const char *> instance_extension_names;
std::vector<const char *> device_extension_names;
instance_extension_names.push_back(DEBUG_REPORT_EXTENSION_NAME);
/*
* Since CreateDbgMsgCallback is an instance level extension call
* any extension / layer that utilizes that feature also needs
* to be enabled at create instance time.
*/
// Use Threading layer first to protect others from ThreadCmdBufferCollision test
instance_layer_names.push_back("Threading");
instance_layer_names.push_back("ObjectTracker");
instance_layer_names.push_back("MemTracker");
instance_layer_names.push_back("DrawState");
instance_layer_names.push_back("ShaderChecker");
device_layer_names.push_back("Threading");
device_layer_names.push_back("ObjectTracker");
device_layer_names.push_back("MemTracker");
device_layer_names.push_back("DrawState");
device_layer_names.push_back("ShaderChecker");
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(instance_layer_names, device_layer_names,
instance_extension_names, device_extension_names,
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 && renderPass()) {
cmdBuffer.BeginRenderPass(renderPass(), framebuffer());
}
return result;
}
VkResult VkLayerTest::EndCommandBuffer(VkCommandBufferObj &cmdBuffer)
{
VkResult result;
if (renderPass()) {
cmdBuffer.EndRenderPass();
}
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);
}
// Make sure depthWriteEnable is set so that DepthStencil fail test will work correctly
VkStencilOpState stencil = {
.stencilFailOp = VK_STENCIL_OP_KEEP,
.stencilPassOp = VK_STENCIL_OP_KEEP,
.stencilDepthFailOp = VK_STENCIL_OP_KEEP,
.stencilCompareOp = VK_COMPARE_OP_NEVER
};
VkPipelineDsStateCreateInfo ds_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DS_STATE_CREATE_INFO,
.pNext = NULL,
.format = VK_FORMAT_D24_UNORM_S8_UINT,
.depthTestEnable = VK_FALSE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_NEVER,
.depthBoundsEnable = VK_FALSE,
.stencilTestEnable = VK_FALSE,
.front = stencil,
.back = stencil
};
pipelineobj.SetDepthStencil(&ds_ci);
descriptorSet.CreateVKDescriptorSet(cmdBuffer);
pipelineobj.CreateVKPipeline(descriptorSet);
cmdBuffer->BindPipeline(pipelineobj);
cmdBuffer->BindDescriptorSet(descriptorSet);
}
// ********************************************************************************************************************
// ********************************************************************************************************************
// ********************************************************************************************************************
// ********************************************************************************************************************
#if MEM_TRACKER_TESTS
TEST_F(VkLayerTest, CallResetCmdBufferBeforeCompletion)
{
vk_testing::Fence testFence;
VkFlags msgFlags;
std::string msgString;
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.pNext = NULL;
fenceInfo.flags = 0;
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);
// Bypass framework since it does the waits automatically
VkResult err = VK_SUCCESS;
err = vkQueueSubmit( m_device->m_queue, 1, &cmdBuffer.obj(), testFence.obj());
ASSERT_VK_SUCCESS( err );
m_errorMonitor->ClearState();
// Introduce failure by calling begin again before checking fence
vkResetCommandBuffer(cmdBuffer.obj());
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive an err after calling ResetCommandBuffer on an active Command Buffer";
if (!strstr(msgString.c_str(),"Resetting CB")) {
FAIL() << "Error received was not 'Resetting CB (0xaddress) before it has completed. You must check CB flag before'";
}
}
TEST_F(VkLayerTest, CallBeginCmdBufferBeforeCompletion)
{
vk_testing::Fence testFence;
VkFlags msgFlags;
std::string msgString;
VkFenceCreateInfo fenceInfo = {};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.pNext = NULL;
fenceInfo.flags = 0;
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);
// Bypass framework since it does the waits automatically
VkResult err = VK_SUCCESS;
err = vkQueueSubmit( m_device->m_queue, 1, &cmdBuffer.obj(), testFence.obj());
ASSERT_VK_SUCCESS( err );
m_errorMonitor->ClearState();
// Introduce failure by calling begin again before checking fence
BeginCommandBuffer(cmdBuffer);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive an err after calling BeginCommandBuffer on an active Command Buffer";
if (!strstr(msgString.c_str(),"Calling vkBeginCommandBuffer() on active CB")) {
FAIL() << "Error received was not 'Calling vkBeginCommandBuffer() on an active CB (0xaddress) before it has completed'";
}
}
TEST_F(VkLayerTest, MapMemWithoutHostVisibleBit)
{
VkFlags msgFlags;
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;
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
.memoryTypeIndex = 1,
};
err = vkCreateImage(m_device->device(), &image_create_info, &image);
ASSERT_VK_SUCCESS(err);
err = vkGetObjectMemoryRequirements(m_device->device(),
VK_OBJECT_TYPE_IMAGE,
image,
&mem_reqs);
ASSERT_VK_SUCCESS(err);
mem_alloc.allocationSize = mem_reqs.size;
// allocate memory
err = vkAllocMemory(m_device->device(), &mem_alloc, &mem);
ASSERT_VK_SUCCESS(err);
// Try to bind free memory that has been freed
err = vkBindObjectMemory(m_device->device(), VK_OBJECT_TYPE_IMAGE, image, mem, 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, &mappedAddress);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "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)
{
VkFlags msgFlags;
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;
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,
.memoryTypeIndex = 0,
};
err = vkCreateImage(m_device->device(), &image_create_info, &image);
ASSERT_VK_SUCCESS(err);
err = vkGetObjectMemoryRequirements(m_device->device(),
VK_OBJECT_TYPE_IMAGE,
image,
&mem_reqs);
ASSERT_VK_SUCCESS(err);
mem_alloc.allocationSize = mem_reqs.size;
err = m_device->gpu().set_memory_type(mem_reqs.memoryTypeBits, &mem_alloc, 0);
ASSERT_VK_SUCCESS(err);
// allocate memory
err = vkAllocMemory(m_device->device(), &mem_alloc, &mem);
ASSERT_VK_SUCCESS(err);
// Introduce validation failure, free memory before binding
vkFreeMemory(m_device->device(), mem);
ASSERT_VK_SUCCESS(err);
// Try to bind free memory that has been freed
err = vkBindObjectMemory(m_device->device(), VK_OBJECT_TYPE_IMAGE, image, mem, 0);
ASSERT_VK_SUCCESS(err);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive an error while tring to bind a freed memory object";
if (!strstr(msgString.c_str(),"couldn't find info for mem obj")) {
FAIL() << "Error received did not match expected error message from BindObjectMemory in MemTracker";
}
}
TEST_F(VkLayerTest, FreeBoundMemory)
{
VkFlags msgFlags;
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;
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,
.memoryTypeIndex = 0,
};
err = vkCreateImage(m_device->device(), &image_create_info, &image);
ASSERT_VK_SUCCESS(err);
err = vkGetObjectMemoryRequirements(m_device->device(),
VK_OBJECT_TYPE_IMAGE,
image,
&mem_reqs);
ASSERT_VK_SUCCESS(err);
mem_alloc.allocationSize = mem_reqs.size;
err = m_device->gpu().set_memory_type(mem_reqs.memoryTypeBits, &mem_alloc, 0);
ASSERT_VK_SUCCESS(err);
// allocate memory
err = vkAllocMemory(m_device->device(), &mem_alloc, &mem);
ASSERT_VK_SUCCESS(err);
// Bind memory to Image object
err = vkBindObjectMemory(m_device->device(), VK_OBJECT_TYPE_IMAGE, image, mem, 0);
ASSERT_VK_SUCCESS(err);
// Introduce validation failure, free memory while still bound to object
vkFreeMemory(m_device->device(), mem);
ASSERT_VK_SUCCESS(err);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "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, RebindMemory)
{
VkFlags msgFlags;
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 mem1;
VkDeviceMemory mem2;
VkMemoryRequirements mem_reqs;
const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM;
const int32_t tex_width = 32;
const int32_t tex_height = 32;
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,
.memoryTypeIndex = 0,
};
err = vkCreateImage(m_device->device(), &image_create_info, &image);
ASSERT_VK_SUCCESS(err);
err = vkGetObjectMemoryRequirements(m_device->device(),
VK_OBJECT_TYPE_IMAGE,
image,
&mem_reqs);
ASSERT_VK_SUCCESS(err);
mem_alloc.allocationSize = mem_reqs.size;
err = m_device->gpu().set_memory_type(mem_reqs.memoryTypeBits, &mem_alloc, 0);
ASSERT_VK_SUCCESS(err);
// allocate 2 memory objects
err = vkAllocMemory(m_device->device(), &mem_alloc, &mem1);
ASSERT_VK_SUCCESS(err);
err = vkAllocMemory(m_device->device(), &mem_alloc, &mem2);
ASSERT_VK_SUCCESS(err);
// Bind first memory object to Image object
err = vkBindObjectMemory(m_device->device(), VK_OBJECT_TYPE_IMAGE, image, mem1, 0);
ASSERT_VK_SUCCESS(err);
// Introduce validation failure, try to bind a different memory object to the same image object
err = vkBindObjectMemory(m_device->device(), VK_OBJECT_TYPE_IMAGE, image, mem2, 0);
ASSERT_VK_SUCCESS(err);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive an error while tring to rebind an object";
if (!strstr(msgString.c_str(),"which has already been bound to mem object")) {
FAIL() << "Error received did not match expected message when rebinding memory to an object";
}
}
TEST_F(VkLayerTest, BindMemoryToDestroyedObject)
{
VkFlags msgFlags;
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;
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,
.memoryTypeIndex = 0,
};
err = vkCreateImage(m_device->device(), &image_create_info, &image);
ASSERT_VK_SUCCESS(err);
err = vkGetObjectMemoryRequirements(m_device->device(),
VK_OBJECT_TYPE_IMAGE,
image,
&mem_reqs);
ASSERT_VK_SUCCESS(err);
mem_alloc.allocationSize = mem_reqs.size;
err = m_device->gpu().set_memory_type(mem_reqs.memoryTypeBits, &mem_alloc, 0);
ASSERT_VK_SUCCESS(err);
// Allocate memory
err = vkAllocMemory(m_device->device(), &mem_alloc, &mem);
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, mem, 0);
ASSERT_VK_SUCCESS(err);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive an error while binding memory to a destroyed object";
if (!strstr(msgString.c_str(),"that's not in global list")) {
FAIL() << "Error received did not match expected error message from updateObjectBinding in MemTracker";
}
}
TEST_F(VkLayerTest, SubmitSignaledFence)
{
vk_testing::Fence testFence;
VkFlags msgFlags;
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());
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "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;
VkFlags msgFlags;
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);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "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'";
}
}
#endif
#if OBJ_TRACKER_TESTS
TEST_F(VkLayerTest, RasterStateNotBound)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
TEST_DESCRIPTION("Simple Draw Call that validates failure when a raster state object is not bound beforehand");
VKTriangleTest(bindStateVertShaderText, bindStateFragShaderText, BsoFailRaster);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "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)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
TEST_DESCRIPTION("Simple Draw Call that validates failure when a viewport state object is not bound beforehand");
VKTriangleTest(bindStateVertShaderText, bindStateFragShaderText, BsoFailViewport);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "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)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
TEST_DESCRIPTION("Simple Draw Call that validates failure when a color-blend state object is not bound beforehand");
VKTriangleTest(bindStateVertShaderText, bindStateFragShaderText, BsoFailColorBlend);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "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)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
TEST_DESCRIPTION("Simple Draw Call that validates failure when a depth-stencil state object is not bound beforehand");
VKTriangleTest(bindStateVertShaderText, bindStateFragShaderText, BsoFailDepthStencil);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "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'";
}
}
#endif
#if DRAW_STATE_TESTS
TEST_F(VkLayerTest, BindPipelineNoRenderPass)
{
// Initiate Draw w/o a PSO bound
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
VkCommandBufferObj cmdBuffer(m_device);
BeginCommandBuffer(cmdBuffer);
VkPipeline badPipeline = (VkPipeline)0xbaadb1be;
vkCmdBindPipeline(cmdBuffer.GetBufferHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, badPipeline);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after binding pipeline to CmdBuffer w/o active RenderPass";
if (!strstr(msgString.c_str(),"Incorrectly binding graphics pipeline ")) {
FAIL() << "Error received was not 'Incorrectly binding graphics pipeline (0xbaadb1be) without an active RenderPass'";
}
}
TEST_F(VkLayerTest, InvalidDescriptorPool)
{
// TODO : Simple check for bad object should be added to ObjectTracker to catch this case
// The DS check for this is after driver has been called to validate DS internal data struct
// Attempt to clear DS Pool with bad object
/* VkFlags msgFlags;
std::string msgString;
VkDescriptorPool badPool = (VkDescriptorPool)0xbaad6001;
vkResetDescriptorPool(device(), badPool);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive an error from Resetting an invalid DescriptorPool Object";
if (!strstr(msgString.c_str(),"Unable to find pool node for pool 0xbaad6001 specified in vkResetDescriptorPool() call")) {
FAIL() << "Error received was note 'Unable to find pool node for pool 0xbaad6001 specified in vkResetDescriptorPool() call'";
}*/
}
TEST_F(VkLayerTest, InvalidDescriptorSet)
{
// TODO : Simple check for bad object should be added to ObjectTracker to catch this case
// The DS check for this is after driver has been called to validate DS internal data struct
// Create a valid cmd buffer
// call vkCmdBindDescriptorSets w/ false DS
}
TEST_F(VkLayerTest, InvalidDescriptorSetLayout)
{
// TODO : Simple check for bad object should be added to ObjectTracker to catch this case
// The DS check for this is after driver has been called to validate DS internal data struct
}
TEST_F(VkLayerTest, InvalidPipeline)
{
// TODO : Simple check for bad object should be added to ObjectTracker to catch this case
// The DS check for this is after driver has been called to validate DS internal data struct
// Create a valid cmd buffer
// call vkCmdBindPipeline w/ false Pipeline
// VkFlags msgFlags;
// std::string msgString;
//
// ASSERT_NO_FATAL_FAILURE(InitState());
// m_errorMonitor->ClearState();
// VkCommandBufferObj cmdBuffer(m_device);
// BeginCommandBuffer(cmdBuffer);
// VkPipeline badPipeline = (VkPipeline)0xbaadb1be;
// vkCmdBindPipeline(cmdBuffer.GetBufferHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, badPipeline);
// msgFlags = m_errorMonitor->GetState(&msgString);
// ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after binding invalid pipeline to CmdBuffer";
// if (!strstr(msgString.c_str(),"Attempt to bind Pipeline ")) {
// FAIL() << "Error received was not 'Attempt to bind Pipeline 0xbaadb1be that doesn't exist!'";
// }
}
TEST_F(VkLayerTest, DescriptorSetNotUpdated)
{
// Create and update CmdBuffer then call QueueSubmit w/o calling End on CmdBuffer
VkFlags msgFlags;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
VkCommandBufferObj cmdBuffer(m_device);
const VkDescriptorTypeCount ds_type_count = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.count = 1,
};
const VkDescriptorPoolCreateInfo ds_pool_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pTypeCount = &ds_type_count,
};
VkDescriptorPool ds_pool;
err = vkCreateDescriptorPool(m_device->device(), VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1, &ds_pool_ci, &ds_pool);
ASSERT_VK_SUCCESS(err);
const VkDescriptorSetLayoutBinding dsl_binding = {
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.arraySize = 1,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = NULL,
};
const VkDescriptorSetLayoutCreateInfo ds_layout_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pBinding = &dsl_binding,
};
VkDescriptorSetLayout ds_layout;
err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, &ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
uint32_t ds_count = 0;
err = vkAllocDescriptorSets(m_device->device(), ds_pool, VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, 1, &ds_layout, &descriptorSet, &ds_count);
ASSERT_VK_SUCCESS(err);
const VkPipelineLayoutCreateInfo pipeline_layout_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = NULL,
.descriptorSetCount = 1,
.pSetLayouts = &ds_layout,
};
VkPipelineLayout pipeline_layout;
err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, &pipeline_layout);
ASSERT_VK_SUCCESS(err);
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX, this);
const VkPipelineShaderStageCreateInfo pipe_vs_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = NULL,
.stage = VK_SHADER_STAGE_VERTEX,
.shader = vs.obj(),
.linkConstBufferCount = 0,
.pLinkConstBufferInfo = NULL,
.pSpecializationInfo = NULL,
};
const VkGraphicsPipelineCreateInfo gp_ci = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = NULL,
.stageCount = 1,
.pStages = &pipe_vs_ci,
.pVertexInputState = NULL,
.pIaState = NULL,
.pTessState = NULL,
.pVpState = NULL,
.pRsState = NULL,
.pMsState = NULL,
.pDsState = NULL,
.pCbState = NULL,
.flags = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT,
.layout = pipeline_layout,
};
VkPipeline pipeline;
err = vkCreateGraphicsPipeline(m_device->device(), &gp_ci, &pipeline);
ASSERT_VK_SUCCESS(err);
ASSERT_NO_FATAL_FAILURE(InitState());
ASSERT_NO_FATAL_FAILURE(InitViewport());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
cmdBuffer.AddRenderTarget(m_renderTargets[0]);
BeginCommandBuffer(cmdBuffer);
vkCmdBindPipeline(cmdBuffer.GetBufferHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdBindDescriptorSets(cmdBuffer.GetBufferHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptorSet, 0, NULL);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_WARN_BIT) << "Did not warn after binding a DescriptorSet that was never updated.";
if (!strstr(msgString.c_str()," bound but it was never updated. ")) {
FAIL() << "Error received was not 'DS <blah> bound but it was never updated. You may want to either update it or not bind it.'";
}
}
TEST_F(VkLayerTest, NoBeginCmdBuffer)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
VkCommandBufferObj cmdBuffer(m_device);
// Call EndCommandBuffer() w/o calling BeginCommandBuffer()
vkEndCommandBuffer(cmdBuffer.GetBufferHandle());
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after ending a CmdBuffer w/o calling BeginCommandBuffer()";
if (!strstr(msgString.c_str(),"You must call vkBeginCommandBuffer() before this call to ")) {
FAIL() << "Error received was not 'You must call vkBeginCommandBuffer() before this call to vkEndCommandBuffer()'";
}
}
TEST_F(VkLayerTest, InvalidPipelineCreateState)
{
// Attempt to Create Gfx Pipeline w/o a VS
VkFlags msgFlags;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
VkCommandBufferObj cmdBuffer(m_device);
const VkDescriptorTypeCount ds_type_count = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.count = 1,
};
const VkDescriptorPoolCreateInfo ds_pool_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pTypeCount = &ds_type_count,
};
VkDescriptorPool ds_pool;
err = vkCreateDescriptorPool(m_device->device(), VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1, &ds_pool_ci, &ds_pool);
ASSERT_VK_SUCCESS(err);
const VkDescriptorSetLayoutBinding dsl_binding = {
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.arraySize = 1,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = NULL,
};
const VkDescriptorSetLayoutCreateInfo ds_layout_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pBinding = &dsl_binding,
};
VkDescriptorSetLayout ds_layout;
err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, &ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
uint32_t ds_count = 0;
err = vkAllocDescriptorSets(m_device->device(), ds_pool, VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, 1, &ds_layout, &descriptorSet, &ds_count);
ASSERT_VK_SUCCESS(err);
const VkPipelineLayoutCreateInfo pipeline_layout_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = NULL,
.descriptorSetCount = 1,
.pSetLayouts = &ds_layout,
};
VkPipelineLayout pipeline_layout;
err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, &pipeline_layout);
ASSERT_VK_SUCCESS(err);
const VkGraphicsPipelineCreateInfo gp_ci = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = NULL,
.stageCount = 0,
.pStages = NULL, // Creating Gfx Pipeline w/o VS is a violation
.pVertexInputState = NULL,
.pIaState = NULL,
.pTessState = NULL,
.pVpState = NULL,
.pRsState = NULL,
.pMsState = NULL,
.pDsState = NULL,
.pCbState = NULL,
.flags = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT,
.layout = pipeline_layout,
};
VkPipeline pipeline;
err = vkCreateGraphicsPipeline(m_device->device(), &gp_ci, &pipeline);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after creating Gfx Pipeline w/o VS.";
if (!strstr(msgString.c_str(),"Invalid Pipeline CreateInfo State: Vtx Shader required")) {
FAIL() << "Error received was not 'Invalid Pipeline CreateInfo State: Vtx Shader required'";
}
}
TEST_F(VkLayerTest, NullRenderPass)
{
// Bind a NULL RenderPass
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
m_errorMonitor->ClearState();
VkCommandBufferObj cmdBuffer(m_device);
cmdBuffer.AddRenderTarget(m_renderTargets[0]);
BeginCommandBuffer(cmdBuffer);
// Don't care about RenderPass handle b/c error should be flagged before that
vkCmdBeginRenderPass(cmdBuffer.GetBufferHandle(), NULL);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after binding NULL RenderPass.";
if (!strstr(msgString.c_str(),"You cannot use a NULL RenderPass object in vkCmdBeginRenderPass()")) {
FAIL() << "Error received was not 'You cannot use a NULL RenderPass object in vkCmdBeginRenderPass()'";
}
}
TEST_F(VkLayerTest, RenderPassWithinRenderPass)
{
// Bind a BeginRenderPass within an active RenderPass
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
m_errorMonitor->ClearState();
VkCommandBufferObj cmdBuffer(m_device);
cmdBuffer.AddRenderTarget(m_renderTargets[0]);
BeginCommandBuffer(cmdBuffer);
// Just create a dummy Renderpass that's non-NULL so we can get to the proper error
const VkRenderPassBegin rp_begin = {
.renderPass = (VkRenderPass) 0xc001d00d,
.framebuffer = NULL
};
vkCmdBeginRenderPass(cmdBuffer.GetBufferHandle(), &rp_begin);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after binding RenderPass w/i an active RenderPass.";
if (!strstr(msgString.c_str(),"Cannot call vkCmdBeginRenderPass() during an active RenderPass ")) {
FAIL() << "Error received was not 'Cannot call vkCmdBeginRenderPass() during an active RenderPass...'";
}
}
TEST_F(VkLayerTest, InvalidDynamicStateObject)
{
// Create a valid cmd buffer
// call vkCmdBindDynamicStateObject w/ false DS Obj
// TODO : Simple check for bad object should be added to ObjectTracker to catch this case
// The DS check for this is after driver has been called to validate DS internal data struct
}
TEST_F(VkLayerTest, VtxBufferNoRenderPass)
{
// Bind VBO out-of-bounds for given PSO
VkFlags msgFlags;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
VkCommandBufferObj cmdBuffer(m_device);
const VkDescriptorTypeCount ds_type_count = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.count = 1,
};
const VkDescriptorPoolCreateInfo ds_pool_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pTypeCount = &ds_type_count,
};
VkDescriptorPool ds_pool;
err = vkCreateDescriptorPool(m_device->device(), VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1, &ds_pool_ci, &ds_pool);
ASSERT_VK_SUCCESS(err);
const VkDescriptorSetLayoutBinding dsl_binding = {
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.arraySize = 1,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = NULL,
};
const VkDescriptorSetLayoutCreateInfo ds_layout_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pBinding = &dsl_binding,
};
VkDescriptorSetLayout ds_layout;
err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, &ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
uint32_t ds_count = 0;
err = vkAllocDescriptorSets(m_device->device(), ds_pool, VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, 1, &ds_layout, &descriptorSet, &ds_count);
ASSERT_VK_SUCCESS(err);
const VkPipelineLayoutCreateInfo pipeline_layout_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = NULL,
.descriptorSetCount = 1,
.pSetLayouts = &ds_layout,
};
VkPipelineLayout pipeline_layout;
err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, &pipeline_layout);
ASSERT_VK_SUCCESS(err);
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX, this);
const VkPipelineShaderStageCreateInfo pipe_vs_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = NULL,
.stage = VK_SHADER_STAGE_VERTEX,
.shader = vs.obj(),
.linkConstBufferCount = 0,
.pLinkConstBufferInfo = NULL,
.pSpecializationInfo = NULL,
};
const VkGraphicsPipelineCreateInfo gp_ci = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = NULL,
.stageCount = 1,
.pStages = &pipe_vs_ci,
.pVertexInputState = NULL,
.pIaState = NULL,
.pTessState = NULL,
.pVpState = NULL,
.pRsState = NULL,
.pMsState = NULL,
.pDsState = NULL,
.pCbState = NULL,
.flags = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT,
.layout = pipeline_layout,
};
VkPipeline pipeline;
err = vkCreateGraphicsPipeline(m_device->device(), &gp_ci, &pipeline);
ASSERT_VK_SUCCESS(err);
err= cmdBuffer.BeginCommandBuffer();
ASSERT_VK_SUCCESS(err);
vkCmdBindPipeline(cmdBuffer.GetBufferHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
// Should error before calling to driver so don't care about actual data
vkCmdBindVertexBuffers(cmdBuffer.GetBufferHandle(), 0, 1, NULL, NULL);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after vkCmdBindVertexBuffers() w/o active RenderPass.";
if (!strstr(msgString.c_str(),"Incorrect call to vkCmdBindVertexBuffers() without an active RenderPass.")) {
FAIL() << "Error received was not 'Incorrect call to vkCmdBindVertexBuffers() without an active RenderPass.'";
}
}
TEST_F(VkLayerTest, DSTypeMismatch)
{
// Create DS w/ layout of one type and attempt Update w/ mis-matched type
VkFlags msgFlags;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
//VkDescriptorSetObj descriptorSet(m_device);
const VkDescriptorTypeCount ds_type_count = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.count = 1,
};
const VkDescriptorPoolCreateInfo ds_pool_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pTypeCount = &ds_type_count,
};
VkDescriptorPool ds_pool;
err = vkCreateDescriptorPool(m_device->device(), VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1, &ds_pool_ci, &ds_pool);
ASSERT_VK_SUCCESS(err);
const VkDescriptorSetLayoutBinding dsl_binding = {
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.arraySize = 1,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = NULL,
};
const VkDescriptorSetLayoutCreateInfo ds_layout_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pBinding = &dsl_binding,
};
VkDescriptorSetLayout ds_layout;
err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, &ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
uint32_t ds_count = 0;
err = vkAllocDescriptorSets(m_device->device(), ds_pool, VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, 1, &ds_layout, &descriptorSet, &ds_count);
ASSERT_VK_SUCCESS(err);
const VkSamplerCreateInfo sampler_ci = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = NULL,
.magFilter = VK_TEX_FILTER_NEAREST,
.minFilter = VK_TEX_FILTER_NEAREST,
.mipMode = VK_TEX_MIPMAP_MODE_BASE,
.addressU = VK_TEX_ADDRESS_CLAMP,
.addressV = VK_TEX_ADDRESS_CLAMP,
.addressW = VK_TEX_ADDRESS_CLAMP,
.mipLodBias = 1.0,
.maxAnisotropy = 1,
.compareOp = VK_COMPARE_OP_NEVER,
.minLod = 1.0,
.maxLod = 1.0,
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
};
VkSampler sampler;
err = vkCreateSampler(m_device->device(), &sampler_ci, &sampler);
ASSERT_VK_SUCCESS(err);
VkDescriptorInfo descriptor_info;
memset(&descriptor_info, 0, sizeof(descriptor_info));
descriptor_info.sampler = sampler;
VkWriteDescriptorSet descriptor_write;
memset(&descriptor_write, 0, sizeof(descriptor_write));
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.destSet = descriptorSet;
descriptor_write.count = 1;
// This is a mismatched type for the layout which expects BUFFER
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
descriptor_write.pDescriptors = &descriptor_info;
vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after updating BUFFER Descriptor w/ incorrect type of SAMPLER.";
if (!strstr(msgString.c_str(),"Descriptor update type of VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET does not match ")) {
FAIL() << "Error received was not 'Descriptor update type of VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET does not match overlapping binding type!'";
}
}
TEST_F(VkLayerTest, DSUpdateOutOfBounds)
{
// For overlapping Update, have arrayIndex exceed that of layout
VkFlags msgFlags;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
//VkDescriptorSetObj descriptorSet(m_device);
const VkDescriptorTypeCount ds_type_count = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.count = 1,
};
const VkDescriptorPoolCreateInfo ds_pool_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pTypeCount = &ds_type_count,
};
VkDescriptorPool ds_pool;
err = vkCreateDescriptorPool(m_device->device(), VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1, &ds_pool_ci, &ds_pool);
ASSERT_VK_SUCCESS(err);
const VkDescriptorSetLayoutBinding dsl_binding = {
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.arraySize = 1,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = NULL,
};
const VkDescriptorSetLayoutCreateInfo ds_layout_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pBinding = &dsl_binding,
};
VkDescriptorSetLayout ds_layout;
err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, &ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
uint32_t ds_count = 0;
err = vkAllocDescriptorSets(m_device->device(), ds_pool, VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, 1, &ds_layout, &descriptorSet, &ds_count);
ASSERT_VK_SUCCESS(err);
const VkSamplerCreateInfo sampler_ci = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = NULL,
.magFilter = VK_TEX_FILTER_NEAREST,
.minFilter = VK_TEX_FILTER_NEAREST,
.mipMode = VK_TEX_MIPMAP_MODE_BASE,
.addressU = VK_TEX_ADDRESS_CLAMP,
.addressV = VK_TEX_ADDRESS_CLAMP,
.addressW = VK_TEX_ADDRESS_CLAMP,
.mipLodBias = 1.0,
.maxAnisotropy = 1,
.compareOp = VK_COMPARE_OP_NEVER,
.minLod = 1.0,
.maxLod = 1.0,
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
};
VkSampler sampler;
err = vkCreateSampler(m_device->device(), &sampler_ci, &sampler);
ASSERT_VK_SUCCESS(err);
VkDescriptorInfo descriptor_info;
memset(&descriptor_info, 0, sizeof(descriptor_info));
descriptor_info.sampler = sampler;
VkWriteDescriptorSet descriptor_write;
memset(&descriptor_write, 0, sizeof(descriptor_write));
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.destSet = descriptorSet;
descriptor_write.destArrayElement = 1; /* This index out of bounds for the update */
descriptor_write.count = 1;
// This is the wrong type, but out of bounds will be flagged first
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
descriptor_write.pDescriptors = &descriptor_info;
vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after updating Descriptor w/ index out of bounds.";
if (!strstr(msgString.c_str(),"Descriptor update type of VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET is out of bounds for matching binding")) {
FAIL() << "Error received was not 'Descriptor update type of VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET is out of bounds for matching binding...'";
}
}
TEST_F(VkLayerTest, InvalidDSUpdateIndex)
{
// Create layout w/ count of 1 and attempt update to that layout w/ binding index 2
VkFlags msgFlags;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
//VkDescriptorSetObj descriptorSet(m_device);
const VkDescriptorTypeCount ds_type_count = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.count = 1,
};
const VkDescriptorPoolCreateInfo ds_pool_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pTypeCount = &ds_type_count,
};
VkDescriptorPool ds_pool;
err = vkCreateDescriptorPool(m_device->device(), VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1, &ds_pool_ci, &ds_pool);
ASSERT_VK_SUCCESS(err);
const VkDescriptorSetLayoutBinding dsl_binding = {
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.arraySize = 1,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = NULL,
};
const VkDescriptorSetLayoutCreateInfo ds_layout_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pBinding = &dsl_binding,
};
VkDescriptorSetLayout ds_layout;
err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, &ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
uint32_t ds_count = 0;
err = vkAllocDescriptorSets(m_device->device(), ds_pool, VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, 1, &ds_layout, &descriptorSet, &ds_count);
ASSERT_VK_SUCCESS(err);
const VkSamplerCreateInfo sampler_ci = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = NULL,
.magFilter = VK_TEX_FILTER_NEAREST,
.minFilter = VK_TEX_FILTER_NEAREST,
.mipMode = VK_TEX_MIPMAP_MODE_BASE,
.addressU = VK_TEX_ADDRESS_CLAMP,
.addressV = VK_TEX_ADDRESS_CLAMP,
.addressW = VK_TEX_ADDRESS_CLAMP,
.mipLodBias = 1.0,
.maxAnisotropy = 1,
.compareOp = VK_COMPARE_OP_NEVER,
.minLod = 1.0,
.maxLod = 1.0,
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
};
VkSampler sampler;
err = vkCreateSampler(m_device->device(), &sampler_ci, &sampler);
ASSERT_VK_SUCCESS(err);
VkDescriptorInfo descriptor_info;
memset(&descriptor_info, 0, sizeof(descriptor_info));
descriptor_info.sampler = sampler;
VkWriteDescriptorSet descriptor_write;
memset(&descriptor_write, 0, sizeof(descriptor_write));
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descriptor_write.destSet = descriptorSet;
descriptor_write.destBinding = 2;
descriptor_write.count = 1;
// This is the wrong type, but out of bounds will be flagged first
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
descriptor_write.pDescriptors = &descriptor_info;
vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after updating Descriptor w/ count too large for layout.";
if (!strstr(msgString.c_str()," does not have binding to match update binding ")) {
FAIL() << "Error received was not 'Descriptor Set <blah> does not have binding to match update binding '";
}
}
TEST_F(VkLayerTest, InvalidDSUpdateStruct)
{
// Call UpdateDS w/ struct type other than valid VK_STRUCTUR_TYPE_UPDATE_* types
VkFlags msgFlags;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
m_errorMonitor->ClearState();
//VkDescriptorSetObj descriptorSet(m_device);
const VkDescriptorTypeCount ds_type_count = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.count = 1,
};
const VkDescriptorPoolCreateInfo ds_pool_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pTypeCount = &ds_type_count,
};
VkDescriptorPool ds_pool;
err = vkCreateDescriptorPool(m_device->device(), VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1, &ds_pool_ci, &ds_pool);
ASSERT_VK_SUCCESS(err);
const VkDescriptorSetLayoutBinding dsl_binding = {
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.arraySize = 1,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = NULL,
};
const VkDescriptorSetLayoutCreateInfo ds_layout_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pBinding = &dsl_binding,
};
VkDescriptorSetLayout ds_layout;
err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, &ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
uint32_t ds_count = 0;
err = vkAllocDescriptorSets(m_device->device(), ds_pool, VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, 1, &ds_layout, &descriptorSet, &ds_count);
ASSERT_VK_SUCCESS(err);
const VkSamplerCreateInfo sampler_ci = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = NULL,
.magFilter = VK_TEX_FILTER_NEAREST,
.minFilter = VK_TEX_FILTER_NEAREST,
.mipMode = VK_TEX_MIPMAP_MODE_BASE,
.addressU = VK_TEX_ADDRESS_CLAMP,
.addressV = VK_TEX_ADDRESS_CLAMP,
.addressW = VK_TEX_ADDRESS_CLAMP,
.mipLodBias = 1.0,
.maxAnisotropy = 1,
.compareOp = VK_COMPARE_OP_NEVER,
.minLod = 1.0,
.maxLod = 1.0,
.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
};
VkSampler sampler;
err = vkCreateSampler(m_device->device(), &sampler_ci, &sampler);
ASSERT_VK_SUCCESS(err);
VkDescriptorInfo descriptor_info;
memset(&descriptor_info, 0, sizeof(descriptor_info));
descriptor_info.sampler = sampler;
VkWriteDescriptorSet descriptor_write;
memset(&descriptor_write, 0, sizeof(descriptor_write));
descriptor_write.sType = (VkStructureType)0x99999999; /* Intentionally broken struct type */
descriptor_write.destSet = descriptorSet;
descriptor_write.count = 1;
// This is the wrong type, but out of bounds will be flagged first
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
descriptor_write.pDescriptors = &descriptor_info;
vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after updating Descriptor w/ invalid struct type.";
if (!strstr(msgString.c_str(),"Unexpected UPDATE struct of type ")) {
FAIL() << "Error received was not 'Unexpected UPDATE struct of type '";
}
}
TEST_F(VkLayerTest, NumSamplesMismatch)
{
// Create CmdBuffer where MSAA samples doesn't match RenderPass sampleCount
VkFlags msgFlags;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
m_errorMonitor->ClearState();
VkCommandBufferObj cmdBuffer(m_device);
const VkDescriptorTypeCount ds_type_count = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.count = 1,
};
const VkDescriptorPoolCreateInfo ds_pool_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pTypeCount = &ds_type_count,
};
VkDescriptorPool ds_pool;
err = vkCreateDescriptorPool(m_device->device(), VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1, &ds_pool_ci, &ds_pool);
ASSERT_VK_SUCCESS(err);
const VkDescriptorSetLayoutBinding dsl_binding = {
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.arraySize = 1,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = NULL,
};
const VkDescriptorSetLayoutCreateInfo ds_layout_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pBinding = &dsl_binding,
};
VkDescriptorSetLayout ds_layout;
err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, &ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
uint32_t ds_count = 0;
err = vkAllocDescriptorSets(m_device->device(), ds_pool, VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, 1, &ds_layout, &descriptorSet, &ds_count);
ASSERT_VK_SUCCESS(err);
const VkPipelineMsStateCreateInfo pipe_ms_state_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MS_STATE_CREATE_INFO,
.pNext = NULL,
.rasterSamples = 4,
.multisampleEnable = 1,
.sampleShadingEnable = 0,
.minSampleShading = 1.0,
.sampleMask = 15,
};
const VkPipelineLayoutCreateInfo pipeline_layout_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = NULL,
.descriptorSetCount = 1,
.pSetLayouts = &ds_layout,
};
VkPipelineLayout pipeline_layout;
err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, &pipeline_layout);
ASSERT_VK_SUCCESS(err);
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX, this);
const VkPipelineShaderStageCreateInfo pipe_vs_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = NULL,
.stage = VK_SHADER_STAGE_VERTEX,
.shader = vs.obj(),
.linkConstBufferCount = 0,
.pLinkConstBufferInfo = NULL,
.pSpecializationInfo = NULL,
};
const VkGraphicsPipelineCreateInfo gp_ci = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = NULL,
.stageCount = 1,
.pStages = &pipe_vs_ci,
.pVertexInputState = NULL,
.pIaState = NULL,
.pTessState = NULL,
.pVpState = NULL,
.pRsState = NULL,
.pMsState = &pipe_ms_state_ci,
.pDsState = NULL,
.pCbState = NULL,
.flags = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT,
.layout = pipeline_layout,
};
VkPipeline pipeline;
err = vkCreateGraphicsPipeline(m_device->device(), &gp_ci, &pipeline);
ASSERT_VK_SUCCESS(err);
cmdBuffer.AddRenderTarget(m_renderTargets[0]);
BeginCommandBuffer(cmdBuffer);
vkCmdBindPipeline(cmdBuffer.GetBufferHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after binding RenderPass w/ mismatched MSAA from PSO.";
if (!strstr(msgString.c_str(),"Num samples mismatch! ")) {
FAIL() << "Error received was not 'Num samples mismatch!...'";
}
}
TEST_F(VkLayerTest, PipelineNotBound)
{
VkFlags msgFlags;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
m_errorMonitor->ClearState();
VkCommandBufferObj cmdBuffer(m_device);
const VkDescriptorTypeCount ds_type_count = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.count = 1,
};
const VkDescriptorPoolCreateInfo ds_pool_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pTypeCount = &ds_type_count,
};
VkDescriptorPool ds_pool;
err = vkCreateDescriptorPool(m_device->device(), VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1, &ds_pool_ci, &ds_pool);
ASSERT_VK_SUCCESS(err);
const VkDescriptorSetLayoutBinding dsl_binding = {
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.arraySize = 1,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = NULL,
};
const VkDescriptorSetLayoutCreateInfo ds_layout_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pBinding = &dsl_binding,
};
VkDescriptorSetLayout ds_layout;
err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, &ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
uint32_t ds_count = 0;
err = vkAllocDescriptorSets(m_device->device(), ds_pool, VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, 1, &ds_layout, &descriptorSet, &ds_count);
ASSERT_VK_SUCCESS(err);
const VkPipelineLayoutCreateInfo pipeline_layout_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = NULL,
.descriptorSetCount = 1,
.pSetLayouts = &ds_layout,
};
VkPipelineLayout pipeline_layout;
err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, &pipeline_layout);
ASSERT_VK_SUCCESS(err);
VkPipeline badPipeline = (VkPipeline)0xbaadb1be;
//err = vkCreateGraphicsPipeline(m_device->device(), &gp_ci, &pipeline);
ASSERT_VK_SUCCESS(err);
cmdBuffer.AddRenderTarget(m_renderTargets[0]);
BeginCommandBuffer(cmdBuffer);
vkCmdBindPipeline(cmdBuffer.GetBufferHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, badPipeline);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after binding invalid pipeline to CmdBuffer";
if (!strstr(msgString.c_str(),"Attempt to bind Pipeline ")) {
FAIL() << "Error received was not 'Attempt to bind Pipeline 0xbaadb1be that doesn't exist!'";
}
}
TEST_F(VkLayerTest, VtxBufferBadIndex)
{
// Create CmdBuffer where MSAA samples doesn't match RenderPass sampleCount
VkFlags msgFlags;
std::string msgString;
VkResult err;
ASSERT_NO_FATAL_FAILURE(InitState());
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
m_errorMonitor->ClearState();
VkCommandBufferObj cmdBuffer(m_device);
const VkDescriptorTypeCount ds_type_count = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.count = 1,
};
const VkDescriptorPoolCreateInfo ds_pool_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pTypeCount = &ds_type_count,
};
VkDescriptorPool ds_pool;
err = vkCreateDescriptorPool(m_device->device(), VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1, &ds_pool_ci, &ds_pool);
ASSERT_VK_SUCCESS(err);
const VkDescriptorSetLayoutBinding dsl_binding = {
.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.arraySize = 1,
.stageFlags = VK_SHADER_STAGE_ALL,
.pImmutableSamplers = NULL,
};
const VkDescriptorSetLayoutCreateInfo ds_layout_ci = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = NULL,
.count = 1,
.pBinding = &dsl_binding,
};
VkDescriptorSetLayout ds_layout;
err = vkCreateDescriptorSetLayout(m_device->device(), &ds_layout_ci, &ds_layout);
ASSERT_VK_SUCCESS(err);
VkDescriptorSet descriptorSet;
uint32_t ds_count = 0;
err = vkAllocDescriptorSets(m_device->device(), ds_pool, VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, 1, &ds_layout, &descriptorSet, &ds_count);
ASSERT_VK_SUCCESS(err);
const VkPipelineMsStateCreateInfo pipe_ms_state_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_MS_STATE_CREATE_INFO,
.pNext = NULL,
.rasterSamples = 1,
.multisampleEnable = 1,
.sampleShadingEnable = 0,
.minSampleShading = 1.0,
.sampleMask = 15,
};
const VkPipelineLayoutCreateInfo pipeline_layout_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.pNext = NULL,
.descriptorSetCount = 1,
.pSetLayouts = &ds_layout,
};
VkPipelineLayout pipeline_layout;
err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_ci, &pipeline_layout);
ASSERT_VK_SUCCESS(err);
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX, this);
const VkPipelineShaderStageCreateInfo pipe_vs_ci = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.pNext = NULL,
.stage = VK_SHADER_STAGE_VERTEX,
.shader = vs.obj(),
.linkConstBufferCount = 0,
.pLinkConstBufferInfo = NULL,
.pSpecializationInfo = NULL,
};
const VkGraphicsPipelineCreateInfo gp_ci = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = NULL,
.stageCount = 1,
.pStages = &pipe_vs_ci,
.pVertexInputState = NULL,
.pIaState = NULL,
.pTessState = NULL,
.pVpState = NULL,
.pRsState = NULL,
.pMsState = &pipe_ms_state_ci,
.pDsState = NULL,
.pCbState = NULL,
.flags = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT,
.layout = pipeline_layout,
};
VkPipeline pipeline;
err = vkCreateGraphicsPipeline(m_device->device(), &gp_ci, &pipeline);
ASSERT_VK_SUCCESS(err);
cmdBuffer.AddRenderTarget(m_renderTargets[0]);
BeginCommandBuffer(cmdBuffer);
vkCmdBindPipeline(cmdBuffer.GetBufferHandle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
// Should error before calling to driver so don't care about actual data
vkCmdBindVertexBuffers(cmdBuffer.GetBufferHandle(), 0, 1, NULL, NULL);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "Did not receive error after binding Vtx Buffer w/o VBO attached to PSO.";
if (!strstr(msgString.c_str(),"Vtx Buffer Index 0 was bound, but no vtx buffers are attached to PSO.")) {
FAIL() << "Error received was not 'Vtx Buffer Index 0 was bound, but no vtx buffers are attached to PSO.'";
}
}
#endif
#if THREADING_TESTS
#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_PIPELINE_STAGE_ALL_GPU_COMMANDS);
if (data->bailout) {
break;
}
}
return NULL;
}
TEST_F(VkLayerTest, ThreadCmdBufferCollision)
{
VkFlags msgFlags;
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;
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 = vkGetObjectMemoryRequirements(device(), VK_OBJECT_TYPE_EVENT, event, &mem_req);
ASSERT_VK_SUCCESS(err);
VkMemoryAllocInfo mem_info;
VkDeviceMemory event_mem;
memset(&mem_info, 0, sizeof(mem_info));
mem_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOC_INFO;
mem_info.allocationSize = mem_req.size;
mem_info.memoryTypeIndex = 0;
err = m_device->gpu().set_memory_type(mem_req.memoryTypeBits, &mem_info, 0);
ASSERT_VK_SUCCESS(err);
err = vkAllocMemory(device(), &mem_info, &event_mem);
ASSERT_VK_SUCCESS(err);
err = vkBindObjectMemory(device(), VK_OBJECT_TYPE_EVENT, event, 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);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT) << "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
#endif
#if SHADER_CHECKER_TESTS
TEST_F(VkLayerTest, CreatePipelineVertexOutputNotConsumed)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ScopedUseGlsl useGlsl(false);
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) out float x;\n"
"void main(){\n"
" gl_Position = vec4(1);\n"
" x = 0;\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) out vec4 color;\n"
"void main(){\n"
" color = vec4(1);\n"
"}\n";
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
m_errorMonitor->ClearState();
pipe.CreateVKPipeline(descriptorSet);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_WARN_BIT);
if (!strstr(msgString.c_str(),"not consumed by fragment shader")) {
FAIL() << "Incorrect warning: " << msgString;
}
}
TEST_F(VkLayerTest, CreatePipelineFragmentInputNotProvided)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ScopedUseGlsl useGlsl(false);
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"void main(){\n"
" gl_Position = vec4(1);\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) in float x;\n"
"layout(location=0) out vec4 color;\n"
"void main(){\n"
" color = vec4(x);\n"
"}\n";
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
m_errorMonitor->ClearState();
pipe.CreateVKPipeline(descriptorSet);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT);
if (!strstr(msgString.c_str(),"not written by vertex shader")) {
FAIL() << "Incorrect error: " << msgString;
}
}
TEST_F(VkLayerTest, CreatePipelineVsFsTypeMismatch)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ScopedUseGlsl useGlsl(false);
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) out int x;\n"
"void main(){\n"
" x = 0;\n"
" gl_Position = vec4(1);\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) in float x;\n" /* VS writes int */
"layout(location=0) out vec4 color;\n"
"void main(){\n"
" color = vec4(x);\n"
"}\n";
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
m_errorMonitor->ClearState();
pipe.CreateVKPipeline(descriptorSet);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT);
if (!strstr(msgString.c_str(),"Type mismatch on location 0")) {
FAIL() << "Incorrect error: " << msgString;
}
}
TEST_F(VkLayerTest, CreatePipelineAttribNotConsumed)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ScopedUseGlsl useGlsl(false);
VkVertexInputBindingDescription input_binding;
memset(&input_binding, 0, sizeof(input_binding));
VkVertexInputAttributeDescription input_attrib;
memset(&input_attrib, 0, sizeof(input_attrib));
input_attrib.format = VK_FORMAT_R32_SFLOAT;
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"void main(){\n"
" gl_Position = vec4(1);\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) out vec4 color;\n"
"void main(){\n"
" color = vec4(1);\n"
"}\n";
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
pipe.AddVertexInputBindings(&input_binding, 1);
pipe.AddVertexInputAttribs(&input_attrib, 1);
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
m_errorMonitor->ClearState();
pipe.CreateVKPipeline(descriptorSet);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_WARN_BIT);
if (!strstr(msgString.c_str(),"location 0 not consumed by VS")) {
FAIL() << "Incorrect warning: " << msgString;
}
}
TEST_F(VkLayerTest, CreatePipelineAttribNotProvided)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ScopedUseGlsl useGlsl(false);
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) in vec4 x;\n" /* not provided */
"void main(){\n"
" gl_Position = x;\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) out vec4 color;\n"
"void main(){\n"
" color = vec4(1);\n"
"}\n";
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
m_errorMonitor->ClearState();
pipe.CreateVKPipeline(descriptorSet);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT);
if (!strstr(msgString.c_str(),"VS consumes input at location 0 but not provided")) {
FAIL() << "Incorrect warning: " << msgString;
}
}
TEST_F(VkLayerTest, CreatePipelineAttribTypeMismatch)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ScopedUseGlsl useGlsl(false);
VkVertexInputBindingDescription input_binding;
memset(&input_binding, 0, sizeof(input_binding));
VkVertexInputAttributeDescription input_attrib;
memset(&input_attrib, 0, sizeof(input_attrib));
input_attrib.format = VK_FORMAT_R32_SFLOAT;
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) in int x;\n" /* attrib provided float */
"void main(){\n"
" gl_Position = vec4(x);\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) out vec4 color;\n"
"void main(){\n"
" color = vec4(1);\n"
"}\n";
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
pipe.AddVertexInputBindings(&input_binding, 1);
pipe.AddVertexInputAttribs(&input_attrib, 1);
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
m_errorMonitor->ClearState();
pipe.CreateVKPipeline(descriptorSet);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT);
if (!strstr(msgString.c_str(),"location 0 does not match VS input type")) {
FAIL() << "Incorrect error: " << msgString;
}
}
TEST_F(VkLayerTest, CreatePipelineAttribBindingConflict)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ScopedUseGlsl useGlsl(false);
/* Two binding descriptions for binding 0 */
VkVertexInputBindingDescription input_bindings[2];
memset(input_bindings, 0, sizeof(input_bindings));
VkVertexInputAttributeDescription input_attrib;
memset(&input_attrib, 0, sizeof(input_attrib));
input_attrib.format = VK_FORMAT_R32_SFLOAT;
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) in float x;\n" /* attrib provided float */
"void main(){\n"
" gl_Position = vec4(x);\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) out vec4 color;\n"
"void main(){\n"
" color = vec4(1);\n"
"}\n";
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
pipe.AddVertexInputBindings(input_bindings, 2);
pipe.AddVertexInputAttribs(&input_attrib, 1);
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
m_errorMonitor->ClearState();
pipe.CreateVKPipeline(descriptorSet);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT);
if (!strstr(msgString.c_str(),"Duplicate vertex input binding descriptions for binding 0")) {
FAIL() << "Incorrect error: " << msgString;
}
}
/* TODO: would be nice to test the mixed broadcast & custom case, but the GLSL->SPV compiler
* rejects it. */
TEST_F(VkLayerTest, CreatePipelineFragmentOutputNotWritten)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ScopedUseGlsl useGlsl(false);
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"void main(){\n"
" gl_Position = vec4(1);\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"void main(){\n"
"}\n";
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
/* implicit CB 0 set up by the test framework, not written */
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
m_errorMonitor->ClearState();
pipe.CreateVKPipeline(descriptorSet);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT);
if (!strstr(msgString.c_str(),"Attachment 0 not written by FS")) {
FAIL() << "Incorrect error: " << msgString;
}
}
TEST_F(VkLayerTest, CreatePipelineFragmentOutputNotConsumed)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ScopedUseGlsl useGlsl(false);
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"void main(){\n"
" gl_Position = vec4(1);\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) out vec4 x;\n"
"layout(location=1) out vec4 y;\n" /* no matching attachment for this */
"void main(){\n"
" x = vec4(1);\n"
" y = vec4(1);\n"
"}\n";
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
/* implicit CB 0 set up by the test framework */
/* FS writes CB 1, but we don't configure it */
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
m_errorMonitor->ClearState();
pipe.CreateVKPipeline(descriptorSet);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_WARN_BIT);
if (!strstr(msgString.c_str(),"FS writes to output location 1 with no matching attachment")) {
FAIL() << "Incorrect warning: " << msgString;
}
}
TEST_F(VkLayerTest, CreatePipelineFragmentOutputTypeMismatch)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
ScopedUseGlsl useGlsl(false);
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"void main(){\n"
" gl_Position = vec4(1);\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) out ivec4 x;\n" /* not UNORM */
"void main(){\n"
" x = ivec4(1);\n"
"}\n";
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
/* implicit CB 0 set up by test framework, is UNORM. */
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
m_errorMonitor->ClearState();
pipe.CreateVKPipeline(descriptorSet);
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_ERROR_BIT);
if (!strstr(msgString.c_str(),"does not match FS output type")) {
FAIL() << "Incorrect error: " << msgString;
}
}
TEST_F(VkLayerTest, CreatePipelineNonSpirvShader)
{
VkFlags msgFlags;
std::string msgString;
ASSERT_NO_FATAL_FAILURE(InitState());
/* Intentionally provided GLSL rather than compiling to SPIRV first */
ScopedUseGlsl useGlsl(true);
char const *vsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"void main(){\n"
" gl_Position = vec4(1);\n"
"}\n";
char const *fsSource =
"#version 140\n"
"#extension GL_ARB_separate_shader_objects: require\n"
"#extension GL_ARB_shading_language_420pack: require\n"
"\n"
"layout(location=0) out vec4 x;\n"
"void main(){\n"
" x = vec4(1);\n"
"}\n";
m_errorMonitor->ClearState();
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX, this);
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT, this);
VkPipelineObj pipe(m_device);
pipe.AddShader(&vs);
pipe.AddShader(&fs);
/* implicit CB 0 set up by test framework, is UNORM. */
VkCommandBufferObj dummyCmd(m_device);
VkDescriptorSetObj descriptorSet(m_device);
descriptorSet.AppendDummy();
descriptorSet.CreateVKDescriptorSet(&dummyCmd);
VkResult res = pipe.CreateVKPipeline(descriptorSet);
/* pipeline creation should have succeeded */
ASSERT_EQ(VK_SUCCESS, res);
/* should have emitted a warning: the shader is not SPIRV, so we're
* not going to be able to analyze it */
msgFlags = m_errorMonitor->GetState(&msgString);
ASSERT_TRUE(msgFlags & VK_DBG_REPORT_WARN_BIT);
if (!strstr(msgString.c_str(),"is not SPIR-V")) {
FAIL() << "Incorrect warning: " << msgString;
}
}
#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;
}