8351 lines
370 KiB
C++
8351 lines
370 KiB
C++
/*
|
|
* Copyright (c) 2015-2019 The Khronos Group Inc.
|
|
* Copyright (c) 2015-2019 Valve Corporation
|
|
* Copyright (c) 2015-2019 LunarG, Inc.
|
|
* Copyright (c) 2015-2019 Google, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Author: Chia-I Wu <olvaffe@gmail.com>
|
|
* Author: Chris Forbes <chrisf@ijw.co.nz>
|
|
* Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
|
|
* Author: Mark Lobodzinski <mark@lunarg.com>
|
|
* Author: Mike Stroyan <mike@LunarG.com>
|
|
* Author: Tobin Ehlis <tobine@google.com>
|
|
* Author: Tony Barbour <tony@LunarG.com>
|
|
* Author: Cody Northrop <cnorthrop@google.com>
|
|
* Author: Dave Houlton <daveh@lunarg.com>
|
|
* Author: Jeremy Kniager <jeremyk@lunarg.com>
|
|
* Author: Shannon McPherson <shannon@lunarg.com>
|
|
* Author: John Zulauf <jzulauf@lunarg.com>
|
|
*/
|
|
|
|
#include "cast_utils.h"
|
|
#include "layer_validation_tests.h"
|
|
//
|
|
// POSITIVE VALIDATION TESTS
|
|
//
|
|
// These tests do not expect to encounter ANY validation errors pass only if this is true
|
|
|
|
TEST_F(VkPositiveLayerTest, NullFunctionPointer) {
|
|
TEST_DESCRIPTION("On 1_0 instance , call GetDeviceProcAddr on promoted 1_1 device-level entrypoint");
|
|
SetTargetApiVersion(VK_API_VERSION_1_0);
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
if (DeviceExtensionSupported(gpu(), nullptr, "VK_KHR_get_memory_requirements2")) {
|
|
m_device_extension_names.push_back("VK_KHR_get_memory_requirements2");
|
|
} else {
|
|
printf("%s VK_KHR_get_memory_reqirements2 extension not supported, skipping NullFunctionPointer test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
auto fpGetBufferMemoryRequirements =
|
|
(PFN_vkGetBufferMemoryRequirements2)vkGetDeviceProcAddr(m_device->device(), "vkGetBufferMemoryRequirements2");
|
|
if (fpGetBufferMemoryRequirements) {
|
|
m_errorMonitor->SetError("Null was expected!");
|
|
}
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, SecondaryCommandBufferBarrier) {
|
|
TEST_DESCRIPTION("Add a pipeline barrier in a secondary command buffer");
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
// A renderpass with a single subpass that declared a self-dependency
|
|
VkAttachmentDescription attach[] = {
|
|
{0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL},
|
|
};
|
|
VkAttachmentReference ref = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
VkSubpassDescription subpasses[] = {
|
|
{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &ref, nullptr, nullptr, 0, nullptr},
|
|
};
|
|
VkSubpassDependency dep = {0,
|
|
0,
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
VK_ACCESS_SHADER_WRITE_BIT,
|
|
VK_ACCESS_SHADER_WRITE_BIT,
|
|
VK_DEPENDENCY_BY_REGION_BIT};
|
|
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 1, attach, 1, subpasses, 1, &dep};
|
|
VkRenderPass rp;
|
|
|
|
VkResult err = vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkImageObj image(m_device);
|
|
image.Init(32, 32, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL, 0);
|
|
VkImageView imageView = image.targetView(VK_FORMAT_R8G8B8A8_UNORM);
|
|
|
|
VkFramebufferCreateInfo fbci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, rp, 1, &imageView, 32, 32, 1};
|
|
VkFramebuffer fb;
|
|
err = vkCreateFramebuffer(m_device->device(), &fbci, nullptr, &fb);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
m_commandBuffer->begin();
|
|
|
|
VkRenderPassBeginInfo rpbi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
|
nullptr,
|
|
rp,
|
|
fb,
|
|
{{
|
|
0,
|
|
0,
|
|
},
|
|
{32, 32}},
|
|
0,
|
|
nullptr};
|
|
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
|
|
|
|
VkCommandPoolObj pool(m_device, m_device->graphics_queue_node_index_, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
|
|
VkCommandBufferObj secondary(m_device, &pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
|
|
|
|
VkCommandBufferInheritanceInfo cbii = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
|
|
nullptr,
|
|
rp,
|
|
0,
|
|
VK_NULL_HANDLE, // Set to NULL FB handle intentionally to flesh out any errors
|
|
VK_FALSE,
|
|
0,
|
|
0};
|
|
VkCommandBufferBeginInfo cbbi = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr,
|
|
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT,
|
|
&cbii};
|
|
vkBeginCommandBuffer(secondary.handle(), &cbbi);
|
|
VkMemoryBarrier mem_barrier = {};
|
|
mem_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
|
mem_barrier.pNext = NULL;
|
|
mem_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
|
mem_barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
|
vkCmdPipelineBarrier(secondary.handle(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
VK_DEPENDENCY_BY_REGION_BIT, 1, &mem_barrier, 0, nullptr, 0, nullptr);
|
|
|
|
image.ImageMemoryBarrier(&secondary, VK_IMAGE_ASPECT_COLOR_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_SHADER_WRITE_BIT,
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
|
secondary.end();
|
|
|
|
vkCmdExecuteCommands(m_commandBuffer->handle(), 1, &secondary.handle());
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_commandBuffer->end();
|
|
|
|
VkSubmitInfo submit_info = {};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &m_commandBuffer->handle();
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
|
|
vkDestroyFramebuffer(m_device->device(), fb, nullptr);
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, RenderPassCreateAttachmentUsedTwiceOK) {
|
|
TEST_DESCRIPTION("Attachment is used simultaneously as color and input, with the same layout. This is OK.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
VkAttachmentDescription attach[] = {
|
|
{0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL},
|
|
};
|
|
VkAttachmentReference ref = {0, VK_IMAGE_LAYOUT_GENERAL};
|
|
VkSubpassDescription subpasses[] = {
|
|
{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 1, &ref, 1, &ref, nullptr, nullptr, 0, nullptr},
|
|
};
|
|
|
|
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 1, attach, 1, subpasses, 0, nullptr};
|
|
VkRenderPass rp;
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, RenderPassCreateInitialLayoutUndefined) {
|
|
TEST_DESCRIPTION(
|
|
"Ensure that CmdBeginRenderPass with an attachment's initialLayout of VK_IMAGE_LAYOUT_UNDEFINED works when the command "
|
|
"buffer has prior knowledge of that attachment's layout.");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
// A renderpass with one color attachment.
|
|
VkAttachmentDescription attachment = {0,
|
|
VK_FORMAT_R8G8B8A8_UNORM,
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_STORE,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
|
|
VkAttachmentReference att_ref = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
|
|
VkSubpassDescription subpass = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &att_ref, nullptr, nullptr, 0, nullptr};
|
|
|
|
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 1, &attachment, 1, &subpass, 0, nullptr};
|
|
|
|
VkRenderPass rp;
|
|
VkResult err = vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// A compatible framebuffer.
|
|
VkImageObj image(m_device);
|
|
image.Init(32, 32, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
|
|
VkImageViewCreateInfo ivci = {
|
|
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
nullptr,
|
|
0,
|
|
image.handle(),
|
|
VK_IMAGE_VIEW_TYPE_2D,
|
|
VK_FORMAT_R8G8B8A8_UNORM,
|
|
{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
VK_COMPONENT_SWIZZLE_IDENTITY},
|
|
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
|
|
};
|
|
VkImageView view;
|
|
err = vkCreateImageView(m_device->device(), &ivci, nullptr, &view);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkFramebufferCreateInfo fci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, rp, 1, &view, 32, 32, 1};
|
|
VkFramebuffer fb;
|
|
err = vkCreateFramebuffer(m_device->device(), &fci, nullptr, &fb);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Record a single command buffer which uses this renderpass twice. The
|
|
// bug is triggered at the beginning of the second renderpass, when the
|
|
// command buffer already has a layout recorded for the attachment.
|
|
VkRenderPassBeginInfo rpbi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, rp, fb, {{0, 0}, {32, 32}}, 0, nullptr};
|
|
m_commandBuffer->begin();
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_INLINE);
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_commandBuffer->end();
|
|
|
|
vkDestroyFramebuffer(m_device->device(), fb, nullptr);
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
vkDestroyImageView(m_device->device(), view, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, RenderPassCreateAttachmentLayoutWithLoadOpThenReadOnly) {
|
|
TEST_DESCRIPTION(
|
|
"Positive test where we create a renderpass with an attachment that uses LOAD_OP_CLEAR, the first subpass has a valid "
|
|
"layout, and a second subpass then uses a valid *READ_ONLY* layout.");
|
|
m_errorMonitor->ExpectSuccess();
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
auto depth_format = FindSupportedDepthStencilFormat(gpu());
|
|
if (!depth_format) {
|
|
printf("%s No Depth + Stencil format found. Skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
VkAttachmentReference attach[2] = {};
|
|
attach[0].attachment = 0;
|
|
attach[0].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
attach[1].attachment = 0;
|
|
attach[1].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
|
|
VkSubpassDescription subpasses[2] = {};
|
|
// First subpass clears DS attach on load
|
|
subpasses[0].pDepthStencilAttachment = &attach[0];
|
|
// 2nd subpass reads in DS as input attachment
|
|
subpasses[1].inputAttachmentCount = 1;
|
|
subpasses[1].pInputAttachments = &attach[1];
|
|
VkAttachmentDescription attach_desc = {};
|
|
attach_desc.format = depth_format;
|
|
attach_desc.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
attach_desc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
attach_desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attach_desc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
attach_desc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
attach_desc.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
attach_desc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
|
|
VkRenderPassCreateInfo rpci = {};
|
|
rpci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
rpci.attachmentCount = 1;
|
|
rpci.pAttachments = &attach_desc;
|
|
rpci.subpassCount = 2;
|
|
rpci.pSubpasses = subpasses;
|
|
|
|
// Now create RenderPass and verify no errors
|
|
VkRenderPass rp;
|
|
vkCreateRenderPass(m_device->device(), &rpci, NULL, &rp);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyRenderPass(m_device->device(), rp, NULL);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, RenderPassBeginSubpassZeroTransitionsApplied) {
|
|
TEST_DESCRIPTION("Ensure that CmdBeginRenderPass applies the layout transitions for the first subpass");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
// A renderpass with one color attachment.
|
|
VkAttachmentDescription attachment = {0,
|
|
VK_FORMAT_R8G8B8A8_UNORM,
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_STORE,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
|
|
VkAttachmentReference att_ref = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
|
|
VkSubpassDescription subpass = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &att_ref, nullptr, nullptr, 0, nullptr};
|
|
|
|
VkSubpassDependency dep = {0,
|
|
0,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
VK_DEPENDENCY_BY_REGION_BIT};
|
|
|
|
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 1, &attachment, 1, &subpass, 1, &dep};
|
|
|
|
VkResult err;
|
|
VkRenderPass rp;
|
|
err = vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// A compatible framebuffer.
|
|
VkImageObj image(m_device);
|
|
image.Init(32, 32, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
|
|
VkImageView view = image.targetView(VK_FORMAT_R8G8B8A8_UNORM);
|
|
|
|
VkFramebufferCreateInfo fci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, rp, 1, &view, 32, 32, 1};
|
|
VkFramebuffer fb;
|
|
err = vkCreateFramebuffer(m_device->device(), &fci, nullptr, &fb);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Record a single command buffer which issues a pipeline barrier w/
|
|
// image memory barrier for the attachment. This detects the previously
|
|
// missing tracking of the subpass layout by throwing a validation error
|
|
// if it doesn't occur.
|
|
VkRenderPassBeginInfo rpbi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, rp, fb, {{0, 0}, {32, 32}}, 0, nullptr};
|
|
m_commandBuffer->begin();
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_INLINE);
|
|
|
|
image.ImageMemoryBarrier(m_commandBuffer, VK_IMAGE_ASPECT_COLOR_BIT, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
|
|
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_errorMonitor->VerifyNotFound();
|
|
m_commandBuffer->end();
|
|
|
|
vkDestroyFramebuffer(m_device->device(), fb, nullptr);
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, RenderPassBeginTransitionsAttachmentUnused) {
|
|
TEST_DESCRIPTION(
|
|
"Ensure that layout transitions work correctly without errors, when an attachment reference is VK_ATTACHMENT_UNUSED");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
// A renderpass with no attachments
|
|
VkAttachmentReference att_ref = {VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
|
|
VkSubpassDescription subpass = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &att_ref, nullptr, nullptr, 0, nullptr};
|
|
|
|
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 0, nullptr, 1, &subpass, 0, nullptr};
|
|
|
|
VkRenderPass rp;
|
|
VkResult err = vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// A compatible framebuffer.
|
|
VkFramebufferCreateInfo fci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, rp, 0, nullptr, 32, 32, 1};
|
|
VkFramebuffer fb;
|
|
err = vkCreateFramebuffer(m_device->device(), &fci, nullptr, &fb);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Record a command buffer which just begins and ends the renderpass. The
|
|
// bug manifests in BeginRenderPass.
|
|
VkRenderPassBeginInfo rpbi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, rp, fb, {{0, 0}, {32, 32}}, 0, nullptr};
|
|
m_commandBuffer->begin();
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_INLINE);
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_errorMonitor->VerifyNotFound();
|
|
m_commandBuffer->end();
|
|
|
|
vkDestroyFramebuffer(m_device->device(), fb, nullptr);
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, RenderPassBeginStencilLoadOp) {
|
|
TEST_DESCRIPTION("Create a stencil-only attachment with a LOAD_OP set to CLEAR. stencil[Load|Store]Op used to be ignored.");
|
|
VkResult result = VK_SUCCESS;
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
auto depth_format = FindSupportedDepthStencilFormat(gpu());
|
|
if (!depth_format) {
|
|
printf("%s No Depth + Stencil format found. Skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
VkImageFormatProperties formatProps;
|
|
vkGetPhysicalDeviceImageFormatProperties(gpu(), depth_format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
|
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 0,
|
|
&formatProps);
|
|
if (formatProps.maxExtent.width < 100 || formatProps.maxExtent.height < 100) {
|
|
printf("%s Image format max extent is too small.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
VkFormat depth_stencil_fmt = depth_format;
|
|
m_depthStencil->Init(m_device, 100, 100, depth_stencil_fmt,
|
|
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
|
|
VkAttachmentDescription att = {};
|
|
VkAttachmentReference ref = {};
|
|
att.format = depth_stencil_fmt;
|
|
att.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
att.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
att.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
att.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
att.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
att.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
att.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
VkClearValue clear;
|
|
clear.depthStencil.depth = 1.0;
|
|
clear.depthStencil.stencil = 0;
|
|
ref.attachment = 0;
|
|
ref.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
|
|
VkSubpassDescription subpass = {};
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass.flags = 0;
|
|
subpass.inputAttachmentCount = 0;
|
|
subpass.pInputAttachments = NULL;
|
|
subpass.colorAttachmentCount = 0;
|
|
subpass.pColorAttachments = NULL;
|
|
subpass.pResolveAttachments = NULL;
|
|
subpass.pDepthStencilAttachment = &ref;
|
|
subpass.preserveAttachmentCount = 0;
|
|
subpass.pPreserveAttachments = NULL;
|
|
|
|
VkRenderPass rp;
|
|
VkRenderPassCreateInfo rp_info = {};
|
|
rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
rp_info.attachmentCount = 1;
|
|
rp_info.pAttachments = &att;
|
|
rp_info.subpassCount = 1;
|
|
rp_info.pSubpasses = &subpass;
|
|
result = vkCreateRenderPass(device(), &rp_info, NULL, &rp);
|
|
ASSERT_VK_SUCCESS(result);
|
|
|
|
VkImageView *depthView = m_depthStencil->BindInfo();
|
|
VkFramebufferCreateInfo fb_info = {};
|
|
fb_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
|
fb_info.pNext = NULL;
|
|
fb_info.renderPass = rp;
|
|
fb_info.attachmentCount = 1;
|
|
fb_info.pAttachments = depthView;
|
|
fb_info.width = 100;
|
|
fb_info.height = 100;
|
|
fb_info.layers = 1;
|
|
VkFramebuffer fb;
|
|
result = vkCreateFramebuffer(device(), &fb_info, NULL, &fb);
|
|
ASSERT_VK_SUCCESS(result);
|
|
|
|
VkRenderPassBeginInfo rpbinfo = {};
|
|
rpbinfo.clearValueCount = 1;
|
|
rpbinfo.pClearValues = &clear;
|
|
rpbinfo.pNext = NULL;
|
|
rpbinfo.renderPass = rp;
|
|
rpbinfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
rpbinfo.renderArea.extent.width = 100;
|
|
rpbinfo.renderArea.extent.height = 100;
|
|
rpbinfo.renderArea.offset.x = 0;
|
|
rpbinfo.renderArea.offset.y = 0;
|
|
rpbinfo.framebuffer = fb;
|
|
|
|
VkFenceObj fence;
|
|
fence.init(*m_device, VkFenceObj::create_info());
|
|
ASSERT_TRUE(fence.initialized());
|
|
|
|
m_commandBuffer->begin();
|
|
m_commandBuffer->BeginRenderPass(rpbinfo);
|
|
m_commandBuffer->EndRenderPass();
|
|
m_commandBuffer->end();
|
|
m_commandBuffer->QueueCommandBuffer(fence);
|
|
|
|
VkImageObj destImage(m_device);
|
|
destImage.Init(100, 100, 1, depth_stencil_fmt, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT,
|
|
VK_IMAGE_TILING_OPTIMAL, 0);
|
|
fence.wait(VK_TRUE, UINT64_MAX);
|
|
VkCommandBufferObj cmdbuf(m_device, m_commandPool);
|
|
cmdbuf.begin();
|
|
|
|
m_depthStencil->ImageMemoryBarrier(&cmdbuf, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
|
|
VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT,
|
|
VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
|
|
destImage.ImageMemoryBarrier(&cmdbuf, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
|
|
VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, 0,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
VkImageCopy cregion;
|
|
cregion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
cregion.srcSubresource.mipLevel = 0;
|
|
cregion.srcSubresource.baseArrayLayer = 0;
|
|
cregion.srcSubresource.layerCount = 1;
|
|
cregion.srcOffset.x = 0;
|
|
cregion.srcOffset.y = 0;
|
|
cregion.srcOffset.z = 0;
|
|
cregion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
cregion.dstSubresource.mipLevel = 0;
|
|
cregion.dstSubresource.baseArrayLayer = 0;
|
|
cregion.dstSubresource.layerCount = 1;
|
|
cregion.dstOffset.x = 0;
|
|
cregion.dstOffset.y = 0;
|
|
cregion.dstOffset.z = 0;
|
|
cregion.extent.width = 100;
|
|
cregion.extent.height = 100;
|
|
cregion.extent.depth = 1;
|
|
cmdbuf.CopyImage(m_depthStencil->handle(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, destImage.handle(),
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &cregion);
|
|
cmdbuf.end();
|
|
|
|
VkSubmitInfo submit_info;
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.pNext = NULL;
|
|
submit_info.waitSemaphoreCount = 0;
|
|
submit_info.pWaitSemaphores = NULL;
|
|
submit_info.pWaitDstStageMask = NULL;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &cmdbuf.handle();
|
|
submit_info.signalSemaphoreCount = 0;
|
|
submit_info.pSignalSemaphores = NULL;
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
vkDestroyFramebuffer(m_device->device(), fb, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, RenderPassBeginInlineAndSecondaryCommandBuffers) {
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
m_commandBuffer->begin();
|
|
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &m_renderPassBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &m_renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
m_commandBuffer->end();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, RenderPassBeginDepthStencilLayoutTransitionFromUndefined) {
|
|
TEST_DESCRIPTION(
|
|
"Create a render pass with depth-stencil attachment where layout transition from UNDEFINED TO DS_READ_ONLY_OPTIMAL is set "
|
|
"by render pass and verify that transition has correctly occurred at queue submit time with no validation errors.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
auto depth_format = FindSupportedDepthStencilFormat(gpu());
|
|
if (!depth_format) {
|
|
printf("%s No Depth + Stencil format found. Skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
VkImageFormatProperties format_props;
|
|
vkGetPhysicalDeviceImageFormatProperties(gpu(), depth_format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
|
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, 0, &format_props);
|
|
if (format_props.maxExtent.width < 32 || format_props.maxExtent.height < 32) {
|
|
printf("%s Depth extent too small, RenderPassDepthStencilLayoutTransition skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
// A renderpass with one depth/stencil attachment.
|
|
VkAttachmentDescription attachment = {0,
|
|
depth_format,
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};
|
|
|
|
VkAttachmentReference att_ref = {0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};
|
|
|
|
VkSubpassDescription subpass = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 0, nullptr, nullptr, &att_ref, 0, nullptr};
|
|
|
|
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 1, &attachment, 1, &subpass, 0, nullptr};
|
|
|
|
VkRenderPass rp;
|
|
VkResult err = vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
ASSERT_VK_SUCCESS(err);
|
|
// A compatible ds image.
|
|
VkImageObj image(m_device);
|
|
image.Init(32, 32, 1, depth_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
|
|
VkImageViewCreateInfo ivci = {
|
|
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
nullptr,
|
|
0,
|
|
image.handle(),
|
|
VK_IMAGE_VIEW_TYPE_2D,
|
|
depth_format,
|
|
{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
VK_COMPONENT_SWIZZLE_IDENTITY},
|
|
{VK_IMAGE_ASPECT_DEPTH_BIT, 0, 1, 0, 1},
|
|
};
|
|
VkImageView view;
|
|
err = vkCreateImageView(m_device->device(), &ivci, nullptr, &view);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkFramebufferCreateInfo fci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, rp, 1, &view, 32, 32, 1};
|
|
VkFramebuffer fb;
|
|
err = vkCreateFramebuffer(m_device->device(), &fci, nullptr, &fb);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkRenderPassBeginInfo rpbi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, rp, fb, {{0, 0}, {32, 32}}, 0, nullptr};
|
|
m_commandBuffer->begin();
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_INLINE);
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_commandBuffer->end();
|
|
m_commandBuffer->QueueCommandBuffer(false);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
// Cleanup
|
|
vkDestroyImageView(m_device->device(), view, NULL);
|
|
vkDestroyRenderPass(m_device->device(), rp, NULL);
|
|
vkDestroyFramebuffer(m_device->device(), fb, NULL);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, DestroyPipelineRenderPass) {
|
|
TEST_DESCRIPTION("Draw using a pipeline whose create renderPass has been destroyed.");
|
|
m_errorMonitor->ExpectSuccess();
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkResult err;
|
|
|
|
// Create a renderPass that's compatible with Draw-time renderPass
|
|
VkAttachmentDescription att = {};
|
|
att.format = m_render_target_fmt;
|
|
att.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
att.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
att.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
att.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
att.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
att.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
att.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
VkAttachmentReference ref = {};
|
|
ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
ref.attachment = 0;
|
|
|
|
m_renderPassClearValues.clear();
|
|
VkClearValue clear = {};
|
|
clear.color = m_clear_color;
|
|
|
|
VkSubpassDescription subpass = {};
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass.flags = 0;
|
|
subpass.inputAttachmentCount = 0;
|
|
subpass.pInputAttachments = NULL;
|
|
subpass.colorAttachmentCount = 1;
|
|
subpass.pColorAttachments = &ref;
|
|
subpass.pResolveAttachments = NULL;
|
|
|
|
subpass.pDepthStencilAttachment = NULL;
|
|
subpass.preserveAttachmentCount = 0;
|
|
subpass.pPreserveAttachments = NULL;
|
|
|
|
VkRenderPassCreateInfo rp_info = {};
|
|
rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
rp_info.attachmentCount = 1;
|
|
rp_info.pAttachments = &att;
|
|
rp_info.subpassCount = 1;
|
|
rp_info.pSubpasses = &subpass;
|
|
|
|
VkRenderPass rp;
|
|
err = vkCreateRenderPass(device(), &rp_info, NULL, &rp);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
VkPipelineObj pipe(m_device);
|
|
pipe.AddDefaultColorAttachment();
|
|
pipe.AddShader(&vs);
|
|
pipe.AddShader(&fs);
|
|
VkViewport viewport = {0.0f, 0.0f, 64.0f, 64.0f, 0.0f, 1.0f};
|
|
m_viewports.push_back(viewport);
|
|
pipe.SetViewport(m_viewports);
|
|
VkRect2D rect = {{0, 0}, {64, 64}};
|
|
m_scissors.push_back(rect);
|
|
pipe.SetScissor(m_scissors);
|
|
|
|
const VkPipelineLayoutObj pl(m_device);
|
|
pipe.CreateVKPipeline(pl.handle(), rp);
|
|
|
|
m_commandBuffer->begin();
|
|
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
|
|
vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle());
|
|
// Destroy renderPass before pipeline is used in Draw
|
|
// We delay until after CmdBindPipeline to verify that invalid binding isn't
|
|
// created between CB & renderPass, which we used to do.
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
vkCmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0);
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_commandBuffer->end();
|
|
|
|
VkSubmitInfo submit_info = {};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &m_commandBuffer->handle();
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, BasicQuery) {
|
|
TEST_DESCRIPTION("Use a couple occlusion queries");
|
|
m_errorMonitor->ExpectSuccess();
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
uint32_t qfi = 0;
|
|
VkBufferCreateInfo bci = {};
|
|
bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bci.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
bci.size = 4 * sizeof(uint64_t);
|
|
bci.queueFamilyIndexCount = 1;
|
|
bci.pQueueFamilyIndices = &qfi;
|
|
VkBufferObj buffer;
|
|
VkMemoryPropertyFlags mem_props = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
|
buffer.init(*m_device, bci, mem_props);
|
|
|
|
VkQueryPool query_pool;
|
|
VkQueryPoolCreateInfo query_pool_info;
|
|
query_pool_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
|
|
query_pool_info.pNext = NULL;
|
|
query_pool_info.queryType = VK_QUERY_TYPE_OCCLUSION;
|
|
query_pool_info.flags = 0;
|
|
query_pool_info.queryCount = 2;
|
|
query_pool_info.pipelineStatistics = 0;
|
|
|
|
VkResult res = vkCreateQueryPool(m_device->handle(), &query_pool_info, NULL, &query_pool);
|
|
ASSERT_VK_SUCCESS(res);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
|
|
m_commandBuffer->begin();
|
|
vkCmdResetQueryPool(m_commandBuffer->handle(), query_pool, 0, 2);
|
|
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
|
|
vkCmdBeginQuery(m_commandBuffer->handle(), query_pool, 0, 0);
|
|
vkCmdEndQuery(m_commandBuffer->handle(), query_pool, 0);
|
|
vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_);
|
|
vkCmdBeginQuery(m_commandBuffer->handle(), query_pool, 1, 0);
|
|
vkCmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0);
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
vkCmdEndQuery(m_commandBuffer->handle(), query_pool, 1);
|
|
vkCmdCopyQueryPoolResults(m_commandBuffer->handle(), query_pool, 0, 2, buffer.handle(), 0, sizeof(uint64_t),
|
|
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
|
|
m_commandBuffer->end();
|
|
|
|
VkSubmitInfo submit_info = {};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &m_commandBuffer->handle();
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
uint64_t samples_passed[4];
|
|
res = vkGetQueryPoolResults(m_device->handle(), query_pool, 0, 2, sizeof(samples_passed), samples_passed, sizeof(uint64_t),
|
|
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
|
|
ASSERT_VK_SUCCESS(res);
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkDestroyQueryPool(m_device->handle(), query_pool, NULL);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, MultiplaneGetImageSubresourceLayout) {
|
|
TEST_DESCRIPTION("Positive test, query layout of a single plane of a multiplane image. (repro Github #2530)");
|
|
|
|
// Enable KHR multiplane req'd extensions
|
|
bool mp_extensions = InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
|
VK_KHR_GET_MEMORY_REQUIREMENTS_2_SPEC_VERSION);
|
|
if (mp_extensions) {
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
|
|
if (mp_extensions) {
|
|
m_device_extension_names.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
|
m_device_extension_names.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
|
m_device_extension_names.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
|
|
m_device_extension_names.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s test requires KHR multiplane extensions, not available. Skipping.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
VkImageCreateInfo ci = {};
|
|
ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
ci.pNext = NULL;
|
|
ci.flags = 0;
|
|
ci.imageType = VK_IMAGE_TYPE_2D;
|
|
ci.format = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR;
|
|
ci.extent = {128, 128, 1};
|
|
ci.mipLevels = 1;
|
|
ci.arrayLayers = 1;
|
|
ci.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
ci.tiling = VK_IMAGE_TILING_LINEAR;
|
|
ci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
// Verify format
|
|
bool supported = ImageFormatAndFeaturesSupported(instance(), gpu(), ci, VK_FORMAT_FEATURE_TRANSFER_SRC_BIT);
|
|
if (!supported) {
|
|
printf("%s Multiplane image format not supported. Skipping test.\n", kSkipPrefix);
|
|
return; // Assume there's low ROI on searching for different mp formats
|
|
}
|
|
|
|
VkImage image;
|
|
VkResult err = vkCreateImage(device(), &ci, NULL, &image);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Query layout of 3rd plane
|
|
VkImageSubresource subres = {};
|
|
subres.aspectMask = VK_IMAGE_ASPECT_PLANE_2_BIT_KHR;
|
|
subres.mipLevel = 0;
|
|
subres.arrayLayer = 0;
|
|
VkSubresourceLayout layout = {};
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
vkGetImageSubresourceLayout(device(), image, &subres, &layout);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyImage(device(), image, NULL);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, OwnershipTranfersImage) {
|
|
TEST_DESCRIPTION("Valid image ownership transfers that shouldn't create errors");
|
|
ASSERT_NO_FATAL_FAILURE(Init(nullptr, nullptr, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT));
|
|
|
|
uint32_t no_gfx = m_device->QueueFamilyWithoutCapabilities(VK_QUEUE_GRAPHICS_BIT);
|
|
if (no_gfx == UINT32_MAX) {
|
|
printf("%s Required queue families not present (non-graphics capable required).\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
VkQueueObj *no_gfx_queue = m_device->queue_family_queues(no_gfx)[0].get();
|
|
|
|
VkCommandPoolObj no_gfx_pool(m_device, no_gfx, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
|
|
VkCommandBufferObj no_gfx_cb(m_device, &no_gfx_pool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, no_gfx_queue);
|
|
|
|
// Create an "exclusive" image owned by the graphics queue.
|
|
VkImageObj image(m_device);
|
|
VkFlags image_use = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
image.Init(32, 32, 1, VK_FORMAT_B8G8R8A8_UNORM, image_use, VK_IMAGE_TILING_OPTIMAL, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
auto image_subres = image.subresource_range(VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1);
|
|
auto image_barrier = image.image_memory_barrier(0, 0, image.Layout(), image.Layout(), image_subres);
|
|
image_barrier.srcQueueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
image_barrier.dstQueueFamilyIndex = no_gfx;
|
|
|
|
ValidOwnershipTransfer(m_errorMonitor, m_commandBuffer, &no_gfx_cb, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, nullptr, &image_barrier);
|
|
|
|
// Change layouts while changing ownership
|
|
image_barrier.srcQueueFamilyIndex = no_gfx;
|
|
image_barrier.dstQueueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
image_barrier.oldLayout = image.Layout();
|
|
// Make sure the new layout is different from the old
|
|
if (image_barrier.oldLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) {
|
|
image_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
} else {
|
|
image_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
}
|
|
|
|
ValidOwnershipTransfer(m_errorMonitor, &no_gfx_cb, m_commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, nullptr, &image_barrier);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, OwnershipTranfersBuffer) {
|
|
TEST_DESCRIPTION("Valid buffer ownership transfers that shouldn't create errors");
|
|
ASSERT_NO_FATAL_FAILURE(Init(nullptr, nullptr, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT));
|
|
|
|
uint32_t no_gfx = m_device->QueueFamilyWithoutCapabilities(VK_QUEUE_GRAPHICS_BIT);
|
|
if (no_gfx == UINT32_MAX) {
|
|
printf("%s Required queue families not present (non-graphics capable required).\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
VkQueueObj *no_gfx_queue = m_device->queue_family_queues(no_gfx)[0].get();
|
|
|
|
VkCommandPoolObj no_gfx_pool(m_device, no_gfx, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
|
|
VkCommandBufferObj no_gfx_cb(m_device, &no_gfx_pool, VK_COMMAND_BUFFER_LEVEL_PRIMARY, no_gfx_queue);
|
|
|
|
// Create a buffer
|
|
const VkDeviceSize buffer_size = 256;
|
|
uint8_t data[buffer_size] = {0xFF};
|
|
VkConstantBufferObj buffer(m_device, buffer_size, data, VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT);
|
|
ASSERT_TRUE(buffer.initialized());
|
|
auto buffer_barrier = buffer.buffer_memory_barrier(0, 0, 0, VK_WHOLE_SIZE);
|
|
|
|
// Let gfx own it.
|
|
buffer_barrier.srcQueueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
buffer_barrier.dstQueueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
ValidOwnershipTransferOp(m_errorMonitor, m_commandBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
&buffer_barrier, nullptr);
|
|
|
|
// Transfer it to non-gfx
|
|
buffer_barrier.dstQueueFamilyIndex = no_gfx;
|
|
ValidOwnershipTransfer(m_errorMonitor, m_commandBuffer, &no_gfx_cb, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
|
|
VK_PIPELINE_STAGE_TRANSFER_BIT, &buffer_barrier, nullptr);
|
|
|
|
// Transfer it to gfx
|
|
buffer_barrier.srcQueueFamilyIndex = no_gfx;
|
|
buffer_barrier.dstQueueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
ValidOwnershipTransfer(m_errorMonitor, &no_gfx_cb, m_commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, &buffer_barrier, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, LayoutFromPresentWithoutAccessMemoryRead) {
|
|
// Transition an image away from PRESENT_SRC_KHR without ACCESS_MEMORY_READ
|
|
// in srcAccessMask.
|
|
|
|
// The required behavior here was a bit unclear in earlier versions of the
|
|
// spec, but there is no memory dependency required here, so this should
|
|
// work without warnings.
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
VkImageObj image(m_device);
|
|
image.Init(128, 128, 1, VK_FORMAT_B8G8R8A8_UNORM, (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT),
|
|
VK_IMAGE_TILING_OPTIMAL, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
|
|
VkImageMemoryBarrier barrier = {};
|
|
VkImageSubresourceRange range;
|
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
|
barrier.dstAccessMask = 0;
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
barrier.image = image.handle();
|
|
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
range.baseMipLevel = 0;
|
|
range.levelCount = 1;
|
|
range.baseArrayLayer = 0;
|
|
range.layerCount = 1;
|
|
barrier.subresourceRange = range;
|
|
VkCommandBufferObj cmdbuf(m_device, m_commandPool);
|
|
cmdbuf.begin();
|
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1,
|
|
&barrier);
|
|
barrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
barrier.srcAccessMask = 0;
|
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1,
|
|
&barrier);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CopyNonupdatedDescriptors) {
|
|
TEST_DESCRIPTION("Copy non-updated descriptors");
|
|
unsigned int i;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
OneOffDescriptorSet src_descriptor_set(m_device, {
|
|
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
{1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
{2, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
});
|
|
OneOffDescriptorSet dst_descriptor_set(m_device, {
|
|
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
{1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
});
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
const unsigned int copy_size = 2;
|
|
VkCopyDescriptorSet copy_ds_update[copy_size];
|
|
memset(copy_ds_update, 0, sizeof(copy_ds_update));
|
|
for (i = 0; i < copy_size; i++) {
|
|
copy_ds_update[i].sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET;
|
|
copy_ds_update[i].srcSet = src_descriptor_set.set_;
|
|
copy_ds_update[i].srcBinding = i;
|
|
copy_ds_update[i].dstSet = dst_descriptor_set.set_;
|
|
copy_ds_update[i].dstBinding = i;
|
|
copy_ds_update[i].descriptorCount = 1;
|
|
}
|
|
vkUpdateDescriptorSets(m_device->device(), 0, NULL, copy_size, copy_ds_update);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ConfirmNoVLErrorWhenVkCmdClearAttachmentsCalledInSecondaryCB) {
|
|
TEST_DESCRIPTION(
|
|
"This test is to verify that when vkCmdClearAttachments is called by a secondary commandbuffer, the validation layers do "
|
|
"not throw an error if the primary commandbuffer begins a renderpass before executing the secondary commandbuffer.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkCommandBufferObj secondary(m_device, m_commandPool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
|
|
|
|
VkCommandBufferBeginInfo info = {};
|
|
VkCommandBufferInheritanceInfo hinfo = {};
|
|
info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
|
|
info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
info.pInheritanceInfo = &hinfo;
|
|
hinfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
|
|
hinfo.pNext = NULL;
|
|
hinfo.renderPass = renderPass();
|
|
hinfo.subpass = 0;
|
|
hinfo.framebuffer = m_framebuffer;
|
|
hinfo.occlusionQueryEnable = VK_FALSE;
|
|
hinfo.queryFlags = 0;
|
|
hinfo.pipelineStatistics = 0;
|
|
|
|
secondary.begin(&info);
|
|
VkClearAttachment color_attachment;
|
|
color_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
color_attachment.clearValue.color.float32[0] = 0.0;
|
|
color_attachment.clearValue.color.float32[1] = 0.0;
|
|
color_attachment.clearValue.color.float32[2] = 0.0;
|
|
color_attachment.clearValue.color.float32[3] = 0.0;
|
|
color_attachment.colorAttachment = 0;
|
|
VkClearRect clear_rect = {{{0, 0}, {(uint32_t)m_width, (uint32_t)m_height}}, 0, 1};
|
|
vkCmdClearAttachments(secondary.handle(), 1, &color_attachment, 1, &clear_rect);
|
|
secondary.end();
|
|
// Modify clear rect here to verify that it doesn't cause validation error
|
|
clear_rect = {{{0, 0}, {99999999, 99999999}}, 0, 0};
|
|
|
|
m_commandBuffer->begin();
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &m_renderPassBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
|
|
vkCmdExecuteCommands(m_commandBuffer->handle(), 1, &secondary.handle());
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_commandBuffer->end();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineComplexTypes) {
|
|
TEST_DESCRIPTION("Smoke test for complex types across VS/FS boundary");
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
if (!m_device->phy().features().tessellationShader) {
|
|
printf("%s Device does not support tessellation shaders; skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj tcs(m_device, bindStateTscShaderText, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, this);
|
|
VkShaderObj tes(m_device, bindStateTeshaderText, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo iasci{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, nullptr, 0,
|
|
VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, VK_FALSE};
|
|
VkPipelineTessellationStateCreateInfo tsci{VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, nullptr, 0, 3};
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.gp_ci_.pTessellationState = &tsci;
|
|
pipe.gp_ci_.pInputAssemblyState = &iasci;
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), tcs.GetStageCreateInfo(), tes.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ShaderRelaxedBlockLayout) {
|
|
// This is a positive test, no errors expected
|
|
// Verifies the ability to relax block layout rules with a shader that requires them to be relaxed
|
|
TEST_DESCRIPTION("Create a shader that requires relaxed block layout.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
// The Relaxed Block Layout extension was promoted to core in 1.1.
|
|
// Go ahead and check for it and turn it on in case a 1.0 device has it.
|
|
if (!DeviceExtensionSupported(gpu(), nullptr, VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME)) {
|
|
printf("%s Extension %s not supported, skipping this pass. \n", kSkipPrefix, VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
m_device_extension_names.push_back(VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME);
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
// Vertex shader requiring relaxed layout.
|
|
// Without relaxed layout, we would expect a message like:
|
|
// "Structure id 2 decorated as Block for variable in Uniform storage class
|
|
// must follow standard uniform buffer layout rules: member 1 at offset 4 is not aligned to 16"
|
|
|
|
const std::string spv_source = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %main "main"
|
|
OpSource GLSL 450
|
|
OpMemberDecorate %S 0 Offset 0
|
|
OpMemberDecorate %S 1 Offset 4
|
|
OpDecorate %S Block
|
|
OpDecorate %B DescriptorSet 0
|
|
OpDecorate %B Binding 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v3float = OpTypeVector %float 3
|
|
%S = OpTypeStruct %float %v3float
|
|
%_ptr_Uniform_S = OpTypePointer Uniform %S
|
|
%B = OpVariable %_ptr_Uniform_S Uniform
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
m_errorMonitor->ExpectSuccess();
|
|
VkShaderObj vs(m_device, spv_source, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ShaderUboStd430Layout) {
|
|
// This is a positive test, no errors expected
|
|
// Verifies the ability to scalar block layout rules with a shader that requires them to be relaxed
|
|
TEST_DESCRIPTION("Create a shader that requires UBO std430 layout.");
|
|
// Enable req'd extensions
|
|
if (!InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
|
|
printf("%s Extension %s not supported, skipping this pass. \n", kSkipPrefix,
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
// Check for the UBO standard block layout extension and turn it on if it's available
|
|
if (!DeviceExtensionSupported(gpu(), nullptr, VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME)) {
|
|
printf("%s Extension %s not supported, skipping this pass. \n", kSkipPrefix,
|
|
VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
m_device_extension_names.push_back(VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME);
|
|
|
|
PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2 =
|
|
(PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(instance(), "vkGetPhysicalDeviceFeatures2KHR");
|
|
|
|
auto uniform_buffer_standard_layout_features = lvl_init_struct<VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR>(NULL);
|
|
uniform_buffer_standard_layout_features.uniformBufferStandardLayout = VK_TRUE;
|
|
auto query_features2 = lvl_init_struct<VkPhysicalDeviceFeatures2>(&uniform_buffer_standard_layout_features);
|
|
vkGetPhysicalDeviceFeatures2(gpu(), &query_features2);
|
|
|
|
auto set_features2 = lvl_init_struct<VkPhysicalDeviceFeatures2>(&uniform_buffer_standard_layout_features);
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &set_features2));
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
// Vertex shader requiring std430 in a uniform buffer.
|
|
// Without uniform buffer standard layout, we would expect a message like:
|
|
// "Structure id 3 decorated as Block for variable in Uniform storage class
|
|
// must follow standard uniform buffer layout rules: member 0 is an array
|
|
// with stride 4 not satisfying alignment to 16"
|
|
|
|
const std::string spv_source = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %main "main"
|
|
OpSource GLSL 460
|
|
OpDecorate %_arr_float_uint_8 ArrayStride 4
|
|
OpMemberDecorate %foo 0 Offset 0
|
|
OpDecorate %foo Block
|
|
OpDecorate %b DescriptorSet 0
|
|
OpDecorate %b Binding 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%uint = OpTypeInt 32 0
|
|
%uint_8 = OpConstant %uint 8
|
|
%_arr_float_uint_8 = OpTypeArray %float %uint_8
|
|
%foo = OpTypeStruct %_arr_float_uint_8
|
|
%_ptr_Uniform_foo = OpTypePointer Uniform %foo
|
|
%b = OpVariable %_ptr_Uniform_foo Uniform
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
std::vector<unsigned int> spv;
|
|
VkShaderModuleCreateInfo module_create_info;
|
|
VkShaderModule shader_module;
|
|
module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
|
module_create_info.pNext = NULL;
|
|
ASMtoSPV(SPV_ENV_VULKAN_1_0, 0, spv_source.data(), spv);
|
|
module_create_info.pCode = spv.data();
|
|
module_create_info.codeSize = spv.size() * sizeof(unsigned int);
|
|
module_create_info.flags = 0;
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
VkResult err = vkCreateShaderModule(m_device->handle(), &module_create_info, NULL, &shader_module);
|
|
m_errorMonitor->VerifyNotFound();
|
|
if (err == VK_SUCCESS) {
|
|
vkDestroyShaderModule(m_device->handle(), shader_module, NULL);
|
|
}
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ShaderScalarBlockLayout) {
|
|
// This is a positive test, no errors expected
|
|
// Verifies the ability to scalar block layout rules with a shader that requires them to be relaxed
|
|
TEST_DESCRIPTION("Create a shader that requires scalar block layout.");
|
|
// Enable req'd extensions
|
|
if (!InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
|
|
printf("%s Extension %s not supported, skipping this pass. \n", kSkipPrefix,
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
// Check for the Scalar Block Layout extension and turn it on if it's available
|
|
if (!DeviceExtensionSupported(gpu(), nullptr, VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME)) {
|
|
printf("%s Extension %s not supported, skipping this pass. \n", kSkipPrefix, VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
m_device_extension_names.push_back(VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME);
|
|
|
|
PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2 =
|
|
(PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(instance(), "vkGetPhysicalDeviceFeatures2KHR");
|
|
|
|
auto scalar_block_features = lvl_init_struct<VkPhysicalDeviceScalarBlockLayoutFeaturesEXT>(NULL);
|
|
scalar_block_features.scalarBlockLayout = VK_TRUE;
|
|
auto query_features2 = lvl_init_struct<VkPhysicalDeviceFeatures2>(&scalar_block_features);
|
|
vkGetPhysicalDeviceFeatures2(gpu(), &query_features2);
|
|
|
|
auto set_features2 = lvl_init_struct<VkPhysicalDeviceFeatures2>(&scalar_block_features);
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &set_features2));
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
// Vertex shader requiring scalar layout.
|
|
// Without scalar layout, we would expect a message like:
|
|
// "Structure id 2 decorated as Block for variable in Uniform storage class
|
|
// must follow standard uniform buffer layout rules: member 1 at offset 4 is not aligned to 16"
|
|
|
|
const std::string spv_source = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %main "main"
|
|
OpSource GLSL 450
|
|
OpMemberDecorate %S 0 Offset 0
|
|
OpMemberDecorate %S 1 Offset 4
|
|
OpMemberDecorate %S 2 Offset 8
|
|
OpDecorate %S Block
|
|
OpDecorate %B DescriptorSet 0
|
|
OpDecorate %B Binding 0
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v3float = OpTypeVector %float 3
|
|
%S = OpTypeStruct %float %float %v3float
|
|
%_ptr_Uniform_S = OpTypePointer Uniform %S
|
|
%B = OpVariable %_ptr_Uniform_S Uniform
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
VkShaderObj vs(m_device, spv_source, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, SpirvGroupDecorations) {
|
|
TEST_DESCRIPTION("Test shader validation support for group decorations.");
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
const std::string spv_source = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint GLCompute %main "main" %gl_GlobalInvocationID
|
|
OpExecutionMode %main LocalSize 1 1 1
|
|
OpSource GLSL 430
|
|
OpName %main "main"
|
|
OpName %gl_GlobalInvocationID "gl_GlobalInvocationID"
|
|
OpDecorate %gl_GlobalInvocationID BuiltIn GlobalInvocationId
|
|
OpDecorate %_runtimearr_float ArrayStride 4
|
|
OpDecorate %4 BufferBlock
|
|
OpDecorate %5 Offset 0
|
|
%4 = OpDecorationGroup
|
|
%5 = OpDecorationGroup
|
|
OpGroupDecorate %4 %_struct_6 %_struct_7 %_struct_8 %_struct_9 %_struct_10 %_struct_11
|
|
OpGroupMemberDecorate %5 %_struct_6 0 %_struct_7 0 %_struct_8 0 %_struct_9 0 %_struct_10 0 %_struct_11 0
|
|
OpDecorate %12 DescriptorSet 0
|
|
OpDecorate %13 DescriptorSet 0
|
|
OpDecorate %13 NonWritable
|
|
OpDecorate %13 Restrict
|
|
%14 = OpDecorationGroup
|
|
%12 = OpDecorationGroup
|
|
%13 = OpDecorationGroup
|
|
OpGroupDecorate %12 %15
|
|
OpGroupDecorate %12 %15
|
|
OpGroupDecorate %12 %15
|
|
OpDecorate %15 DescriptorSet 0
|
|
OpDecorate %15 Binding 5
|
|
OpGroupDecorate %14 %16
|
|
OpDecorate %16 DescriptorSet 0
|
|
OpDecorate %16 Binding 0
|
|
OpGroupDecorate %12 %17
|
|
OpDecorate %17 Binding 1
|
|
OpGroupDecorate %13 %18 %19
|
|
OpDecorate %18 Binding 2
|
|
OpDecorate %19 Binding 3
|
|
OpGroupDecorate %14 %20
|
|
OpGroupDecorate %12 %20
|
|
OpGroupDecorate %13 %20
|
|
OpDecorate %20 Binding 4
|
|
%bool = OpTypeBool
|
|
%void = OpTypeVoid
|
|
%23 = OpTypeFunction %void
|
|
%uint = OpTypeInt 32 0
|
|
%int = OpTypeInt 32 1
|
|
%float = OpTypeFloat 32
|
|
%v3uint = OpTypeVector %uint 3
|
|
%v3float = OpTypeVector %float 3
|
|
%_ptr_Input_v3uint = OpTypePointer Input %v3uint
|
|
%_ptr_Uniform_int = OpTypePointer Uniform %int
|
|
%_ptr_Uniform_float = OpTypePointer Uniform %float
|
|
%_runtimearr_int = OpTypeRuntimeArray %int
|
|
%_runtimearr_float = OpTypeRuntimeArray %float
|
|
%gl_GlobalInvocationID = OpVariable %_ptr_Input_v3uint Input
|
|
%int_0 = OpConstant %int 0
|
|
%_struct_6 = OpTypeStruct %_runtimearr_float
|
|
%_ptr_Uniform__struct_6 = OpTypePointer Uniform %_struct_6
|
|
%15 = OpVariable %_ptr_Uniform__struct_6 Uniform
|
|
%_struct_7 = OpTypeStruct %_runtimearr_float
|
|
%_ptr_Uniform__struct_7 = OpTypePointer Uniform %_struct_7
|
|
%16 = OpVariable %_ptr_Uniform__struct_7 Uniform
|
|
%_struct_8 = OpTypeStruct %_runtimearr_float
|
|
%_ptr_Uniform__struct_8 = OpTypePointer Uniform %_struct_8
|
|
%17 = OpVariable %_ptr_Uniform__struct_8 Uniform
|
|
%_struct_9 = OpTypeStruct %_runtimearr_float
|
|
%_ptr_Uniform__struct_9 = OpTypePointer Uniform %_struct_9
|
|
%18 = OpVariable %_ptr_Uniform__struct_9 Uniform
|
|
%_struct_10 = OpTypeStruct %_runtimearr_float
|
|
%_ptr_Uniform__struct_10 = OpTypePointer Uniform %_struct_10
|
|
%19 = OpVariable %_ptr_Uniform__struct_10 Uniform
|
|
%_struct_11 = OpTypeStruct %_runtimearr_float
|
|
%_ptr_Uniform__struct_11 = OpTypePointer Uniform %_struct_11
|
|
%20 = OpVariable %_ptr_Uniform__struct_11 Uniform
|
|
%main = OpFunction %void None %23
|
|
%40 = OpLabel
|
|
%41 = OpLoad %v3uint %gl_GlobalInvocationID
|
|
%42 = OpCompositeExtract %uint %41 0
|
|
%43 = OpAccessChain %_ptr_Uniform_float %16 %int_0 %42
|
|
%44 = OpAccessChain %_ptr_Uniform_float %17 %int_0 %42
|
|
%45 = OpAccessChain %_ptr_Uniform_float %18 %int_0 %42
|
|
%46 = OpAccessChain %_ptr_Uniform_float %19 %int_0 %42
|
|
%47 = OpAccessChain %_ptr_Uniform_float %20 %int_0 %42
|
|
%48 = OpAccessChain %_ptr_Uniform_float %15 %int_0 %42
|
|
%49 = OpLoad %float %43
|
|
%50 = OpLoad %float %44
|
|
%51 = OpLoad %float %45
|
|
%52 = OpLoad %float %46
|
|
%53 = OpLoad %float %47
|
|
%54 = OpFAdd %float %49 %50
|
|
%55 = OpFAdd %float %54 %51
|
|
%56 = OpFAdd %float %55 %52
|
|
%57 = OpFAdd %float %56 %53
|
|
OpStore %48 %57
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
// CreateDescriptorSetLayout
|
|
VkDescriptorSetLayoutBinding dslb[6] = {};
|
|
size_t dslb_size = size(dslb);
|
|
for (size_t i = 0; i < dslb_size; i++) {
|
|
dslb[i].binding = i;
|
|
dslb[i].descriptorCount = 1;
|
|
dslb[i].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
dslb[i].pImmutableSamplers = NULL;
|
|
dslb[i].stageFlags = VK_SHADER_STAGE_COMPUTE_BIT | VK_SHADER_STAGE_ALL;
|
|
}
|
|
if (m_device->props.limits.maxPerStageDescriptorStorageBuffers < dslb_size) {
|
|
printf("%sNeeded storage buffer bindings exceeds this devices limit. Skipping tests.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
CreateComputePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.dsl_bindings_.resize(dslb_size);
|
|
memcpy(pipe.dsl_bindings_.data(), dslb, dslb_size * sizeof(VkDescriptorSetLayoutBinding));
|
|
pipe.cs_.reset(new VkShaderObj(m_device, bindStateMinimalShaderText, VK_SHADER_STAGE_COMPUTE_BIT, this));
|
|
pipe.InitState();
|
|
m_errorMonitor->ExpectSuccess();
|
|
pipe.CreateComputePipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineCheckShaderCapabilityExtension1of2) {
|
|
// This is a positive test, no errors expected
|
|
// Verifies the ability to deal with a shader that declares a non-unique SPIRV capability ID
|
|
TEST_DESCRIPTION("Create a shader in which uses a non-unique capability ID extension, 1 of 2");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
if (!DeviceExtensionSupported(gpu(), nullptr, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME)) {
|
|
printf("%s Extension %s not supported, skipping this pass. \n", kSkipPrefix,
|
|
VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
m_device_extension_names.push_back(VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME);
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
// These tests require that the device support multiViewport
|
|
if (!m_device->phy().features().multiViewport) {
|
|
printf("%s Device does not support multiViewport, test skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
// Vertex shader using viewport array capability
|
|
char const *vsSource =
|
|
"#version 450\n"
|
|
"#extension GL_ARB_shader_viewport_layer_array : enable\n"
|
|
"void main() {\n"
|
|
" gl_ViewportIndex = 1;\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
m_errorMonitor->ExpectSuccess();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineCheckShaderCapabilityExtension2of2) {
|
|
// This is a positive test, no errors expected
|
|
// Verifies the ability to deal with a shader that declares a non-unique SPIRV capability ID
|
|
TEST_DESCRIPTION("Create a shader in which uses a non-unique capability ID extension, 2 of 2");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
if (!DeviceExtensionSupported(gpu(), nullptr, VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME)) {
|
|
printf("%s Extension %s not supported, skipping this pass. \n", kSkipPrefix, VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
m_device_extension_names.push_back(VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME);
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
// These tests require that the device support multiViewport
|
|
if (!m_device->phy().features().multiViewport) {
|
|
printf("%s Device does not support multiViewport, test skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
// Vertex shader using viewport array capability
|
|
char const *vsSource =
|
|
"#version 450\n"
|
|
"#extension GL_ARB_shader_viewport_layer_array : enable\n"
|
|
"void main() {\n"
|
|
" gl_ViewportIndex = 1;\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
m_errorMonitor->ExpectSuccess();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineFragmentOutputNotWrittenButMasked) {
|
|
TEST_DESCRIPTION(
|
|
"Test that no error is produced when the fragment shader fails to declare an output, but the corresponding attachment's "
|
|
"write mask is 0.");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
char const *fsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"void main(){\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
VkPipelineObj pipe(m_device);
|
|
pipe.AddShader(&vs);
|
|
pipe.AddShader(&fs);
|
|
|
|
/* set up CB 0, not written, but also masked */
|
|
pipe.AddDefaultColorAttachment(0);
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkDescriptorSetObj descriptorSet(m_device);
|
|
descriptorSet.AppendDummy();
|
|
descriptorSet.CreateVKDescriptorSet(m_commandBuffer);
|
|
|
|
pipe.CreateVKPipeline(descriptorSet.GetPipelineLayout(), renderPass());
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, StatelessValidationDisable) {
|
|
TEST_DESCRIPTION("Specify a non-zero value for a reserved parameter with stateless validation disabled");
|
|
|
|
VkValidationFeatureDisableEXT disables[] = {VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT};
|
|
VkValidationFeaturesEXT features = {};
|
|
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
|
|
features.disabledValidationFeatureCount = 1;
|
|
features.pDisabledValidationFeatures = disables;
|
|
VkCommandPoolCreateFlags pool_flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
ASSERT_NO_FATAL_FAILURE(Init(nullptr, nullptr, pool_flags, &features));
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
// Specify 0 for a reserved VkFlags parameter. Normally this is expected to trigger an stateless validation error, but this
|
|
// validation was disabled via the features extension, so no errors should be forthcoming.
|
|
VkEvent event_handle = VK_NULL_HANDLE;
|
|
VkEventCreateInfo event_info = {};
|
|
event_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
|
|
event_info.flags = 1;
|
|
vkCreateEvent(device(), &event_info, NULL, &event_handle);
|
|
vkDestroyEvent(device(), event_handle, NULL);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, PointSizeWriteInFunction) {
|
|
TEST_DESCRIPTION("Create a pipeline using TOPOLOGY_POINT_LIST and write PointSize in vertex shader function.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
m_errorMonitor->ExpectSuccess();
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
ASSERT_NO_FATAL_FAILURE(InitViewport());
|
|
|
|
// Create VS declaring PointSize and write to it in a function call.
|
|
VkShaderObj vs(m_device, bindStateVertPointSizeShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj ps(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
{
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), ps.GetStageCreateInfo()};
|
|
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
}
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, PointSizeGeomShaderSuccess) {
|
|
TEST_DESCRIPTION(
|
|
"Create a pipeline using TOPOLOGY_POINT_LIST, set PointSize vertex shader, and write in the final geometry stage.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
if ((!m_device->phy().features().geometryShader) || (!m_device->phy().features().shaderTessellationAndGeometryPointSize)) {
|
|
printf("%s Device does not support the required geometry shader features; skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
ASSERT_NO_FATAL_FAILURE(InitViewport());
|
|
|
|
// Create VS declaring PointSize and writing to it
|
|
VkShaderObj vs(m_device, bindStateVertPointSizeShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj gs(m_device, bindStateGeomPointSizeShaderText, VK_SHADER_STAGE_GEOMETRY_BIT, this);
|
|
VkShaderObj ps(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), ps.GetStageCreateInfo()};
|
|
// Set Input Assembly to TOPOLOGY POINT LIST
|
|
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, LoosePointSizeWrite) {
|
|
TEST_DESCRIPTION("Create a pipeline using TOPOLOGY_POINT_LIST and write PointSize outside of a structure.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
m_errorMonitor->ExpectSuccess();
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
ASSERT_NO_FATAL_FAILURE(InitViewport());
|
|
|
|
const std::string LoosePointSizeWrite = R"(
|
|
OpCapability Shader
|
|
%1 = OpExtInstImport "GLSL.std.450"
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Vertex %main "main" %glposition %glpointsize %gl_VertexIndex
|
|
OpSource GLSL 450
|
|
OpName %main "main"
|
|
OpName %vertices "vertices"
|
|
OpName %glposition "glposition"
|
|
OpName %glpointsize "glpointsize"
|
|
OpName %gl_VertexIndex "gl_VertexIndex"
|
|
OpDecorate %glposition BuiltIn Position
|
|
OpDecorate %glpointsize BuiltIn PointSize
|
|
OpDecorate %gl_VertexIndex BuiltIn VertexIndex
|
|
%void = OpTypeVoid
|
|
%3 = OpTypeFunction %void
|
|
%float = OpTypeFloat 32
|
|
%v2float = OpTypeVector %float 2
|
|
%uint = OpTypeInt 32 0
|
|
%uint_3 = OpConstant %uint 3
|
|
%_arr_v2float_uint_3 = OpTypeArray %v2float %uint_3
|
|
%_ptr_Private__arr_v2float_uint_3 = OpTypePointer Private %_arr_v2float_uint_3
|
|
%vertices = OpVariable %_ptr_Private__arr_v2float_uint_3 Private
|
|
%int = OpTypeInt 32 1
|
|
%int_0 = OpConstant %int 0
|
|
%float_n1 = OpConstant %float -1
|
|
%16 = OpConstantComposite %v2float %float_n1 %float_n1
|
|
%_ptr_Private_v2float = OpTypePointer Private %v2float
|
|
%int_1 = OpConstant %int 1
|
|
%float_1 = OpConstant %float 1
|
|
%21 = OpConstantComposite %v2float %float_1 %float_n1
|
|
%int_2 = OpConstant %int 2
|
|
%float_0 = OpConstant %float 0
|
|
%25 = OpConstantComposite %v2float %float_0 %float_1
|
|
%v4float = OpTypeVector %float 4
|
|
%_ptr_Output_gl_Position = OpTypePointer Output %v4float
|
|
%glposition = OpVariable %_ptr_Output_gl_Position Output
|
|
%_ptr_Output_gl_PointSize = OpTypePointer Output %float
|
|
%glpointsize = OpVariable %_ptr_Output_gl_PointSize Output
|
|
%_ptr_Input_int = OpTypePointer Input %int
|
|
%gl_VertexIndex = OpVariable %_ptr_Input_int Input
|
|
%int_3 = OpConstant %int 3
|
|
%_ptr_Output_v4float = OpTypePointer Output %v4float
|
|
%_ptr_Output_float = OpTypePointer Output %float
|
|
%main = OpFunction %void None %3
|
|
%5 = OpLabel
|
|
%18 = OpAccessChain %_ptr_Private_v2float %vertices %int_0
|
|
OpStore %18 %16
|
|
%22 = OpAccessChain %_ptr_Private_v2float %vertices %int_1
|
|
OpStore %22 %21
|
|
%26 = OpAccessChain %_ptr_Private_v2float %vertices %int_2
|
|
OpStore %26 %25
|
|
%33 = OpLoad %int %gl_VertexIndex
|
|
%35 = OpSMod %int %33 %int_3
|
|
%36 = OpAccessChain %_ptr_Private_v2float %vertices %35
|
|
%37 = OpLoad %v2float %36
|
|
%38 = OpCompositeExtract %float %37 0
|
|
%39 = OpCompositeExtract %float %37 1
|
|
%40 = OpCompositeConstruct %v4float %38 %39 %float_0 %float_1
|
|
%42 = OpAccessChain %_ptr_Output_v4float %glposition
|
|
OpStore %42 %40
|
|
OpStore %glpointsize %float_1
|
|
OpReturn
|
|
OpFunctionEnd
|
|
)";
|
|
|
|
// Create VS declaring PointSize and write to it in a function call.
|
|
VkShaderObj vs(m_device, LoosePointSizeWrite, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj ps(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
{
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), ps.GetStageCreateInfo()};
|
|
// Set Input Assembly to TOPOLOGY POINT LIST
|
|
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
}
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, UncompressedToCompressedImageCopy) {
|
|
TEST_DESCRIPTION("Image copies between compressed and uncompressed images");
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
// Verify format support
|
|
// Size-compatible (64-bit) formats. Uncompressed is 64 bits per texel, compressed is 64 bits per 4x4 block (or 4bpt).
|
|
if (!ImageFormatAndFeaturesSupported(gpu(), VK_FORMAT_R16G16B16A16_UINT, VK_IMAGE_TILING_OPTIMAL,
|
|
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR | VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR) ||
|
|
!ImageFormatAndFeaturesSupported(gpu(), VK_FORMAT_BC1_RGBA_SRGB_BLOCK, VK_IMAGE_TILING_OPTIMAL,
|
|
VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR | VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR)) {
|
|
printf("%s Required formats/features not supported - UncompressedToCompressedImageCopy skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
VkImageObj uncomp_10x10t_image(m_device); // Size = 10 * 10 * 64 = 6400
|
|
VkImageObj comp_10x10b_40x40t_image(m_device); // Size = 40 * 40 * 4 = 6400
|
|
|
|
uncomp_10x10t_image.Init(10, 10, 1, VK_FORMAT_R16G16B16A16_UINT,
|
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
comp_10x10b_40x40t_image.Init(40, 40, 1, VK_FORMAT_BC1_RGBA_SRGB_BLOCK,
|
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
|
|
if (!uncomp_10x10t_image.initialized() || !comp_10x10b_40x40t_image.initialized()) {
|
|
printf("%s Unable to initialize surfaces - UncompressedToCompressedImageCopy skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
// Both copies represent the same number of bytes. Bytes Per Texel = 1 for bc6, 16 for uncompressed
|
|
// Copy compressed to uncompressed
|
|
VkImageCopy copy_region = {};
|
|
copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
copy_region.srcSubresource.mipLevel = 0;
|
|
copy_region.dstSubresource.mipLevel = 0;
|
|
copy_region.srcSubresource.baseArrayLayer = 0;
|
|
copy_region.dstSubresource.baseArrayLayer = 0;
|
|
copy_region.srcSubresource.layerCount = 1;
|
|
copy_region.dstSubresource.layerCount = 1;
|
|
copy_region.srcOffset = {0, 0, 0};
|
|
copy_region.dstOffset = {0, 0, 0};
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
m_commandBuffer->begin();
|
|
|
|
// Copy from uncompressed to compressed
|
|
copy_region.extent = {10, 10, 1}; // Dimensions in (uncompressed) texels
|
|
vkCmdCopyImage(m_commandBuffer->handle(), uncomp_10x10t_image.handle(), VK_IMAGE_LAYOUT_GENERAL,
|
|
comp_10x10b_40x40t_image.handle(), VK_IMAGE_LAYOUT_GENERAL, 1, ©_region);
|
|
|
|
// And from compressed to uncompressed
|
|
copy_region.extent = {40, 40, 1}; // Dimensions in (compressed) texels
|
|
vkCmdCopyImage(m_commandBuffer->handle(), comp_10x10b_40x40t_image.handle(), VK_IMAGE_LAYOUT_GENERAL,
|
|
uncomp_10x10t_image.handle(), VK_IMAGE_LAYOUT_GENERAL, 1, ©_region);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
m_commandBuffer->end();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, DeleteDescriptorSetLayoutsBeforeDescriptorSets) {
|
|
TEST_DESCRIPTION("Create DSLayouts and DescriptorSets and then delete the DSLayouts before the DescriptorSets.");
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
VkResult err;
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkDescriptorPoolSize ds_type_count = {};
|
|
ds_type_count.type = VK_DESCRIPTOR_TYPE_SAMPLER;
|
|
ds_type_count.descriptorCount = 1;
|
|
|
|
VkDescriptorPoolCreateInfo ds_pool_ci = {};
|
|
ds_pool_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
ds_pool_ci.pNext = NULL;
|
|
ds_pool_ci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
|
ds_pool_ci.maxSets = 1;
|
|
ds_pool_ci.poolSizeCount = 1;
|
|
ds_pool_ci.pPoolSizes = &ds_type_count;
|
|
|
|
VkDescriptorPool ds_pool_one;
|
|
err = vkCreateDescriptorPool(m_device->device(), &ds_pool_ci, NULL, &ds_pool_one);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkDescriptorSetLayoutBinding dsl_binding = {};
|
|
dsl_binding.binding = 0;
|
|
dsl_binding.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER;
|
|
dsl_binding.descriptorCount = 1;
|
|
dsl_binding.stageFlags = VK_SHADER_STAGE_ALL;
|
|
dsl_binding.pImmutableSamplers = NULL;
|
|
|
|
VkDescriptorSet descriptorSet;
|
|
{
|
|
const VkDescriptorSetLayoutObj ds_layout(m_device, {dsl_binding});
|
|
|
|
VkDescriptorSetAllocateInfo alloc_info = {};
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
alloc_info.descriptorSetCount = 1;
|
|
alloc_info.descriptorPool = ds_pool_one;
|
|
alloc_info.pSetLayouts = &ds_layout.handle();
|
|
err = vkAllocateDescriptorSets(m_device->device(), &alloc_info, &descriptorSet);
|
|
ASSERT_VK_SUCCESS(err);
|
|
} // ds_layout destroyed
|
|
err = vkFreeDescriptorSets(m_device->device(), ds_pool_one, 1, &descriptorSet);
|
|
|
|
vkDestroyDescriptorPool(m_device->device(), ds_pool_one, NULL);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CommandPoolDeleteWithReferences) {
|
|
TEST_DESCRIPTION("Ensure the validation layers bookkeeping tracks the implicit command buffer frees.");
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
VkCommandPoolCreateInfo cmd_pool_info = {};
|
|
cmd_pool_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
cmd_pool_info.pNext = NULL;
|
|
cmd_pool_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
cmd_pool_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
cmd_pool_info.flags = 0;
|
|
|
|
VkCommandPool secondary_cmd_pool;
|
|
VkResult res = vkCreateCommandPool(m_device->handle(), &cmd_pool_info, NULL, &secondary_cmd_pool);
|
|
ASSERT_VK_SUCCESS(res);
|
|
|
|
VkCommandBufferAllocateInfo cmdalloc = vk_testing::CommandBuffer::create_info(secondary_cmd_pool);
|
|
cmdalloc.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
|
|
|
|
VkCommandBuffer secondary_cmds;
|
|
res = vkAllocateCommandBuffers(m_device->handle(), &cmdalloc, &secondary_cmds);
|
|
|
|
VkCommandBufferInheritanceInfo cmd_buf_inheritance_info = {};
|
|
cmd_buf_inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
|
|
cmd_buf_inheritance_info.pNext = NULL;
|
|
cmd_buf_inheritance_info.renderPass = VK_NULL_HANDLE;
|
|
cmd_buf_inheritance_info.subpass = 0;
|
|
cmd_buf_inheritance_info.framebuffer = VK_NULL_HANDLE;
|
|
cmd_buf_inheritance_info.occlusionQueryEnable = VK_FALSE;
|
|
cmd_buf_inheritance_info.queryFlags = 0;
|
|
cmd_buf_inheritance_info.pipelineStatistics = 0;
|
|
|
|
VkCommandBufferBeginInfo secondary_begin = {};
|
|
secondary_begin.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
secondary_begin.pNext = NULL;
|
|
secondary_begin.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
secondary_begin.pInheritanceInfo = &cmd_buf_inheritance_info;
|
|
|
|
res = vkBeginCommandBuffer(secondary_cmds, &secondary_begin);
|
|
ASSERT_VK_SUCCESS(res);
|
|
vkEndCommandBuffer(secondary_cmds);
|
|
|
|
m_commandBuffer->begin();
|
|
vkCmdExecuteCommands(m_commandBuffer->handle(), 1, &secondary_cmds);
|
|
m_commandBuffer->end();
|
|
|
|
// DestroyCommandPool *implicitly* frees the command buffers allocated from it
|
|
vkDestroyCommandPool(m_device->handle(), secondary_cmd_pool, NULL);
|
|
// If bookkeeping has been lax, validating the reset will attempt to touch deleted data
|
|
res = vkResetCommandPool(m_device->handle(), m_commandPool->handle(), 0);
|
|
ASSERT_VK_SUCCESS(res);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, SecondaryCommandBufferClearColorAttachments) {
|
|
TEST_DESCRIPTION("Create a secondary command buffer and record a CmdClearAttachments call into it");
|
|
m_errorMonitor->ExpectSuccess();
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info = {};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = m_commandPool->handle();
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
|
|
command_buffer_allocate_info.commandBufferCount = 1;
|
|
|
|
VkCommandBuffer secondary_command_buffer;
|
|
ASSERT_VK_SUCCESS(vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, &secondary_command_buffer));
|
|
VkCommandBufferBeginInfo command_buffer_begin_info = {};
|
|
VkCommandBufferInheritanceInfo command_buffer_inheritance_info = {};
|
|
command_buffer_inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
|
|
command_buffer_inheritance_info.renderPass = m_renderPass;
|
|
command_buffer_inheritance_info.framebuffer = m_framebuffer;
|
|
|
|
command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
command_buffer_begin_info.flags =
|
|
VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
|
|
command_buffer_begin_info.pInheritanceInfo = &command_buffer_inheritance_info;
|
|
|
|
vkBeginCommandBuffer(secondary_command_buffer, &command_buffer_begin_info);
|
|
VkClearAttachment color_attachment;
|
|
color_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
color_attachment.clearValue.color.float32[0] = 0;
|
|
color_attachment.clearValue.color.float32[1] = 0;
|
|
color_attachment.clearValue.color.float32[2] = 0;
|
|
color_attachment.clearValue.color.float32[3] = 0;
|
|
color_attachment.colorAttachment = 0;
|
|
VkClearRect clear_rect = {{{0, 0}, {32, 32}}, 0, 1};
|
|
vkCmdClearAttachments(secondary_command_buffer, 1, &color_attachment, 1, &clear_rect);
|
|
vkEndCommandBuffer(secondary_command_buffer);
|
|
m_commandBuffer->begin();
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &m_renderPassBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
|
|
vkCmdExecuteCommands(m_commandBuffer->handle(), 1, &secondary_command_buffer);
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_commandBuffer->end();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, SecondaryCommandBufferImageLayoutTransitions) {
|
|
TEST_DESCRIPTION("Perform an image layout transition in a secondary command buffer followed by a transition in the primary.");
|
|
VkResult err;
|
|
m_errorMonitor->ExpectSuccess();
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
auto depth_format = FindSupportedDepthStencilFormat(gpu());
|
|
if (!depth_format) {
|
|
printf("%s Couldn't find depth stencil format.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
// Allocate a secondary and primary cmd buffer
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info = {};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = m_commandPool->handle();
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
|
|
command_buffer_allocate_info.commandBufferCount = 1;
|
|
|
|
VkCommandBuffer secondary_command_buffer;
|
|
ASSERT_VK_SUCCESS(vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, &secondary_command_buffer));
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
VkCommandBuffer primary_command_buffer;
|
|
ASSERT_VK_SUCCESS(vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, &primary_command_buffer));
|
|
VkCommandBufferBeginInfo command_buffer_begin_info = {};
|
|
VkCommandBufferInheritanceInfo command_buffer_inheritance_info = {};
|
|
command_buffer_inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
|
|
command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
|
command_buffer_begin_info.pInheritanceInfo = &command_buffer_inheritance_info;
|
|
|
|
err = vkBeginCommandBuffer(secondary_command_buffer, &command_buffer_begin_info);
|
|
ASSERT_VK_SUCCESS(err);
|
|
VkImageObj image(m_device);
|
|
image.Init(128, 128, 1, depth_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
VkImageMemoryBarrier img_barrier = {};
|
|
img_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
img_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
|
img_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
img_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
img_barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
img_barrier.image = image.handle();
|
|
img_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
img_barrier.subresourceRange.baseArrayLayer = 0;
|
|
img_barrier.subresourceRange.baseMipLevel = 0;
|
|
img_barrier.subresourceRange.layerCount = 1;
|
|
img_barrier.subresourceRange.levelCount = 1;
|
|
vkCmdPipelineBarrier(secondary_command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0, 0, nullptr,
|
|
0, nullptr, 1, &img_barrier);
|
|
err = vkEndCommandBuffer(secondary_command_buffer);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Now update primary cmd buffer to execute secondary and transitions image
|
|
command_buffer_begin_info.pInheritanceInfo = nullptr;
|
|
err = vkBeginCommandBuffer(primary_command_buffer, &command_buffer_begin_info);
|
|
ASSERT_VK_SUCCESS(err);
|
|
vkCmdExecuteCommands(primary_command_buffer, 1, &secondary_command_buffer);
|
|
VkImageMemoryBarrier img_barrier2 = {};
|
|
img_barrier2.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
img_barrier2.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
|
img_barrier2.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
img_barrier2.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
img_barrier2.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
img_barrier2.image = image.handle();
|
|
img_barrier2.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier2.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier2.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
img_barrier2.subresourceRange.baseArrayLayer = 0;
|
|
img_barrier2.subresourceRange.baseMipLevel = 0;
|
|
img_barrier2.subresourceRange.layerCount = 1;
|
|
img_barrier2.subresourceRange.levelCount = 1;
|
|
vkCmdPipelineBarrier(primary_command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0, 0, nullptr, 0,
|
|
nullptr, 1, &img_barrier2);
|
|
err = vkEndCommandBuffer(primary_command_buffer);
|
|
ASSERT_VK_SUCCESS(err);
|
|
VkSubmitInfo submit_info = {};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &primary_command_buffer;
|
|
err = vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
ASSERT_VK_SUCCESS(err);
|
|
m_errorMonitor->VerifyNotFound();
|
|
err = vkDeviceWaitIdle(m_device->device());
|
|
ASSERT_VK_SUCCESS(err);
|
|
vkFreeCommandBuffers(m_device->device(), m_commandPool->handle(), 1, &secondary_command_buffer);
|
|
vkFreeCommandBuffers(m_device->device(), m_commandPool->handle(), 1, &primary_command_buffer);
|
|
}
|
|
|
|
// This is a positive test. No failures are expected.
|
|
TEST_F(VkPositiveLayerTest, IgnoreUnrelatedDescriptor) {
|
|
TEST_DESCRIPTION(
|
|
"Ensure that the vkUpdateDescriptorSets validation code is ignoring VkWriteDescriptorSet members that are not related to "
|
|
"the descriptor type specified by VkWriteDescriptorSet::descriptorType. Correct validation behavior will result in the "
|
|
"test running to completion without validation errors.");
|
|
|
|
const uintptr_t invalid_ptr = 0xcdcdcdcd;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
// Verify VK_FORMAT_R8_UNORM supports VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT
|
|
const VkFormat format_texel_case = VK_FORMAT_R8_UNORM;
|
|
const char *format_texel_case_string = "VK_FORMAT_R8_UNORM";
|
|
VkFormatProperties format_properties;
|
|
vkGetPhysicalDeviceFormatProperties(gpu(), format_texel_case, &format_properties);
|
|
if (!(format_properties.bufferFeatures & VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT)) {
|
|
printf("%s Test requires %s to support VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT\n", kSkipPrefix, format_texel_case_string);
|
|
return;
|
|
}
|
|
|
|
// Image Case
|
|
{
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkImageObj image(m_device);
|
|
image.Init(32, 32, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_TILING_OPTIMAL, 0);
|
|
|
|
VkImageView view = image.targetView(VK_FORMAT_B8G8R8A8_UNORM);
|
|
|
|
OneOffDescriptorSet descriptor_set(m_device, {
|
|
{0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
});
|
|
|
|
VkDescriptorImageInfo image_info = {};
|
|
image_info.imageView = view;
|
|
image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
VkWriteDescriptorSet descriptor_write;
|
|
memset(&descriptor_write, 0, sizeof(descriptor_write));
|
|
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_write.dstSet = descriptor_set.set_;
|
|
descriptor_write.dstBinding = 0;
|
|
descriptor_write.descriptorCount = 1;
|
|
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
|
descriptor_write.pImageInfo = &image_info;
|
|
|
|
// Set pBufferInfo and pTexelBufferView to invalid values, which should
|
|
// be
|
|
// ignored for descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE.
|
|
// This will most likely produce a crash if the parameter_validation
|
|
// layer
|
|
// does not correctly ignore pBufferInfo.
|
|
descriptor_write.pBufferInfo = reinterpret_cast<const VkDescriptorBufferInfo *>(invalid_ptr);
|
|
descriptor_write.pTexelBufferView = reinterpret_cast<const VkBufferView *>(invalid_ptr);
|
|
|
|
vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// Buffer Case
|
|
{
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
uint32_t queue_family_index = 0;
|
|
VkBufferCreateInfo buffer_create_info = {};
|
|
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
buffer_create_info.size = 1024;
|
|
buffer_create_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
|
buffer_create_info.queueFamilyIndexCount = 1;
|
|
buffer_create_info.pQueueFamilyIndices = &queue_family_index;
|
|
|
|
VkBufferObj buffer;
|
|
buffer.init(*m_device, buffer_create_info);
|
|
|
|
OneOffDescriptorSet descriptor_set(m_device, {
|
|
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
});
|
|
|
|
VkDescriptorBufferInfo buffer_info = {};
|
|
buffer_info.buffer = buffer.handle();
|
|
buffer_info.offset = 0;
|
|
buffer_info.range = 1024;
|
|
|
|
VkWriteDescriptorSet descriptor_write;
|
|
memset(&descriptor_write, 0, sizeof(descriptor_write));
|
|
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_write.dstSet = descriptor_set.set_;
|
|
descriptor_write.dstBinding = 0;
|
|
descriptor_write.descriptorCount = 1;
|
|
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
descriptor_write.pBufferInfo = &buffer_info;
|
|
|
|
// Set pImageInfo and pTexelBufferView to invalid values, which should
|
|
// be
|
|
// ignored for descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER.
|
|
// This will most likely produce a crash if the parameter_validation
|
|
// layer
|
|
// does not correctly ignore pImageInfo.
|
|
descriptor_write.pImageInfo = reinterpret_cast<const VkDescriptorImageInfo *>(invalid_ptr);
|
|
descriptor_write.pTexelBufferView = reinterpret_cast<const VkBufferView *>(invalid_ptr);
|
|
|
|
vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// Texel Buffer Case
|
|
{
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
uint32_t queue_family_index = 0;
|
|
VkBufferCreateInfo buffer_create_info = {};
|
|
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
buffer_create_info.size = 1024;
|
|
buffer_create_info.usage = VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
|
|
buffer_create_info.queueFamilyIndexCount = 1;
|
|
buffer_create_info.pQueueFamilyIndices = &queue_family_index;
|
|
|
|
VkBufferObj buffer;
|
|
buffer.init(*m_device, buffer_create_info);
|
|
|
|
VkBufferViewCreateInfo buff_view_ci = {};
|
|
buff_view_ci.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
|
|
buff_view_ci.buffer = buffer.handle();
|
|
buff_view_ci.format = format_texel_case;
|
|
buff_view_ci.range = VK_WHOLE_SIZE;
|
|
VkBufferView buffer_view;
|
|
VkResult err = vkCreateBufferView(m_device->device(), &buff_view_ci, NULL, &buffer_view);
|
|
ASSERT_VK_SUCCESS(err);
|
|
OneOffDescriptorSet descriptor_set(m_device,
|
|
{
|
|
{0, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
});
|
|
|
|
VkWriteDescriptorSet descriptor_write;
|
|
memset(&descriptor_write, 0, sizeof(descriptor_write));
|
|
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_write.dstSet = descriptor_set.set_;
|
|
descriptor_write.dstBinding = 0;
|
|
descriptor_write.descriptorCount = 1;
|
|
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
|
|
descriptor_write.pTexelBufferView = &buffer_view;
|
|
|
|
// Set pImageInfo and pBufferInfo to invalid values, which should be
|
|
// ignored for descriptorType ==
|
|
// VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER.
|
|
// This will most likely produce a crash if the parameter_validation
|
|
// layer
|
|
// does not correctly ignore pImageInfo and pBufferInfo.
|
|
descriptor_write.pImageInfo = reinterpret_cast<const VkDescriptorImageInfo *>(invalid_ptr);
|
|
descriptor_write.pBufferInfo = reinterpret_cast<const VkDescriptorBufferInfo *>(invalid_ptr);
|
|
|
|
vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyBufferView(m_device->device(), buffer_view, NULL);
|
|
}
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ImmutableSamplerOnlyDescriptor) {
|
|
TEST_DESCRIPTION("Bind a DescriptorSet with only an immutable sampler and make sure that we don't warn for no update.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
OneOffDescriptorSet descriptor_set(m_device, {
|
|
{0, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
|
|
});
|
|
|
|
VkSamplerCreateInfo sampler_ci = SafeSaneSamplerCreateInfo();
|
|
VkSampler sampler;
|
|
VkResult err = vkCreateSampler(m_device->device(), &sampler_ci, NULL, &sampler);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
const VkPipelineLayoutObj pipeline_layout(m_device, {&descriptor_set.layout_});
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
m_commandBuffer->begin();
|
|
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
|
|
|
|
vkCmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1,
|
|
&descriptor_set.set_, 0, nullptr);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroySampler(m_device->device(), sampler, NULL);
|
|
|
|
m_commandBuffer->EndRenderPass();
|
|
m_commandBuffer->end();
|
|
}
|
|
|
|
// This is a positive test. No failures are expected.
|
|
TEST_F(VkPositiveLayerTest, EmptyDescriptorUpdateTest) {
|
|
TEST_DESCRIPTION("Update last descriptor in a set that includes an empty binding");
|
|
VkResult err;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
// Create layout with two uniform buffer descriptors w/ empty binding between them
|
|
OneOffDescriptorSet ds(m_device, {
|
|
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
{1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0 /*!*/, 0, nullptr},
|
|
{2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
});
|
|
|
|
// Create a buffer to be used for update
|
|
VkBufferCreateInfo buff_ci = {};
|
|
buff_ci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
buff_ci.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
|
buff_ci.size = 256;
|
|
buff_ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
VkBuffer buffer;
|
|
err = vkCreateBuffer(m_device->device(), &buff_ci, NULL, &buffer);
|
|
ASSERT_VK_SUCCESS(err);
|
|
// Have to bind memory to buffer before descriptor update
|
|
VkMemoryAllocateInfo mem_alloc = {};
|
|
mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
mem_alloc.pNext = NULL;
|
|
mem_alloc.allocationSize = 512; // one allocation for both buffers
|
|
mem_alloc.memoryTypeIndex = 0;
|
|
|
|
VkMemoryRequirements mem_reqs;
|
|
vkGetBufferMemoryRequirements(m_device->device(), buffer, &mem_reqs);
|
|
bool pass = m_device->phy().set_memory_type(mem_reqs.memoryTypeBits, &mem_alloc, 0);
|
|
if (!pass) {
|
|
printf("%s Failed to allocate memory.\n", kSkipPrefix);
|
|
vkDestroyBuffer(m_device->device(), buffer, NULL);
|
|
return;
|
|
}
|
|
// Make sure allocation is sufficiently large to accommodate buffer requirements
|
|
if (mem_reqs.size > mem_alloc.allocationSize) {
|
|
mem_alloc.allocationSize = mem_reqs.size;
|
|
}
|
|
|
|
VkDeviceMemory mem;
|
|
err = vkAllocateMemory(m_device->device(), &mem_alloc, NULL, &mem);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkBindBufferMemory(m_device->device(), buffer, mem, 0);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Only update the descriptor at binding 2
|
|
VkDescriptorBufferInfo buff_info = {};
|
|
buff_info.buffer = buffer;
|
|
buff_info.offset = 0;
|
|
buff_info.range = VK_WHOLE_SIZE;
|
|
VkWriteDescriptorSet descriptor_write = {};
|
|
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_write.dstBinding = 2;
|
|
descriptor_write.descriptorCount = 1;
|
|
descriptor_write.pTexelBufferView = nullptr;
|
|
descriptor_write.pBufferInfo = &buff_info;
|
|
descriptor_write.pImageInfo = nullptr;
|
|
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
descriptor_write.dstSet = ds.set_;
|
|
|
|
vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
// Cleanup
|
|
vkFreeMemory(m_device->device(), mem, NULL);
|
|
vkDestroyBuffer(m_device->device(), buffer, NULL);
|
|
}
|
|
|
|
// This is a positive test. No failures are expected.
|
|
TEST_F(VkPositiveLayerTest, PushDescriptorNullDstSetTest) {
|
|
TEST_DESCRIPTION("Use null dstSet in CmdPushDescriptorSetKHR");
|
|
|
|
if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s Did not find VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s Push Descriptors Extension not supported, skipping tests\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
auto push_descriptor_prop = GetPushDescriptorProperties(instance(), gpu());
|
|
if (push_descriptor_prop.maxPushDescriptors < 1) {
|
|
// Some implementations report an invalid maxPushDescriptors of 0
|
|
printf("%s maxPushDescriptors is zero, skipping tests\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitViewport());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkDescriptorSetLayoutBinding dsl_binding = {};
|
|
dsl_binding.binding = 2;
|
|
dsl_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
dsl_binding.descriptorCount = 1;
|
|
dsl_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
dsl_binding.pImmutableSamplers = NULL;
|
|
|
|
const VkDescriptorSetLayoutObj ds_layout(m_device, {dsl_binding});
|
|
// Create push descriptor set layout
|
|
const VkDescriptorSetLayoutObj push_ds_layout(m_device, {dsl_binding}, VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR);
|
|
|
|
// Use helper to create graphics pipeline
|
|
CreatePipelineHelper helper(*this);
|
|
helper.InitInfo();
|
|
helper.InitState();
|
|
helper.pipeline_layout_ = VkPipelineLayoutObj(m_device, {&push_ds_layout, &ds_layout});
|
|
helper.CreateGraphicsPipeline();
|
|
|
|
const float vbo_data[3] = {1.f, 0.f, 1.f};
|
|
VkConstantBufferObj vbo(m_device, sizeof(vbo_data), (const void *)&vbo_data, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
|
|
|
|
VkDescriptorBufferInfo buff_info;
|
|
buff_info.buffer = vbo.handle();
|
|
buff_info.offset = 0;
|
|
buff_info.range = sizeof(vbo_data);
|
|
VkWriteDescriptorSet descriptor_write = {};
|
|
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_write.dstBinding = 2;
|
|
descriptor_write.descriptorCount = 1;
|
|
descriptor_write.pTexelBufferView = nullptr;
|
|
descriptor_write.pBufferInfo = &buff_info;
|
|
descriptor_write.pImageInfo = nullptr;
|
|
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
descriptor_write.dstSet = 0; // Should not cause a validation error
|
|
|
|
// Find address of extension call and make the call
|
|
PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSetKHR =
|
|
(PFN_vkCmdPushDescriptorSetKHR)vkGetDeviceProcAddr(m_device->device(), "vkCmdPushDescriptorSetKHR");
|
|
assert(vkCmdPushDescriptorSetKHR != nullptr);
|
|
|
|
m_commandBuffer->begin();
|
|
|
|
// In Intel GPU, it needs to bind pipeline before push descriptor set.
|
|
vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, helper.pipeline_);
|
|
vkCmdPushDescriptorSetKHR(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, helper.pipeline_layout_.handle(), 0, 1,
|
|
&descriptor_write);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// This is a positive test. No failures are expected.
|
|
TEST_F(VkPositiveLayerTest, PushDescriptorUnboundSetTest) {
|
|
TEST_DESCRIPTION("Ensure that no validation errors are produced for not bound push descriptor sets");
|
|
if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s Did not find VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s Push Descriptors Extension not supported, skipping tests\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
auto push_descriptor_prop = GetPushDescriptorProperties(instance(), gpu());
|
|
if (push_descriptor_prop.maxPushDescriptors < 1) {
|
|
// Some implementations report an invalid maxPushDescriptors of 0
|
|
printf("%s maxPushDescriptors is zero, skipping tests\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitViewport());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
// Create descriptor set layout
|
|
VkDescriptorSetLayoutBinding dsl_binding = {};
|
|
dsl_binding.binding = 2;
|
|
dsl_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
dsl_binding.descriptorCount = 1;
|
|
dsl_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
dsl_binding.pImmutableSamplers = NULL;
|
|
|
|
OneOffDescriptorSet descriptor_set(m_device, {dsl_binding}, 0, nullptr, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
|
nullptr);
|
|
|
|
// Create push descriptor set layout
|
|
const VkDescriptorSetLayoutObj push_ds_layout(m_device, {dsl_binding}, VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR);
|
|
|
|
// Create PSO
|
|
char const fsSource[] =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(location=0) out vec4 x;\n"
|
|
"layout(set=0) layout(binding=2) uniform foo1 { float x; } bar1;\n"
|
|
"layout(set=1) layout(binding=2) uniform foo2 { float y; } bar2;\n"
|
|
"void main(){\n"
|
|
" x = vec4(bar1.x) + vec4(bar2.y);\n"
|
|
"}\n";
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
// Now use the descriptor layouts to create a pipeline layout
|
|
pipe.pipeline_layout_ = VkPipelineLayoutObj(m_device, {&push_ds_layout, &descriptor_set.layout_});
|
|
pipe.CreateGraphicsPipeline();
|
|
|
|
const float bo_data[1] = {1.f};
|
|
VkConstantBufferObj buffer(m_device, sizeof(bo_data), (const void *)&bo_data, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
|
|
|
|
// Update descriptor set
|
|
descriptor_set.WriteDescriptorBufferInfo(2, buffer.handle(), sizeof(bo_data));
|
|
descriptor_set.UpdateDescriptorSets();
|
|
|
|
PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSetKHR =
|
|
(PFN_vkCmdPushDescriptorSetKHR)vkGetDeviceProcAddr(m_device->device(), "vkCmdPushDescriptorSetKHR");
|
|
assert(vkCmdPushDescriptorSetKHR != nullptr);
|
|
|
|
m_commandBuffer->begin();
|
|
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
|
|
vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_);
|
|
|
|
// Push descriptors and bind descriptor set
|
|
vkCmdPushDescriptorSetKHR(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_layout_.handle(), 0, 1,
|
|
descriptor_set.descriptor_writes.data());
|
|
vkCmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_layout_.handle(), 1, 1,
|
|
&descriptor_set.set_, 0, NULL);
|
|
|
|
// No errors should be generated.
|
|
vkCmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
m_commandBuffer->EndRenderPass();
|
|
m_commandBuffer->end();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, PushDescriptorSetUpdatingSetNumber) {
|
|
TEST_DESCRIPTION(
|
|
"Ensure that no validation errors are produced when the push descriptor set number changes "
|
|
"between two vkCmdPushDescriptorSetKHR calls.");
|
|
|
|
if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s %s Extension not supported, skipping tests\n", kSkipPrefix,
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s %s Extension not supported, skipping tests\n", kSkipPrefix, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
auto push_descriptor_prop = GetPushDescriptorProperties(instance(), gpu());
|
|
if (push_descriptor_prop.maxPushDescriptors < 1) {
|
|
// Some implementations report an invalid maxPushDescriptors of 0
|
|
printf("%s maxPushDescriptors is zero, skipping tests\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitViewport());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
// Create a descriptor to push
|
|
const uint32_t buffer_data[4] = {4, 5, 6, 7};
|
|
VkConstantBufferObj buffer_obj(
|
|
m_device, sizeof(buffer_data), &buffer_data,
|
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
|
|
ASSERT_TRUE(buffer_obj.initialized());
|
|
|
|
VkDescriptorBufferInfo buffer_info = {buffer_obj.handle(), 0, VK_WHOLE_SIZE};
|
|
|
|
PFN_vkCmdPushDescriptorSetKHR vkCmdPushDescriptorSetKHR =
|
|
(PFN_vkCmdPushDescriptorSetKHR)vkGetDeviceProcAddr(m_device->device(), "vkCmdPushDescriptorSetKHR");
|
|
ASSERT_TRUE(vkCmdPushDescriptorSetKHR != nullptr);
|
|
|
|
const VkDescriptorSetLayoutBinding ds_binding_0 = {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
nullptr};
|
|
const VkDescriptorSetLayoutBinding ds_binding_1 = {1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
nullptr};
|
|
const VkDescriptorSetLayoutObj ds_layout(m_device, {ds_binding_0, ds_binding_1});
|
|
ASSERT_TRUE(ds_layout.initialized());
|
|
|
|
const VkDescriptorSetLayoutBinding push_ds_binding_0 = {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
nullptr};
|
|
const VkDescriptorSetLayoutObj push_ds_layout(m_device, {push_ds_binding_0},
|
|
VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR);
|
|
ASSERT_TRUE(push_ds_layout.initialized());
|
|
|
|
m_commandBuffer->begin();
|
|
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
|
|
|
|
VkPipelineObj pipe0(m_device);
|
|
VkPipelineObj pipe1(m_device);
|
|
{
|
|
// Note: the push descriptor set is set number 2.
|
|
const VkPipelineLayoutObj pipeline_layout(m_device, {&ds_layout, &ds_layout, &push_ds_layout, &ds_layout});
|
|
ASSERT_TRUE(pipeline_layout.initialized());
|
|
|
|
char const *fsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(location=0) out vec4 x;\n"
|
|
"layout(set=2) layout(binding=0) uniform foo { vec4 y; } bar;\n"
|
|
"void main(){\n"
|
|
" x = bar.y;\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
VkPipelineObj &pipe = pipe0;
|
|
pipe.SetViewport(m_viewports);
|
|
pipe.SetScissor(m_scissors);
|
|
pipe.AddShader(&vs);
|
|
pipe.AddShader(&fs);
|
|
pipe.AddDefaultColorAttachment();
|
|
pipe.CreateVKPipeline(pipeline_layout.handle(), renderPass());
|
|
|
|
vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle());
|
|
|
|
const VkWriteDescriptorSet descriptor_write = vk_testing::Device::write_descriptor_set(
|
|
vk_testing::DescriptorSet(), 0, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, &buffer_info);
|
|
|
|
// Note: pushing to desciptor set number 2.
|
|
vkCmdPushDescriptorSetKHR(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 2, 1,
|
|
&descriptor_write);
|
|
vkCmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0);
|
|
}
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
{
|
|
// Note: the push descriptor set is now set number 3.
|
|
const VkPipelineLayoutObj pipeline_layout(m_device, {&ds_layout, &ds_layout, &ds_layout, &push_ds_layout});
|
|
ASSERT_TRUE(pipeline_layout.initialized());
|
|
|
|
const VkWriteDescriptorSet descriptor_write = vk_testing::Device::write_descriptor_set(
|
|
vk_testing::DescriptorSet(), 0, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, &buffer_info);
|
|
|
|
char const *fsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(location=0) out vec4 x;\n"
|
|
"layout(set=3) layout(binding=0) uniform foo { vec4 y; } bar;\n"
|
|
"void main(){\n"
|
|
" x = bar.y;\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
VkPipelineObj &pipe = pipe1;
|
|
pipe.SetViewport(m_viewports);
|
|
pipe.SetScissor(m_scissors);
|
|
pipe.AddShader(&vs);
|
|
pipe.AddShader(&fs);
|
|
pipe.AddDefaultColorAttachment();
|
|
pipe.CreateVKPipeline(pipeline_layout.handle(), renderPass());
|
|
|
|
vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle());
|
|
|
|
// Note: now pushing to desciptor set number 3.
|
|
vkCmdPushDescriptorSetKHR(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 3, 1,
|
|
&descriptor_write);
|
|
vkCmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0);
|
|
}
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
m_commandBuffer->EndRenderPass();
|
|
m_commandBuffer->end();
|
|
}
|
|
|
|
// This is a positive test. No failures are expected.
|
|
TEST_F(VkPositiveLayerTest, TestAliasedMemoryTracking) {
|
|
VkResult err;
|
|
bool pass;
|
|
|
|
TEST_DESCRIPTION(
|
|
"Create a buffer, allocate memory, bind memory, destroy the buffer, create an image, and bind the same memory to it");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
VkBuffer buffer;
|
|
VkImage image;
|
|
VkDeviceMemory mem;
|
|
VkMemoryRequirements mem_reqs;
|
|
|
|
VkBufferCreateInfo buf_info = {};
|
|
buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
buf_info.pNext = NULL;
|
|
buf_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
|
buf_info.size = 256;
|
|
buf_info.queueFamilyIndexCount = 0;
|
|
buf_info.pQueueFamilyIndices = NULL;
|
|
buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
buf_info.flags = 0;
|
|
err = vkCreateBuffer(m_device->device(), &buf_info, NULL, &buffer);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
vkGetBufferMemoryRequirements(m_device->device(), buffer, &mem_reqs);
|
|
|
|
VkMemoryAllocateInfo alloc_info = {};
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
alloc_info.pNext = NULL;
|
|
alloc_info.memoryTypeIndex = 0;
|
|
|
|
// Ensure memory is big enough for both bindings
|
|
alloc_info.allocationSize = 0x10000;
|
|
|
|
pass = m_device->phy().set_memory_type(mem_reqs.memoryTypeBits, &alloc_info, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
|
|
if (!pass) {
|
|
printf("%s Failed to allocate memory.\n", kSkipPrefix);
|
|
vkDestroyBuffer(m_device->device(), buffer, NULL);
|
|
return;
|
|
}
|
|
|
|
err = vkAllocateMemory(m_device->device(), &alloc_info, NULL, &mem);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
uint8_t *pData;
|
|
err = vkMapMemory(m_device->device(), mem, 0, mem_reqs.size, 0, (void **)&pData);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
memset(pData, 0xCADECADE, static_cast<size_t>(mem_reqs.size));
|
|
|
|
vkUnmapMemory(m_device->device(), mem);
|
|
|
|
err = vkBindBufferMemory(m_device->device(), buffer, mem, 0);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// NOW, destroy the buffer. Obviously, the resource no longer occupies this
|
|
// memory. In fact, it was never used by the GPU.
|
|
// Just be sure, wait for idle.
|
|
vkDestroyBuffer(m_device->device(), buffer, NULL);
|
|
vkDeviceWaitIdle(m_device->device());
|
|
|
|
// Use optimal as some platforms report linear support but then fail image creation
|
|
VkImageTiling image_tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
VkImageFormatProperties image_format_properties;
|
|
vkGetPhysicalDeviceImageFormatProperties(gpu(), VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TYPE_2D, image_tiling,
|
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 0, &image_format_properties);
|
|
if (image_format_properties.maxExtent.width == 0) {
|
|
printf("%s Image format not supported; skipped.\n", kSkipPrefix);
|
|
vkFreeMemory(m_device->device(), mem, NULL);
|
|
return;
|
|
}
|
|
VkImageCreateInfo image_create_info = {};
|
|
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
image_create_info.pNext = NULL;
|
|
image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
|
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
image_create_info.extent.width = 64;
|
|
image_create_info.extent.height = 64;
|
|
image_create_info.extent.depth = 1;
|
|
image_create_info.mipLevels = 1;
|
|
image_create_info.arrayLayers = 1;
|
|
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
image_create_info.tiling = image_tiling;
|
|
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
|
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
image_create_info.queueFamilyIndexCount = 0;
|
|
image_create_info.pQueueFamilyIndices = NULL;
|
|
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
image_create_info.flags = 0;
|
|
|
|
/* Create a mappable image. It will be the texture if linear images are OK
|
|
* to be textures or it will be the staging image if they are not.
|
|
*/
|
|
err = vkCreateImage(m_device->device(), &image_create_info, NULL, &image);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
vkGetImageMemoryRequirements(m_device->device(), image, &mem_reqs);
|
|
|
|
VkMemoryAllocateInfo mem_alloc = {};
|
|
mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
mem_alloc.pNext = NULL;
|
|
mem_alloc.allocationSize = 0;
|
|
mem_alloc.memoryTypeIndex = 0;
|
|
mem_alloc.allocationSize = mem_reqs.size;
|
|
|
|
pass = m_device->phy().set_memory_type(mem_reqs.memoryTypeBits, &mem_alloc, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
|
|
if (!pass) {
|
|
printf("%s Failed to allocate memory.\n", kSkipPrefix);
|
|
vkFreeMemory(m_device->device(), mem, NULL);
|
|
vkDestroyImage(m_device->device(), image, NULL);
|
|
return;
|
|
}
|
|
|
|
// VALIDATION FAILURE:
|
|
err = vkBindImageMemory(m_device->device(), image, mem, 0);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkFreeMemory(m_device->device(), mem, NULL);
|
|
vkDestroyImage(m_device->device(), image, NULL);
|
|
}
|
|
|
|
// This is a positive test. No failures are expected.
|
|
TEST_F(VkPositiveLayerTest, TestDestroyFreeNullHandles) {
|
|
VkResult err;
|
|
|
|
TEST_DESCRIPTION("Call all applicable destroy and free routines with NULL handles, expecting no validation errors");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
vkDestroyBuffer(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyBufferView(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyCommandPool(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyDescriptorPool(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyDescriptorSetLayout(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyDevice(VK_NULL_HANDLE, NULL);
|
|
vkDestroyEvent(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyFence(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyFramebuffer(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyImage(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyImageView(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyInstance(VK_NULL_HANDLE, NULL);
|
|
vkDestroyPipeline(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyPipelineCache(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyPipelineLayout(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyQueryPool(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyRenderPass(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroySampler(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroySemaphore(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
vkDestroyShaderModule(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
VkCommandBuffer command_buffers[3] = {};
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 1;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, &command_buffers[1]);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 3, command_buffers);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
|
|
VkDescriptorPoolSize ds_type_count = {};
|
|
ds_type_count.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
ds_type_count.descriptorCount = 1;
|
|
|
|
VkDescriptorPoolCreateInfo ds_pool_ci = {};
|
|
ds_pool_ci.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
|
ds_pool_ci.pNext = NULL;
|
|
ds_pool_ci.maxSets = 1;
|
|
ds_pool_ci.poolSizeCount = 1;
|
|
ds_pool_ci.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
|
|
ds_pool_ci.pPoolSizes = &ds_type_count;
|
|
|
|
VkDescriptorPool ds_pool;
|
|
err = vkCreateDescriptorPool(m_device->device(), &ds_pool_ci, NULL, &ds_pool);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkDescriptorSetLayoutBinding dsl_binding = {};
|
|
dsl_binding.binding = 2;
|
|
dsl_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
dsl_binding.descriptorCount = 1;
|
|
dsl_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
dsl_binding.pImmutableSamplers = NULL;
|
|
|
|
const VkDescriptorSetLayoutObj ds_layout(m_device, {dsl_binding});
|
|
|
|
VkDescriptorSet descriptor_sets[3] = {};
|
|
VkDescriptorSetAllocateInfo alloc_info = {};
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
alloc_info.descriptorSetCount = 1;
|
|
alloc_info.descriptorPool = ds_pool;
|
|
alloc_info.pSetLayouts = &ds_layout.handle();
|
|
err = vkAllocateDescriptorSets(m_device->device(), &alloc_info, &descriptor_sets[1]);
|
|
ASSERT_VK_SUCCESS(err);
|
|
vkFreeDescriptorSets(m_device->device(), ds_pool, 3, descriptor_sets);
|
|
vkDestroyDescriptorPool(m_device->device(), ds_pool, NULL);
|
|
|
|
vkFreeMemory(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, QueueSubmitSemaphoresAndLayoutTracking) {
|
|
TEST_DESCRIPTION("Submit multiple command buffers with chained semaphore signals and layout transitions");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
VkCommandBuffer cmd_bufs[4];
|
|
VkCommandBufferAllocateInfo alloc_info;
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
alloc_info.pNext = NULL;
|
|
alloc_info.commandBufferCount = 4;
|
|
alloc_info.commandPool = m_commandPool->handle();
|
|
alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &alloc_info, cmd_bufs);
|
|
VkImageObj image(m_device);
|
|
image.Init(128, 128, 1, VK_FORMAT_B8G8R8A8_UNORM,
|
|
(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT),
|
|
VK_IMAGE_TILING_OPTIMAL, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
VkCommandBufferBeginInfo cb_binfo;
|
|
cb_binfo.pNext = NULL;
|
|
cb_binfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
cb_binfo.pInheritanceInfo = VK_NULL_HANDLE;
|
|
cb_binfo.flags = 0;
|
|
// Use 4 command buffers, each with an image layout transition, ColorAO->General->ColorAO->TransferSrc->TransferDst
|
|
vkBeginCommandBuffer(cmd_bufs[0], &cb_binfo);
|
|
VkImageMemoryBarrier img_barrier = {};
|
|
img_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
img_barrier.pNext = NULL;
|
|
img_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
|
img_barrier.dstAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
|
img_barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
img_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
img_barrier.image = image.handle();
|
|
img_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
img_barrier.subresourceRange.baseArrayLayer = 0;
|
|
img_barrier.subresourceRange.baseMipLevel = 0;
|
|
img_barrier.subresourceRange.layerCount = 1;
|
|
img_barrier.subresourceRange.levelCount = 1;
|
|
vkCmdPipelineBarrier(cmd_bufs[0], VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 0, nullptr, 1,
|
|
&img_barrier);
|
|
vkEndCommandBuffer(cmd_bufs[0]);
|
|
vkBeginCommandBuffer(cmd_bufs[1], &cb_binfo);
|
|
img_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
img_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
vkCmdPipelineBarrier(cmd_bufs[1], VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 0, nullptr, 1,
|
|
&img_barrier);
|
|
vkEndCommandBuffer(cmd_bufs[1]);
|
|
vkBeginCommandBuffer(cmd_bufs[2], &cb_binfo);
|
|
img_barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
img_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
|
vkCmdPipelineBarrier(cmd_bufs[2], VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 0, nullptr, 1,
|
|
&img_barrier);
|
|
vkEndCommandBuffer(cmd_bufs[2]);
|
|
vkBeginCommandBuffer(cmd_bufs[3], &cb_binfo);
|
|
img_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
|
img_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
vkCmdPipelineBarrier(cmd_bufs[3], VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0, 0, nullptr, 0, nullptr, 1,
|
|
&img_barrier);
|
|
vkEndCommandBuffer(cmd_bufs[3]);
|
|
|
|
// Submit 4 command buffers in 3 submits, with submits 2 and 3 waiting for semaphores from submits 1 and 2
|
|
VkSemaphore semaphore1, semaphore2;
|
|
VkSemaphoreCreateInfo semaphore_create_info{};
|
|
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
vkCreateSemaphore(m_device->device(), &semaphore_create_info, nullptr, &semaphore1);
|
|
vkCreateSemaphore(m_device->device(), &semaphore_create_info, nullptr, &semaphore2);
|
|
VkPipelineStageFlags flags[]{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};
|
|
VkSubmitInfo submit_info[3];
|
|
submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info[0].pNext = nullptr;
|
|
submit_info[0].commandBufferCount = 1;
|
|
submit_info[0].pCommandBuffers = &cmd_bufs[0];
|
|
submit_info[0].signalSemaphoreCount = 1;
|
|
submit_info[0].pSignalSemaphores = &semaphore1;
|
|
submit_info[0].waitSemaphoreCount = 0;
|
|
submit_info[0].pWaitDstStageMask = nullptr;
|
|
submit_info[0].pWaitDstStageMask = flags;
|
|
submit_info[1].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info[1].pNext = nullptr;
|
|
submit_info[1].commandBufferCount = 1;
|
|
submit_info[1].pCommandBuffers = &cmd_bufs[1];
|
|
submit_info[1].waitSemaphoreCount = 1;
|
|
submit_info[1].pWaitSemaphores = &semaphore1;
|
|
submit_info[1].signalSemaphoreCount = 1;
|
|
submit_info[1].pSignalSemaphores = &semaphore2;
|
|
submit_info[1].pWaitDstStageMask = flags;
|
|
submit_info[2].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info[2].pNext = nullptr;
|
|
submit_info[2].commandBufferCount = 2;
|
|
submit_info[2].pCommandBuffers = &cmd_bufs[2];
|
|
submit_info[2].waitSemaphoreCount = 1;
|
|
submit_info[2].pWaitSemaphores = &semaphore2;
|
|
submit_info[2].signalSemaphoreCount = 0;
|
|
submit_info[2].pSignalSemaphores = nullptr;
|
|
submit_info[2].pWaitDstStageMask = flags;
|
|
vkQueueSubmit(m_device->m_queue, 3, submit_info, VK_NULL_HANDLE);
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
|
|
vkDestroySemaphore(m_device->device(), semaphore1, NULL);
|
|
vkDestroySemaphore(m_device->device(), semaphore2, NULL);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, DynamicOffsetWithInactiveBinding) {
|
|
// Create a descriptorSet w/ dynamic descriptors where 1 binding is inactive
|
|
// We previously had a bug where dynamic offset of inactive bindings was still being used
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitViewport());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
OneOffDescriptorSet descriptor_set(m_device,
|
|
{
|
|
{2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
|
|
{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
|
|
{1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
|
|
});
|
|
|
|
// Create two buffers to update the descriptors with
|
|
// The first will be 2k and used for bindings 0 & 1, the second is 1k for binding 2
|
|
uint32_t qfi = 0;
|
|
VkBufferCreateInfo buffCI = {};
|
|
buffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
buffCI.size = 2048;
|
|
buffCI.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
|
buffCI.queueFamilyIndexCount = 1;
|
|
buffCI.pQueueFamilyIndices = &qfi;
|
|
|
|
VkBufferObj dynamic_uniform_buffer_1, dynamic_uniform_buffer_2;
|
|
dynamic_uniform_buffer_1.init(*m_device, buffCI);
|
|
buffCI.size = 1024;
|
|
dynamic_uniform_buffer_2.init(*m_device, buffCI);
|
|
|
|
// Update descriptors
|
|
const uint32_t BINDING_COUNT = 3;
|
|
VkDescriptorBufferInfo buff_info[BINDING_COUNT] = {};
|
|
buff_info[0].buffer = dynamic_uniform_buffer_1.handle();
|
|
buff_info[0].offset = 0;
|
|
buff_info[0].range = 256;
|
|
buff_info[1].buffer = dynamic_uniform_buffer_1.handle();
|
|
buff_info[1].offset = 256;
|
|
buff_info[1].range = 512;
|
|
buff_info[2].buffer = dynamic_uniform_buffer_2.handle();
|
|
buff_info[2].offset = 0;
|
|
buff_info[2].range = 512;
|
|
|
|
VkWriteDescriptorSet descriptor_write;
|
|
memset(&descriptor_write, 0, sizeof(descriptor_write));
|
|
descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_write.dstSet = descriptor_set.set_;
|
|
descriptor_write.dstBinding = 0;
|
|
descriptor_write.descriptorCount = BINDING_COUNT;
|
|
descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
|
descriptor_write.pBufferInfo = buff_info;
|
|
|
|
vkUpdateDescriptorSets(m_device->device(), 1, &descriptor_write, 0, NULL);
|
|
|
|
m_commandBuffer->begin();
|
|
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
|
|
|
|
// Create PSO to be used for draw-time errors below
|
|
char const *fsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(location=0) out vec4 x;\n"
|
|
"layout(set=0) layout(binding=0) uniform foo1 { int x; int y; } bar1;\n"
|
|
"layout(set=0) layout(binding=2) uniform foo2 { int x; int y; } bar2;\n"
|
|
"void main(){\n"
|
|
" x = vec4(bar1.y) + vec4(bar2.y);\n"
|
|
"}\n";
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.InitState();
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.pipeline_layout_ = VkPipelineLayoutObj(m_device, {&descriptor_set.layout_});
|
|
pipe.CreateGraphicsPipeline();
|
|
|
|
vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_);
|
|
// This update should succeed, but offset of inactive binding 1 oversteps binding 2 buffer size
|
|
// we used to have a bug in this case.
|
|
uint32_t dyn_off[BINDING_COUNT] = {0, 1024, 256};
|
|
vkCmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_layout_.handle(), 0, 1,
|
|
&descriptor_set.set_, BINDING_COUNT, dyn_off);
|
|
m_commandBuffer->Draw(1, 0, 0, 0);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
m_commandBuffer->EndRenderPass();
|
|
m_commandBuffer->end();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, NonCoherentMemoryMapping) {
|
|
TEST_DESCRIPTION(
|
|
"Ensure that validations handling of non-coherent memory mapping while using VK_WHOLE_SIZE does not cause access "
|
|
"violations");
|
|
VkResult err;
|
|
uint8_t *pData;
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
VkDeviceMemory mem;
|
|
VkMemoryRequirements mem_reqs;
|
|
mem_reqs.memoryTypeBits = 0xFFFFFFFF;
|
|
const VkDeviceSize atom_size = m_device->props.limits.nonCoherentAtomSize;
|
|
VkMemoryAllocateInfo alloc_info = {};
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
alloc_info.pNext = NULL;
|
|
alloc_info.memoryTypeIndex = 0;
|
|
|
|
static const VkDeviceSize allocation_size = 32 * atom_size;
|
|
alloc_info.allocationSize = allocation_size;
|
|
|
|
// Find a memory configurations WITHOUT a COHERENT bit, otherwise exit
|
|
bool pass = m_device->phy().set_memory_type(mem_reqs.memoryTypeBits, &alloc_info, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
|
if (!pass) {
|
|
pass = m_device->phy().set_memory_type(mem_reqs.memoryTypeBits, &alloc_info,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
|
if (!pass) {
|
|
pass = m_device->phy().set_memory_type(
|
|
mem_reqs.memoryTypeBits, &alloc_info,
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT,
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
|
if (!pass) {
|
|
printf("%s Couldn't find a memory type wihtout a COHERENT bit.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
err = vkAllocateMemory(m_device->device(), &alloc_info, NULL, &mem);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Map/Flush/Invalidate using WHOLE_SIZE and zero offsets and entire mapped range
|
|
m_errorMonitor->ExpectSuccess();
|
|
err = vkMapMemory(m_device->device(), mem, 0, VK_WHOLE_SIZE, 0, (void **)&pData);
|
|
ASSERT_VK_SUCCESS(err);
|
|
VkMappedMemoryRange mmr = {};
|
|
mmr.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
|
mmr.memory = mem;
|
|
mmr.offset = 0;
|
|
mmr.size = VK_WHOLE_SIZE;
|
|
err = vkFlushMappedMemoryRanges(m_device->device(), 1, &mmr);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkInvalidateMappedMemoryRanges(m_device->device(), 1, &mmr);
|
|
ASSERT_VK_SUCCESS(err);
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkUnmapMemory(m_device->device(), mem);
|
|
|
|
// Map/Flush/Invalidate using WHOLE_SIZE and an offset and entire mapped range
|
|
m_errorMonitor->ExpectSuccess();
|
|
err = vkMapMemory(m_device->device(), mem, 5 * atom_size, VK_WHOLE_SIZE, 0, (void **)&pData);
|
|
ASSERT_VK_SUCCESS(err);
|
|
mmr.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
|
mmr.memory = mem;
|
|
mmr.offset = 6 * atom_size;
|
|
mmr.size = VK_WHOLE_SIZE;
|
|
err = vkFlushMappedMemoryRanges(m_device->device(), 1, &mmr);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkInvalidateMappedMemoryRanges(m_device->device(), 1, &mmr);
|
|
ASSERT_VK_SUCCESS(err);
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkUnmapMemory(m_device->device(), mem);
|
|
|
|
// Map with offset and size
|
|
// Flush/Invalidate subrange of mapped area with offset and size
|
|
m_errorMonitor->ExpectSuccess();
|
|
err = vkMapMemory(m_device->device(), mem, 3 * atom_size, 9 * atom_size, 0, (void **)&pData);
|
|
ASSERT_VK_SUCCESS(err);
|
|
mmr.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
|
mmr.memory = mem;
|
|
mmr.offset = 4 * atom_size;
|
|
mmr.size = 2 * atom_size;
|
|
err = vkFlushMappedMemoryRanges(m_device->device(), 1, &mmr);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkInvalidateMappedMemoryRanges(m_device->device(), 1, &mmr);
|
|
ASSERT_VK_SUCCESS(err);
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkUnmapMemory(m_device->device(), mem);
|
|
|
|
// Map without offset and flush WHOLE_SIZE with two separate offsets
|
|
m_errorMonitor->ExpectSuccess();
|
|
err = vkMapMemory(m_device->device(), mem, 0, VK_WHOLE_SIZE, 0, (void **)&pData);
|
|
ASSERT_VK_SUCCESS(err);
|
|
mmr.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
|
mmr.memory = mem;
|
|
mmr.offset = allocation_size - (4 * atom_size);
|
|
mmr.size = VK_WHOLE_SIZE;
|
|
err = vkFlushMappedMemoryRanges(m_device->device(), 1, &mmr);
|
|
ASSERT_VK_SUCCESS(err);
|
|
mmr.offset = allocation_size - (6 * atom_size);
|
|
mmr.size = VK_WHOLE_SIZE;
|
|
err = vkFlushMappedMemoryRanges(m_device->device(), 1, &mmr);
|
|
ASSERT_VK_SUCCESS(err);
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkUnmapMemory(m_device->device(), mem);
|
|
|
|
vkFreeMemory(m_device->device(), mem, NULL);
|
|
}
|
|
|
|
// This is a positive test. We used to expect error in this case but spec now allows it
|
|
TEST_F(VkPositiveLayerTest, ResetUnsignaledFence) {
|
|
m_errorMonitor->ExpectSuccess();
|
|
vk_testing::Fence testFence;
|
|
VkFenceCreateInfo fenceInfo = {};
|
|
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
fenceInfo.pNext = NULL;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
testFence.init(*m_device, fenceInfo);
|
|
VkFence fences[1] = {testFence.handle()};
|
|
VkResult result = vkResetFences(m_device->device(), 1, fences);
|
|
ASSERT_VK_SUCCESS(result);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CommandBufferSimultaneousUseSync) {
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
VkResult err;
|
|
|
|
// Record (empty!) command buffer that can be submitted multiple times
|
|
// simultaneously.
|
|
VkCommandBufferBeginInfo cbbi = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr,
|
|
VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, nullptr};
|
|
m_commandBuffer->begin(&cbbi);
|
|
m_commandBuffer->end();
|
|
|
|
VkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, 0};
|
|
VkFence fence;
|
|
err = vkCreateFence(m_device->device(), &fci, nullptr, &fence);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkSemaphoreCreateInfo sci = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0};
|
|
VkSemaphore s1, s2;
|
|
err = vkCreateSemaphore(m_device->device(), &sci, nullptr, &s1);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkCreateSemaphore(m_device->device(), &sci, nullptr, &s2);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Submit CB once signaling s1, with fence so we can roll forward to its retirement.
|
|
VkSubmitInfo si = {VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, 0, nullptr, nullptr, 1, &m_commandBuffer->handle(), 1, &s1};
|
|
err = vkQueueSubmit(m_device->m_queue, 1, &si, fence);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Submit CB again, signaling s2.
|
|
si.pSignalSemaphores = &s2;
|
|
err = vkQueueSubmit(m_device->m_queue, 1, &si, VK_NULL_HANDLE);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Wait for fence.
|
|
err = vkWaitForFences(m_device->device(), 1, &fence, VK_TRUE, UINT64_MAX);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// CB is still in flight from second submission, but semaphore s1 is no
|
|
// longer in flight. delete it.
|
|
vkDestroySemaphore(m_device->device(), s1, nullptr);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
// Force device idle and clean up remaining objects
|
|
vkDeviceWaitIdle(m_device->device());
|
|
vkDestroySemaphore(m_device->device(), s2, nullptr);
|
|
vkDestroyFence(m_device->device(), fence, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, FenceCreateSignaledWaitHandling) {
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
VkResult err;
|
|
|
|
// A fence created signaled
|
|
VkFenceCreateInfo fci1 = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT};
|
|
VkFence f1;
|
|
err = vkCreateFence(m_device->device(), &fci1, nullptr, &f1);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// A fence created not
|
|
VkFenceCreateInfo fci2 = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, 0};
|
|
VkFence f2;
|
|
err = vkCreateFence(m_device->device(), &fci2, nullptr, &f2);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Submit the unsignaled fence
|
|
VkSubmitInfo si = {VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, 0, nullptr, nullptr, 0, nullptr, 0, nullptr};
|
|
err = vkQueueSubmit(m_device->m_queue, 1, &si, f2);
|
|
|
|
// Wait on both fences, with signaled first.
|
|
VkFence fences[] = {f1, f2};
|
|
vkWaitForFences(m_device->device(), 2, fences, VK_TRUE, UINT64_MAX);
|
|
|
|
// Should have both retired!
|
|
vkDestroyFence(m_device->device(), f1, nullptr);
|
|
vkDestroyFence(m_device->device(), f2, nullptr);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreateImageViewFollowsParameterCompatibilityRequirements) {
|
|
TEST_DESCRIPTION("Verify that creating an ImageView with valid usage does not generate validation errors.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkImageCreateInfo imgInfo = {VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
|
nullptr,
|
|
VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT,
|
|
VK_IMAGE_TYPE_2D,
|
|
VK_FORMAT_R8G8B8A8_UNORM,
|
|
{128, 128, 1},
|
|
1,
|
|
1,
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_IMAGE_TILING_OPTIMAL,
|
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
|
VK_SHARING_MODE_EXCLUSIVE,
|
|
0,
|
|
nullptr,
|
|
VK_IMAGE_LAYOUT_UNDEFINED};
|
|
VkImageObj image(m_device);
|
|
image.init(&imgInfo);
|
|
ASSERT_TRUE(image.initialized());
|
|
image.targetView(VK_FORMAT_R8G8B8A8_UNORM);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ValidUsage) {
|
|
TEST_DESCRIPTION("Verify that creating an image view from an image with valid usage doesn't generate validation errors");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
// Verify that we can create a view with usage INPUT_ATTACHMENT
|
|
VkImageObj image(m_device);
|
|
image.Init(128, 128, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
VkImageView imageView;
|
|
VkImageViewCreateInfo ivci = {};
|
|
ivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
ivci.image = image.handle();
|
|
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
ivci.format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
ivci.subresourceRange.layerCount = 1;
|
|
ivci.subresourceRange.baseMipLevel = 0;
|
|
ivci.subresourceRange.levelCount = 1;
|
|
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
vkCreateImageView(m_device->device(), &ivci, NULL, &imageView);
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkDestroyImageView(m_device->device(), imageView, NULL);
|
|
}
|
|
|
|
// This is a positive test. No failures are expected.
|
|
TEST_F(VkPositiveLayerTest, BindSparse) {
|
|
TEST_DESCRIPTION("Bind 2 memory ranges to one image using vkQueueBindSparse, destroy the image and then free the memory");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
auto index = m_device->graphics_queue_node_index_;
|
|
if (!(m_device->queue_props[index].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT)) {
|
|
printf("%s Graphics queue does not have sparse binding bit.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
if (!m_device->phy().features().sparseBinding) {
|
|
printf("%s Device does not support sparse bindings.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess(VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT);
|
|
|
|
VkImage image;
|
|
VkImageCreateInfo image_create_info = {};
|
|
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
image_create_info.pNext = NULL;
|
|
image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
|
image_create_info.format = VK_FORMAT_B8G8R8A8_UNORM;
|
|
image_create_info.extent.width = 64;
|
|
image_create_info.extent.height = 64;
|
|
image_create_info.extent.depth = 1;
|
|
image_create_info.mipLevels = 1;
|
|
image_create_info.arrayLayers = 1;
|
|
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT;
|
|
VkResult err = vkCreateImage(m_device->device(), &image_create_info, NULL, &image);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkMemoryRequirements memory_reqs;
|
|
VkDeviceMemory memory_one, memory_two;
|
|
bool pass;
|
|
VkMemoryAllocateInfo memory_info = {};
|
|
memory_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
memory_info.pNext = NULL;
|
|
memory_info.allocationSize = 0;
|
|
memory_info.memoryTypeIndex = 0;
|
|
vkGetImageMemoryRequirements(m_device->device(), image, &memory_reqs);
|
|
// Find an image big enough to allow sparse mapping of 2 memory regions
|
|
// Increase the image size until it is at least twice the
|
|
// size of the required alignment, to ensure we can bind both
|
|
// allocated memory blocks to the image on aligned offsets.
|
|
while (memory_reqs.size < (memory_reqs.alignment * 2)) {
|
|
vkDestroyImage(m_device->device(), image, nullptr);
|
|
image_create_info.extent.width *= 2;
|
|
image_create_info.extent.height *= 2;
|
|
err = vkCreateImage(m_device->device(), &image_create_info, nullptr, &image);
|
|
ASSERT_VK_SUCCESS(err);
|
|
vkGetImageMemoryRequirements(m_device->device(), image, &memory_reqs);
|
|
}
|
|
// Allocate 2 memory regions of minimum alignment size, bind one at 0, the other
|
|
// at the end of the first
|
|
memory_info.allocationSize = memory_reqs.alignment;
|
|
pass = m_device->phy().set_memory_type(memory_reqs.memoryTypeBits, &memory_info, 0);
|
|
ASSERT_TRUE(pass);
|
|
err = vkAllocateMemory(m_device->device(), &memory_info, NULL, &memory_one);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkAllocateMemory(m_device->device(), &memory_info, NULL, &memory_two);
|
|
ASSERT_VK_SUCCESS(err);
|
|
VkSparseMemoryBind binds[2];
|
|
binds[0].flags = 0;
|
|
binds[0].memory = memory_one;
|
|
binds[0].memoryOffset = 0;
|
|
binds[0].resourceOffset = 0;
|
|
binds[0].size = memory_info.allocationSize;
|
|
binds[1].flags = 0;
|
|
binds[1].memory = memory_two;
|
|
binds[1].memoryOffset = 0;
|
|
binds[1].resourceOffset = memory_info.allocationSize;
|
|
binds[1].size = memory_info.allocationSize;
|
|
|
|
VkSparseImageOpaqueMemoryBindInfo opaqueBindInfo;
|
|
opaqueBindInfo.image = image;
|
|
opaqueBindInfo.bindCount = 2;
|
|
opaqueBindInfo.pBinds = binds;
|
|
|
|
VkFence fence = VK_NULL_HANDLE;
|
|
VkBindSparseInfo bindSparseInfo = {};
|
|
bindSparseInfo.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
|
|
bindSparseInfo.imageOpaqueBindCount = 1;
|
|
bindSparseInfo.pImageOpaqueBinds = &opaqueBindInfo;
|
|
|
|
vkQueueBindSparse(m_device->m_queue, 1, &bindSparseInfo, fence);
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
vkDestroyImage(m_device->device(), image, NULL);
|
|
vkFreeMemory(m_device->device(), memory_one, NULL);
|
|
vkFreeMemory(m_device->device(), memory_two, NULL);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, BindSparseMetadata) {
|
|
TEST_DESCRIPTION("Bind memory for the metadata aspect of a sparse image");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
auto index = m_device->graphics_queue_node_index_;
|
|
if (!(m_device->queue_props[index].queueFlags & VK_QUEUE_SPARSE_BINDING_BIT)) {
|
|
printf("%s Graphics queue does not have sparse binding bit.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
if (!m_device->phy().features().sparseResidencyImage2D) {
|
|
printf("%s Device does not support sparse residency for images.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess(VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT);
|
|
|
|
// Create a sparse image
|
|
VkImage image;
|
|
VkImageCreateInfo image_create_info = {};
|
|
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
image_create_info.pNext = NULL;
|
|
image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
|
image_create_info.format = VK_FORMAT_B8G8R8A8_UNORM;
|
|
image_create_info.extent.width = 64;
|
|
image_create_info.extent.height = 64;
|
|
image_create_info.extent.depth = 1;
|
|
image_create_info.mipLevels = 1;
|
|
image_create_info.arrayLayers = 1;
|
|
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
image_create_info.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
image_create_info.flags = VK_IMAGE_CREATE_SPARSE_BINDING_BIT | VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT;
|
|
VkResult err = vkCreateImage(m_device->device(), &image_create_info, NULL, &image);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Query image memory requirements
|
|
VkMemoryRequirements memory_reqs;
|
|
vkGetImageMemoryRequirements(m_device->device(), image, &memory_reqs);
|
|
|
|
// Query sparse memory requirements
|
|
uint32_t sparse_reqs_count = 0;
|
|
vkGetImageSparseMemoryRequirements(m_device->device(), image, &sparse_reqs_count, nullptr);
|
|
std::vector<VkSparseImageMemoryRequirements> sparse_reqs(sparse_reqs_count);
|
|
vkGetImageSparseMemoryRequirements(m_device->device(), image, &sparse_reqs_count, sparse_reqs.data());
|
|
|
|
// Find requirements for metadata aspect
|
|
const VkSparseImageMemoryRequirements *metadata_reqs = nullptr;
|
|
for (auto const &aspect_sparse_reqs : sparse_reqs) {
|
|
if (aspect_sparse_reqs.formatProperties.aspectMask == VK_IMAGE_ASPECT_METADATA_BIT) {
|
|
metadata_reqs = &aspect_sparse_reqs;
|
|
}
|
|
}
|
|
|
|
if (!metadata_reqs) {
|
|
printf("%s Sparse image does not require memory for metadata.\n", kSkipPrefix);
|
|
} else {
|
|
// Allocate memory for the metadata
|
|
VkDeviceMemory metadata_memory = VK_NULL_HANDLE;
|
|
VkMemoryAllocateInfo metadata_memory_info = {};
|
|
metadata_memory_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
metadata_memory_info.allocationSize = metadata_reqs->imageMipTailSize;
|
|
m_device->phy().set_memory_type(memory_reqs.memoryTypeBits, &metadata_memory_info, 0);
|
|
err = vkAllocateMemory(m_device->device(), &metadata_memory_info, NULL, &metadata_memory);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Bind metadata
|
|
VkSparseMemoryBind sparse_bind = {};
|
|
sparse_bind.resourceOffset = metadata_reqs->imageMipTailOffset;
|
|
sparse_bind.size = metadata_reqs->imageMipTailSize;
|
|
sparse_bind.memory = metadata_memory;
|
|
sparse_bind.memoryOffset = 0;
|
|
sparse_bind.flags = VK_SPARSE_MEMORY_BIND_METADATA_BIT;
|
|
|
|
VkSparseImageOpaqueMemoryBindInfo opaque_bind_info = {};
|
|
opaque_bind_info.image = image;
|
|
opaque_bind_info.bindCount = 1;
|
|
opaque_bind_info.pBinds = &sparse_bind;
|
|
|
|
VkBindSparseInfo bind_info = {};
|
|
bind_info.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
|
|
bind_info.imageOpaqueBindCount = 1;
|
|
bind_info.pImageOpaqueBinds = &opaque_bind_info;
|
|
|
|
vkQueueBindSparse(m_device->m_queue, 1, &bind_info, VK_NULL_HANDLE);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
// Cleanup
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
vkFreeMemory(m_device->device(), metadata_memory, NULL);
|
|
}
|
|
|
|
vkDestroyImage(m_device->device(), image, NULL);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, FramebufferBindingDestroyCommandPool) {
|
|
TEST_DESCRIPTION(
|
|
"This test should pass. Create a Framebuffer and command buffer, bind them together, then destroy command pool and "
|
|
"framebuffer and verify there are no errors.");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
// A renderpass with one color attachment.
|
|
VkAttachmentDescription attachment = {0,
|
|
VK_FORMAT_R8G8B8A8_UNORM,
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_STORE,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
|
|
VkAttachmentReference att_ref = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
|
|
VkSubpassDescription subpass = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1, &att_ref, nullptr, nullptr, 0, nullptr};
|
|
|
|
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 1, &attachment, 1, &subpass, 0, nullptr};
|
|
|
|
VkRenderPass rp;
|
|
VkResult err = vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// A compatible framebuffer.
|
|
VkImageObj image(m_device);
|
|
image.Init(32, 32, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
|
|
VkImageView view = image.targetView(VK_FORMAT_R8G8B8A8_UNORM);
|
|
|
|
VkFramebufferCreateInfo fci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, rp, 1, &view, 32, 32, 1};
|
|
VkFramebuffer fb;
|
|
err = vkCreateFramebuffer(m_device->device(), &fci, nullptr, &fb);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Explicitly create a command buffer to bind the FB to so that we can then
|
|
// destroy the command pool in order to implicitly free command buffer
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer;
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 1;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, &command_buffer);
|
|
|
|
// Begin our cmd buffer with renderpass using our framebuffer
|
|
VkRenderPassBeginInfo rpbi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, rp, fb, {{0, 0}, {32, 32}}, 0, nullptr};
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer, &begin_info);
|
|
|
|
vkCmdBeginRenderPass(command_buffer, &rpbi, VK_SUBPASS_CONTENTS_INLINE);
|
|
vkCmdEndRenderPass(command_buffer);
|
|
vkEndCommandBuffer(command_buffer);
|
|
// Destroy command pool to implicitly free command buffer
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
vkDestroyFramebuffer(m_device->device(), fb, nullptr);
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, FramebufferCreateDepthStencilLayoutTransitionForDepthOnlyImageView) {
|
|
TEST_DESCRIPTION(
|
|
"Validate that when an imageView of a depth/stencil image is used as a depth/stencil framebuffer attachment, the "
|
|
"aspectMask is ignored and both depth and stencil image subresources are used.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
VkFormatProperties format_properties;
|
|
vkGetPhysicalDeviceFormatProperties(gpu(), VK_FORMAT_D32_SFLOAT_S8_UINT, &format_properties);
|
|
if (!(format_properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
|
|
printf("%s Image format does not support sampling.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkAttachmentDescription attachment = {0,
|
|
VK_FORMAT_D32_SFLOAT_S8_UINT,
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_STORE,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};
|
|
|
|
VkAttachmentReference att_ref = {0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL};
|
|
|
|
VkSubpassDescription subpass = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 0, nullptr, nullptr, &att_ref, 0, nullptr};
|
|
|
|
VkSubpassDependency dep = {0,
|
|
0,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
VK_DEPENDENCY_BY_REGION_BIT};
|
|
|
|
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 1, &attachment, 1, &subpass, 1, &dep};
|
|
|
|
VkResult err;
|
|
VkRenderPass rp;
|
|
err = vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkImageObj image(m_device);
|
|
image.InitNoLayout(32, 32, 1, VK_FORMAT_D32_SFLOAT_S8_UINT,
|
|
0x26, // usage
|
|
VK_IMAGE_TILING_OPTIMAL, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
image.SetLayout(0x6, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
|
|
|
|
VkImageView view = image.targetView(VK_FORMAT_D32_SFLOAT_S8_UINT, VK_IMAGE_ASPECT_DEPTH_BIT);
|
|
|
|
VkFramebufferCreateInfo fci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, rp, 1, &view, 32, 32, 1};
|
|
VkFramebuffer fb;
|
|
err = vkCreateFramebuffer(m_device->device(), &fci, nullptr, &fb);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
m_commandBuffer->begin();
|
|
|
|
VkImageMemoryBarrier imb = {};
|
|
imb.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
imb.pNext = nullptr;
|
|
imb.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
|
imb.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
imb.oldLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
|
|
imb.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
imb.srcQueueFamilyIndex = 0;
|
|
imb.dstQueueFamilyIndex = 0;
|
|
imb.image = image.handle();
|
|
imb.subresourceRange.aspectMask = 0x6;
|
|
imb.subresourceRange.baseMipLevel = 0;
|
|
imb.subresourceRange.levelCount = 0x1;
|
|
imb.subresourceRange.baseArrayLayer = 0;
|
|
imb.subresourceRange.layerCount = 0x1;
|
|
|
|
vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT,
|
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, &imb);
|
|
|
|
m_commandBuffer->end();
|
|
m_commandBuffer->QueueCommandBuffer(false);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyFramebuffer(m_device->device(), fb, nullptr);
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, BarrierLayoutToImageUsage) {
|
|
TEST_DESCRIPTION("Ensure barriers' new and old VkImageLayout are compatible with their images' VkImageUsageFlags");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
auto depth_format = FindSupportedDepthStencilFormat(gpu());
|
|
if (!depth_format) {
|
|
printf("%s No Depth + Stencil format found. Skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkImageMemoryBarrier img_barrier = {};
|
|
img_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
img_barrier.pNext = NULL;
|
|
img_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
|
img_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
img_barrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
img_barrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
img_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
img_barrier.subresourceRange.baseArrayLayer = 0;
|
|
img_barrier.subresourceRange.baseMipLevel = 0;
|
|
img_barrier.subresourceRange.layerCount = 1;
|
|
img_barrier.subresourceRange.levelCount = 1;
|
|
|
|
{
|
|
VkImageObj img_color(m_device);
|
|
img_color.Init(128, 128, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
ASSERT_TRUE(img_color.initialized());
|
|
|
|
VkImageObj img_ds1(m_device);
|
|
img_ds1.Init(128, 128, 1, depth_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
ASSERT_TRUE(img_ds1.initialized());
|
|
|
|
VkImageObj img_ds2(m_device);
|
|
img_ds2.Init(128, 128, 1, depth_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
ASSERT_TRUE(img_ds2.initialized());
|
|
|
|
VkImageObj img_xfer_src(m_device);
|
|
img_xfer_src.Init(128, 128, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
ASSERT_TRUE(img_xfer_src.initialized());
|
|
|
|
VkImageObj img_xfer_dst(m_device);
|
|
img_xfer_dst.Init(128, 128, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
ASSERT_TRUE(img_xfer_dst.initialized());
|
|
|
|
VkImageObj img_sampled(m_device);
|
|
img_sampled.Init(32, 32, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
ASSERT_TRUE(img_sampled.initialized());
|
|
|
|
VkImageObj img_input(m_device);
|
|
img_input.Init(128, 128, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
ASSERT_TRUE(img_input.initialized());
|
|
|
|
const struct {
|
|
VkImageObj &image_obj;
|
|
VkImageLayout old_layout;
|
|
VkImageLayout new_layout;
|
|
} buffer_layouts[] = {
|
|
// clang-format off
|
|
{img_color, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL},
|
|
{img_ds1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL},
|
|
{img_ds2, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL},
|
|
{img_sampled, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL},
|
|
{img_input, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL},
|
|
{img_xfer_src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL},
|
|
{img_xfer_dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL},
|
|
// clang-format on
|
|
};
|
|
const uint32_t layout_count = sizeof(buffer_layouts) / sizeof(buffer_layouts[0]);
|
|
|
|
m_commandBuffer->begin();
|
|
for (uint32_t i = 0; i < layout_count; ++i) {
|
|
img_barrier.image = buffer_layouts[i].image_obj.handle();
|
|
const VkImageUsageFlags usage = buffer_layouts[i].image_obj.usage();
|
|
img_barrier.subresourceRange.aspectMask = (usage == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
|
|
? (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)
|
|
: VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
img_barrier.oldLayout = buffer_layouts[i].old_layout;
|
|
img_barrier.newLayout = buffer_layouts[i].new_layout;
|
|
vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 1, &img_barrier);
|
|
|
|
img_barrier.oldLayout = buffer_layouts[i].new_layout;
|
|
img_barrier.newLayout = buffer_layouts[i].old_layout;
|
|
vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 1, &img_barrier);
|
|
}
|
|
m_commandBuffer->end();
|
|
|
|
img_barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
img_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
}
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, WaitEventThenSet) {
|
|
TEST_DESCRIPTION("Wait on a event then set it after the wait has been submitted.");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
VkEvent event;
|
|
VkEventCreateInfo event_create_info{};
|
|
event_create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
|
|
vkCreateEvent(m_device->device(), &event_create_info, nullptr, &event);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer;
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 1;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, &command_buffer);
|
|
|
|
VkQueue queue = VK_NULL_HANDLE;
|
|
vkGetDeviceQueue(m_device->device(), m_device->graphics_queue_node_index_, 0, &queue);
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer, &begin_info);
|
|
|
|
vkCmdWaitEvents(command_buffer, 1, &event, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, nullptr, 0,
|
|
nullptr, 0, nullptr);
|
|
vkCmdResetEvent(command_buffer, event, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
|
|
vkEndCommandBuffer(command_buffer);
|
|
}
|
|
{
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer;
|
|
submit_info.signalSemaphoreCount = 0;
|
|
submit_info.pSignalSemaphores = nullptr;
|
|
vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
{ vkSetEvent(m_device->device(), event); }
|
|
|
|
vkQueueWaitIdle(queue);
|
|
|
|
vkDestroyEvent(m_device->device(), event, nullptr);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 1, &command_buffer);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, QueryAndCopySecondaryCommandBuffers) {
|
|
TEST_DESCRIPTION("Issue a query on a secondary command buffer and copy it on a primary.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
if ((m_device->queue_props.empty()) || (m_device->queue_props[0].queueCount < 2)) {
|
|
printf("%s Queue family needs to have multiple queues to run this test.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkQueryPool query_pool;
|
|
VkQueryPoolCreateInfo query_pool_create_info{};
|
|
query_pool_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
|
|
query_pool_create_info.queryType = VK_QUERY_TYPE_TIMESTAMP;
|
|
query_pool_create_info.queryCount = 1;
|
|
vkCreateQueryPool(m_device->device(), &query_pool_create_info, nullptr, &query_pool);
|
|
|
|
VkCommandPoolObj command_pool(m_device, m_device->graphics_queue_node_index_, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
|
|
VkCommandBufferObj primary_buffer(m_device, &command_pool);
|
|
VkCommandBufferObj secondary_buffer(m_device, &command_pool, VK_COMMAND_BUFFER_LEVEL_SECONDARY);
|
|
|
|
VkQueue queue = VK_NULL_HANDLE;
|
|
vkGetDeviceQueue(m_device->device(), m_device->graphics_queue_node_index_, 1, &queue);
|
|
|
|
uint32_t qfi = 0;
|
|
VkBufferCreateInfo buff_create_info = {};
|
|
buff_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
buff_create_info.size = 1024;
|
|
buff_create_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
buff_create_info.queueFamilyIndexCount = 1;
|
|
buff_create_info.pQueueFamilyIndices = &qfi;
|
|
|
|
VkBufferObj buffer;
|
|
buffer.init(*m_device, buff_create_info);
|
|
|
|
VkCommandBufferInheritanceInfo hinfo = {};
|
|
hinfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
|
|
hinfo.renderPass = VK_NULL_HANDLE;
|
|
hinfo.subpass = 0;
|
|
hinfo.framebuffer = VK_NULL_HANDLE;
|
|
hinfo.occlusionQueryEnable = VK_FALSE;
|
|
hinfo.queryFlags = 0;
|
|
hinfo.pipelineStatistics = 0;
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
begin_info.pInheritanceInfo = &hinfo;
|
|
secondary_buffer.begin(&begin_info);
|
|
vkCmdResetQueryPool(secondary_buffer.handle(), query_pool, 0, 1);
|
|
vkCmdWriteTimestamp(secondary_buffer.handle(), VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, query_pool, 0);
|
|
secondary_buffer.end();
|
|
|
|
primary_buffer.begin();
|
|
vkCmdExecuteCommands(primary_buffer.handle(), 1, &secondary_buffer.handle());
|
|
vkCmdCopyQueryPoolResults(primary_buffer.handle(), query_pool, 0, 1, buffer.handle(), 0, 0, VK_QUERY_RESULT_WAIT_BIT);
|
|
primary_buffer.end();
|
|
}
|
|
|
|
primary_buffer.QueueCommandBuffer();
|
|
vkQueueWaitIdle(queue);
|
|
|
|
vkDestroyQueryPool(m_device->device(), query_pool, nullptr);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, QueryAndCopyMultipleCommandBuffers) {
|
|
TEST_DESCRIPTION("Issue a query and copy from it on a second command buffer.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
if ((m_device->queue_props.empty()) || (m_device->queue_props[0].queueCount < 2)) {
|
|
printf("%s Queue family needs to have multiple queues to run this test.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkQueryPool query_pool;
|
|
VkQueryPoolCreateInfo query_pool_create_info{};
|
|
query_pool_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
|
|
query_pool_create_info.queryType = VK_QUERY_TYPE_TIMESTAMP;
|
|
query_pool_create_info.queryCount = 1;
|
|
vkCreateQueryPool(m_device->device(), &query_pool_create_info, nullptr, &query_pool);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer[2];
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 2;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, command_buffer);
|
|
|
|
VkQueue queue = VK_NULL_HANDLE;
|
|
vkGetDeviceQueue(m_device->device(), m_device->graphics_queue_node_index_, 1, &queue);
|
|
|
|
uint32_t qfi = 0;
|
|
VkBufferCreateInfo buff_create_info = {};
|
|
buff_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
buff_create_info.size = 1024;
|
|
buff_create_info.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
|
|
buff_create_info.queueFamilyIndexCount = 1;
|
|
buff_create_info.pQueueFamilyIndices = &qfi;
|
|
|
|
VkBufferObj buffer;
|
|
buffer.init(*m_device, buff_create_info);
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[0], &begin_info);
|
|
|
|
vkCmdResetQueryPool(command_buffer[0], query_pool, 0, 1);
|
|
vkCmdWriteTimestamp(command_buffer[0], VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, query_pool, 0);
|
|
|
|
vkEndCommandBuffer(command_buffer[0]);
|
|
|
|
vkBeginCommandBuffer(command_buffer[1], &begin_info);
|
|
|
|
vkCmdCopyQueryPoolResults(command_buffer[1], query_pool, 0, 1, buffer.handle(), 0, 0, VK_QUERY_RESULT_WAIT_BIT);
|
|
|
|
vkEndCommandBuffer(command_buffer[1]);
|
|
}
|
|
{
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 2;
|
|
submit_info.pCommandBuffers = command_buffer;
|
|
submit_info.signalSemaphoreCount = 0;
|
|
submit_info.pSignalSemaphores = nullptr;
|
|
vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
|
|
vkQueueWaitIdle(queue);
|
|
|
|
vkDestroyQueryPool(m_device->device(), query_pool, nullptr);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 2, command_buffer);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, TwoFencesThreeFrames) {
|
|
TEST_DESCRIPTION(
|
|
"Two command buffers with two separate fences are each run through a Submit & WaitForFences cycle 3 times. This previously "
|
|
"revealed a bug so running this positive test to prevent a regression.");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
VkQueue queue = VK_NULL_HANDLE;
|
|
vkGetDeviceQueue(m_device->device(), m_device->graphics_queue_node_index_, 0, &queue);
|
|
|
|
static const uint32_t NUM_OBJECTS = 2;
|
|
static const uint32_t NUM_FRAMES = 3;
|
|
VkCommandBuffer cmd_buffers[NUM_OBJECTS] = {};
|
|
VkFence fences[NUM_OBJECTS] = {};
|
|
|
|
VkCommandPool cmd_pool;
|
|
VkCommandPoolCreateInfo cmd_pool_ci = {};
|
|
cmd_pool_ci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
cmd_pool_ci.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
cmd_pool_ci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
VkResult err = vkCreateCommandPool(m_device->device(), &cmd_pool_ci, nullptr, &cmd_pool);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkCommandBufferAllocateInfo cmd_buf_info = {};
|
|
cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
cmd_buf_info.commandPool = cmd_pool;
|
|
cmd_buf_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
cmd_buf_info.commandBufferCount = 1;
|
|
|
|
VkFenceCreateInfo fence_ci = {};
|
|
fence_ci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
fence_ci.pNext = nullptr;
|
|
fence_ci.flags = 0;
|
|
|
|
for (uint32_t i = 0; i < NUM_OBJECTS; ++i) {
|
|
err = vkAllocateCommandBuffers(m_device->device(), &cmd_buf_info, &cmd_buffers[i]);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkCreateFence(m_device->device(), &fence_ci, nullptr, &fences[i]);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
|
|
for (uint32_t frame = 0; frame < NUM_FRAMES; ++frame) {
|
|
for (uint32_t obj = 0; obj < NUM_OBJECTS; ++obj) {
|
|
// Create empty cmd buffer
|
|
VkCommandBufferBeginInfo cmdBufBeginDesc = {};
|
|
cmdBufBeginDesc.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
|
|
err = vkBeginCommandBuffer(cmd_buffers[obj], &cmdBufBeginDesc);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkEndCommandBuffer(cmd_buffers[obj]);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkSubmitInfo submit_info = {};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &cmd_buffers[obj];
|
|
// Submit cmd buffer and wait for fence
|
|
err = vkQueueSubmit(queue, 1, &submit_info, fences[obj]);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkWaitForFences(m_device->device(), 1, &fences[obj], VK_TRUE, UINT64_MAX);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkResetFences(m_device->device(), 1, &fences[obj]);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
}
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkDestroyCommandPool(m_device->device(), cmd_pool, NULL);
|
|
for (uint32_t i = 0; i < NUM_OBJECTS; ++i) {
|
|
vkDestroyFence(m_device->device(), fences[i], nullptr);
|
|
}
|
|
}
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, TwoQueueSubmitsSeparateQueuesWithSemaphoreAndOneFenceQWI) {
|
|
TEST_DESCRIPTION(
|
|
"Two command buffers, each in a separate QueueSubmit call submitted on separate queues followed by a QueueWaitIdle.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
if ((m_device->queue_props.empty()) || (m_device->queue_props[0].queueCount < 2)) {
|
|
printf("%s Queue family needs to have multiple queues to run this test.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkSemaphore semaphore;
|
|
VkSemaphoreCreateInfo semaphore_create_info{};
|
|
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
vkCreateSemaphore(m_device->device(), &semaphore_create_info, nullptr, &semaphore);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer[2];
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 2;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, command_buffer);
|
|
|
|
VkQueue queue = VK_NULL_HANDLE;
|
|
vkGetDeviceQueue(m_device->device(), m_device->graphics_queue_node_index_, 1, &queue);
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[0], &begin_info);
|
|
|
|
vkCmdPipelineBarrier(command_buffer[0], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 0, nullptr);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[0], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[0]);
|
|
}
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[1], &begin_info);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[1], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[1]);
|
|
}
|
|
{
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[0];
|
|
submit_info.signalSemaphoreCount = 1;
|
|
submit_info.pSignalSemaphores = &semaphore;
|
|
vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
{
|
|
VkPipelineStageFlags flags[]{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[1];
|
|
submit_info.waitSemaphoreCount = 1;
|
|
submit_info.pWaitSemaphores = &semaphore;
|
|
submit_info.pWaitDstStageMask = flags;
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
|
|
vkDestroySemaphore(m_device->device(), semaphore, nullptr);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 2, &command_buffer[0]);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, TwoQueueSubmitsSeparateQueuesWithSemaphoreAndOneFenceQWIFence) {
|
|
TEST_DESCRIPTION(
|
|
"Two command buffers, each in a separate QueueSubmit call submitted on separate queues, the second having a fence followed "
|
|
"by a QueueWaitIdle.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
if ((m_device->queue_props.empty()) || (m_device->queue_props[0].queueCount < 2)) {
|
|
printf("%s Queue family needs to have multiple queues to run this test.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkFence fence;
|
|
VkFenceCreateInfo fence_create_info{};
|
|
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
vkCreateFence(m_device->device(), &fence_create_info, nullptr, &fence);
|
|
|
|
VkSemaphore semaphore;
|
|
VkSemaphoreCreateInfo semaphore_create_info{};
|
|
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
vkCreateSemaphore(m_device->device(), &semaphore_create_info, nullptr, &semaphore);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer[2];
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 2;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, command_buffer);
|
|
|
|
VkQueue queue = VK_NULL_HANDLE;
|
|
vkGetDeviceQueue(m_device->device(), m_device->graphics_queue_node_index_, 1, &queue);
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[0], &begin_info);
|
|
|
|
vkCmdPipelineBarrier(command_buffer[0], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 0, nullptr);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[0], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[0]);
|
|
}
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[1], &begin_info);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[1], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[1]);
|
|
}
|
|
{
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[0];
|
|
submit_info.signalSemaphoreCount = 1;
|
|
submit_info.pSignalSemaphores = &semaphore;
|
|
vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
{
|
|
VkPipelineStageFlags flags[]{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[1];
|
|
submit_info.waitSemaphoreCount = 1;
|
|
submit_info.pWaitSemaphores = &semaphore;
|
|
submit_info.pWaitDstStageMask = flags;
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, fence);
|
|
}
|
|
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
|
|
vkDestroyFence(m_device->device(), fence, nullptr);
|
|
vkDestroySemaphore(m_device->device(), semaphore, nullptr);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 2, &command_buffer[0]);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, TwoQueueSubmitsSeparateQueuesWithSemaphoreAndOneFenceTwoWFF) {
|
|
TEST_DESCRIPTION(
|
|
"Two command buffers, each in a separate QueueSubmit call submitted on separate queues, the second having a fence followed "
|
|
"by two consecutive WaitForFences calls on the same fence.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
if ((m_device->queue_props.empty()) || (m_device->queue_props[0].queueCount < 2)) {
|
|
printf("%s Queue family needs to have multiple queues to run this test.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkFence fence;
|
|
VkFenceCreateInfo fence_create_info{};
|
|
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
vkCreateFence(m_device->device(), &fence_create_info, nullptr, &fence);
|
|
|
|
VkSemaphore semaphore;
|
|
VkSemaphoreCreateInfo semaphore_create_info{};
|
|
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
vkCreateSemaphore(m_device->device(), &semaphore_create_info, nullptr, &semaphore);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer[2];
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 2;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, command_buffer);
|
|
|
|
VkQueue queue = VK_NULL_HANDLE;
|
|
vkGetDeviceQueue(m_device->device(), m_device->graphics_queue_node_index_, 1, &queue);
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[0], &begin_info);
|
|
|
|
vkCmdPipelineBarrier(command_buffer[0], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 0, nullptr);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[0], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[0]);
|
|
}
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[1], &begin_info);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[1], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[1]);
|
|
}
|
|
{
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[0];
|
|
submit_info.signalSemaphoreCount = 1;
|
|
submit_info.pSignalSemaphores = &semaphore;
|
|
vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
{
|
|
VkPipelineStageFlags flags[]{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[1];
|
|
submit_info.waitSemaphoreCount = 1;
|
|
submit_info.pWaitSemaphores = &semaphore;
|
|
submit_info.pWaitDstStageMask = flags;
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, fence);
|
|
}
|
|
|
|
vkWaitForFences(m_device->device(), 1, &fence, VK_TRUE, UINT64_MAX);
|
|
vkWaitForFences(m_device->device(), 1, &fence, VK_TRUE, UINT64_MAX);
|
|
|
|
vkDestroyFence(m_device->device(), fence, nullptr);
|
|
vkDestroySemaphore(m_device->device(), semaphore, nullptr);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 2, &command_buffer[0]);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, TwoQueuesEnsureCorrectRetirementWithWorkStolen) {
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
if ((m_device->queue_props.empty()) || (m_device->queue_props[0].queueCount < 2)) {
|
|
printf("%s Test requires two queues, skipping\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
VkResult err;
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkQueue q0 = m_device->m_queue;
|
|
VkQueue q1 = nullptr;
|
|
vkGetDeviceQueue(m_device->device(), m_device->graphics_queue_node_index_, 1, &q1);
|
|
ASSERT_NE(q1, nullptr);
|
|
|
|
// An (empty) command buffer. We must have work in the first submission --
|
|
// the layer treats unfenced work differently from fenced work.
|
|
VkCommandPoolCreateInfo cpci = {VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, nullptr, 0, 0};
|
|
VkCommandPool pool;
|
|
err = vkCreateCommandPool(m_device->device(), &cpci, nullptr, &pool);
|
|
ASSERT_VK_SUCCESS(err);
|
|
VkCommandBufferAllocateInfo cbai = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, nullptr, pool,
|
|
VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1};
|
|
VkCommandBuffer cb;
|
|
err = vkAllocateCommandBuffers(m_device->device(), &cbai, &cb);
|
|
ASSERT_VK_SUCCESS(err);
|
|
VkCommandBufferBeginInfo cbbi = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr, 0, nullptr};
|
|
err = vkBeginCommandBuffer(cb, &cbbi);
|
|
ASSERT_VK_SUCCESS(err);
|
|
err = vkEndCommandBuffer(cb);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// A semaphore
|
|
VkSemaphoreCreateInfo sci = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0};
|
|
VkSemaphore s;
|
|
err = vkCreateSemaphore(m_device->device(), &sci, nullptr, &s);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// First submission, to q0
|
|
VkSubmitInfo s0 = {VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, 0, nullptr, nullptr, 1, &cb, 1, &s};
|
|
|
|
err = vkQueueSubmit(q0, 1, &s0, VK_NULL_HANDLE);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Second submission, to q1, waiting on s
|
|
VkFlags waitmask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; // doesn't really matter what this value is.
|
|
VkSubmitInfo s1 = {VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, 1, &s, &waitmask, 0, nullptr, 0, nullptr};
|
|
|
|
err = vkQueueSubmit(q1, 1, &s1, VK_NULL_HANDLE);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Wait for q0 idle
|
|
err = vkQueueWaitIdle(q0);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Command buffer should have been completed (it was on q0); reset the pool.
|
|
vkFreeCommandBuffers(m_device->device(), pool, 1, &cb);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
// Force device completely idle and clean up resources
|
|
vkDeviceWaitIdle(m_device->device());
|
|
vkDestroyCommandPool(m_device->device(), pool, nullptr);
|
|
vkDestroySemaphore(m_device->device(), s, nullptr);
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, TwoQueueSubmitsSeparateQueuesWithSemaphoreAndOneFence) {
|
|
TEST_DESCRIPTION(
|
|
"Two command buffers, each in a separate QueueSubmit call submitted on separate queues, the second having a fence, "
|
|
"followed by a WaitForFences call.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
if ((m_device->queue_props.empty()) || (m_device->queue_props[0].queueCount < 2)) {
|
|
printf("%s Queue family needs to have multiple queues to run this test.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkFence fence;
|
|
VkFenceCreateInfo fence_create_info{};
|
|
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
vkCreateFence(m_device->device(), &fence_create_info, nullptr, &fence);
|
|
|
|
VkSemaphore semaphore;
|
|
VkSemaphoreCreateInfo semaphore_create_info{};
|
|
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
vkCreateSemaphore(m_device->device(), &semaphore_create_info, nullptr, &semaphore);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer[2];
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 2;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, command_buffer);
|
|
|
|
VkQueue queue = VK_NULL_HANDLE;
|
|
vkGetDeviceQueue(m_device->device(), m_device->graphics_queue_node_index_, 1, &queue);
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[0], &begin_info);
|
|
|
|
vkCmdPipelineBarrier(command_buffer[0], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 0, nullptr);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[0], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[0]);
|
|
}
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[1], &begin_info);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[1], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[1]);
|
|
}
|
|
{
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[0];
|
|
submit_info.signalSemaphoreCount = 1;
|
|
submit_info.pSignalSemaphores = &semaphore;
|
|
vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
{
|
|
VkPipelineStageFlags flags[]{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[1];
|
|
submit_info.waitSemaphoreCount = 1;
|
|
submit_info.pWaitSemaphores = &semaphore;
|
|
submit_info.pWaitDstStageMask = flags;
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, fence);
|
|
}
|
|
|
|
vkWaitForFences(m_device->device(), 1, &fence, VK_TRUE, UINT64_MAX);
|
|
|
|
vkDestroyFence(m_device->device(), fence, nullptr);
|
|
vkDestroySemaphore(m_device->device(), semaphore, nullptr);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 2, &command_buffer[0]);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, TwoQueueSubmitsOneQueueWithSemaphoreAndOneFence) {
|
|
TEST_DESCRIPTION(
|
|
"Two command buffers, each in a separate QueueSubmit call on the same queue, sharing a signal/wait semaphore, the second "
|
|
"having a fence, followed by a WaitForFences call.");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
VkFence fence;
|
|
VkFenceCreateInfo fence_create_info{};
|
|
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
vkCreateFence(m_device->device(), &fence_create_info, nullptr, &fence);
|
|
|
|
VkSemaphore semaphore;
|
|
VkSemaphoreCreateInfo semaphore_create_info{};
|
|
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
vkCreateSemaphore(m_device->device(), &semaphore_create_info, nullptr, &semaphore);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer[2];
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 2;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, command_buffer);
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[0], &begin_info);
|
|
|
|
vkCmdPipelineBarrier(command_buffer[0], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 0, nullptr);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[0], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[0]);
|
|
}
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[1], &begin_info);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[1], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[1]);
|
|
}
|
|
{
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[0];
|
|
submit_info.signalSemaphoreCount = 1;
|
|
submit_info.pSignalSemaphores = &semaphore;
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
{
|
|
VkPipelineStageFlags flags[]{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[1];
|
|
submit_info.waitSemaphoreCount = 1;
|
|
submit_info.pWaitSemaphores = &semaphore;
|
|
submit_info.pWaitDstStageMask = flags;
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, fence);
|
|
}
|
|
|
|
vkWaitForFences(m_device->device(), 1, &fence, VK_TRUE, UINT64_MAX);
|
|
|
|
vkDestroyFence(m_device->device(), fence, nullptr);
|
|
vkDestroySemaphore(m_device->device(), semaphore, nullptr);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 2, &command_buffer[0]);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, TwoQueueSubmitsOneQueueNullQueueSubmitWithFence) {
|
|
TEST_DESCRIPTION(
|
|
"Two command buffers, each in a separate QueueSubmit call on the same queue, no fences, followed by a third QueueSubmit "
|
|
"with NO SubmitInfos but with a fence, followed by a WaitForFences call.");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
VkFence fence;
|
|
VkFenceCreateInfo fence_create_info{};
|
|
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
vkCreateFence(m_device->device(), &fence_create_info, nullptr, &fence);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer[2];
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 2;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, command_buffer);
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[0], &begin_info);
|
|
|
|
vkCmdPipelineBarrier(command_buffer[0], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 0, nullptr);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[0], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[0]);
|
|
}
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[1], &begin_info);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[1], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[1]);
|
|
}
|
|
{
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[0];
|
|
submit_info.signalSemaphoreCount = 0;
|
|
submit_info.pSignalSemaphores = VK_NULL_HANDLE;
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
{
|
|
VkPipelineStageFlags flags[]{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[1];
|
|
submit_info.waitSemaphoreCount = 0;
|
|
submit_info.pWaitSemaphores = VK_NULL_HANDLE;
|
|
submit_info.pWaitDstStageMask = flags;
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
|
|
vkQueueSubmit(m_device->m_queue, 0, NULL, fence);
|
|
|
|
VkResult err = vkWaitForFences(m_device->device(), 1, &fence, VK_TRUE, UINT64_MAX);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
vkDestroyFence(m_device->device(), fence, nullptr);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 2, &command_buffer[0]);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, TwoQueueSubmitsOneQueueOneFence) {
|
|
TEST_DESCRIPTION(
|
|
"Two command buffers, each in a separate QueueSubmit call on the same queue, the second having a fence, followed by a "
|
|
"WaitForFences call.");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
VkFence fence;
|
|
VkFenceCreateInfo fence_create_info{};
|
|
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
vkCreateFence(m_device->device(), &fence_create_info, nullptr, &fence);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer[2];
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 2;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, command_buffer);
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[0], &begin_info);
|
|
|
|
vkCmdPipelineBarrier(command_buffer[0], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 0, nullptr);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[0], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[0]);
|
|
}
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[1], &begin_info);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[1], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[1]);
|
|
}
|
|
{
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[0];
|
|
submit_info.signalSemaphoreCount = 0;
|
|
submit_info.pSignalSemaphores = VK_NULL_HANDLE;
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
}
|
|
{
|
|
VkPipelineStageFlags flags[]{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};
|
|
VkSubmitInfo submit_info{};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &command_buffer[1];
|
|
submit_info.waitSemaphoreCount = 0;
|
|
submit_info.pWaitSemaphores = VK_NULL_HANDLE;
|
|
submit_info.pWaitDstStageMask = flags;
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, fence);
|
|
}
|
|
|
|
vkWaitForFences(m_device->device(), 1, &fence, VK_TRUE, UINT64_MAX);
|
|
|
|
vkDestroyFence(m_device->device(), fence, nullptr);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 2, &command_buffer[0]);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// This is a positive test. No errors should be generated.
|
|
TEST_F(VkPositiveLayerTest, TwoSubmitInfosWithSemaphoreOneQueueSubmitsOneFence) {
|
|
TEST_DESCRIPTION(
|
|
"Two command buffers each in a separate SubmitInfo sent in a single QueueSubmit call followed by a WaitForFences call.");
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkFence fence;
|
|
VkFenceCreateInfo fence_create_info{};
|
|
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
|
vkCreateFence(m_device->device(), &fence_create_info, nullptr, &fence);
|
|
|
|
VkSemaphore semaphore;
|
|
VkSemaphoreCreateInfo semaphore_create_info{};
|
|
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
|
vkCreateSemaphore(m_device->device(), &semaphore_create_info, nullptr, &semaphore);
|
|
|
|
VkCommandPool command_pool;
|
|
VkCommandPoolCreateInfo pool_create_info{};
|
|
pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
|
pool_create_info.queueFamilyIndex = m_device->graphics_queue_node_index_;
|
|
pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
vkCreateCommandPool(m_device->device(), &pool_create_info, nullptr, &command_pool);
|
|
|
|
VkCommandBuffer command_buffer[2];
|
|
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
|
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
|
command_buffer_allocate_info.commandPool = command_pool;
|
|
command_buffer_allocate_info.commandBufferCount = 2;
|
|
command_buffer_allocate_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
vkAllocateCommandBuffers(m_device->device(), &command_buffer_allocate_info, command_buffer);
|
|
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[0], &begin_info);
|
|
|
|
vkCmdPipelineBarrier(command_buffer[0], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 0, nullptr);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[0], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[0]);
|
|
}
|
|
{
|
|
VkCommandBufferBeginInfo begin_info{};
|
|
begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
|
vkBeginCommandBuffer(command_buffer[1], &begin_info);
|
|
|
|
VkViewport viewport{};
|
|
viewport.maxDepth = 1.0f;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.width = 512;
|
|
viewport.height = 512;
|
|
viewport.x = 0;
|
|
viewport.y = 0;
|
|
vkCmdSetViewport(command_buffer[1], 0, 1, &viewport);
|
|
vkEndCommandBuffer(command_buffer[1]);
|
|
}
|
|
{
|
|
VkSubmitInfo submit_info[2];
|
|
VkPipelineStageFlags flags[]{VK_PIPELINE_STAGE_ALL_COMMANDS_BIT};
|
|
|
|
submit_info[0].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info[0].pNext = NULL;
|
|
submit_info[0].commandBufferCount = 1;
|
|
submit_info[0].pCommandBuffers = &command_buffer[0];
|
|
submit_info[0].signalSemaphoreCount = 1;
|
|
submit_info[0].pSignalSemaphores = &semaphore;
|
|
submit_info[0].waitSemaphoreCount = 0;
|
|
submit_info[0].pWaitSemaphores = NULL;
|
|
submit_info[0].pWaitDstStageMask = 0;
|
|
|
|
submit_info[1].sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info[1].pNext = NULL;
|
|
submit_info[1].commandBufferCount = 1;
|
|
submit_info[1].pCommandBuffers = &command_buffer[1];
|
|
submit_info[1].waitSemaphoreCount = 1;
|
|
submit_info[1].pWaitSemaphores = &semaphore;
|
|
submit_info[1].pWaitDstStageMask = flags;
|
|
submit_info[1].signalSemaphoreCount = 0;
|
|
submit_info[1].pSignalSemaphores = NULL;
|
|
vkQueueSubmit(m_device->m_queue, 2, &submit_info[0], fence);
|
|
}
|
|
|
|
vkWaitForFences(m_device->device(), 1, &fence, VK_TRUE, UINT64_MAX);
|
|
|
|
vkDestroyFence(m_device->device(), fence, nullptr);
|
|
vkFreeCommandBuffers(m_device->device(), command_pool, 2, &command_buffer[0]);
|
|
vkDestroyCommandPool(m_device->device(), command_pool, NULL);
|
|
vkDestroySemaphore(m_device->device(), semaphore, nullptr);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineAttribMatrixType) {
|
|
TEST_DESCRIPTION("Test that pipeline validation accepts matrices passed as vertex attributes");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkVertexInputBindingDescription input_binding;
|
|
memset(&input_binding, 0, sizeof(input_binding));
|
|
|
|
VkVertexInputAttributeDescription input_attribs[2];
|
|
memset(input_attribs, 0, sizeof(input_attribs));
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
input_attribs[i].format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
input_attribs[i].location = i;
|
|
}
|
|
|
|
char const *vsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(location=0) in mat2x4 x;\n"
|
|
"void main(){\n"
|
|
" gl_Position = x[0] + x[1];\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.vi_ci_.pVertexBindingDescriptions = &input_binding;
|
|
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
|
|
pipe.vi_ci_.pVertexAttributeDescriptions = input_attribs;
|
|
pipe.vi_ci_.vertexAttributeDescriptionCount = 2;
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
/* expect success */
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineAttribArrayType) {
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkVertexInputBindingDescription input_binding;
|
|
memset(&input_binding, 0, sizeof(input_binding));
|
|
|
|
VkVertexInputAttributeDescription input_attribs[2];
|
|
memset(input_attribs, 0, sizeof(input_attribs));
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
input_attribs[i].format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
input_attribs[i].location = i;
|
|
}
|
|
|
|
char const *vsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(location=0) in vec4 x[2];\n"
|
|
"void main(){\n"
|
|
" gl_Position = x[0] + x[1];\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.vi_ci_.pVertexBindingDescriptions = &input_binding;
|
|
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
|
|
pipe.vi_ci_.pVertexAttributeDescriptions = input_attribs;
|
|
pipe.vi_ci_.vertexAttributeDescriptionCount = 2;
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineAttribComponents) {
|
|
TEST_DESCRIPTION(
|
|
"Test that pipeline validation accepts consuming a vertex attribute through multiple vertex shader inputs, each consuming "
|
|
"a different subset of the components, and that fragment shader-attachment validation tolerates multiple duplicate "
|
|
"location outputs");
|
|
m_errorMonitor->ExpectSuccess(VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT);
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkVertexInputBindingDescription input_binding;
|
|
memset(&input_binding, 0, sizeof(input_binding));
|
|
|
|
VkVertexInputAttributeDescription input_attribs[3];
|
|
memset(input_attribs, 0, sizeof(input_attribs));
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
input_attribs[i].format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
input_attribs[i].location = i;
|
|
}
|
|
|
|
char const *vsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(location=0) in vec4 x;\n"
|
|
"layout(location=1) in vec3 y1;\n"
|
|
"layout(location=1, component=3) in float y2;\n"
|
|
"layout(location=2) in vec4 z;\n"
|
|
"void main(){\n"
|
|
" gl_Position = x + vec4(y1, y2) + z;\n"
|
|
"}\n";
|
|
char const *fsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(location=0, component=0) out float color0;\n"
|
|
"layout(location=0, component=1) out float color1;\n"
|
|
"layout(location=0, component=2) out float color2;\n"
|
|
"layout(location=0, component=3) out float color3;\n"
|
|
"layout(location=1, component=0) out vec2 second_color0;\n"
|
|
"layout(location=1, component=2) out vec2 second_color1;\n"
|
|
"void main(){\n"
|
|
" color0 = float(1);\n"
|
|
" second_color0 = vec2(1);\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
VkPipelineObj pipe(m_device);
|
|
|
|
VkDescriptorSetObj descriptorSet(m_device);
|
|
descriptorSet.AppendDummy();
|
|
descriptorSet.CreateVKDescriptorSet(m_commandBuffer);
|
|
|
|
// Create a renderPass with two color attachments
|
|
VkAttachmentReference attachments[2] = {};
|
|
attachments[0].layout = VK_IMAGE_LAYOUT_GENERAL;
|
|
attachments[1].attachment = 1;
|
|
attachments[1].layout = VK_IMAGE_LAYOUT_GENERAL;
|
|
|
|
VkSubpassDescription subpass = {};
|
|
subpass.pColorAttachments = attachments;
|
|
subpass.colorAttachmentCount = 2;
|
|
|
|
VkRenderPassCreateInfo rpci = {};
|
|
rpci.subpassCount = 1;
|
|
rpci.pSubpasses = &subpass;
|
|
rpci.attachmentCount = 2;
|
|
|
|
VkAttachmentDescription attach_desc[2] = {};
|
|
attach_desc[0].format = VK_FORMAT_B8G8R8A8_UNORM;
|
|
attach_desc[0].samples = VK_SAMPLE_COUNT_1_BIT;
|
|
attach_desc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
attach_desc[0].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
attach_desc[0].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attach_desc[1].format = VK_FORMAT_B8G8R8A8_UNORM;
|
|
attach_desc[1].samples = VK_SAMPLE_COUNT_1_BIT;
|
|
attach_desc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
attach_desc[1].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
|
|
attach_desc[1].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
|
|
rpci.pAttachments = attach_desc;
|
|
rpci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
|
|
VkRenderPass renderpass;
|
|
vkCreateRenderPass(m_device->device(), &rpci, NULL, &renderpass);
|
|
pipe.AddShader(&vs);
|
|
pipe.AddShader(&fs);
|
|
|
|
VkPipelineColorBlendAttachmentState att_state1 = {};
|
|
att_state1.dstAlphaBlendFactor = VK_BLEND_FACTOR_CONSTANT_COLOR;
|
|
att_state1.blendEnable = VK_FALSE;
|
|
|
|
pipe.AddColorAttachment(0, att_state1);
|
|
pipe.AddColorAttachment(1, att_state1);
|
|
pipe.AddVertexInputBindings(&input_binding, 1);
|
|
pipe.AddVertexInputAttribs(input_attribs, 3);
|
|
pipe.CreateVKPipeline(descriptorSet.GetPipelineLayout(), renderpass);
|
|
vkDestroyRenderPass(m_device->device(), renderpass, nullptr);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineSimplePositive) {
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineRelaxedTypeMatch) {
|
|
TEST_DESCRIPTION(
|
|
"Test that pipeline validation accepts the relaxed type matching rules set out in 14.1.3: fundamental type must match, and "
|
|
"producer side must have at least as many components");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
// VK 1.0.8 Specification, 14.1.3 "Additionally,..." block
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
char const *vsSource =
|
|
"#version 450\n"
|
|
"layout(location=0) out vec3 x;\n"
|
|
"layout(location=1) out ivec3 y;\n"
|
|
"layout(location=2) out vec3 z;\n"
|
|
"void main(){\n"
|
|
" gl_Position = vec4(0);\n"
|
|
" x = vec3(0); y = ivec3(0); z = vec3(0);\n"
|
|
"}\n";
|
|
char const *fsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(location=0) out vec4 color;\n"
|
|
"layout(location=0) in float x;\n"
|
|
"layout(location=1) flat in int y;\n"
|
|
"layout(location=2) in vec2 z;\n"
|
|
"void main(){\n"
|
|
" color = vec4(1 + x + y + z.x);\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineTessPerVertex) {
|
|
TEST_DESCRIPTION("Test that pipeline validation accepts per-vertex variables passed between the TCS and TES stages");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
if (!m_device->phy().features().tessellationShader) {
|
|
printf("%s Device does not support tessellation shaders; skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
char const *tcsSource =
|
|
"#version 450\n"
|
|
"layout(location=0) out int x[];\n"
|
|
"layout(vertices=3) out;\n"
|
|
"void main(){\n"
|
|
" gl_TessLevelOuter[0] = gl_TessLevelOuter[1] = gl_TessLevelOuter[2] = 1;\n"
|
|
" gl_TessLevelInner[0] = 1;\n"
|
|
" x[gl_InvocationID] = gl_InvocationID;\n"
|
|
"}\n";
|
|
char const *tesSource =
|
|
"#version 450\n"
|
|
"layout(triangles, equal_spacing, cw) in;\n"
|
|
"layout(location=0) in int x[];\n"
|
|
"void main(){\n"
|
|
" gl_Position.xyz = gl_TessCoord;\n"
|
|
" gl_Position.w = x[0] + x[1] + x[2];\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, bindStateMinimalShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj tcs(m_device, tcsSource, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, this);
|
|
VkShaderObj tes(m_device, tesSource, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo iasci{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, nullptr, 0,
|
|
VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, VK_FALSE};
|
|
|
|
VkPipelineTessellationStateCreateInfo tsci{VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO, nullptr, 0, 3};
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.gp_ci_.pTessellationState = &tsci;
|
|
pipe.gp_ci_.pInputAssemblyState = &iasci;
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), tcs.GetStageCreateInfo(), tes.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineGeometryInputBlockPositive) {
|
|
TEST_DESCRIPTION(
|
|
"Test that pipeline validation accepts a user-defined interface block passed into the geometry shader. This is interesting "
|
|
"because the 'extra' array level is not present on the member type, but on the block instance.");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
if (!m_device->phy().features().geometryShader) {
|
|
printf("%s Device does not support geometry shaders; skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
char const *gsSource =
|
|
"#version 450\n"
|
|
"layout(triangles) in;\n"
|
|
"layout(triangle_strip, max_vertices=3) out;\n"
|
|
"layout(location=0) in VertexData { vec4 x; } gs_in[];\n"
|
|
"void main() {\n"
|
|
" gl_Position = gs_in[0].x;\n"
|
|
" EmitVertex();\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj gs(m_device, gsSource, VK_SHADER_STAGE_GEOMETRY_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), gs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipeline64BitAttributesPositive) {
|
|
TEST_DESCRIPTION(
|
|
"Test that pipeline validation accepts basic use of 64bit vertex attributes. This is interesting because they consume "
|
|
"multiple locations.");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
if (!EnableDeviceProfileLayer()) {
|
|
printf("%s Failed to enable device profile layer.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
if (!m_device->phy().features().shaderFloat64) {
|
|
printf("%s Device does not support 64bit vertex attributes; skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
// Set 64bit format to support VTX Buffer feature
|
|
PFN_vkSetPhysicalDeviceFormatPropertiesEXT fpvkSetPhysicalDeviceFormatPropertiesEXT = nullptr;
|
|
PFN_vkGetOriginalPhysicalDeviceFormatPropertiesEXT fpvkGetOriginalPhysicalDeviceFormatPropertiesEXT = nullptr;
|
|
|
|
// Load required functions
|
|
if (!LoadDeviceProfileLayer(fpvkSetPhysicalDeviceFormatPropertiesEXT, fpvkGetOriginalPhysicalDeviceFormatPropertiesEXT)) {
|
|
return;
|
|
}
|
|
VkFormatProperties format_props;
|
|
fpvkGetOriginalPhysicalDeviceFormatPropertiesEXT(gpu(), VK_FORMAT_R64G64B64A64_SFLOAT, &format_props);
|
|
format_props.bufferFeatures |= VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT;
|
|
fpvkSetPhysicalDeviceFormatPropertiesEXT(gpu(), VK_FORMAT_R64G64B64A64_SFLOAT, format_props);
|
|
|
|
VkVertexInputBindingDescription input_bindings[1];
|
|
memset(input_bindings, 0, sizeof(input_bindings));
|
|
|
|
VkVertexInputAttributeDescription input_attribs[4];
|
|
memset(input_attribs, 0, sizeof(input_attribs));
|
|
input_attribs[0].location = 0;
|
|
input_attribs[0].offset = 0;
|
|
input_attribs[0].format = VK_FORMAT_R64G64B64A64_SFLOAT;
|
|
input_attribs[1].location = 2;
|
|
input_attribs[1].offset = 32;
|
|
input_attribs[1].format = VK_FORMAT_R64G64B64A64_SFLOAT;
|
|
input_attribs[2].location = 4;
|
|
input_attribs[2].offset = 64;
|
|
input_attribs[2].format = VK_FORMAT_R64G64B64A64_SFLOAT;
|
|
input_attribs[3].location = 6;
|
|
input_attribs[3].offset = 96;
|
|
input_attribs[3].format = VK_FORMAT_R64G64B64A64_SFLOAT;
|
|
|
|
char const *vsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(location=0) in dmat4 x;\n"
|
|
"void main(){\n"
|
|
" gl_Position = vec4(x[0][0]);\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.vi_ci_.pVertexBindingDescriptions = input_bindings;
|
|
pipe.vi_ci_.vertexBindingDescriptionCount = 1;
|
|
pipe.vi_ci_.pVertexAttributeDescriptions = input_attribs;
|
|
pipe.vi_ci_.vertexAttributeDescriptionCount = 4;
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineInputAttachmentPositive) {
|
|
TEST_DESCRIPTION("Positive test for a correctly matched input attachment");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
char const *fsSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(input_attachment_index=0, set=0, binding=0) uniform subpassInput x;\n"
|
|
"layout(location=0) out vec4 color;\n"
|
|
"void main() {\n"
|
|
" color = subpassLoad(x);\n"
|
|
"}\n";
|
|
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
VkPipelineObj pipe(m_device);
|
|
pipe.AddShader(&vs);
|
|
pipe.AddShader(&fs);
|
|
pipe.AddDefaultColorAttachment();
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkDescriptorSetLayoutBinding dslb = {0, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
|
|
const VkDescriptorSetLayoutObj dsl(m_device, {dslb});
|
|
const VkPipelineLayoutObj pl(m_device, {&dsl});
|
|
|
|
VkAttachmentDescription descs[2] = {
|
|
{0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
|
|
VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL},
|
|
{0, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE,
|
|
VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_GENERAL},
|
|
};
|
|
VkAttachmentReference color = {
|
|
0,
|
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
|
};
|
|
VkAttachmentReference input = {
|
|
1,
|
|
VK_IMAGE_LAYOUT_GENERAL,
|
|
};
|
|
|
|
VkSubpassDescription sd = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 1, &input, 1, &color, nullptr, nullptr, 0, nullptr};
|
|
|
|
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, 2, descs, 1, &sd, 0, nullptr};
|
|
VkRenderPass rp;
|
|
VkResult err = vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// should be OK. would go wrong here if it's going to...
|
|
pipe.CreateVKPipeline(pl.handle(), rp);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreateComputePipelineMissingDescriptorUnusedPositive) {
|
|
TEST_DESCRIPTION(
|
|
"Test that pipeline validation accepts a compute pipeline which declares a descriptor-backed resource which is not "
|
|
"provided, but the shader does not statically use it. This is interesting because it requires compute pipelines to have a "
|
|
"proper descriptor use walk, which they didn't for some time.");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
char const *csSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(local_size_x=1) in;\n"
|
|
"layout(set=0, binding=0) buffer block { vec4 x; };\n"
|
|
"void main(){\n"
|
|
" // x is not used.\n"
|
|
"}\n";
|
|
|
|
CreateComputePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.cs_.reset(new VkShaderObj(m_device, csSource, VK_SHADER_STAGE_COMPUTE_BIT, this));
|
|
pipe.InitState();
|
|
pipe.CreateComputePipeline();
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreateComputePipelineCombinedImageSamplerConsumedAsSampler) {
|
|
TEST_DESCRIPTION(
|
|
"Test that pipeline validation accepts a shader consuming only the sampler portion of a combined image + sampler");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
std::vector<VkDescriptorSetLayoutBinding> bindings = {
|
|
{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
|
|
{1, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
|
|
{2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
|
|
};
|
|
|
|
char const *csSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(local_size_x=1) in;\n"
|
|
"layout(set=0, binding=0) uniform sampler s;\n"
|
|
"layout(set=0, binding=1) uniform texture2D t;\n"
|
|
"layout(set=0, binding=2) buffer block { vec4 x; };\n"
|
|
"void main() {\n"
|
|
" x = texture(sampler2D(t, s), vec2(0));\n"
|
|
"}\n";
|
|
CreateComputePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.dsl_bindings_.resize(bindings.size());
|
|
memcpy(pipe.dsl_bindings_.data(), bindings.data(), bindings.size() * sizeof(VkDescriptorSetLayoutBinding));
|
|
pipe.cs_.reset(new VkShaderObj(m_device, csSource, VK_SHADER_STAGE_COMPUTE_BIT, this));
|
|
pipe.InitState();
|
|
m_errorMonitor->ExpectSuccess();
|
|
pipe.CreateComputePipeline();
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreateComputePipelineCombinedImageSamplerConsumedAsImage) {
|
|
TEST_DESCRIPTION(
|
|
"Test that pipeline validation accepts a shader consuming only the image portion of a combined image + sampler");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
std::vector<VkDescriptorSetLayoutBinding> bindings = {
|
|
{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
|
|
{1, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
|
|
{2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
|
|
};
|
|
|
|
char const *csSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(local_size_x=1) in;\n"
|
|
"layout(set=0, binding=0) uniform texture2D t;\n"
|
|
"layout(set=0, binding=1) uniform sampler s;\n"
|
|
"layout(set=0, binding=2) buffer block { vec4 x; };\n"
|
|
"void main() {\n"
|
|
" x = texture(sampler2D(t, s), vec2(0));\n"
|
|
"}\n";
|
|
CreateComputePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.dsl_bindings_.resize(bindings.size());
|
|
memcpy(pipe.dsl_bindings_.data(), bindings.data(), bindings.size() * sizeof(VkDescriptorSetLayoutBinding));
|
|
pipe.cs_.reset(new VkShaderObj(m_device, csSource, VK_SHADER_STAGE_COMPUTE_BIT, this));
|
|
pipe.InitState();
|
|
m_errorMonitor->ExpectSuccess();
|
|
pipe.CreateComputePipeline();
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreateComputePipelineCombinedImageSamplerConsumedAsBoth) {
|
|
TEST_DESCRIPTION(
|
|
"Test that pipeline validation accepts a shader consuming both the sampler and the image of a combined image+sampler but "
|
|
"via separate variables");
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
std::vector<VkDescriptorSetLayoutBinding> bindings = {
|
|
{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
|
|
{1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr},
|
|
};
|
|
|
|
char const *csSource =
|
|
"#version 450\n"
|
|
"\n"
|
|
"layout(local_size_x=1) in;\n"
|
|
"layout(set=0, binding=0) uniform texture2D t;\n"
|
|
"layout(set=0, binding=0) uniform sampler s; // both binding 0!\n"
|
|
"layout(set=0, binding=1) buffer block { vec4 x; };\n"
|
|
"void main() {\n"
|
|
" x = texture(sampler2D(t, s), vec2(0));\n"
|
|
"}\n";
|
|
CreateComputePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.dsl_bindings_.resize(bindings.size());
|
|
memcpy(pipe.dsl_bindings_.data(), bindings.data(), bindings.size() * sizeof(VkDescriptorSetLayoutBinding));
|
|
pipe.cs_.reset(new VkShaderObj(m_device, csSource, VK_SHADER_STAGE_COMPUTE_BIT, this));
|
|
pipe.InitState();
|
|
m_errorMonitor->ExpectSuccess();
|
|
pipe.CreateComputePipeline();
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreateDescriptorSetBindingWithIgnoredSamplers) {
|
|
TEST_DESCRIPTION("Test that layers conditionally do ignore the pImmutableSamplers on vkCreateDescriptorSetLayout");
|
|
|
|
bool prop2_found = false;
|
|
if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
prop2_found = true;
|
|
} else {
|
|
printf("%s %s Extension not supported, skipping push descriptor sub-tests\n", kSkipPrefix,
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
bool push_descriptor_found = false;
|
|
if (prop2_found && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
|
|
// In addition to the extension being supported we need to have at least one available
|
|
// Some implementations report an invalid maxPushDescriptors of 0
|
|
push_descriptor_found = GetPushDescriptorProperties(instance(), gpu()).maxPushDescriptors > 0;
|
|
} else {
|
|
printf("%s %s Extension not supported, skipping push descriptor sub-tests\n", kSkipPrefix,
|
|
VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME);
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
const uint64_t fake_address_64 = 0xCDCDCDCDCDCDCDCD;
|
|
const uint64_t fake_address_32 = 0xCDCDCDCD;
|
|
const void *fake_pointer =
|
|
sizeof(void *) == 8 ? reinterpret_cast<void *>(fake_address_64) : reinterpret_cast<void *>(fake_address_32);
|
|
const VkSampler *hopefully_undereferencable_pointer = reinterpret_cast<const VkSampler *>(fake_pointer);
|
|
|
|
// regular descriptors
|
|
m_errorMonitor->ExpectSuccess();
|
|
{
|
|
const VkDescriptorSetLayoutBinding non_sampler_bindings[] = {
|
|
{0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{2, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{3, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{4, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{6, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{8, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
};
|
|
const VkDescriptorSetLayoutCreateInfo dslci = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
|
static_cast<uint32_t>(size(non_sampler_bindings)), non_sampler_bindings};
|
|
VkDescriptorSetLayout dsl;
|
|
const VkResult err = vkCreateDescriptorSetLayout(m_device->device(), &dslci, nullptr, &dsl);
|
|
ASSERT_VK_SUCCESS(err);
|
|
vkDestroyDescriptorSetLayout(m_device->device(), dsl, nullptr);
|
|
}
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
if (push_descriptor_found) {
|
|
// push descriptors
|
|
m_errorMonitor->ExpectSuccess();
|
|
{
|
|
const VkDescriptorSetLayoutBinding non_sampler_bindings[] = {
|
|
{0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{1, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{2, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{3, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{4, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
{6, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, VK_SHADER_STAGE_FRAGMENT_BIT, hopefully_undereferencable_pointer},
|
|
};
|
|
const VkDescriptorSetLayoutCreateInfo dslci = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr,
|
|
VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR,
|
|
static_cast<uint32_t>(size(non_sampler_bindings)), non_sampler_bindings};
|
|
VkDescriptorSetLayout dsl;
|
|
const VkResult err = vkCreateDescriptorSetLayout(m_device->device(), &dslci, nullptr, &dsl);
|
|
ASSERT_VK_SUCCESS(err);
|
|
vkDestroyDescriptorSetLayout(m_device->device(), dsl, nullptr);
|
|
}
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
}
|
|
TEST_F(VkPositiveLayerTest, GpuValidationInlineUniformBlock) {
|
|
TEST_DESCRIPTION("GPU validation: Make sure inline uniform blocks don't generate false validation errors");
|
|
m_errorMonitor->ExpectSuccess();
|
|
VkValidationFeatureEnableEXT enables[] = {VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT};
|
|
VkValidationFeaturesEXT features = {};
|
|
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
|
|
features.enabledValidationFeatureCount = 1;
|
|
features.pEnabledValidationFeatures = enables;
|
|
bool descriptor_indexing = CheckDescriptorIndexingSupportAndInitFramework(this, m_instance_extension_names,
|
|
m_device_extension_names, &features, m_errorMonitor);
|
|
if (DeviceIsMockICD() || DeviceSimulation()) {
|
|
printf("%s Test not supported by MockICD, skipping tests\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
VkPhysicalDeviceFeatures2KHR features2 = {};
|
|
auto indexing_features = lvl_init_struct<VkPhysicalDeviceDescriptorIndexingFeaturesEXT>();
|
|
auto inline_uniform_block_features = lvl_init_struct<VkPhysicalDeviceInlineUniformBlockFeaturesEXT>(&indexing_features);
|
|
bool inline_uniform_block = DeviceExtensionSupported(gpu(), nullptr, VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME);
|
|
if (!(descriptor_indexing && inline_uniform_block)) {
|
|
printf("Descriptor indexing and/or inline uniform block not supported Skipping test\n");
|
|
return;
|
|
}
|
|
m_device_extension_names.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
|
m_device_extension_names.push_back(VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME);
|
|
PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR =
|
|
(PFN_vkGetPhysicalDeviceFeatures2KHR)vkGetInstanceProcAddr(instance(), "vkGetPhysicalDeviceFeatures2KHR");
|
|
ASSERT_TRUE(vkGetPhysicalDeviceFeatures2KHR != nullptr);
|
|
|
|
features2 = lvl_init_struct<VkPhysicalDeviceFeatures2KHR>(&inline_uniform_block_features);
|
|
vkGetPhysicalDeviceFeatures2KHR(gpu(), &features2);
|
|
if (!indexing_features.descriptorBindingPartiallyBound || !inline_uniform_block_features.inlineUniformBlock) {
|
|
printf("Not all features supported, skipping test\n");
|
|
return;
|
|
}
|
|
auto inline_uniform_props = lvl_init_struct<VkPhysicalDeviceInlineUniformBlockPropertiesEXT>();
|
|
auto prop2 = lvl_init_struct<VkPhysicalDeviceProperties2KHR>(&inline_uniform_props);
|
|
vkGetPhysicalDeviceProperties2(gpu(), &prop2);
|
|
|
|
VkCommandPoolCreateFlags pool_flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &features2, pool_flags));
|
|
if (m_device->props.apiVersion < VK_API_VERSION_1_1) {
|
|
printf("%s GPU-Assisted validation test requires Vulkan 1.1+.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
auto c_queue = m_device->GetDefaultComputeQueue();
|
|
if (nullptr == c_queue) {
|
|
printf("Compute not supported, skipping test\n");
|
|
return;
|
|
}
|
|
|
|
uint32_t qfi = 0;
|
|
VkBufferCreateInfo bci = {};
|
|
bci.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
bci.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
bci.size = 4;
|
|
bci.queueFamilyIndexCount = 1;
|
|
bci.pQueueFamilyIndices = &qfi;
|
|
VkBufferObj buffer0;
|
|
VkMemoryPropertyFlags mem_props = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
|
buffer0.init(*m_device, bci, mem_props);
|
|
|
|
VkDescriptorBindingFlagsEXT ds_binding_flags[2] = {};
|
|
ds_binding_flags[1] = VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT;
|
|
VkDescriptorSetLayoutBindingFlagsCreateInfoEXT layout_createinfo_binding_flags[1] = {};
|
|
layout_createinfo_binding_flags[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT;
|
|
layout_createinfo_binding_flags[0].pNext = NULL;
|
|
layout_createinfo_binding_flags[0].bindingCount = 2;
|
|
layout_createinfo_binding_flags[0].pBindingFlags = ds_binding_flags;
|
|
|
|
OneOffDescriptorSet descriptor_set(m_device,
|
|
{
|
|
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
{1, VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, 20, VK_SHADER_STAGE_ALL,
|
|
nullptr}, // 16 bytes for ivec4, 4 more for int
|
|
},
|
|
0, layout_createinfo_binding_flags, 0);
|
|
const VkPipelineLayoutObj pipeline_layout(m_device, {&descriptor_set.layout_});
|
|
|
|
VkDescriptorBufferInfo buffer_info[1] = {};
|
|
buffer_info[0].buffer = buffer0.handle();
|
|
buffer_info[0].offset = 0;
|
|
buffer_info[0].range = sizeof(uint32_t);
|
|
|
|
const uint32_t test_data = 0xdeadca7;
|
|
VkWriteDescriptorSetInlineUniformBlockEXT write_inline_uniform = {};
|
|
write_inline_uniform.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT;
|
|
write_inline_uniform.dataSize = 4;
|
|
write_inline_uniform.pData = &test_data;
|
|
|
|
VkWriteDescriptorSet descriptor_writes[2] = {};
|
|
descriptor_writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_writes[0].dstSet = descriptor_set.set_;
|
|
descriptor_writes[0].dstBinding = 0;
|
|
descriptor_writes[0].descriptorCount = 1;
|
|
descriptor_writes[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
|
descriptor_writes[0].pBufferInfo = buffer_info;
|
|
|
|
descriptor_writes[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
descriptor_writes[1].dstSet = descriptor_set.set_;
|
|
descriptor_writes[1].dstBinding = 1;
|
|
descriptor_writes[1].dstArrayElement = 16; // Skip first 16 bytes (dummy)
|
|
descriptor_writes[1].descriptorCount = 4; // Write 4 bytes to val
|
|
descriptor_writes[1].descriptorType = VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT;
|
|
descriptor_writes[1].pNext = &write_inline_uniform;
|
|
vkUpdateDescriptorSets(m_device->device(), 2, descriptor_writes, 0, NULL);
|
|
|
|
char const *csSource =
|
|
"#version 450\n"
|
|
"#extension GL_EXT_nonuniform_qualifier : enable\n "
|
|
"layout(set = 0, binding = 0) buffer StorageBuffer { uint index; } u_index;"
|
|
"layout(set = 0, binding = 1) uniform inlineubodef { ivec4 dummy; int val; } inlineubo;\n"
|
|
|
|
"void main() {\n"
|
|
" u_index.index = inlineubo.val;\n"
|
|
"}\n";
|
|
|
|
auto shader_module = new VkShaderObj(m_device, csSource, VK_SHADER_STAGE_COMPUTE_BIT, this);
|
|
|
|
VkPipelineShaderStageCreateInfo stage;
|
|
stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
stage.pNext = nullptr;
|
|
stage.flags = 0;
|
|
stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
|
|
stage.module = shader_module->handle();
|
|
stage.pName = "main";
|
|
stage.pSpecializationInfo = nullptr;
|
|
|
|
// CreateComputePipelines
|
|
VkComputePipelineCreateInfo pipeline_info = {};
|
|
pipeline_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
|
pipeline_info.pNext = nullptr;
|
|
pipeline_info.flags = 0;
|
|
pipeline_info.layout = pipeline_layout.handle();
|
|
pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
|
|
pipeline_info.basePipelineIndex = -1;
|
|
pipeline_info.stage = stage;
|
|
|
|
VkPipeline c_pipeline;
|
|
vkCreateComputePipelines(device(), VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &c_pipeline);
|
|
|
|
m_commandBuffer->begin();
|
|
vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_COMPUTE, c_pipeline);
|
|
vkCmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout.handle(), 0, 1,
|
|
&descriptor_set.set_, 0, nullptr);
|
|
vkCmdDispatch(m_commandBuffer->handle(), 1, 1, 1);
|
|
m_commandBuffer->end();
|
|
|
|
VkSubmitInfo submit_info = {};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &m_commandBuffer->handle();
|
|
vkQueueSubmit(c_queue->handle(), 1, &submit_info, VK_NULL_HANDLE);
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkDestroyPipeline(m_device->handle(), c_pipeline, NULL);
|
|
vkDestroyShaderModule(m_device->handle(), shader_module->handle(), NULL);
|
|
|
|
uint32_t *data = (uint32_t *)buffer0.memory().map();
|
|
ASSERT_TRUE(*data = test_data);
|
|
buffer0.memory().unmap();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, Maintenance1Tests) {
|
|
TEST_DESCRIPTION("Validate various special cases for the Maintenance1_KHR extension");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_KHR_MAINTENANCE1_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s Maintenance1 Extension not supported, skipping tests\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkCommandBufferObj cmd_buf(m_device, m_commandPool);
|
|
cmd_buf.begin();
|
|
// Set Negative height, should give error if Maintenance 1 is not enabled
|
|
VkViewport viewport = {0, 0, 16, -16, 0, 1};
|
|
vkCmdSetViewport(cmd_buf.handle(), 0, 1, &viewport);
|
|
cmd_buf.end();
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ValidStructPNext) {
|
|
TEST_DESCRIPTION("Verify that a valid pNext value is handled correctly");
|
|
|
|
// Positive test to check parameter_validation and unique_objects support for NV_dedicated_allocation
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME Extension not supported, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkDedicatedAllocationBufferCreateInfoNV dedicated_buffer_create_info = {};
|
|
dedicated_buffer_create_info.sType = VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV;
|
|
dedicated_buffer_create_info.pNext = nullptr;
|
|
dedicated_buffer_create_info.dedicatedAllocation = VK_TRUE;
|
|
|
|
uint32_t queue_family_index = 0;
|
|
VkBufferCreateInfo buffer_create_info = {};
|
|
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
buffer_create_info.pNext = &dedicated_buffer_create_info;
|
|
buffer_create_info.size = 1024;
|
|
buffer_create_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
|
|
buffer_create_info.queueFamilyIndexCount = 1;
|
|
buffer_create_info.pQueueFamilyIndices = &queue_family_index;
|
|
|
|
VkBuffer buffer;
|
|
VkResult err = vkCreateBuffer(m_device->device(), &buffer_create_info, NULL, &buffer);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkMemoryRequirements memory_reqs;
|
|
vkGetBufferMemoryRequirements(m_device->device(), buffer, &memory_reqs);
|
|
|
|
VkDedicatedAllocationMemoryAllocateInfoNV dedicated_memory_info = {};
|
|
dedicated_memory_info.sType = VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV;
|
|
dedicated_memory_info.pNext = nullptr;
|
|
dedicated_memory_info.buffer = buffer;
|
|
dedicated_memory_info.image = VK_NULL_HANDLE;
|
|
|
|
VkMemoryAllocateInfo memory_info = {};
|
|
memory_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
memory_info.pNext = &dedicated_memory_info;
|
|
memory_info.allocationSize = memory_reqs.size;
|
|
|
|
bool pass;
|
|
pass = m_device->phy().set_memory_type(memory_reqs.memoryTypeBits, &memory_info, 0);
|
|
ASSERT_TRUE(pass);
|
|
|
|
VkDeviceMemory buffer_memory;
|
|
err = vkAllocateMemory(m_device->device(), &memory_info, NULL, &buffer_memory);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
err = vkBindBufferMemory(m_device->device(), buffer, buffer_memory, 0);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
vkDestroyBuffer(m_device->device(), buffer, NULL);
|
|
vkFreeMemory(m_device->device(), buffer_memory, NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, PSOPolygonModeValid) {
|
|
TEST_DESCRIPTION("Verify that using a solid polygon fill mode works correctly.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
std::vector<const char *> device_extension_names;
|
|
auto features = m_device->phy().features();
|
|
// Artificially disable support for non-solid fill modes
|
|
features.fillModeNonSolid = false;
|
|
// The sacrificial device object
|
|
VkDeviceObj test_device(0, gpu(), device_extension_names, &features);
|
|
|
|
VkRenderpassObj render_pass(&test_device);
|
|
|
|
const VkPipelineLayoutObj pipeline_layout(&test_device);
|
|
|
|
VkPipelineRasterizationStateCreateInfo rs_ci = {};
|
|
rs_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
|
rs_ci.pNext = nullptr;
|
|
rs_ci.lineWidth = 1.0f;
|
|
rs_ci.rasterizerDiscardEnable = false;
|
|
|
|
VkShaderObj vs(&test_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(&test_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
// Set polygonMode=FILL. No error is expected
|
|
m_errorMonitor->ExpectSuccess();
|
|
{
|
|
VkPipelineObj pipe(&test_device);
|
|
pipe.AddShader(&vs);
|
|
pipe.AddShader(&fs);
|
|
pipe.AddDefaultColorAttachment();
|
|
// Set polygonMode to a good value
|
|
rs_ci.polygonMode = VK_POLYGON_MODE_FILL;
|
|
pipe.SetRasterization(&rs_ci);
|
|
pipe.CreateVKPipeline(pipeline_layout.handle(), render_pass.handle());
|
|
}
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, LongSemaphoreChain) {
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
VkResult err;
|
|
|
|
std::vector<VkSemaphore> semaphores;
|
|
|
|
const int chainLength = 32768;
|
|
VkPipelineStageFlags flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
|
|
|
for (int i = 0; i < chainLength; i++) {
|
|
VkSemaphoreCreateInfo sci = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0};
|
|
VkSemaphore semaphore;
|
|
err = vkCreateSemaphore(m_device->device(), &sci, nullptr, &semaphore);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
semaphores.push_back(semaphore);
|
|
|
|
VkSubmitInfo si = {VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
nullptr,
|
|
semaphores.size() > 1 ? 1u : 0u,
|
|
semaphores.size() > 1 ? &semaphores[semaphores.size() - 2] : nullptr,
|
|
&flags,
|
|
0,
|
|
nullptr,
|
|
1,
|
|
&semaphores[semaphores.size() - 1]};
|
|
err = vkQueueSubmit(m_device->m_queue, 1, &si, VK_NULL_HANDLE);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
|
|
VkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, 0};
|
|
VkFence fence;
|
|
err = vkCreateFence(m_device->device(), &fci, nullptr, &fence);
|
|
ASSERT_VK_SUCCESS(err);
|
|
VkSubmitInfo si = {VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, 1, &semaphores.back(), &flags, 0, nullptr, 0, nullptr};
|
|
err = vkQueueSubmit(m_device->m_queue, 1, &si, fence);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
vkWaitForFences(m_device->device(), 1, &fence, VK_TRUE, UINT64_MAX);
|
|
|
|
for (auto semaphore : semaphores) vkDestroySemaphore(m_device->device(), semaphore, nullptr);
|
|
|
|
vkDestroyFence(m_device->device(), fence, nullptr);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ExternalSemaphore) {
|
|
#ifdef _WIN32
|
|
const auto extension_name = VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME;
|
|
const auto handle_type = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR;
|
|
#else
|
|
const auto extension_name = VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME;
|
|
const auto handle_type = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
|
|
#endif
|
|
// Check for external semaphore instance extensions
|
|
if (InstanceExtensionSupported(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME)) {
|
|
m_instance_extension_names.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s External semaphore extension not supported, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
// Check for external semaphore device extensions
|
|
if (DeviceExtensionSupported(gpu(), nullptr, extension_name)) {
|
|
m_device_extension_names.push_back(extension_name);
|
|
m_device_extension_names.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s External semaphore extension not supported, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
// Check for external semaphore import and export capability
|
|
VkPhysicalDeviceExternalSemaphoreInfoKHR esi = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR, nullptr,
|
|
handle_type};
|
|
VkExternalSemaphorePropertiesKHR esp = {VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR, nullptr};
|
|
auto vkGetPhysicalDeviceExternalSemaphorePropertiesKHR =
|
|
(PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR)vkGetInstanceProcAddr(
|
|
instance(), "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR");
|
|
vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(gpu(), &esi, &esp);
|
|
|
|
if (!(esp.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR) ||
|
|
!(esp.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR)) {
|
|
printf("%s External semaphore does not support importing and exporting, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
VkResult err;
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
// Create a semaphore to export payload from
|
|
VkExportSemaphoreCreateInfoKHR esci = {VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR, nullptr, handle_type};
|
|
VkSemaphoreCreateInfo sci = {VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &esci, 0};
|
|
|
|
VkSemaphore export_semaphore;
|
|
err = vkCreateSemaphore(m_device->device(), &sci, nullptr, &export_semaphore);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Create a semaphore to import payload into
|
|
sci.pNext = nullptr;
|
|
VkSemaphore import_semaphore;
|
|
err = vkCreateSemaphore(m_device->device(), &sci, nullptr, &import_semaphore);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
#ifdef _WIN32
|
|
// Export semaphore payload to an opaque handle
|
|
HANDLE handle = nullptr;
|
|
VkSemaphoreGetWin32HandleInfoKHR ghi = {VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR, nullptr, export_semaphore,
|
|
handle_type};
|
|
auto vkGetSemaphoreWin32HandleKHR =
|
|
(PFN_vkGetSemaphoreWin32HandleKHR)vkGetDeviceProcAddr(m_device->device(), "vkGetSemaphoreWin32HandleKHR");
|
|
err = vkGetSemaphoreWin32HandleKHR(m_device->device(), &ghi, &handle);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Import opaque handle exported above
|
|
VkImportSemaphoreWin32HandleInfoKHR ihi = {
|
|
VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR, nullptr, import_semaphore, 0, handle_type, handle, nullptr};
|
|
auto vkImportSemaphoreWin32HandleKHR =
|
|
(PFN_vkImportSemaphoreWin32HandleKHR)vkGetDeviceProcAddr(m_device->device(), "vkImportSemaphoreWin32HandleKHR");
|
|
err = vkImportSemaphoreWin32HandleKHR(m_device->device(), &ihi);
|
|
ASSERT_VK_SUCCESS(err);
|
|
#else
|
|
// Export semaphore payload to an opaque handle
|
|
int fd = 0;
|
|
VkSemaphoreGetFdInfoKHR ghi = {VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, nullptr, export_semaphore, handle_type};
|
|
auto vkGetSemaphoreFdKHR = (PFN_vkGetSemaphoreFdKHR)vkGetDeviceProcAddr(m_device->device(), "vkGetSemaphoreFdKHR");
|
|
err = vkGetSemaphoreFdKHR(m_device->device(), &ghi, &fd);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// Import opaque handle exported above
|
|
VkImportSemaphoreFdInfoKHR ihi = {
|
|
VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, nullptr, import_semaphore, 0, handle_type, fd};
|
|
auto vkImportSemaphoreFdKHR = (PFN_vkImportSemaphoreFdKHR)vkGetDeviceProcAddr(m_device->device(), "vkImportSemaphoreFdKHR");
|
|
err = vkImportSemaphoreFdKHR(m_device->device(), &ihi);
|
|
ASSERT_VK_SUCCESS(err);
|
|
#endif
|
|
|
|
// Signal the exported semaphore and wait on the imported semaphore
|
|
VkPipelineStageFlags flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
|
VkSubmitInfo si[] = {
|
|
{VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, 0, nullptr, &flags, 0, nullptr, 1, &export_semaphore},
|
|
{VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, 1, &import_semaphore, &flags, 0, nullptr, 0, nullptr},
|
|
{VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, 0, nullptr, &flags, 0, nullptr, 1, &export_semaphore},
|
|
{VK_STRUCTURE_TYPE_SUBMIT_INFO, nullptr, 1, &import_semaphore, &flags, 0, nullptr, 0, nullptr},
|
|
};
|
|
err = vkQueueSubmit(m_device->m_queue, 4, si, VK_NULL_HANDLE);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
if (m_device->phy().features().sparseBinding) {
|
|
// Signal the imported semaphore and wait on the exported semaphore
|
|
VkBindSparseInfo bi[] = {
|
|
{VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 1, &import_semaphore},
|
|
{VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, nullptr, 1, &export_semaphore, 0, nullptr, 0, nullptr, 0, nullptr, 0, nullptr},
|
|
{VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 0, nullptr, 1, &import_semaphore},
|
|
{VK_STRUCTURE_TYPE_BIND_SPARSE_INFO, nullptr, 1, &export_semaphore, 0, nullptr, 0, nullptr, 0, nullptr, 0, nullptr},
|
|
};
|
|
err = vkQueueBindSparse(m_device->m_queue, 4, bi, VK_NULL_HANDLE);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
|
|
// Cleanup
|
|
err = vkQueueWaitIdle(m_device->m_queue);
|
|
ASSERT_VK_SUCCESS(err);
|
|
vkDestroySemaphore(m_device->device(), export_semaphore, nullptr);
|
|
vkDestroySemaphore(m_device->device(), import_semaphore, nullptr);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ExternalFence) {
|
|
#ifdef _WIN32
|
|
const auto extension_name = VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME;
|
|
const auto handle_type = VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
|
|
#else
|
|
const auto extension_name = VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME;
|
|
const auto handle_type = VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
|
|
#endif
|
|
// Check for external fence instance extensions
|
|
if (InstanceExtensionSupported(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME)) {
|
|
m_instance_extension_names.push_back(VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME);
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s External fence extension not supported, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
// Check for external fence device extensions
|
|
if (DeviceExtensionSupported(gpu(), nullptr, extension_name)) {
|
|
m_device_extension_names.push_back(extension_name);
|
|
m_device_extension_names.push_back(VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s External fence extension not supported, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
// Check for external fence import and export capability
|
|
VkPhysicalDeviceExternalFenceInfoKHR efi = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO_KHR, nullptr, handle_type};
|
|
VkExternalFencePropertiesKHR efp = {VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES_KHR, nullptr};
|
|
auto vkGetPhysicalDeviceExternalFencePropertiesKHR = (PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR)vkGetInstanceProcAddr(
|
|
instance(), "vkGetPhysicalDeviceExternalFencePropertiesKHR");
|
|
vkGetPhysicalDeviceExternalFencePropertiesKHR(gpu(), &efi, &efp);
|
|
|
|
if (!(efp.externalFenceFeatures & VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT_KHR) ||
|
|
!(efp.externalFenceFeatures & VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT_KHR)) {
|
|
printf("%s External fence does not support importing and exporting, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
VkResult err;
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
// Create a fence to export payload from
|
|
VkFence export_fence;
|
|
{
|
|
VkExportFenceCreateInfoKHR efci = {VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO_KHR, nullptr, handle_type};
|
|
VkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, &efci, 0};
|
|
err = vkCreateFence(m_device->device(), &fci, nullptr, &export_fence);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
|
|
// Create a fence to import payload into
|
|
VkFence import_fence;
|
|
{
|
|
VkFenceCreateInfo fci = {VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, 0};
|
|
err = vkCreateFence(m_device->device(), &fci, nullptr, &import_fence);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
// Export fence payload to an opaque handle
|
|
HANDLE handle = nullptr;
|
|
{
|
|
VkFenceGetWin32HandleInfoKHR ghi = {VK_STRUCTURE_TYPE_FENCE_GET_WIN32_HANDLE_INFO_KHR, nullptr, export_fence, handle_type};
|
|
auto vkGetFenceWin32HandleKHR =
|
|
(PFN_vkGetFenceWin32HandleKHR)vkGetDeviceProcAddr(m_device->device(), "vkGetFenceWin32HandleKHR");
|
|
err = vkGetFenceWin32HandleKHR(m_device->device(), &ghi, &handle);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
|
|
// Import opaque handle exported above
|
|
{
|
|
VkImportFenceWin32HandleInfoKHR ifi = {
|
|
VK_STRUCTURE_TYPE_IMPORT_FENCE_WIN32_HANDLE_INFO_KHR, nullptr, import_fence, 0, handle_type, handle, nullptr};
|
|
auto vkImportFenceWin32HandleKHR =
|
|
(PFN_vkImportFenceWin32HandleKHR)vkGetDeviceProcAddr(m_device->device(), "vkImportFenceWin32HandleKHR");
|
|
err = vkImportFenceWin32HandleKHR(m_device->device(), &ifi);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
#else
|
|
// Export fence payload to an opaque handle
|
|
int fd = 0;
|
|
{
|
|
VkFenceGetFdInfoKHR gfi = {VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR, nullptr, export_fence, handle_type};
|
|
auto vkGetFenceFdKHR = (PFN_vkGetFenceFdKHR)vkGetDeviceProcAddr(m_device->device(), "vkGetFenceFdKHR");
|
|
err = vkGetFenceFdKHR(m_device->device(), &gfi, &fd);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
|
|
// Import opaque handle exported above
|
|
{
|
|
VkImportFenceFdInfoKHR ifi = {VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR, nullptr, import_fence, 0, handle_type, fd};
|
|
auto vkImportFenceFdKHR = (PFN_vkImportFenceFdKHR)vkGetDeviceProcAddr(m_device->device(), "vkImportFenceFdKHR");
|
|
err = vkImportFenceFdKHR(m_device->device(), &ifi);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
#endif
|
|
|
|
// Signal the exported fence and wait on the imported fence
|
|
vkQueueSubmit(m_device->m_queue, 0, nullptr, export_fence);
|
|
vkWaitForFences(m_device->device(), 1, &import_fence, VK_TRUE, 1000000000);
|
|
vkResetFences(m_device->device(), 1, &import_fence);
|
|
vkQueueSubmit(m_device->m_queue, 0, nullptr, export_fence);
|
|
vkWaitForFences(m_device->device(), 1, &import_fence, VK_TRUE, 1000000000);
|
|
vkResetFences(m_device->device(), 1, &import_fence);
|
|
|
|
// Signal the imported fence and wait on the exported fence
|
|
vkQueueSubmit(m_device->m_queue, 0, nullptr, import_fence);
|
|
vkWaitForFences(m_device->device(), 1, &export_fence, VK_TRUE, 1000000000);
|
|
vkResetFences(m_device->device(), 1, &export_fence);
|
|
vkQueueSubmit(m_device->m_queue, 0, nullptr, import_fence);
|
|
vkWaitForFences(m_device->device(), 1, &export_fence, VK_TRUE, 1000000000);
|
|
vkResetFences(m_device->device(), 1, &export_fence);
|
|
|
|
// Cleanup
|
|
err = vkQueueWaitIdle(m_device->m_queue);
|
|
ASSERT_VK_SUCCESS(err);
|
|
vkDestroyFence(m_device->device(), export_fence, nullptr);
|
|
vkDestroyFence(m_device->device(), import_fence, nullptr);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ThreadNullFenceCollision) {
|
|
test_platform_thread thread;
|
|
|
|
m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "THREADING ERROR");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
struct thread_data_struct data;
|
|
data.device = m_device->device();
|
|
data.bailout = false;
|
|
m_errorMonitor->SetBailout(&data.bailout);
|
|
|
|
// Call vkDestroyFence of VK_NULL_HANDLE repeatedly using multiple threads.
|
|
// There should be no validation error from collision of that non-object.
|
|
test_platform_thread_create(&thread, ReleaseNullFence, (void *)&data);
|
|
for (int i = 0; i < 40000; i++) {
|
|
vkDestroyFence(m_device->device(), VK_NULL_HANDLE, NULL);
|
|
}
|
|
test_platform_thread_join(thread, NULL);
|
|
|
|
m_errorMonitor->SetBailout(NULL);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ClearColorImageWithValidRange) {
|
|
TEST_DESCRIPTION("Record clear color with a valid VkImageSubresourceRange");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkImageObj image(m_device);
|
|
image.Init(32, 32, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
ASSERT_TRUE(image.create_info().arrayLayers == 1);
|
|
ASSERT_TRUE(image.initialized());
|
|
image.SetLayout(VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
|
|
const VkClearColorValue clear_color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
|
|
|
m_commandBuffer->begin();
|
|
const auto cb_handle = m_commandBuffer->handle();
|
|
|
|
// Try good case
|
|
{
|
|
m_errorMonitor->ExpectSuccess();
|
|
VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
|
|
vkCmdClearColorImage(cb_handle, image.handle(), image.Layout(), &clear_color, 1, &range);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// Try good case with VK_REMAINING
|
|
{
|
|
m_errorMonitor->ExpectSuccess();
|
|
VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS};
|
|
vkCmdClearColorImage(cb_handle, image.handle(), image.Layout(), &clear_color, 1, &range);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ClearDepthStencilWithValidRange) {
|
|
TEST_DESCRIPTION("Record clear depth with a valid VkImageSubresourceRange");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
auto depth_format = FindSupportedDepthStencilFormat(gpu());
|
|
if (!depth_format) {
|
|
printf("%s No Depth + Stencil format found. Skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
VkImageObj image(m_device);
|
|
image.Init(32, 32, 1, depth_format, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_TILING_OPTIMAL);
|
|
ASSERT_TRUE(image.create_info().arrayLayers == 1);
|
|
ASSERT_TRUE(image.initialized());
|
|
const VkImageAspectFlags ds_aspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
|
image.SetLayout(ds_aspect, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
|
|
const VkClearDepthStencilValue clear_value = {};
|
|
|
|
m_commandBuffer->begin();
|
|
const auto cb_handle = m_commandBuffer->handle();
|
|
|
|
// Try good case
|
|
{
|
|
m_errorMonitor->ExpectSuccess();
|
|
VkImageSubresourceRange range = {ds_aspect, 0, 1, 0, 1};
|
|
vkCmdClearDepthStencilImage(cb_handle, image.handle(), image.Layout(), &clear_value, 1, &range);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
// Try good case with VK_REMAINING
|
|
{
|
|
m_errorMonitor->ExpectSuccess();
|
|
VkImageSubresourceRange range = {ds_aspect, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS};
|
|
vkCmdClearDepthStencilImage(cb_handle, image.handle(), image.Layout(), &clear_value, 1, &range);
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreateGraphicsPipelineWithIgnoredPointers) {
|
|
TEST_DESCRIPTION("Create Graphics Pipeline with pointers that must be ignored by layers");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
m_depth_stencil_fmt = FindSupportedDepthStencilFormat(gpu());
|
|
ASSERT_TRUE(m_depth_stencil_fmt != 0);
|
|
|
|
m_depthStencil->Init(m_device, static_cast<int32_t>(m_width), static_cast<int32_t>(m_height), m_depth_stencil_fmt);
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget(m_depthStencil->BindInfo()));
|
|
|
|
const uint64_t fake_address_64 = 0xCDCDCDCDCDCDCDCD;
|
|
const uint64_t fake_address_32 = 0xCDCDCDCD;
|
|
void *hopefully_undereferencable_pointer =
|
|
sizeof(void *) == 8 ? reinterpret_cast<void *>(fake_address_64) : reinterpret_cast<void *>(fake_address_32);
|
|
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
|
|
const VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info{
|
|
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
0,
|
|
nullptr, // bindings
|
|
0,
|
|
nullptr // attributes
|
|
};
|
|
|
|
const VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly_state_create_info{
|
|
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
|
VK_FALSE // primitive restart
|
|
};
|
|
|
|
const VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info_template{
|
|
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
VK_FALSE, // depthClamp
|
|
VK_FALSE, // rasterizerDiscardEnable
|
|
VK_POLYGON_MODE_FILL,
|
|
VK_CULL_MODE_NONE,
|
|
VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
|
VK_FALSE, // depthBias
|
|
0.0f,
|
|
0.0f,
|
|
0.0f, // depthBias params
|
|
1.0f // lineWidth
|
|
};
|
|
|
|
VkPipelineLayout pipeline_layout;
|
|
{
|
|
VkPipelineLayoutCreateInfo pipeline_layout_create_info{
|
|
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
0,
|
|
nullptr, // layouts
|
|
0,
|
|
nullptr // push constants
|
|
};
|
|
|
|
VkResult err = vkCreatePipelineLayout(m_device->device(), &pipeline_layout_create_info, nullptr, &pipeline_layout);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
|
|
// try disabled rasterizer and no tessellation
|
|
{
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info =
|
|
pipeline_rasterization_state_create_info_template;
|
|
pipeline_rasterization_state_create_info.rasterizerDiscardEnable = VK_TRUE;
|
|
|
|
VkGraphicsPipelineCreateInfo graphics_pipeline_create_info{
|
|
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
1, // stageCount
|
|
&vs.GetStageCreateInfo(),
|
|
&pipeline_vertex_input_state_create_info,
|
|
&pipeline_input_assembly_state_create_info,
|
|
reinterpret_cast<const VkPipelineTessellationStateCreateInfo *>(hopefully_undereferencable_pointer),
|
|
reinterpret_cast<const VkPipelineViewportStateCreateInfo *>(hopefully_undereferencable_pointer),
|
|
&pipeline_rasterization_state_create_info,
|
|
reinterpret_cast<const VkPipelineMultisampleStateCreateInfo *>(hopefully_undereferencable_pointer),
|
|
reinterpret_cast<const VkPipelineDepthStencilStateCreateInfo *>(hopefully_undereferencable_pointer),
|
|
reinterpret_cast<const VkPipelineColorBlendStateCreateInfo *>(hopefully_undereferencable_pointer),
|
|
nullptr, // dynamic states
|
|
pipeline_layout,
|
|
m_renderPass,
|
|
0, // subpass
|
|
VK_NULL_HANDLE,
|
|
0};
|
|
|
|
VkPipeline pipeline;
|
|
vkCreateGraphicsPipelines(m_device->handle(), VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &pipeline);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyPipeline(m_device->handle(), pipeline, nullptr);
|
|
}
|
|
|
|
const VkPipelineMultisampleStateCreateInfo pipeline_multisample_state_create_info{
|
|
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_FALSE, // sample shading
|
|
0.0f, // minSampleShading
|
|
nullptr, // pSampleMask
|
|
VK_FALSE, // alphaToCoverageEnable
|
|
VK_FALSE // alphaToOneEnable
|
|
};
|
|
|
|
// try enabled rasterizer but no subpass attachments
|
|
{
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info =
|
|
pipeline_rasterization_state_create_info_template;
|
|
pipeline_rasterization_state_create_info.rasterizerDiscardEnable = VK_FALSE;
|
|
|
|
VkViewport viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
|
VkRect2D scissor = {{0, 0}, {static_cast<uint32_t>(m_width), static_cast<uint32_t>(m_height)}};
|
|
|
|
const VkPipelineViewportStateCreateInfo pipeline_viewport_state_create_info{
|
|
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
1,
|
|
&viewport,
|
|
1,
|
|
&scissor};
|
|
|
|
VkRenderPass render_pass;
|
|
{
|
|
VkSubpassDescription subpass_desc = {};
|
|
|
|
VkRenderPassCreateInfo render_pass_create_info{
|
|
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
0,
|
|
nullptr, // attachments
|
|
1,
|
|
&subpass_desc,
|
|
0,
|
|
nullptr // subpass dependencies
|
|
};
|
|
|
|
VkResult err = vkCreateRenderPass(m_device->handle(), &render_pass_create_info, nullptr, &render_pass);
|
|
ASSERT_VK_SUCCESS(err);
|
|
}
|
|
|
|
VkGraphicsPipelineCreateInfo graphics_pipeline_create_info{
|
|
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
1, // stageCount
|
|
&vs.GetStageCreateInfo(),
|
|
&pipeline_vertex_input_state_create_info,
|
|
&pipeline_input_assembly_state_create_info,
|
|
nullptr,
|
|
&pipeline_viewport_state_create_info,
|
|
&pipeline_rasterization_state_create_info,
|
|
&pipeline_multisample_state_create_info,
|
|
reinterpret_cast<const VkPipelineDepthStencilStateCreateInfo *>(hopefully_undereferencable_pointer),
|
|
reinterpret_cast<const VkPipelineColorBlendStateCreateInfo *>(hopefully_undereferencable_pointer),
|
|
nullptr, // dynamic states
|
|
pipeline_layout,
|
|
render_pass,
|
|
0, // subpass
|
|
VK_NULL_HANDLE,
|
|
0};
|
|
|
|
VkPipeline pipeline;
|
|
vkCreateGraphicsPipelines(m_device->handle(), VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &pipeline);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyPipeline(m_device->handle(), pipeline, nullptr);
|
|
vkDestroyRenderPass(m_device->handle(), render_pass, nullptr);
|
|
}
|
|
|
|
// try dynamic viewport and scissor
|
|
{
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info =
|
|
pipeline_rasterization_state_create_info_template;
|
|
pipeline_rasterization_state_create_info.rasterizerDiscardEnable = VK_FALSE;
|
|
|
|
const VkPipelineViewportStateCreateInfo pipeline_viewport_state_create_info{
|
|
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
1,
|
|
reinterpret_cast<const VkViewport *>(hopefully_undereferencable_pointer),
|
|
1,
|
|
reinterpret_cast<const VkRect2D *>(hopefully_undereferencable_pointer)};
|
|
|
|
const VkPipelineDepthStencilStateCreateInfo pipeline_depth_stencil_state_create_info{
|
|
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
};
|
|
|
|
const VkPipelineColorBlendAttachmentState pipeline_color_blend_attachment_state = {};
|
|
|
|
const VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state_create_info{
|
|
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
VK_FALSE,
|
|
VK_LOGIC_OP_CLEAR,
|
|
1,
|
|
&pipeline_color_blend_attachment_state,
|
|
{0.0f, 0.0f, 0.0f, 0.0f}};
|
|
|
|
const VkDynamicState dynamic_states[2] = {VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR};
|
|
|
|
const VkPipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info{
|
|
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
2, dynamic_states};
|
|
|
|
VkGraphicsPipelineCreateInfo graphics_pipeline_create_info{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
nullptr, // pNext
|
|
0, // flags
|
|
1, // stageCount
|
|
&vs.GetStageCreateInfo(),
|
|
&pipeline_vertex_input_state_create_info,
|
|
&pipeline_input_assembly_state_create_info,
|
|
nullptr,
|
|
&pipeline_viewport_state_create_info,
|
|
&pipeline_rasterization_state_create_info,
|
|
&pipeline_multisample_state_create_info,
|
|
&pipeline_depth_stencil_state_create_info,
|
|
&pipeline_color_blend_state_create_info,
|
|
&pipeline_dynamic_state_create_info, // dynamic states
|
|
pipeline_layout,
|
|
m_renderPass,
|
|
0, // subpass
|
|
VK_NULL_HANDLE,
|
|
0};
|
|
|
|
VkPipeline pipeline;
|
|
vkCreateGraphicsPipelines(m_device->handle(), VK_NULL_HANDLE, 1, &graphics_pipeline_create_info, nullptr, &pipeline);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyPipeline(m_device->handle(), pipeline, nullptr);
|
|
}
|
|
|
|
vkDestroyPipelineLayout(m_device->handle(), pipeline_layout, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ExternalMemory) {
|
|
TEST_DESCRIPTION("Perform a copy through a pair of buffers linked by external memory");
|
|
|
|
#ifdef _WIN32
|
|
const auto ext_mem_extension_name = VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME;
|
|
const auto handle_type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;
|
|
#else
|
|
const auto ext_mem_extension_name = VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME;
|
|
const auto handle_type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;
|
|
#endif
|
|
|
|
// Check for external memory instance extensions
|
|
std::vector<const char *> reqd_instance_extensions = {
|
|
{VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME}};
|
|
for (auto extension_name : reqd_instance_extensions) {
|
|
if (InstanceExtensionSupported(extension_name)) {
|
|
m_instance_extension_names.push_back(extension_name);
|
|
} else {
|
|
printf("%s Required instance extension %s not supported, skipping test\n", kSkipPrefix, extension_name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
// Check for import/export capability
|
|
VkPhysicalDeviceExternalBufferInfoKHR ebi = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR, nullptr, 0,
|
|
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, handle_type};
|
|
VkExternalBufferPropertiesKHR ebp = {VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR, nullptr, {0, 0, 0}};
|
|
auto vkGetPhysicalDeviceExternalBufferPropertiesKHR = (PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR)vkGetInstanceProcAddr(
|
|
instance(), "vkGetPhysicalDeviceExternalBufferPropertiesKHR");
|
|
ASSERT_TRUE(vkGetPhysicalDeviceExternalBufferPropertiesKHR != nullptr);
|
|
vkGetPhysicalDeviceExternalBufferPropertiesKHR(gpu(), &ebi, &ebp);
|
|
if (!(ebp.externalMemoryProperties.compatibleHandleTypes & handle_type) ||
|
|
!(ebp.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR) ||
|
|
!(ebp.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR)) {
|
|
printf("%s External buffer does not support importing and exporting, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
// Check if dedicated allocation is required
|
|
bool dedicated_allocation =
|
|
ebp.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR;
|
|
if (dedicated_allocation) {
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
|
m_device_extension_names.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s Dedicated allocation extension not supported, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check for external memory device extensions
|
|
if (DeviceExtensionSupported(gpu(), nullptr, ext_mem_extension_name)) {
|
|
m_device_extension_names.push_back(ext_mem_extension_name);
|
|
m_device_extension_names.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s External memory extension not supported, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
m_errorMonitor->ExpectSuccess(VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT);
|
|
|
|
VkMemoryPropertyFlags mem_flags = 0;
|
|
const VkDeviceSize buffer_size = 1024;
|
|
|
|
// Create export and import buffers
|
|
const VkExternalMemoryBufferCreateInfoKHR external_buffer_info = {VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
|
|
nullptr, handle_type};
|
|
auto buffer_info = VkBufferObj::create_info(buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
|
|
buffer_info.pNext = &external_buffer_info;
|
|
VkBufferObj buffer_export;
|
|
buffer_export.init_no_mem(*m_device, buffer_info);
|
|
VkBufferObj buffer_import;
|
|
buffer_import.init_no_mem(*m_device, buffer_info);
|
|
|
|
// Allocation info
|
|
auto alloc_info = vk_testing::DeviceMemory::get_resource_alloc_info(*m_device, buffer_export.memory_requirements(), mem_flags);
|
|
|
|
// Add export allocation info to pNext chain
|
|
VkExportMemoryAllocateInfoKHR export_info = {VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR, nullptr, handle_type};
|
|
alloc_info.pNext = &export_info;
|
|
|
|
// Add dedicated allocation info to pNext chain if required
|
|
VkMemoryDedicatedAllocateInfoKHR dedicated_info = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, nullptr,
|
|
VK_NULL_HANDLE, buffer_export.handle()};
|
|
if (dedicated_allocation) {
|
|
export_info.pNext = &dedicated_info;
|
|
}
|
|
|
|
// Allocate memory to be exported
|
|
vk_testing::DeviceMemory memory_export;
|
|
memory_export.init(*m_device, alloc_info);
|
|
|
|
// Bind exported memory
|
|
buffer_export.bind_memory(memory_export, 0);
|
|
|
|
#ifdef _WIN32
|
|
// Export memory to handle
|
|
auto vkGetMemoryWin32HandleKHR = (PFN_vkGetMemoryWin32HandleKHR)vkGetInstanceProcAddr(instance(), "vkGetMemoryWin32HandleKHR");
|
|
ASSERT_TRUE(vkGetMemoryWin32HandleKHR != nullptr);
|
|
VkMemoryGetWin32HandleInfoKHR mghi = {VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR, nullptr, memory_export.handle(),
|
|
handle_type};
|
|
HANDLE handle;
|
|
ASSERT_VK_SUCCESS(vkGetMemoryWin32HandleKHR(m_device->device(), &mghi, &handle));
|
|
|
|
VkImportMemoryWin32HandleInfoKHR import_info = {VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR, nullptr, handle_type,
|
|
handle};
|
|
#else
|
|
// Export memory to fd
|
|
auto vkGetMemoryFdKHR = (PFN_vkGetMemoryFdKHR)vkGetInstanceProcAddr(instance(), "vkGetMemoryFdKHR");
|
|
ASSERT_TRUE(vkGetMemoryFdKHR != nullptr);
|
|
VkMemoryGetFdInfoKHR mgfi = {VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, nullptr, memory_export.handle(), handle_type};
|
|
int fd;
|
|
ASSERT_VK_SUCCESS(vkGetMemoryFdKHR(m_device->device(), &mgfi, &fd));
|
|
|
|
VkImportMemoryFdInfoKHR import_info = {VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, nullptr, handle_type, fd};
|
|
#endif
|
|
|
|
// Import memory
|
|
alloc_info = vk_testing::DeviceMemory::get_resource_alloc_info(*m_device, buffer_import.memory_requirements(), mem_flags);
|
|
alloc_info.pNext = &import_info;
|
|
vk_testing::DeviceMemory memory_import;
|
|
memory_import.init(*m_device, alloc_info);
|
|
|
|
// Bind imported memory
|
|
buffer_import.bind_memory(memory_import, 0);
|
|
|
|
// Create test buffers and fill input buffer
|
|
VkMemoryPropertyFlags mem_prop = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
|
VkBufferObj buffer_input;
|
|
buffer_input.init_as_src_and_dst(*m_device, buffer_size, mem_prop);
|
|
auto input_mem = (uint8_t *)buffer_input.memory().map();
|
|
for (uint32_t i = 0; i < buffer_size; i++) {
|
|
input_mem[i] = (i & 0xFF);
|
|
}
|
|
buffer_input.memory().unmap();
|
|
VkBufferObj buffer_output;
|
|
buffer_output.init_as_src_and_dst(*m_device, buffer_size, mem_prop);
|
|
|
|
// Copy from input buffer to output buffer through the exported/imported memory
|
|
m_commandBuffer->begin();
|
|
VkBufferCopy copy_info = {0, 0, buffer_size};
|
|
vkCmdCopyBuffer(m_commandBuffer->handle(), buffer_input.handle(), buffer_export.handle(), 1, ©_info);
|
|
// Insert memory barrier to guarantee copy order
|
|
VkMemoryBarrier mem_barrier = {VK_STRUCTURE_TYPE_MEMORY_BARRIER, nullptr, VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
VK_ACCESS_TRANSFER_READ_BIT};
|
|
vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1,
|
|
&mem_barrier, 0, nullptr, 0, nullptr);
|
|
vkCmdCopyBuffer(m_commandBuffer->handle(), buffer_import.handle(), buffer_output.handle(), 1, ©_info);
|
|
m_commandBuffer->end();
|
|
m_commandBuffer->QueueCommandBuffer();
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ParameterLayerFeatures2Capture) {
|
|
TEST_DESCRIPTION("Ensure parameter_validation_layer correctly captures physical device features");
|
|
if (InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s Did not find VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR =
|
|
(PFN_vkGetPhysicalDeviceFeatures2KHR)vkGetInstanceProcAddr(instance(), "vkGetPhysicalDeviceFeatures2KHR");
|
|
ASSERT_TRUE(vkGetPhysicalDeviceFeatures2KHR != nullptr);
|
|
|
|
VkResult err;
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkPhysicalDeviceFeatures2KHR features2;
|
|
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
|
|
features2.pNext = nullptr;
|
|
|
|
vkGetPhysicalDeviceFeatures2KHR(gpu(), &features2);
|
|
|
|
// We're not creating a valid m_device, but the phy wrapper is useful
|
|
vk_testing::PhysicalDevice physical_device(gpu());
|
|
vk_testing::QueueCreateInfoArray queue_info(physical_device.queue_properties());
|
|
// Only request creation with queuefamilies that have at least one queue
|
|
std::vector<VkDeviceQueueCreateInfo> create_queue_infos;
|
|
auto qci = queue_info.data();
|
|
for (uint32_t i = 0; i < queue_info.size(); ++i) {
|
|
if (qci[i].queueCount) {
|
|
create_queue_infos.push_back(qci[i]);
|
|
}
|
|
}
|
|
|
|
VkDeviceCreateInfo dev_info = {};
|
|
dev_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
dev_info.pNext = &features2;
|
|
dev_info.flags = 0;
|
|
dev_info.queueCreateInfoCount = create_queue_infos.size();
|
|
dev_info.pQueueCreateInfos = create_queue_infos.data();
|
|
dev_info.enabledLayerCount = 0;
|
|
dev_info.ppEnabledLayerNames = nullptr;
|
|
dev_info.enabledExtensionCount = 0;
|
|
dev_info.ppEnabledExtensionNames = nullptr;
|
|
dev_info.pEnabledFeatures = nullptr;
|
|
|
|
VkDevice device;
|
|
err = vkCreateDevice(gpu(), &dev_info, nullptr, &device);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
if (features2.features.samplerAnisotropy) {
|
|
// Test that the parameter layer is caching the features correctly using CreateSampler
|
|
VkSamplerCreateInfo sampler_ci = SafeSaneSamplerCreateInfo();
|
|
// If the features were not captured correctly, this should cause an error
|
|
sampler_ci.anisotropyEnable = VK_TRUE;
|
|
sampler_ci.maxAnisotropy = physical_device.properties().limits.maxSamplerAnisotropy;
|
|
|
|
VkSampler sampler = VK_NULL_HANDLE;
|
|
err = vkCreateSampler(device, &sampler_ci, nullptr, &sampler);
|
|
ASSERT_VK_SUCCESS(err);
|
|
vkDestroySampler(device, sampler, nullptr);
|
|
} else {
|
|
printf("%s Feature samplerAnisotropy not enabled; parameter_layer check skipped.\n", kSkipPrefix);
|
|
}
|
|
|
|
// Verify the core validation layer has captured the physical device features by creating a a query pool.
|
|
if (features2.features.pipelineStatisticsQuery) {
|
|
VkQueryPool query_pool;
|
|
VkQueryPoolCreateInfo qpci{};
|
|
qpci.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
|
|
qpci.queryType = VK_QUERY_TYPE_PIPELINE_STATISTICS;
|
|
qpci.queryCount = 1;
|
|
err = vkCreateQueryPool(device, &qpci, nullptr, &query_pool);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
vkDestroyQueryPool(device, query_pool, nullptr);
|
|
} else {
|
|
printf("%s Feature pipelineStatisticsQuery not enabled; core_validation_layer check skipped.\n", kSkipPrefix);
|
|
}
|
|
|
|
vkDestroyDevice(device, nullptr);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, GetMemoryRequirements2) {
|
|
TEST_DESCRIPTION(
|
|
"Get memory requirements with VK_KHR_get_memory_requirements2 instead of core entry points and verify layers do not emit "
|
|
"errors when objects are bound and used");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
// Check for VK_KHR_get_memory_requirementes2 extensions
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s %s not supported, skipping test\n", kSkipPrefix, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
m_errorMonitor->ExpectSuccess(VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT);
|
|
|
|
// Create a test buffer
|
|
VkBufferObj buffer;
|
|
buffer.init_no_mem(*m_device,
|
|
VkBufferObj::create_info(1024, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT));
|
|
|
|
// Use extension to get buffer memory requirements
|
|
auto vkGetBufferMemoryRequirements2KHR = reinterpret_cast<PFN_vkGetBufferMemoryRequirements2KHR>(
|
|
vkGetDeviceProcAddr(m_device->device(), "vkGetBufferMemoryRequirements2KHR"));
|
|
ASSERT_TRUE(vkGetBufferMemoryRequirements2KHR != nullptr);
|
|
VkBufferMemoryRequirementsInfo2KHR buffer_info = {VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR, nullptr,
|
|
buffer.handle()};
|
|
VkMemoryRequirements2KHR buffer_reqs = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR};
|
|
vkGetBufferMemoryRequirements2KHR(m_device->device(), &buffer_info, &buffer_reqs);
|
|
|
|
// Allocate and bind buffer memory
|
|
vk_testing::DeviceMemory buffer_memory;
|
|
buffer_memory.init(*m_device, vk_testing::DeviceMemory::get_resource_alloc_info(*m_device, buffer_reqs.memoryRequirements, 0));
|
|
vkBindBufferMemory(m_device->device(), buffer.handle(), buffer_memory.handle(), 0);
|
|
|
|
// Create a test image
|
|
auto image_ci = vk_testing::Image::create_info();
|
|
image_ci.imageType = VK_IMAGE_TYPE_2D;
|
|
image_ci.extent.width = 32;
|
|
image_ci.extent.height = 32;
|
|
image_ci.format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
image_ci.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
image_ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
vk_testing::Image image;
|
|
image.init_no_mem(*m_device, image_ci);
|
|
|
|
// Use extension to get image memory requirements
|
|
auto vkGetImageMemoryRequirements2KHR = reinterpret_cast<PFN_vkGetImageMemoryRequirements2KHR>(
|
|
vkGetDeviceProcAddr(m_device->device(), "vkGetImageMemoryRequirements2KHR"));
|
|
ASSERT_TRUE(vkGetImageMemoryRequirements2KHR != nullptr);
|
|
VkImageMemoryRequirementsInfo2KHR image_info = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR, nullptr,
|
|
image.handle()};
|
|
VkMemoryRequirements2KHR image_reqs = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR};
|
|
vkGetImageMemoryRequirements2KHR(m_device->device(), &image_info, &image_reqs);
|
|
|
|
// Allocate and bind image memory
|
|
vk_testing::DeviceMemory image_memory;
|
|
image_memory.init(*m_device, vk_testing::DeviceMemory::get_resource_alloc_info(*m_device, image_reqs.memoryRequirements, 0));
|
|
vkBindImageMemory(m_device->device(), image.handle(), image_memory.handle(), 0);
|
|
|
|
// Now execute arbitrary commands that use the test buffer and image
|
|
m_commandBuffer->begin();
|
|
|
|
// Fill buffer with 0
|
|
vkCmdFillBuffer(m_commandBuffer->handle(), buffer.handle(), 0, VK_WHOLE_SIZE, 0);
|
|
|
|
// Transition and clear image
|
|
const auto subresource_range = image.subresource_range(VK_IMAGE_ASPECT_COLOR_BIT);
|
|
const auto barrier = image.image_memory_barrier(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_GENERAL, subresource_range);
|
|
vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 1, &barrier);
|
|
const VkClearColorValue color = {};
|
|
vkCmdClearColorImage(m_commandBuffer->handle(), image.handle(), VK_IMAGE_LAYOUT_GENERAL, &color, 1, &subresource_range);
|
|
|
|
// Submit and verify no validation errors
|
|
m_commandBuffer->end();
|
|
m_commandBuffer->QueueCommandBuffer();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, BindMemory2) {
|
|
TEST_DESCRIPTION(
|
|
"Bind memory with VK_KHR_bind_memory2 instead of core entry points and verify layers do not emit errors when objects are "
|
|
"used");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
// Check for VK_KHR_get_memory_requirementes2 extensions
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_KHR_BIND_MEMORY_2_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s %s not supported, skipping test\n", kSkipPrefix, VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
m_errorMonitor->ExpectSuccess(VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT);
|
|
|
|
// Create a test buffer
|
|
VkBufferObj buffer;
|
|
buffer.init_no_mem(*m_device, VkBufferObj::create_info(1024, VK_BUFFER_USAGE_TRANSFER_DST_BIT));
|
|
|
|
// Allocate buffer memory
|
|
vk_testing::DeviceMemory buffer_memory;
|
|
buffer_memory.init(*m_device, vk_testing::DeviceMemory::get_resource_alloc_info(*m_device, buffer.memory_requirements(), 0));
|
|
|
|
// Bind buffer memory with extension
|
|
auto vkBindBufferMemory2KHR =
|
|
reinterpret_cast<PFN_vkBindBufferMemory2KHR>(vkGetDeviceProcAddr(m_device->device(), "vkBindBufferMemory2KHR"));
|
|
ASSERT_TRUE(vkBindBufferMemory2KHR != nullptr);
|
|
VkBindBufferMemoryInfoKHR buffer_bind_info = {VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR, nullptr, buffer.handle(),
|
|
buffer_memory.handle(), 0};
|
|
vkBindBufferMemory2KHR(m_device->device(), 1, &buffer_bind_info);
|
|
|
|
// Create a test image
|
|
auto image_ci = vk_testing::Image::create_info();
|
|
image_ci.imageType = VK_IMAGE_TYPE_2D;
|
|
image_ci.extent.width = 32;
|
|
image_ci.extent.height = 32;
|
|
image_ci.format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
image_ci.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
image_ci.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
vk_testing::Image image;
|
|
image.init_no_mem(*m_device, image_ci);
|
|
|
|
// Allocate image memory
|
|
vk_testing::DeviceMemory image_memory;
|
|
image_memory.init(*m_device, vk_testing::DeviceMemory::get_resource_alloc_info(*m_device, image.memory_requirements(), 0));
|
|
|
|
// Bind image memory with extension
|
|
auto vkBindImageMemory2KHR =
|
|
reinterpret_cast<PFN_vkBindImageMemory2KHR>(vkGetDeviceProcAddr(m_device->device(), "vkBindImageMemory2KHR"));
|
|
ASSERT_TRUE(vkBindImageMemory2KHR != nullptr);
|
|
VkBindImageMemoryInfoKHR image_bind_info = {VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR, nullptr, image.handle(),
|
|
image_memory.handle(), 0};
|
|
vkBindImageMemory2KHR(m_device->device(), 1, &image_bind_info);
|
|
|
|
// Now execute arbitrary commands that use the test buffer and image
|
|
m_commandBuffer->begin();
|
|
|
|
// Fill buffer with 0
|
|
vkCmdFillBuffer(m_commandBuffer->handle(), buffer.handle(), 0, VK_WHOLE_SIZE, 0);
|
|
|
|
// Transition and clear image
|
|
const auto subresource_range = image.subresource_range(VK_IMAGE_ASPECT_COLOR_BIT);
|
|
const auto barrier = image.image_memory_barrier(0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_GENERAL, subresource_range);
|
|
vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 1, &barrier);
|
|
const VkClearColorValue color = {};
|
|
vkCmdClearColorImage(m_commandBuffer->handle(), image.handle(), VK_IMAGE_LAYOUT_GENERAL, &color, 1, &subresource_range);
|
|
|
|
// Submit and verify no validation errors
|
|
m_commandBuffer->end();
|
|
m_commandBuffer->QueueCommandBuffer();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineWithCoreChecksDisabled) {
|
|
TEST_DESCRIPTION("Test CreatePipeline while the CoreChecks validation object is disabled");
|
|
|
|
// Enable KHR validation features extension
|
|
VkValidationFeatureDisableEXT disables[] = {VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT};
|
|
VkValidationFeaturesEXT features = {};
|
|
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
|
|
features.disabledValidationFeatureCount = 1;
|
|
features.pDisabledValidationFeatures = disables;
|
|
|
|
VkCommandPoolCreateFlags pool_flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
|
ASSERT_NO_FATAL_FAILURE(Init(nullptr, nullptr, pool_flags, &features));
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
VkPipelineInputAssemblyStateCreateInfo iasci{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, nullptr, 0,
|
|
VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, VK_FALSE};
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.gp_ci_.pInputAssemblyState = &iasci;
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
m_errorMonitor->ExpectSuccess();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipeineWithTessellationDomainOrigin) {
|
|
TEST_DESCRIPTION(
|
|
"Test CreatePipeline when VkPipelineTessellationStateCreateInfo.pNext include "
|
|
"VkPipelineTessellationDomainOriginStateCreateInfo");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
if (!m_device->phy().features().tessellationShader) {
|
|
printf("%s Device does not support tessellation shaders; skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj tcs(m_device, bindStateTscShaderText, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, this);
|
|
VkShaderObj tes(m_device, bindStateTeshaderText, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo iasci{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, nullptr, 0,
|
|
VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, VK_FALSE};
|
|
|
|
VkPipelineTessellationDomainOriginStateCreateInfo tessellationDomainOriginStateInfo = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, VK_NULL_HANDLE,
|
|
VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT};
|
|
|
|
VkPipelineTessellationStateCreateInfo tsci{VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
|
|
&tessellationDomainOriginStateInfo, 0, 3};
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.gp_ci_.pTessellationState = &tsci;
|
|
pipe.gp_ci_.pInputAssemblyState = &iasci;
|
|
pipe.shader_stages_ = {vs.GetStageCreateInfo(), tcs.GetStageCreateInfo(), tes.GetStageCreateInfo(), fs.GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
m_errorMonitor->ExpectSuccess();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, MultiplaneImageCopyBufferToImage) {
|
|
TEST_DESCRIPTION("Positive test of multiplane copy buffer to image");
|
|
// Enable KHR multiplane req'd extensions
|
|
bool mp_extensions = InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION);
|
|
if (mp_extensions) {
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
}
|
|
SetTargetApiVersion(VK_API_VERSION_1_1);
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
|
|
if (mp_extensions) {
|
|
m_device_extension_names.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
|
m_device_extension_names.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
|
m_device_extension_names.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
|
|
m_device_extension_names.push_back(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s test requires KHR multiplane extensions, not available. Skipping.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState(nullptr, nullptr, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT));
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkImageCreateInfo ci = {};
|
|
ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
ci.pNext = NULL;
|
|
ci.flags = 0;
|
|
ci.imageType = VK_IMAGE_TYPE_2D;
|
|
ci.format = VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR; // All planes of equal extent
|
|
ci.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
ci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
ci.extent = {16, 16, 1};
|
|
ci.mipLevels = 1;
|
|
ci.arrayLayers = 1;
|
|
ci.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
VkFormatFeatureFlags features = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
|
|
bool supported = ImageFormatAndFeaturesSupported(instance(), gpu(), ci, features);
|
|
if (!supported) {
|
|
printf("%s Multiplane image format not supported. Skipping test.\n", kSkipPrefix);
|
|
return; // Assume there's low ROI on searching for different mp formats
|
|
}
|
|
|
|
VkImageObj image(m_device);
|
|
image.init(&ci);
|
|
|
|
m_commandBuffer->reset();
|
|
m_errorMonitor->ExpectSuccess();
|
|
m_commandBuffer->begin();
|
|
image.ImageMemoryBarrier(m_commandBuffer, VK_IMAGE_ASPECT_COLOR_BIT, 0, VK_ACCESS_TRANSFER_WRITE_BIT,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
|
|
std::array<VkImageAspectFlagBits, 3> aspects = {VK_IMAGE_ASPECT_PLANE_0_BIT, VK_IMAGE_ASPECT_PLANE_1_BIT,
|
|
VK_IMAGE_ASPECT_PLANE_2_BIT};
|
|
std::array<VkBufferObj, 3> buffers;
|
|
VkMemoryPropertyFlags reqs = 0;
|
|
|
|
VkBufferImageCopy copy = {};
|
|
copy.imageSubresource.layerCount = 1;
|
|
copy.imageExtent.depth = 1;
|
|
copy.imageExtent.height = 16;
|
|
copy.imageExtent.width = 16;
|
|
|
|
for (size_t i = 0; i < aspects.size(); ++i) {
|
|
buffers[i].init_as_src(*m_device, (VkDeviceSize)16 * 16 * 1, reqs);
|
|
copy.imageSubresource.aspectMask = aspects[i];
|
|
vkCmdCopyBufferToImage(m_commandBuffer->handle(), buffers[i].handle(), image.handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
1, ©);
|
|
}
|
|
m_commandBuffer->end();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, MultiplaneImageTests) {
|
|
TEST_DESCRIPTION("Positive test of multiplane image operations");
|
|
|
|
// Enable KHR multiplane req'd extensions
|
|
bool mp_extensions = InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION);
|
|
if (mp_extensions) {
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
}
|
|
SetTargetApiVersion(VK_API_VERSION_1_1);
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
|
mp_extensions = mp_extensions && DeviceExtensionSupported(gpu(), nullptr, VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
|
|
if (mp_extensions) {
|
|
m_device_extension_names.push_back(VK_KHR_MAINTENANCE1_EXTENSION_NAME);
|
|
m_device_extension_names.push_back(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
|
m_device_extension_names.push_back(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s test requires KHR multiplane extensions, not available. Skipping.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState(nullptr, nullptr, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT));
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
// Create aliased function pointers for 1.0 and 1.1 contexts
|
|
|
|
PFN_vkBindImageMemory2KHR vkBindImageMemory2Function = nullptr;
|
|
PFN_vkGetImageMemoryRequirements2KHR vkGetImageMemoryRequirements2Function = nullptr;
|
|
PFN_vkGetPhysicalDeviceMemoryProperties2KHR vkGetPhysicalDeviceMemoryProperties2Function = nullptr;
|
|
|
|
if (DeviceValidationVersion() >= VK_API_VERSION_1_1) {
|
|
vkBindImageMemory2Function = vkBindImageMemory2;
|
|
vkGetImageMemoryRequirements2Function = vkGetImageMemoryRequirements2;
|
|
vkGetPhysicalDeviceMemoryProperties2Function = vkGetPhysicalDeviceMemoryProperties2;
|
|
} else {
|
|
vkBindImageMemory2Function = (PFN_vkBindImageMemory2KHR)vkGetDeviceProcAddr(m_device->handle(), "vkBindImageMemory2KHR");
|
|
vkGetImageMemoryRequirements2Function =
|
|
(PFN_vkGetImageMemoryRequirements2KHR)vkGetDeviceProcAddr(m_device->handle(), "vkGetImageMemoryRequirements2KHR");
|
|
vkGetPhysicalDeviceMemoryProperties2Function = (PFN_vkGetPhysicalDeviceMemoryProperties2KHR)vkGetDeviceProcAddr(
|
|
m_device->handle(), "vkGetPhysicalDeviceMemoryProperties2KHR");
|
|
}
|
|
|
|
if (!vkBindImageMemory2Function || !vkGetImageMemoryRequirements2Function || !vkGetPhysicalDeviceMemoryProperties2Function) {
|
|
printf("%s Did not find required device extension support; test skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
VkImageCreateInfo ci = {};
|
|
ci.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
|
ci.pNext = NULL;
|
|
ci.flags = 0;
|
|
ci.imageType = VK_IMAGE_TYPE_2D;
|
|
ci.format = VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR; // All planes of equal extent
|
|
ci.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
ci.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
|
ci.extent = {128, 128, 1};
|
|
ci.mipLevels = 1;
|
|
ci.arrayLayers = 1;
|
|
ci.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
ci.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
ci.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
|
|
// Verify format
|
|
VkFormatFeatureFlags features = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT | VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
|
|
bool supported = ImageFormatAndFeaturesSupported(instance(), gpu(), ci, features);
|
|
if (!supported) {
|
|
printf("%s Multiplane image format not supported. Skipping test.\n", kSkipPrefix);
|
|
return; // Assume there's low ROI on searching for different mp formats
|
|
}
|
|
|
|
VkImage image;
|
|
ASSERT_VK_SUCCESS(vkCreateImage(device(), &ci, NULL, &image));
|
|
|
|
// Allocate & bind memory
|
|
VkPhysicalDeviceMemoryProperties phys_mem_props;
|
|
vkGetPhysicalDeviceMemoryProperties(gpu(), &phys_mem_props);
|
|
VkMemoryRequirements mem_reqs;
|
|
vkGetImageMemoryRequirements(device(), image, &mem_reqs);
|
|
VkDeviceMemory mem_obj = VK_NULL_HANDLE;
|
|
VkMemoryPropertyFlagBits mem_props = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
for (uint32_t type = 0; type < phys_mem_props.memoryTypeCount; type++) {
|
|
if ((mem_reqs.memoryTypeBits & (1 << type)) &&
|
|
((phys_mem_props.memoryTypes[type].propertyFlags & mem_props) == mem_props)) {
|
|
VkMemoryAllocateInfo alloc_info = {};
|
|
alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
|
alloc_info.allocationSize = mem_reqs.size;
|
|
alloc_info.memoryTypeIndex = type;
|
|
ASSERT_VK_SUCCESS(vkAllocateMemory(device(), &alloc_info, NULL, &mem_obj));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (VK_NULL_HANDLE == mem_obj) {
|
|
printf("%s Unable to allocate image memory. Skipping test.\n", kSkipPrefix);
|
|
vkDestroyImage(device(), image, NULL);
|
|
return;
|
|
}
|
|
ASSERT_VK_SUCCESS(vkBindImageMemory(device(), image, mem_obj, 0));
|
|
|
|
// Copy plane 0 to plane 2
|
|
VkImageCopy copyRegion = {};
|
|
copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_0_BIT_KHR;
|
|
copyRegion.srcSubresource.mipLevel = 0;
|
|
copyRegion.srcSubresource.baseArrayLayer = 0;
|
|
copyRegion.srcSubresource.layerCount = 1;
|
|
copyRegion.srcOffset = {0, 0, 0};
|
|
copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_2_BIT_KHR;
|
|
copyRegion.dstSubresource.mipLevel = 0;
|
|
copyRegion.dstSubresource.baseArrayLayer = 0;
|
|
copyRegion.dstSubresource.layerCount = 1;
|
|
copyRegion.dstOffset = {0, 0, 0};
|
|
copyRegion.extent.width = 128;
|
|
copyRegion.extent.height = 128;
|
|
copyRegion.extent.depth = 1;
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
m_commandBuffer->begin();
|
|
m_commandBuffer->CopyImage(image, VK_IMAGE_LAYOUT_GENERAL, image, VK_IMAGE_LAYOUT_GENERAL, 1, ©Region);
|
|
m_commandBuffer->end();
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkFreeMemory(device(), mem_obj, NULL);
|
|
vkDestroyImage(device(), image, NULL);
|
|
|
|
// Repeat bind test on a DISJOINT multi-planar image, with per-plane memory objects, using API2 variants
|
|
//
|
|
features |= VK_FORMAT_FEATURE_DISJOINT_BIT;
|
|
ci.flags = VK_IMAGE_CREATE_DISJOINT_BIT;
|
|
if (ImageFormatAndFeaturesSupported(instance(), gpu(), ci, features)) {
|
|
ASSERT_VK_SUCCESS(vkCreateImage(device(), &ci, NULL, &image));
|
|
|
|
// Allocate & bind memory
|
|
VkPhysicalDeviceMemoryProperties2 phys_mem_props2 = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2};
|
|
vkGetPhysicalDeviceMemoryProperties2Function(gpu(), &phys_mem_props2);
|
|
VkImagePlaneMemoryRequirementsInfo image_plane_req = {VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO};
|
|
VkImageMemoryRequirementsInfo2 mem_req_info2 = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2};
|
|
mem_req_info2.pNext = &image_plane_req;
|
|
mem_req_info2.image = image;
|
|
VkMemoryRequirements2 mem_reqs2 = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};
|
|
|
|
VkDeviceMemory p0_mem, p1_mem, p2_mem;
|
|
mem_props = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
|
VkMemoryAllocateInfo alloc_info = {VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO};
|
|
|
|
// Plane 0
|
|
image_plane_req.planeAspect = VK_IMAGE_ASPECT_PLANE_0_BIT;
|
|
vkGetImageMemoryRequirements2Function(device(), &mem_req_info2, &mem_reqs2);
|
|
uint32_t mem_type = 0;
|
|
for (mem_type = 0; mem_type < phys_mem_props2.memoryProperties.memoryTypeCount; mem_type++) {
|
|
if ((mem_reqs2.memoryRequirements.memoryTypeBits & (1 << mem_type)) &&
|
|
((phys_mem_props2.memoryProperties.memoryTypes[mem_type].propertyFlags & mem_props) == mem_props)) {
|
|
alloc_info.memoryTypeIndex = mem_type;
|
|
break;
|
|
}
|
|
}
|
|
alloc_info.allocationSize = mem_reqs2.memoryRequirements.size;
|
|
ASSERT_VK_SUCCESS(vkAllocateMemory(device(), &alloc_info, NULL, &p0_mem));
|
|
|
|
// Plane 1 & 2 use same memory type
|
|
image_plane_req.planeAspect = VK_IMAGE_ASPECT_PLANE_1_BIT;
|
|
vkGetImageMemoryRequirements2Function(device(), &mem_req_info2, &mem_reqs2);
|
|
alloc_info.allocationSize = mem_reqs2.memoryRequirements.size;
|
|
ASSERT_VK_SUCCESS(vkAllocateMemory(device(), &alloc_info, NULL, &p1_mem));
|
|
|
|
image_plane_req.planeAspect = VK_IMAGE_ASPECT_PLANE_2_BIT;
|
|
vkGetImageMemoryRequirements2Function(device(), &mem_req_info2, &mem_reqs2);
|
|
alloc_info.allocationSize = mem_reqs2.memoryRequirements.size;
|
|
ASSERT_VK_SUCCESS(vkAllocateMemory(device(), &alloc_info, NULL, &p2_mem));
|
|
|
|
// Set up 3-plane binding
|
|
VkBindImageMemoryInfo bind_info[3];
|
|
for (int plane = 0; plane < 3; plane++) {
|
|
bind_info[plane].sType = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO;
|
|
bind_info[plane].pNext = nullptr;
|
|
bind_info[plane].image = image;
|
|
bind_info[plane].memoryOffset = 0;
|
|
}
|
|
bind_info[0].memory = p0_mem;
|
|
bind_info[1].memory = p1_mem;
|
|
bind_info[2].memory = p2_mem;
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
vkBindImageMemory2Function(device(), 3, bind_info);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkFreeMemory(device(), p0_mem, NULL);
|
|
vkFreeMemory(device(), p1_mem, NULL);
|
|
vkFreeMemory(device(), p2_mem, NULL);
|
|
vkDestroyImage(device(), image, NULL);
|
|
}
|
|
|
|
// Test that changing the layout of ASPECT_COLOR also changes the layout of the individual planes
|
|
VkBufferObj buffer;
|
|
VkMemoryPropertyFlags reqs = 0;
|
|
buffer.init_as_src(*m_device, (VkDeviceSize)128 * 128 * 3, reqs);
|
|
VkImageObj mpimage(m_device);
|
|
mpimage.Init(256, 256, 1, VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
|
VK_IMAGE_TILING_OPTIMAL, 0);
|
|
VkBufferImageCopy copy_region = {};
|
|
copy_region.bufferRowLength = 128;
|
|
copy_region.bufferImageHeight = 128;
|
|
copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_PLANE_1_BIT_KHR;
|
|
copy_region.imageSubresource.layerCount = 1;
|
|
copy_region.imageExtent.height = 64;
|
|
copy_region.imageExtent.width = 64;
|
|
copy_region.imageExtent.depth = 1;
|
|
|
|
vkResetCommandBuffer(m_commandBuffer->handle(), 0);
|
|
m_commandBuffer->begin();
|
|
mpimage.ImageMemoryBarrier(m_commandBuffer, VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
vkCmdCopyBufferToImage(m_commandBuffer->handle(), buffer.handle(), mpimage.handle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
|
|
©_region);
|
|
m_commandBuffer->end();
|
|
m_commandBuffer->QueueCommandBuffer(false);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
// Test to verify that views of multiplanar images have layouts tracked correctly
|
|
// by changing the image's layout then using a view of that image
|
|
VkImageView view;
|
|
VkImageViewCreateInfo ivci = {};
|
|
ivci.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
ivci.image = mpimage.handle();
|
|
ivci.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
ivci.format = VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR;
|
|
ivci.subresourceRange.layerCount = 1;
|
|
ivci.subresourceRange.baseMipLevel = 0;
|
|
ivci.subresourceRange.levelCount = 1;
|
|
ivci.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
vkCreateImageView(m_device->device(), &ivci, nullptr, &view);
|
|
|
|
OneOffDescriptorSet descriptor_set(m_device,
|
|
{
|
|
{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr},
|
|
});
|
|
|
|
VkSamplerCreateInfo sampler_ci = SafeSaneSamplerCreateInfo();
|
|
VkSampler sampler;
|
|
|
|
VkResult err;
|
|
err = vkCreateSampler(m_device->device(), &sampler_ci, NULL, &sampler);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
const VkPipelineLayoutObj pipeline_layout(m_device, {&descriptor_set.layout_});
|
|
descriptor_set.WriteDescriptorImageInfo(0, view, sampler);
|
|
descriptor_set.UpdateDescriptorSets();
|
|
|
|
VkShaderObj vs(m_device, bindStateVertShaderText, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragSamplerShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
VkPipelineObj pipe(m_device);
|
|
pipe.AddShader(&vs);
|
|
pipe.AddShader(&fs);
|
|
pipe.AddDefaultColorAttachment();
|
|
pipe.CreateVKPipeline(pipeline_layout.handle(), renderPass());
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
m_commandBuffer->begin();
|
|
VkImageMemoryBarrier img_barrier = {};
|
|
img_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
|
img_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
|
img_barrier.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
|
img_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
img_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
img_barrier.image = mpimage.handle();
|
|
img_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
img_barrier.subresourceRange.baseArrayLayer = 0;
|
|
img_barrier.subresourceRange.baseMipLevel = 0;
|
|
img_barrier.subresourceRange.layerCount = 1;
|
|
img_barrier.subresourceRange.levelCount = 1;
|
|
vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
|
VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, &img_barrier);
|
|
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
|
|
vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.handle());
|
|
vkCmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout.handle(), 0, 1,
|
|
&descriptor_set.set_, 0, nullptr);
|
|
|
|
VkViewport viewport = {0, 0, 16, 16, 0, 1};
|
|
VkRect2D scissor = {{0, 0}, {16, 16}};
|
|
vkCmdSetViewport(m_commandBuffer->handle(), 0, 1, &viewport);
|
|
vkCmdSetScissor(m_commandBuffer->handle(), 0, 1, &scissor);
|
|
|
|
m_commandBuffer->Draw(1, 0, 0, 0);
|
|
m_commandBuffer->EndRenderPass();
|
|
m_commandBuffer->end();
|
|
VkSubmitInfo submit_info = {};
|
|
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &m_commandBuffer->handle();
|
|
vkQueueSubmit(m_device->m_queue, 1, &submit_info, VK_NULL_HANDLE);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkQueueWaitIdle(m_device->m_queue);
|
|
vkDestroyImageView(m_device->device(), view, NULL);
|
|
vkDestroySampler(m_device->device(), sampler, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ApiVersionZero) {
|
|
TEST_DESCRIPTION("Check that apiVersion = 0 is valid.");
|
|
m_errorMonitor->ExpectSuccess();
|
|
app_info.apiVersion = 0U;
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, RayTracingPipelineNV) {
|
|
TEST_DESCRIPTION("Test VK_NV_ray_tracing.");
|
|
|
|
if (!CreateNVRayTracingPipelineHelper::InitInstanceExtensions(*this, m_instance_extension_names)) {
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR =
|
|
(PFN_vkGetPhysicalDeviceFeatures2KHR)vkGetInstanceProcAddr(instance(), "vkGetPhysicalDeviceFeatures2KHR");
|
|
ASSERT_TRUE(vkGetPhysicalDeviceFeatures2KHR != nullptr);
|
|
|
|
if (!CreateNVRayTracingPipelineHelper::InitDeviceExtensions(*this, m_device_extension_names)) {
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
auto ignore_update = [](CreateNVRayTracingPipelineHelper &helper) {};
|
|
CreateNVRayTracingPipelineHelper::OneshotPositiveTest(*this, ignore_update);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, ViewportArray2NV) {
|
|
TEST_DESCRIPTION("Test to validate VK_NV_viewport_array2");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
VkPhysicalDeviceFeatures available_features = {};
|
|
ASSERT_NO_FATAL_FAILURE(GetPhysicalDeviceFeatures(&available_features));
|
|
|
|
if (!available_features.multiViewport) {
|
|
printf("%s VkPhysicalDeviceFeatures::multiViewport is not supported, skipping tests\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
if (!available_features.tessellationShader) {
|
|
printf("%s VkPhysicalDeviceFeatures::tessellationShader is not supported, skipping tests\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
if (!available_features.geometryShader) {
|
|
printf("%s VkPhysicalDeviceFeatures::geometryShader is not supported, skipping tests\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s %s Extension not supported, skipping tests\n", kSkipPrefix, VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
const char tcs_src[] = R"(
|
|
#version 450
|
|
layout(vertices = 3) out;
|
|
|
|
void main() {
|
|
gl_TessLevelOuter[0] = 4.0f;
|
|
gl_TessLevelOuter[1] = 4.0f;
|
|
gl_TessLevelOuter[2] = 4.0f;
|
|
gl_TessLevelInner[0] = 3.0f;
|
|
|
|
gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
|
|
})";
|
|
|
|
// Create tessellation control and fragment shader here since they will not be
|
|
// modified by the different test cases.
|
|
VkShaderObj tcs(m_device, tcs_src, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, this);
|
|
VkShaderObj fs(m_device, bindStateFragShaderText, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
std::vector<VkViewport> vps = {{0.0f, 0.0f, m_width / 2.0f, m_height}, {m_width / 2.0f, 0.0f, m_width / 2.0f, m_height}};
|
|
std::vector<VkRect2D> scs = {
|
|
{{0, 0}, {static_cast<uint32_t>(m_width) / 2, static_cast<uint32_t>(m_height)}},
|
|
{{static_cast<int32_t>(m_width) / 2, 0}, {static_cast<uint32_t>(m_width) / 2, static_cast<uint32_t>(m_height)}}};
|
|
|
|
enum class TestStage { VERTEX = 0, TESSELLATION_EVAL = 1, GEOMETRY = 2 };
|
|
std::array<TestStage, 3> vertex_stages = {{TestStage::VERTEX, TestStage::TESSELLATION_EVAL, TestStage::GEOMETRY}};
|
|
|
|
// Verify that the usage of gl_ViewportMask[] in the allowed vertex processing
|
|
// stages does not cause any errors.
|
|
for (auto stage : vertex_stages) {
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo iaci = {VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
|
|
iaci.topology = (stage != TestStage::VERTEX) ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
|
|
VkPipelineTessellationStateCreateInfo tsci = {VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO};
|
|
tsci.patchControlPoints = 3;
|
|
|
|
const VkPipelineLayoutObj pl(m_device);
|
|
|
|
VkPipelineObj pipe(m_device);
|
|
pipe.AddDefaultColorAttachment();
|
|
pipe.SetInputAssembly(&iaci);
|
|
pipe.SetViewport(vps);
|
|
pipe.SetScissor(scs);
|
|
pipe.AddShader(&fs);
|
|
|
|
std::stringstream vs_src, tes_src, geom_src;
|
|
|
|
vs_src << R"(
|
|
#version 450
|
|
#extension GL_NV_viewport_array2 : require
|
|
|
|
vec2 positions[3] = { vec2( 0.0f, -0.5f),
|
|
vec2( 0.5f, 0.5f),
|
|
vec2(-0.5f, 0.5f)
|
|
};
|
|
void main() {)";
|
|
// Write viewportMask if the vertex shader is the last vertex processing stage.
|
|
if (stage == TestStage::VERTEX) {
|
|
vs_src << "gl_ViewportMask[0] = 3;\n";
|
|
}
|
|
vs_src << R"(
|
|
gl_Position = vec4(positions[gl_VertexIndex % 3], 0.0, 1.0);
|
|
})";
|
|
|
|
VkShaderObj vs(m_device, vs_src.str().c_str(), VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
pipe.AddShader(&vs);
|
|
|
|
std::unique_ptr<VkShaderObj> tes, geom;
|
|
|
|
if (stage >= TestStage::TESSELLATION_EVAL) {
|
|
tes_src << R"(
|
|
#version 450
|
|
#extension GL_NV_viewport_array2 : require
|
|
layout(triangles) in;
|
|
|
|
void main() {
|
|
gl_Position = (gl_in[0].gl_Position * gl_TessCoord.x +
|
|
gl_in[1].gl_Position * gl_TessCoord.y +
|
|
gl_in[2].gl_Position * gl_TessCoord.z);)";
|
|
// Write viewportMask if the tess eval shader is the last vertex processing stage.
|
|
if (stage == TestStage::TESSELLATION_EVAL) {
|
|
tes_src << "gl_ViewportMask[0] = 3;\n";
|
|
}
|
|
tes_src << "}";
|
|
|
|
tes = std::unique_ptr<VkShaderObj>(
|
|
new VkShaderObj(m_device, tes_src.str().c_str(), VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, this));
|
|
pipe.AddShader(tes.get());
|
|
pipe.AddShader(&tcs);
|
|
pipe.SetTessellation(&tsci);
|
|
}
|
|
|
|
if (stage >= TestStage::GEOMETRY) {
|
|
geom_src << R"(
|
|
#version 450
|
|
#extension GL_NV_viewport_array2 : require
|
|
layout(triangles) in;
|
|
layout(triangle_strip, max_vertices = 3) out;
|
|
|
|
void main() {
|
|
gl_ViewportMask[0] = 3;
|
|
for(int i = 0; i < 3; ++i) {
|
|
gl_Position = gl_in[i].gl_Position;
|
|
EmitVertex();
|
|
}
|
|
})";
|
|
|
|
geom =
|
|
std::unique_ptr<VkShaderObj>(new VkShaderObj(m_device, geom_src.str().c_str(), VK_SHADER_STAGE_GEOMETRY_BIT, this));
|
|
pipe.AddShader(geom.get());
|
|
}
|
|
|
|
pipe.CreateVKPipeline(pl.handle(), renderPass());
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, HostQueryResetSuccess) {
|
|
// This is a positive test. No failures are expected.
|
|
TEST_DESCRIPTION("Use vkResetQueryPoolEXT normally");
|
|
|
|
if (!InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) {
|
|
printf("%s Did not find required instance extension %s; skipped.\n", kSkipPrefix,
|
|
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
|
|
m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
if (!DeviceExtensionSupported(gpu(), nullptr, VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME)) {
|
|
printf("%s Extension %s not supported by device; skipped.\n", kSkipPrefix, VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
|
|
m_device_extension_names.push_back(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME);
|
|
|
|
VkPhysicalDeviceHostQueryResetFeaturesEXT host_query_reset_features{};
|
|
host_query_reset_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT;
|
|
host_query_reset_features.hostQueryReset = VK_TRUE;
|
|
|
|
VkPhysicalDeviceFeatures2 pd_features2{};
|
|
pd_features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
|
|
pd_features2.pNext = &host_query_reset_features;
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &pd_features2));
|
|
|
|
auto fpvkResetQueryPoolEXT = (PFN_vkResetQueryPoolEXT)vkGetDeviceProcAddr(m_device->device(), "vkResetQueryPoolEXT");
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
VkQueryPool query_pool;
|
|
VkQueryPoolCreateInfo query_pool_create_info{};
|
|
query_pool_create_info.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
|
|
query_pool_create_info.queryType = VK_QUERY_TYPE_TIMESTAMP;
|
|
query_pool_create_info.queryCount = 1;
|
|
vkCreateQueryPool(m_device->device(), &query_pool_create_info, nullptr, &query_pool);
|
|
fpvkResetQueryPoolEXT(m_device->device(), query_pool, 0, 1);
|
|
vkDestroyQueryPool(m_device->device(), query_pool, nullptr);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, CreatePipelineFragmentOutputNotConsumedButAlphaToCoverageEnabled) {
|
|
TEST_DESCRIPTION(
|
|
"Test that no warning is produced when writing to non-existing color attachment if alpha to coverage is enabled.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget(0u));
|
|
|
|
VkPipelineMultisampleStateCreateInfo ms_state_ci = {};
|
|
ms_state_ci.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
|
ms_state_ci.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
ms_state_ci.alphaToCoverageEnable = VK_TRUE;
|
|
|
|
const auto set_info = [&](CreatePipelineHelper &helper) {
|
|
helper.pipe_ms_state_ci_ = ms_state_ci;
|
|
helper.cb_ci_.attachmentCount = 0;
|
|
};
|
|
CreatePipelineHelper::OneshotTest(*this, set_info, VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT, "", true);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, UseFirstQueueUnqueried) {
|
|
TEST_DESCRIPTION("Use first queue family and one queue without first querying with vkGetPhysicalDeviceQueueFamilyProperties");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
const float q_priority[] = {1.0f};
|
|
VkDeviceQueueCreateInfo queue_ci = {};
|
|
queue_ci.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queue_ci.queueFamilyIndex = 0;
|
|
queue_ci.queueCount = 1;
|
|
queue_ci.pQueuePriorities = q_priority;
|
|
|
|
VkDeviceCreateInfo device_ci = {};
|
|
device_ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
|
device_ci.queueCreateInfoCount = 1;
|
|
device_ci.pQueueCreateInfos = &queue_ci;
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
VkDevice test_device;
|
|
vkCreateDevice(gpu(), &device_ci, nullptr, &test_device);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyDevice(test_device, nullptr);
|
|
}
|
|
|
|
// Android loader returns an error in this case
|
|
#if !defined(ANDROID)
|
|
TEST_F(VkPositiveLayerTest, GetDevProcAddrNullPtr) {
|
|
TEST_DESCRIPTION("Call GetDeviceProcAddr on an enabled instance extension expecting nullptr");
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
if (InstanceExtensionSupported(VK_KHR_SURFACE_EXTENSION_NAME)) {
|
|
m_instance_extension_names.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s %s not supported, skipping test\n", kSkipPrefix, VK_KHR_SURFACE_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
auto fpDestroySurface = (PFN_vkCreateValidationCacheEXT)vkGetDeviceProcAddr(m_device->device(), "vkDestroySurfaceKHR");
|
|
if (fpDestroySurface) {
|
|
m_errorMonitor->SetError("Null was expected!");
|
|
}
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
#endif
|
|
|
|
TEST_F(VkPositiveLayerTest, CmdCopySwapchainImage) {
|
|
TEST_DESCRIPTION("Run vkCmdCopyImage with a swapchain image");
|
|
|
|
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
|
printf(
|
|
"%s According to VUID-01631, VkBindImageMemoryInfo-memory should be NULL. But Android will crash if memory is NULL, "
|
|
"skipping CmdCopySwapchainImage test\n",
|
|
kSkipPrefix);
|
|
return;
|
|
#endif
|
|
|
|
SetTargetApiVersion(VK_API_VERSION_1_1);
|
|
|
|
if (!AddSurfaceInstanceExtension()) {
|
|
printf("%s surface extensions not supported, skipping CmdCopySwapchainImage test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
if (!AddSwapchainDeviceExtension()) {
|
|
printf("%s swapchain extensions not supported, skipping CmdCopySwapchainImage test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
if (DeviceValidationVersion() < VK_API_VERSION_1_1) {
|
|
printf("%s VkBindImageMemoryInfo requires Vulkan 1.1+, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
if (!InitSwapchain()) {
|
|
printf("%s Cannot create surface or swapchain, skipping CmdCopySwapchainImage test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
auto image_create_info = lvl_init_struct<VkImageCreateInfo>();
|
|
image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
|
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
image_create_info.extent.width = 64;
|
|
image_create_info.extent.height = 64;
|
|
image_create_info.extent.depth = 1;
|
|
image_create_info.mipLevels = 1;
|
|
image_create_info.arrayLayers = 1;
|
|
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
|
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
VkImageObj srcImage(m_device);
|
|
srcImage.init(&image_create_info);
|
|
|
|
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
|
|
auto image_swapchain_create_info = lvl_init_struct<VkImageSwapchainCreateInfoKHR>();
|
|
image_swapchain_create_info.swapchain = m_swapchain;
|
|
image_create_info.pNext = &image_swapchain_create_info;
|
|
|
|
VkImage image_from_swapchain;
|
|
vkCreateImage(device(), &image_create_info, NULL, &image_from_swapchain);
|
|
|
|
auto bind_swapchain_info = lvl_init_struct<VkBindImageMemorySwapchainInfoKHR>();
|
|
bind_swapchain_info.swapchain = m_swapchain;
|
|
bind_swapchain_info.imageIndex = 0;
|
|
|
|
auto bind_info = lvl_init_struct<VkBindImageMemoryInfo>(&bind_swapchain_info);
|
|
bind_info.image = image_from_swapchain;
|
|
bind_info.memory = VK_NULL_HANDLE;
|
|
bind_info.memoryOffset = 0;
|
|
|
|
vkBindImageMemory2(m_device->device(), 1, &bind_info);
|
|
|
|
VkImageCopy copy_region = {};
|
|
copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
copy_region.srcSubresource.mipLevel = 0;
|
|
copy_region.dstSubresource.mipLevel = 0;
|
|
copy_region.srcSubresource.baseArrayLayer = 0;
|
|
copy_region.dstSubresource.baseArrayLayer = 0;
|
|
copy_region.srcSubresource.layerCount = 1;
|
|
copy_region.dstSubresource.layerCount = 1;
|
|
copy_region.srcOffset = {0, 0, 0};
|
|
copy_region.dstOffset = {0, 0, 0};
|
|
copy_region.extent = {10, 10, 1};
|
|
|
|
m_commandBuffer->begin();
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
vkCmdCopyImage(m_commandBuffer->handle(), srcImage.handle(), VK_IMAGE_LAYOUT_GENERAL, image_from_swapchain,
|
|
VK_IMAGE_LAYOUT_GENERAL, 1, ©_region);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyImage(m_device->device(), image_from_swapchain, NULL);
|
|
DestroySwapchain();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, TransferImageToSwapchainDeviceGroup) {
|
|
TEST_DESCRIPTION("Transfer an image to a swapchain's image between device group");
|
|
|
|
#if defined(VK_USE_PLATFORM_ANDROID_KHR)
|
|
printf(
|
|
"%s According to VUID-01631, VkBindImageMemoryInfo-memory should be NULL. But Android will crash if memory is NULL, "
|
|
"skipping test\n",
|
|
kSkipPrefix);
|
|
return;
|
|
#endif
|
|
|
|
SetTargetApiVersion(VK_API_VERSION_1_1);
|
|
|
|
if (!AddSurfaceInstanceExtension()) {
|
|
printf("%s surface extensions not supported, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
if (!AddSwapchainDeviceExtension()) {
|
|
printf("%s swapchain extensions not supported, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
if (DeviceValidationVersion() < VK_API_VERSION_1_1) {
|
|
printf("%s VkBindImageMemoryInfo requires Vulkan 1.1+, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
uint32_t physical_device_group_count = 0;
|
|
vkEnumeratePhysicalDeviceGroups(instance(), &physical_device_group_count, nullptr);
|
|
|
|
if (physical_device_group_count == 0) {
|
|
printf("%s physical_device_group_count is 0, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
std::vector<VkPhysicalDeviceGroupProperties> physical_device_group(physical_device_group_count,
|
|
{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES});
|
|
vkEnumeratePhysicalDeviceGroups(instance(), &physical_device_group_count, physical_device_group.data());
|
|
VkDeviceGroupDeviceCreateInfo create_device_pnext = {};
|
|
create_device_pnext.sType = VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO;
|
|
create_device_pnext.physicalDeviceCount = physical_device_group[0].physicalDeviceCount;
|
|
create_device_pnext.pPhysicalDevices = physical_device_group[0].physicalDevices;
|
|
ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &create_device_pnext, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT));
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
if (!InitSwapchain(VK_IMAGE_USAGE_TRANSFER_DST_BIT)) {
|
|
printf("%s Cannot create surface or swapchain, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
auto image_create_info = lvl_init_struct<VkImageCreateInfo>();
|
|
image_create_info.imageType = VK_IMAGE_TYPE_2D;
|
|
image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
image_create_info.extent.width = 64;
|
|
image_create_info.extent.height = 64;
|
|
image_create_info.extent.depth = 1;
|
|
image_create_info.mipLevels = 1;
|
|
image_create_info.arrayLayers = 1;
|
|
image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
|
|
image_create_info.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
|
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
|
image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
VkImageObj src_Image(m_device);
|
|
src_Image.init(&image_create_info);
|
|
|
|
image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
|
image_create_info.flags = VK_IMAGE_CREATE_ALIAS_BIT;
|
|
|
|
auto image_swapchain_create_info = lvl_init_struct<VkImageSwapchainCreateInfoKHR>();
|
|
image_swapchain_create_info.swapchain = m_swapchain;
|
|
image_create_info.pNext = &image_swapchain_create_info;
|
|
|
|
VkImage peer_image;
|
|
vkCreateImage(device(), &image_create_info, NULL, &peer_image);
|
|
|
|
auto bind_devicegroup_info = lvl_init_struct<VkBindImageMemoryDeviceGroupInfo>();
|
|
bind_devicegroup_info.deviceIndexCount = 2;
|
|
std::array<uint32_t, 2> deviceIndices = {0, 0};
|
|
bind_devicegroup_info.pDeviceIndices = deviceIndices.data();
|
|
bind_devicegroup_info.splitInstanceBindRegionCount = 0;
|
|
bind_devicegroup_info.pSplitInstanceBindRegions = nullptr;
|
|
|
|
auto bind_swapchain_info = lvl_init_struct<VkBindImageMemorySwapchainInfoKHR>(&bind_devicegroup_info);
|
|
bind_swapchain_info.swapchain = m_swapchain;
|
|
bind_swapchain_info.imageIndex = 0;
|
|
|
|
auto bind_info = lvl_init_struct<VkBindImageMemoryInfo>(&bind_swapchain_info);
|
|
bind_info.image = peer_image;
|
|
bind_info.memory = VK_NULL_HANDLE;
|
|
bind_info.memoryOffset = 0;
|
|
|
|
vkBindImageMemory2(m_device->device(), 1, &bind_info);
|
|
|
|
uint32_t swapchain_images_count = 0;
|
|
vkGetSwapchainImagesKHR(device(), m_swapchain, &swapchain_images_count, nullptr);
|
|
std::vector<VkImage> swapchain_images;
|
|
swapchain_images.resize(swapchain_images_count);
|
|
vkGetSwapchainImagesKHR(device(), m_swapchain, &swapchain_images_count, swapchain_images.data());
|
|
|
|
m_commandBuffer->begin();
|
|
|
|
auto img_barrier = lvl_init_struct<VkImageMemoryBarrier>();
|
|
img_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
img_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
img_barrier.image = swapchain_images[0];
|
|
img_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
|
img_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
img_barrier.subresourceRange.baseArrayLayer = 0;
|
|
img_barrier.subresourceRange.baseMipLevel = 0;
|
|
img_barrier.subresourceRange.layerCount = 1;
|
|
img_barrier.subresourceRange.levelCount = 1;
|
|
vkCmdPipelineBarrier(m_commandBuffer->handle(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0,
|
|
nullptr, 0, nullptr, 1, &img_barrier);
|
|
|
|
m_commandBuffer->end();
|
|
m_commandBuffer->QueueCommandBuffer();
|
|
|
|
m_commandBuffer->reset();
|
|
m_commandBuffer->begin();
|
|
|
|
VkImageCopy copy_region = {};
|
|
copy_region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
copy_region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
copy_region.srcSubresource.mipLevel = 0;
|
|
copy_region.dstSubresource.mipLevel = 0;
|
|
copy_region.srcSubresource.baseArrayLayer = 0;
|
|
copy_region.dstSubresource.baseArrayLayer = 0;
|
|
copy_region.srcSubresource.layerCount = 1;
|
|
copy_region.dstSubresource.layerCount = 1;
|
|
copy_region.srcOffset = {0, 0, 0};
|
|
copy_region.dstOffset = {0, 0, 0};
|
|
copy_region.extent = {10, 10, 1};
|
|
vkCmdCopyImage(m_commandBuffer->handle(), src_Image.handle(), VK_IMAGE_LAYOUT_GENERAL, peer_image,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
|
|
|
|
m_commandBuffer->end();
|
|
m_errorMonitor->ExpectSuccess();
|
|
m_commandBuffer->QueueCommandBuffer();
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkDestroyImage(m_device->device(), peer_image, NULL);
|
|
DestroySwapchain();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, RenderPassValidStages) {
|
|
TEST_DESCRIPTION("Create render pass with valid stages");
|
|
|
|
bool rp2_supported = InstanceExtensionSupported(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
if (rp2_supported) m_instance_extension_names.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
if (rp2_supported) rp2_supported = CheckCreateRenderPass2Support(this, m_device_extension_names);
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
VkSubpassDescription sci[2] = {};
|
|
sci[0].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
sci[1].pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
|
|
VkSubpassDependency dependency = {};
|
|
// to be filled later by tests
|
|
|
|
VkRenderPassCreateInfo rpci = {};
|
|
rpci.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
|
rpci.subpassCount = 2;
|
|
rpci.pSubpasses = sci;
|
|
rpci.dependencyCount = 1;
|
|
rpci.pDependencies = &dependency;
|
|
|
|
const VkPipelineStageFlags kGraphicsStages =
|
|
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT | VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT |
|
|
VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
|
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
|
|
|
dependency.srcSubpass = 0;
|
|
dependency.dstSubpass = 1;
|
|
dependency.srcStageMask = kGraphicsStages;
|
|
dependency.dstStageMask = kGraphicsStages;
|
|
PositiveTestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2_supported);
|
|
|
|
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
|
dependency.dstSubpass = 0;
|
|
dependency.srcStageMask = kGraphicsStages | VK_PIPELINE_STAGE_HOST_BIT;
|
|
dependency.dstStageMask = kGraphicsStages;
|
|
PositiveTestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2_supported);
|
|
|
|
dependency.srcSubpass = 0;
|
|
dependency.dstSubpass = VK_SUBPASS_EXTERNAL;
|
|
dependency.srcStageMask = kGraphicsStages;
|
|
dependency.dstStageMask = VK_PIPELINE_STAGE_HOST_BIT;
|
|
PositiveTestRenderPassCreate(m_errorMonitor, m_device->device(), &rpci, rp2_supported);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, SampleMaskOverrideCoverageNV) {
|
|
TEST_DESCRIPTION("Test to validate VK_NV_sample_mask_override_coverage");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s %s Extension not supported, skipping tests\n", kSkipPrefix, VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
|
|
const char vs_src[] = R"(
|
|
#version 450
|
|
layout(location=0) out vec4 fragColor;
|
|
|
|
const vec2 pos[3] = { vec2( 0.0f, -0.5f),
|
|
vec2( 0.5f, 0.5f),
|
|
vec2(-0.5f, 0.5f)
|
|
};
|
|
void main()
|
|
{
|
|
gl_Position = vec4(pos[gl_VertexIndex % 3], 0.0f, 1.0f);
|
|
fragColor = vec4(0.0f, 1.0f, 0.0f, 1.0f);
|
|
})";
|
|
|
|
const char fs_src[] = R"(
|
|
#version 450
|
|
#extension GL_NV_sample_mask_override_coverage : require
|
|
|
|
layout(location = 0) in vec4 fragColor;
|
|
layout(location = 0) out vec4 outColor;
|
|
|
|
layout(override_coverage) out int gl_SampleMask[];
|
|
|
|
void main()
|
|
{
|
|
gl_SampleMask[0] = 0xff;
|
|
outColor = fragColor;
|
|
})";
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
const VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_8_BIT;
|
|
|
|
VkAttachmentDescription cAttachment = {};
|
|
cAttachment.format = VK_FORMAT_B8G8R8A8_UNORM;
|
|
cAttachment.samples = sampleCount;
|
|
cAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
cAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
cAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
cAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
cAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
cAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
VkAttachmentReference cAttachRef = {};
|
|
cAttachRef.attachment = 0;
|
|
cAttachRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
VkSubpassDescription subpass = {};
|
|
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass.colorAttachmentCount = 1;
|
|
subpass.pColorAttachments = &cAttachRef;
|
|
|
|
VkRenderPassCreateInfo rpci = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
|
|
rpci.attachmentCount = 1;
|
|
rpci.pAttachments = &cAttachment;
|
|
rpci.subpassCount = 1;
|
|
rpci.pSubpasses = &subpass;
|
|
|
|
VkRenderPass rp;
|
|
vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
|
|
const VkPipelineLayoutObj pl(m_device);
|
|
|
|
VkSampleMask sampleMask = 0x01;
|
|
VkPipelineMultisampleStateCreateInfo msaa = {VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
|
|
msaa.rasterizationSamples = sampleCount;
|
|
msaa.sampleShadingEnable = VK_FALSE;
|
|
msaa.pSampleMask = &sampleMask;
|
|
|
|
VkPipelineObj pipe(m_device);
|
|
pipe.AddDefaultColorAttachment();
|
|
pipe.SetMSAA(&msaa);
|
|
|
|
VkShaderObj vs(m_device, vs_src, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
pipe.AddShader(&vs);
|
|
|
|
VkShaderObj fs(m_device, fs_src, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
pipe.AddShader(&fs);
|
|
|
|
// Create pipeline and make sure that the usage of NV_sample_mask_override_coverage
|
|
// in the fragment shader does not cause any errors.
|
|
pipe.CreateVKPipeline(pl.handle(), rp);
|
|
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, TestRasterizationDiscardEnableTrue) {
|
|
TEST_DESCRIPTION("Ensure it doesn't crash and trigger error msg when rasterizerDiscardEnable = true");
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
VkAttachmentDescription att[1] = {{}};
|
|
att[0].format = VK_FORMAT_R8G8B8A8_UNORM;
|
|
att[0].samples = VK_SAMPLE_COUNT_4_BIT;
|
|
att[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
att[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
VkAttachmentReference cr = {0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
|
VkSubpassDescription sp = {};
|
|
sp.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
sp.colorAttachmentCount = 1;
|
|
sp.pColorAttachments = &cr;
|
|
VkRenderPassCreateInfo rpi = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO};
|
|
rpi.attachmentCount = 1;
|
|
rpi.pAttachments = att;
|
|
rpi.subpassCount = 1;
|
|
rpi.pSubpasses = &sp;
|
|
VkRenderPass rp;
|
|
vkCreateRenderPass(m_device->device(), &rpi, nullptr, &rp);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.gp_ci_.pViewportState = nullptr;
|
|
pipe.gp_ci_.pMultisampleState = nullptr;
|
|
pipe.gp_ci_.pDepthStencilState = nullptr;
|
|
pipe.gp_ci_.pColorBlendState = nullptr;
|
|
pipe.gp_ci_.renderPass = rp;
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
// Skip the test in NexusPlayer. The driver crashes when pViewportState, pMultisampleState, pDepthStencilState, pColorBlendState
|
|
// are NULL.
|
|
pipe.rs_state_ci_.rasterizerDiscardEnable = VK_TRUE;
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, TestSamplerDataForCombinedImageSampler) {
|
|
TEST_DESCRIPTION("Shader code uses sampler data for CombinedImageSampler");
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
const std::string fsSource = R"(
|
|
OpCapability Shader
|
|
OpMemoryModel Logical GLSL450
|
|
OpEntryPoint Fragment %main "main"
|
|
OpExecutionMode %main OriginUpperLeft
|
|
|
|
OpDecorate %InputData DescriptorSet 0
|
|
OpDecorate %InputData Binding 0
|
|
OpDecorate %SamplerData DescriptorSet 0
|
|
OpDecorate %SamplerData Binding 0
|
|
|
|
%void = OpTypeVoid
|
|
%f32 = OpTypeFloat 32
|
|
%Image = OpTypeImage %f32 2D 0 0 0 1 Rgba32f
|
|
%ImagePtr = OpTypePointer UniformConstant %Image
|
|
%InputData = OpVariable %ImagePtr UniformConstant
|
|
%Sampler = OpTypeSampler
|
|
%SamplerPtr = OpTypePointer UniformConstant %Sampler
|
|
%SamplerData = OpVariable %SamplerPtr UniformConstant
|
|
%SampledImage = OpTypeSampledImage %Image
|
|
|
|
%func = OpTypeFunction %void
|
|
%main = OpFunction %void None %func
|
|
%40 = OpLabel
|
|
%call_smp = OpLoad %Sampler %SamplerData
|
|
OpReturn
|
|
OpFunctionEnd)";
|
|
|
|
VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.dsl_bindings_ = {
|
|
{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr},
|
|
};
|
|
pipe.shader_stages_ = {fs.GetStageCreateInfo(), pipe.vs_->GetStageCreateInfo()};
|
|
pipe.InitState();
|
|
pipe.CreateGraphicsPipeline();
|
|
|
|
VkImageObj image(m_device);
|
|
image.Init(32, 32, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_TILING_OPTIMAL, 0);
|
|
VkImageView view = image.targetView(VK_FORMAT_R8G8B8A8_UNORM);
|
|
|
|
VkSamplerCreateInfo sampler_ci = SafeSaneSamplerCreateInfo();
|
|
VkSampler sampler;
|
|
vkCreateSampler(m_device->device(), &sampler_ci, nullptr, &sampler);
|
|
|
|
uint32_t qfi = 0;
|
|
VkBufferCreateInfo buffer_create_info = {};
|
|
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
|
buffer_create_info.size = 1024;
|
|
buffer_create_info.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
|
|
buffer_create_info.queueFamilyIndexCount = 1;
|
|
buffer_create_info.pQueueFamilyIndices = &qfi;
|
|
|
|
VkBufferObj buffer;
|
|
buffer.init(*m_device, buffer_create_info);
|
|
|
|
pipe.descriptor_set_->WriteDescriptorImageInfo(0, view, sampler, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
|
pipe.descriptor_set_->UpdateDescriptorSets();
|
|
|
|
m_commandBuffer->begin();
|
|
m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo);
|
|
vkCmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_);
|
|
vkCmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.pipeline_layout_.handle(), 0, 1,
|
|
&pipe.descriptor_set_->set_, 0, NULL);
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
vkCmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0);
|
|
m_errorMonitor->VerifyNotFound();
|
|
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_commandBuffer->end();
|
|
vkDestroySampler(m_device->device(), sampler, NULL);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, NotPointSizeGeometryShaderSuccess) {
|
|
TEST_DESCRIPTION("Create a pipeline using TOPOLOGY_POINT_LIST, but geometry shader doesn't include PointSize.");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
if ((!m_device->phy().features().geometryShader)) {
|
|
printf("%s Device does not support the required geometry shader features; skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
ASSERT_NO_FATAL_FAILURE(InitViewport());
|
|
|
|
VkShaderObj gs(m_device, bindStateGeomShaderText, VK_SHADER_STAGE_GEOMETRY_BIT, this);
|
|
|
|
CreatePipelineHelper pipe(*this);
|
|
pipe.InitInfo();
|
|
pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), gs.GetStageCreateInfo(), pipe.fs_->GetStageCreateInfo()};
|
|
pipe.ia_ci_.topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
|
|
pipe.InitState();
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
pipe.CreateGraphicsPipeline();
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, SubpassWithReadOnlyLayoutWithoutDependency) {
|
|
TEST_DESCRIPTION("When both subpasses' attachments are the same and layouts are read-only, they don't need dependency.");
|
|
ASSERT_NO_FATAL_FAILURE(Init());
|
|
|
|
auto depth_format = FindSupportedDepthStencilFormat(gpu());
|
|
if (!depth_format) {
|
|
printf("%s No Depth + Stencil format found. Skipped.\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
// A renderpass with one color attachment.
|
|
VkAttachmentDescription attachment = {0,
|
|
depth_format,
|
|
VK_SAMPLE_COUNT_1_BIT,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_STORE,
|
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
VK_IMAGE_LAYOUT_UNDEFINED,
|
|
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL};
|
|
const int size = 2;
|
|
std::array<VkAttachmentDescription, size> attachments = {attachment, attachment};
|
|
|
|
VkAttachmentReference att_ref_depth_stencil = {0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL};
|
|
|
|
std::array<VkSubpassDescription, size> subpasses;
|
|
subpasses[0] = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, 0, 0, nullptr, nullptr, &att_ref_depth_stencil, 0, nullptr};
|
|
subpasses[1] = {0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, 0, 0, nullptr, nullptr, &att_ref_depth_stencil, 0, nullptr};
|
|
|
|
VkRenderPassCreateInfo rpci = {
|
|
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, nullptr, 0, size, attachments.data(), size, subpasses.data(), 0, nullptr};
|
|
|
|
VkRenderPass rp;
|
|
VkResult err = vkCreateRenderPass(m_device->device(), &rpci, nullptr, &rp);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
// A compatible framebuffer.
|
|
VkImageObj image(m_device);
|
|
image.Init(32, 32, 1, depth_format, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_IMAGE_TILING_LINEAR, 0);
|
|
ASSERT_TRUE(image.initialized());
|
|
|
|
VkImageViewCreateInfo ivci = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
nullptr,
|
|
0,
|
|
image.handle(),
|
|
VK_IMAGE_VIEW_TYPE_2D,
|
|
depth_format,
|
|
{VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
VK_COMPONENT_SWIZZLE_IDENTITY},
|
|
{VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 0, 1}};
|
|
|
|
VkImageView view;
|
|
err = vkCreateImageView(m_device->device(), &ivci, nullptr, &view);
|
|
ASSERT_VK_SUCCESS(err);
|
|
std::array<VkImageView, size> views = {view, view};
|
|
|
|
VkFramebufferCreateInfo fci = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, nullptr, 0, rp, size, views.data(), 32, 32, 1};
|
|
VkFramebuffer fb;
|
|
err = vkCreateFramebuffer(m_device->device(), &fci, nullptr, &fb);
|
|
ASSERT_VK_SUCCESS(err);
|
|
|
|
VkRenderPassBeginInfo rpbi = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, nullptr, rp, fb, {{0, 0}, {32, 32}}, 0, nullptr};
|
|
m_commandBuffer->begin();
|
|
vkCmdBeginRenderPass(m_commandBuffer->handle(), &rpbi, VK_SUBPASS_CONTENTS_INLINE);
|
|
vkCmdNextSubpass(m_commandBuffer->handle(), VK_SUBPASS_CONTENTS_INLINE);
|
|
vkCmdEndRenderPass(m_commandBuffer->handle());
|
|
m_commandBuffer->end();
|
|
|
|
vkDestroyFramebuffer(m_device->device(), fb, nullptr);
|
|
vkDestroyRenderPass(m_device->device(), rp, nullptr);
|
|
vkDestroyImageView(m_device->device(), view, nullptr);
|
|
}
|
|
|
|
TEST_F(VkPositiveLayerTest, GeometryShaderPassthroughNV) {
|
|
TEST_DESCRIPTION("Test to validate VK_NV_geometry_shader_passthrough");
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitFramework(myDbgFunc, m_errorMonitor));
|
|
|
|
VkPhysicalDeviceFeatures available_features = {};
|
|
ASSERT_NO_FATAL_FAILURE(GetPhysicalDeviceFeatures(&available_features));
|
|
|
|
if (!available_features.geometryShader) {
|
|
printf("%s VkPhysicalDeviceFeatures::geometryShader is not supported, skipping test\n", kSkipPrefix);
|
|
return;
|
|
}
|
|
|
|
if (DeviceExtensionSupported(gpu(), nullptr, VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME)) {
|
|
m_device_extension_names.push_back(VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME);
|
|
} else {
|
|
printf("%s %s Extension not supported, skipping tests\n", kSkipPrefix, VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME);
|
|
return;
|
|
}
|
|
|
|
ASSERT_NO_FATAL_FAILURE(InitState());
|
|
ASSERT_NO_FATAL_FAILURE(InitRenderTarget());
|
|
|
|
const char vs_src[] = R"(
|
|
#version 450
|
|
|
|
out gl_PerVertex {
|
|
vec4 gl_Position;
|
|
};
|
|
|
|
layout(location = 0) out ColorBlock {vec4 vertexColor;};
|
|
|
|
const vec2 positions[3] = { vec2( 0.0f, -0.5f),
|
|
vec2( 0.5f, 0.5f),
|
|
vec2(-0.5f, 0.5f)
|
|
};
|
|
|
|
const vec4 colors[3] = { vec4(1.0f, 0.0f, 0.0f, 1.0f),
|
|
vec4(0.0f, 1.0f, 0.0f, 1.0f),
|
|
vec4(0.0f, 0.0f, 1.0f, 1.0f)
|
|
};
|
|
void main()
|
|
{
|
|
vertexColor = colors[gl_VertexIndex % 3];
|
|
gl_Position = vec4(positions[gl_VertexIndex % 3], 0.0, 1.0);
|
|
})";
|
|
|
|
const char gs_src[] = R"(
|
|
#version 450
|
|
#extension GL_NV_geometry_shader_passthrough: require
|
|
|
|
layout(triangles) in;
|
|
layout(triangle_strip, max_vertices = 3) out;
|
|
|
|
layout(passthrough) in gl_PerVertex {vec4 gl_Position;};
|
|
layout(location = 0, passthrough) in ColorBlock {vec4 vertexColor;};
|
|
|
|
void main()
|
|
{
|
|
gl_Layer = 0;
|
|
})";
|
|
|
|
const char fs_src[] = R"(
|
|
#version 450
|
|
|
|
layout(location = 0) in ColorBlock {vec4 vertexColor;};
|
|
layout(location = 0) out vec4 outColor;
|
|
|
|
void main() {
|
|
outColor = vertexColor;
|
|
})";
|
|
|
|
m_errorMonitor->ExpectSuccess();
|
|
|
|
const VkPipelineLayoutObj pl(m_device);
|
|
|
|
VkPipelineObj pipe(m_device);
|
|
pipe.AddDefaultColorAttachment();
|
|
|
|
VkShaderObj vs(m_device, vs_src, VK_SHADER_STAGE_VERTEX_BIT, this);
|
|
pipe.AddShader(&vs);
|
|
|
|
VkShaderObj gs(m_device, gs_src, VK_SHADER_STAGE_GEOMETRY_BIT, this);
|
|
pipe.AddShader(&gs);
|
|
|
|
VkShaderObj fs(m_device, fs_src, VK_SHADER_STAGE_FRAGMENT_BIT, this);
|
|
pipe.AddShader(&fs);
|
|
|
|
// Create pipeline and make sure that the usage of NV_geometry_shader_passthrough
|
|
// in the fragment shader does not cause any errors.
|
|
pipe.CreateVKPipeline(pl.handle(), renderPass());
|
|
|
|
m_errorMonitor->VerifyNotFound();
|
|
}
|