blob: c82c0c609281e2c3aeed60c2935ae63af74a987c [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2020 The Khronos Group Inc.
* Copyright (c) 2020 Valve Corporation
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief Vulkan Concurrent Query Tests
*//*--------------------------------------------------------------------*/
#include "vktQueryPoolConcurrentTests.hpp"
#include "vktTestCase.hpp"
#include "vktDrawImageObjectUtil.hpp"
#include "vktDrawBufferObjectUtil.hpp"
#include "vktDrawCreateInfoUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkRef.hpp"
#include "vkRefUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkQueryUtil.hpp"
#include "tcuTestLog.hpp"
#include "tcuImageCompare.hpp"
#include <memory>
namespace vkt
{
namespace QueryPool
{
using namespace Draw;
namespace
{
enum QueryType
{
QUERY_TYPE_OCCLUSION = vk::VK_QUERY_TYPE_OCCLUSION,
QUERY_TYPE_PIPELINE_STATISTICS = vk::VK_QUERY_TYPE_PIPELINE_STATISTICS,
QUERY_TYPE_TIMESTAMP = vk::VK_QUERY_TYPE_TIMESTAMP,
NUM_QUERY_POOLS = 3
};
struct StateObjects
{
StateObjects (const vk::DeviceInterface&vk, vkt::Context &context, const int numVertices, vk::VkPrimitiveTopology primitive);
void setVertices (const vk::DeviceInterface&vk, std::vector<tcu::Vec4> vertices);
enum
{
WIDTH = 128,
HEIGHT = 128
};
vkt::Context &m_context;
vk::Move<vk::VkPipeline> m_pipeline;
vk::Move<vk::VkPipelineLayout> m_pipelineLayout;
de::SharedPtr<Image> m_colorAttachmentImage, m_DepthImage;
vk::Move<vk::VkImageView> m_attachmentView;
vk::Move<vk::VkImageView> m_depthiew;
vk::Move<vk::VkRenderPass> m_renderPass;
vk::Move<vk::VkFramebuffer> m_framebuffer;
de::SharedPtr<Buffer> m_vertexBuffer;
vk::VkFormat m_colorAttachmentFormat;
};
StateObjects::StateObjects (const vk::DeviceInterface&vk, vkt::Context &context, const int numVertices, vk::VkPrimitiveTopology primitive)
: m_context(context)
, m_colorAttachmentFormat(vk::VK_FORMAT_R8G8B8A8_UNORM)
{
vk::VkFormat depthFormat = vk::VK_FORMAT_D16_UNORM;
const vk::VkDevice device = m_context.getDevice();
//attachment images and views
{
vk::VkExtent3D imageExtent =
{
WIDTH, // width;
HEIGHT, // height;
1 // depth;
};
const ImageCreateInfo colorImageCreateInfo(vk::VK_IMAGE_TYPE_2D, m_colorAttachmentFormat, imageExtent, 1, 1, vk::VK_SAMPLE_COUNT_1_BIT, vk::VK_IMAGE_TILING_OPTIMAL,
vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
m_colorAttachmentImage = Image::createAndAlloc(vk, device, colorImageCreateInfo, m_context.getDefaultAllocator(), m_context.getUniversalQueueFamilyIndex());
const ImageViewCreateInfo attachmentViewInfo(m_colorAttachmentImage->object(), vk::VK_IMAGE_VIEW_TYPE_2D, m_colorAttachmentFormat);
m_attachmentView = vk::createImageView(vk, device, &attachmentViewInfo);
ImageCreateInfo depthImageCreateInfo(vk::VK_IMAGE_TYPE_2D, depthFormat, imageExtent, 1, 1, vk::VK_SAMPLE_COUNT_1_BIT, vk::VK_IMAGE_TILING_OPTIMAL,
vk::VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
m_DepthImage = Image::createAndAlloc(vk, device, depthImageCreateInfo, m_context.getDefaultAllocator(), m_context.getUniversalQueueFamilyIndex());
// Construct a depth view from depth image
const ImageViewCreateInfo depthViewInfo(m_DepthImage->object(), vk::VK_IMAGE_VIEW_TYPE_2D, depthFormat);
m_depthiew = vk::createImageView(vk, device, &depthViewInfo);
}
{
// Renderpass and Framebuffer
RenderPassCreateInfo renderPassCreateInfo;
renderPassCreateInfo.addAttachment(AttachmentDescription(m_colorAttachmentFormat, // format
vk::VK_SAMPLE_COUNT_1_BIT, // samples
vk::VK_ATTACHMENT_LOAD_OP_CLEAR, // loadOp
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // storeOp
vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilLoadOp
vk::VK_IMAGE_LAYOUT_GENERAL, // initialLauout
vk::VK_IMAGE_LAYOUT_GENERAL)); // finalLayout
renderPassCreateInfo.addAttachment(AttachmentDescription(depthFormat, // format
vk::VK_SAMPLE_COUNT_1_BIT, // samples
vk::VK_ATTACHMENT_LOAD_OP_CLEAR, // loadOp
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // storeOp
vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp
vk::VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilLoadOp
vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, // initialLauout
vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL)); // finalLayout
const vk::VkAttachmentReference colorAttachmentReference =
{
0, // attachment
vk::VK_IMAGE_LAYOUT_GENERAL // layout
};
const vk::VkAttachmentReference depthAttachmentReference =
{
1, // attachment
vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL // layout
};
renderPassCreateInfo.addSubpass(SubpassDescription(vk::VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint
0, // flags
0, // inputCount
DE_NULL, // pInputAttachments
1, // colorCount
&colorAttachmentReference, // pColorAttachments
DE_NULL, // pResolveAttachments
depthAttachmentReference, // depthStencilAttachment
0, // preserveCount
DE_NULL)); // preserveAttachments
m_renderPass = vk::createRenderPass(vk, device, &renderPassCreateInfo);
std::vector<vk::VkImageView> attachments(2);
attachments[0] = *m_attachmentView;
attachments[1] = *m_depthiew;
FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, attachments, WIDTH, HEIGHT, 1);
m_framebuffer = vk::createFramebuffer(vk, device, &framebufferCreateInfo);
}
{
// Pipeline
vk::Unique<vk::VkShaderModule> vs(vk::createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0));
vk::Unique<vk::VkShaderModule> fs(vk::createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0));
const PipelineCreateInfo::ColorBlendState::Attachment attachmentState;
const PipelineLayoutCreateInfo pipelineLayoutCreateInfo;
m_pipelineLayout = vk::createPipelineLayout(vk, device, &pipelineLayoutCreateInfo);
const vk::VkVertexInputBindingDescription vf_binding_desc =
{
0, // binding;
4 * (deUint32)sizeof(float), // stride;
vk::VK_VERTEX_INPUT_RATE_VERTEX // inputRate
};
const vk::VkVertexInputAttributeDescription vf_attribute_desc =
{
0, // location;
0, // binding;
vk::VK_FORMAT_R32G32B32A32_SFLOAT, // format;
0 // offset;
};
const vk::VkPipelineVertexInputStateCreateInfo vf_info =
{ // sType;
vk::VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // pNext;
NULL, // flags;
0u, // vertexBindingDescriptionCount;
1, // pVertexBindingDescriptions;
&vf_binding_desc, // vertexAttributeDescriptionCount;
1, // pVertexAttributeDescriptions;
&vf_attribute_desc
};
PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, 0);
pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vs, "main", vk::VK_SHADER_STAGE_VERTEX_BIT));
pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fs, "main", vk::VK_SHADER_STAGE_FRAGMENT_BIT));
pipelineCreateInfo.addState(PipelineCreateInfo::InputAssemblerState(primitive));
pipelineCreateInfo.addState(PipelineCreateInfo::ColorBlendState(1, &attachmentState));
const vk::VkViewport viewport = vk::makeViewport(WIDTH, HEIGHT);
const vk::VkRect2D scissor = vk::makeRect2D(WIDTH, HEIGHT);
pipelineCreateInfo.addState(PipelineCreateInfo::ViewportState(1, std::vector<vk::VkViewport>(1, viewport), std::vector<vk::VkRect2D>(1, scissor)));
pipelineCreateInfo.addState(PipelineCreateInfo::DepthStencilState(true, true, vk::VK_COMPARE_OP_GREATER_OR_EQUAL));
pipelineCreateInfo.addState(PipelineCreateInfo::RasterizerState());
pipelineCreateInfo.addState(PipelineCreateInfo::MultiSampleState());
pipelineCreateInfo.addState(vf_info);
m_pipeline = vk::createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo);
}
{
// Vertex buffer
const size_t kBufferSize = numVertices * sizeof(tcu::Vec4);
m_vertexBuffer = Buffer::createAndAlloc(vk, device, BufferCreateInfo(kBufferSize, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible);
}
}
void StateObjects::setVertices (const vk::DeviceInterface&vk, std::vector<tcu::Vec4> vertices)
{
const vk::VkDevice device = m_context.getDevice();
tcu::Vec4 *ptr = reinterpret_cast<tcu::Vec4*>(m_vertexBuffer->getBoundMemory().getHostPtr());
std::copy(vertices.begin(), vertices.end(), ptr);
vk::flushAlloc(vk, device, m_vertexBuffer->getBoundMemory());
}
class PrimaryCommandBufferConcurrentTestInstance : public vkt::TestInstance
{
public:
PrimaryCommandBufferConcurrentTestInstance (vkt::Context &context);
~PrimaryCommandBufferConcurrentTestInstance (void);
private:
tcu::TestStatus iterate (void);
enum
{
NUM_QUERIES_IN_POOL = 2,
QUERY_INDEX_CAPTURE_EMPTY = 0,
QUERY_INDEX_CAPTURE_DRAWCALL = 1,
NUM_VERTICES_IN_DRAWCALL = 3
};
std::unique_ptr<StateObjects> m_stateObjects;
vk::Move<vk::VkQueryPool> m_queryPools[NUM_QUERY_POOLS];
deBool m_supportedQueryType[NUM_QUERY_POOLS];
};
PrimaryCommandBufferConcurrentTestInstance::PrimaryCommandBufferConcurrentTestInstance (vkt::Context &context)
: TestInstance (context)
{
// Check support for multiple query types
{
for(deUint32 poolNdx = 0; poolNdx < NUM_QUERY_POOLS; poolNdx++)
m_supportedQueryType[poolNdx] = DE_FALSE;
deUint32 numSupportedQueryTypes = 0;
m_supportedQueryType[QUERY_TYPE_OCCLUSION] = DE_TRUE;
numSupportedQueryTypes++;
if (context.getDeviceFeatures().pipelineStatisticsQuery)
{
m_supportedQueryType[QUERY_TYPE_PIPELINE_STATISTICS] = DE_TRUE;
numSupportedQueryTypes++;
}
// Check support for timestamp queries
{
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
const std::vector<vk::VkQueueFamilyProperties> queueProperties = vk::getPhysicalDeviceQueueFamilyProperties(context.getInstanceInterface(), context.getPhysicalDevice());
DE_ASSERT(queueFamilyIndex < (deUint32)queueProperties.size());
if (queueProperties[queueFamilyIndex].timestampValidBits)
{
m_supportedQueryType[QUERY_TYPE_TIMESTAMP] = DE_TRUE;
numSupportedQueryTypes++;
}
}
if (numSupportedQueryTypes < 2)
throw tcu::NotSupportedError("Device does not support multiple query types");
}
m_stateObjects = std::unique_ptr<StateObjects>(new StateObjects(m_context.getDeviceInterface(), m_context, NUM_VERTICES_IN_DRAWCALL, vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST));
const vk::VkDevice device = m_context.getDevice();
const vk::DeviceInterface& vk = m_context.getDeviceInterface();
for(deUint32 poolNdx = 0; poolNdx < NUM_QUERY_POOLS; poolNdx++)
{
if (!m_supportedQueryType[poolNdx])
continue;
vk::VkQueryPoolCreateInfo queryPoolCreateInfo =
{
vk::VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
DE_NULL,
0u,
static_cast<vk::VkQueryType>(poolNdx),
NUM_QUERIES_IN_POOL,
0u,
};
if (poolNdx == QUERY_TYPE_PIPELINE_STATISTICS)
queryPoolCreateInfo.pipelineStatistics = vk::VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT;
m_queryPools[poolNdx] = createQueryPool(vk, device, &queryPoolCreateInfo, /*pAllocator*/ DE_NULL);
}
std::vector<tcu::Vec4> vertices(NUM_VERTICES_IN_DRAWCALL);
vertices[0] = tcu::Vec4(0.5, 0.5, 0.0, 1.0);
vertices[1] = tcu::Vec4(0.5, 0.0, 0.0, 1.0);
vertices[2] = tcu::Vec4(0.0, 0.5, 0.0, 1.0);
m_stateObjects->setVertices(vk, vertices);
}
PrimaryCommandBufferConcurrentTestInstance::~PrimaryCommandBufferConcurrentTestInstance (void)
{
}
tcu::TestStatus PrimaryCommandBufferConcurrentTestInstance::iterate (void)
{
tcu::TestLog &log = m_context.getTestContext().getLog();
const vk::VkDevice device = m_context.getDevice();
const vk::VkQueue queue = m_context.getUniversalQueue();
const vk::DeviceInterface& vk = m_context.getDeviceInterface();
const CmdPoolCreateInfo cmdPoolCreateInfo (m_context.getUniversalQueueFamilyIndex());
vk::Move<vk::VkCommandPool> cmdPool = vk::createCommandPool(vk, device, &cmdPoolCreateInfo);
vk::Unique<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
beginCommandBuffer(vk, *cmdBuffer);
initialTransitionColor2DImage(vk, *cmdBuffer, m_stateObjects->m_colorAttachmentImage->object(), vk::VK_IMAGE_LAYOUT_GENERAL,
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
initialTransitionDepth2DImage(vk, *cmdBuffer, m_stateObjects->m_DepthImage->object(), vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
vk::VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, vk::VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | vk::VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
std::vector<vk::VkClearValue> renderPassClearValues(2);
deMemset(&renderPassClearValues[0], 0, static_cast<int>(renderPassClearValues.size()) * sizeof(vk::VkClearValue));
for (deUint32 poolNdx = 0u; poolNdx < NUM_QUERY_POOLS; poolNdx++)
{
if (m_supportedQueryType[poolNdx])
vk.cmdResetQueryPool(*cmdBuffer, *m_queryPools[poolNdx], 0u, NUM_QUERIES_IN_POOL);
}
beginRenderPass(vk, *cmdBuffer, *m_stateObjects->m_renderPass, *m_stateObjects->m_framebuffer, vk::makeRect2D(0, 0, StateObjects::WIDTH, StateObjects::HEIGHT), (deUint32)renderPassClearValues.size(), &renderPassClearValues[0]);
vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_stateObjects->m_pipeline);
vk::VkBuffer vertexBuffer = m_stateObjects->m_vertexBuffer->object();
const vk::VkDeviceSize vertexBufferOffset = 0;
vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
// Begin all queries
for (deUint32 poolNdx = 0u; poolNdx < QUERY_TYPE_TIMESTAMP; poolNdx++)
{
if (m_supportedQueryType[poolNdx])
vk.cmdBeginQuery(*cmdBuffer, *m_queryPools[poolNdx], QUERY_INDEX_CAPTURE_EMPTY, 0u);
}
// End first capture (should not have any result). Start the second one.
for (deUint32 poolNdx = 0u; poolNdx < QUERY_TYPE_TIMESTAMP; poolNdx++)
{
if (m_supportedQueryType[poolNdx])
{
vk.cmdEndQuery(*cmdBuffer, *m_queryPools[poolNdx], QUERY_INDEX_CAPTURE_EMPTY);
vk.cmdBeginQuery(*cmdBuffer, *m_queryPools[poolNdx], QUERY_INDEX_CAPTURE_DRAWCALL, 0u);
}
}
vk.cmdDraw(*cmdBuffer, NUM_VERTICES_IN_DRAWCALL, 1, 0, 0);
if (m_supportedQueryType[QUERY_TYPE_TIMESTAMP])
vk.cmdWriteTimestamp(*cmdBuffer, vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, *m_queryPools[QUERY_TYPE_TIMESTAMP], QUERY_INDEX_CAPTURE_DRAWCALL);
for (deUint32 poolNdx = 0u; poolNdx < QUERY_TYPE_TIMESTAMP; poolNdx++)
{
if (m_supportedQueryType[poolNdx])
vk.cmdEndQuery(*cmdBuffer, *m_queryPools[poolNdx], QUERY_INDEX_CAPTURE_DRAWCALL);
}
endRenderPass(vk, *cmdBuffer);
transition2DImage(vk, *cmdBuffer, m_stateObjects->m_colorAttachmentImage->object(), vk::VK_IMAGE_ASPECT_COLOR_BIT,
vk::VK_IMAGE_LAYOUT_GENERAL, vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
vk::VK_ACCESS_TRANSFER_READ_BIT, vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT);
endCommandBuffer(vk, *cmdBuffer);
submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
deUint64 queryResults[NUM_QUERIES_IN_POOL] = { 0 };
size_t queryResultsSize = sizeof(queryResults);
bool passed = true;
// Occlusion and pipeline statistics queries verification
for (deUint32 poolNdx = 0; poolNdx < QUERY_TYPE_TIMESTAMP; poolNdx++)
{
if (m_supportedQueryType[poolNdx] == DE_FALSE)
continue;
vk::VkResult queryResult = vk.getQueryPoolResults(device, *m_queryPools[poolNdx], 0, NUM_QUERIES_IN_POOL, queryResultsSize, queryResults, sizeof(queryResults[0]), vk::VK_QUERY_RESULT_64_BIT);
if (queryResult == vk::VK_NOT_READY)
{
TCU_FAIL("Query result not available, but vkWaitIdle() was called.");
}
VK_CHECK(queryResult);
std::string name = (poolNdx == QUERY_TYPE_OCCLUSION) ? "OcclusionQueryResults" : "PipelineStatisticsQueryResults";
std::string desc = (poolNdx == QUERY_TYPE_OCCLUSION) ? "Occlusion query results" : "PipelineStatistics query results";
log << tcu::TestLog::Section(name, desc);
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(queryResults); ++ndx)
{
log << tcu::TestLog::Message << "query[slot == " << ndx
<< "] result == " << queryResults[ndx] << tcu::TestLog::EndMessage;
}
for (deUint32 queryNdx = 0; queryNdx < DE_LENGTH_OF_ARRAY(queryResults); ++queryNdx)
{
if (queryNdx == QUERY_INDEX_CAPTURE_EMPTY && queryResults[queryNdx] != 0u)
{
log << tcu::TestLog::Message << "vkGetQueryPoolResults returned "
"wrong value of query for index "
<< queryNdx << ", expected any zero value, got "
<< queryResults[0] << "." << tcu::TestLog::EndMessage;
passed = false;
}
if (queryNdx != QUERY_INDEX_CAPTURE_EMPTY && queryResults[queryNdx] == 0)
{
log << tcu::TestLog::Message << "vkGetQueryPoolResults returned "
"wrong value of query for index "
<< queryNdx << ", expected any non-zero value, got "
<< queryResults[0] << "." << tcu::TestLog::EndMessage;
passed = false;
}
}
log << tcu::TestLog::EndSection;
}
// Timestamp query verification
if (m_supportedQueryType[QUERY_TYPE_TIMESTAMP])
{
std::pair<deUint64, deUint64> queryResultsWithAvailabilityBit[NUM_QUERIES_IN_POOL];
size_t queryResultsWithAvailabilityBitSize = sizeof(queryResultsWithAvailabilityBit);
vk::VkResult queryResult = vk.getQueryPoolResults(device, *m_queryPools[QUERY_TYPE_TIMESTAMP], 0, NUM_QUERIES_IN_POOL, queryResultsWithAvailabilityBitSize, &queryResultsWithAvailabilityBit[0], sizeof(queryResultsWithAvailabilityBit[0]), vk::VK_QUERY_RESULT_64_BIT | vk::VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
if (queryResult != vk::VK_NOT_READY)
{
TCU_FAIL("We don't have available one query, it should return VK_NOT_READY");
}
log << tcu::TestLog::Section("TimestampQueryResults",
"Timestamp query results");
for (int ndx = 0; ndx < NUM_QUERIES_IN_POOL; ++ndx)
{
log << tcu::TestLog::Message << "query[slot == " << ndx
<< "] result == " << queryResultsWithAvailabilityBit[ndx].first << tcu::TestLog::EndMessage;
}
for (deUint32 queryNdx = 0; queryNdx < NUM_QUERIES_IN_POOL; ++queryNdx)
{
if (queryNdx == QUERY_INDEX_CAPTURE_EMPTY && (queryResultsWithAvailabilityBit[queryNdx].first != 0u || queryResultsWithAvailabilityBit[queryNdx].second != 0u))
{
log << tcu::TestLog::Message << "vkGetQueryPoolResults returned "
"either wrong value of query for index "
<< queryNdx << " (expected any zero value, got "
<< queryResultsWithAvailabilityBit[queryNdx].first << ") or the result is available (" << queryResultsWithAvailabilityBit[queryNdx].second << ")"
<< tcu::TestLog::EndMessage;
passed = false;
}
if (queryNdx != QUERY_INDEX_CAPTURE_EMPTY && (queryResultsWithAvailabilityBit[queryNdx].first == 0u || queryResultsWithAvailabilityBit[queryNdx].second == 0u))
{
log << tcu::TestLog::Message << "vkGetQueryPoolResults returned "
"either wrong value of query for index "
<< queryNdx << " (expected any non-zero value, got "
<< queryResults[0] << ") or result is unavailable." << tcu::TestLog::EndMessage;
passed = false;
}
}
log << tcu::TestLog::EndSection;
}
if (passed)
{
return tcu::TestStatus(QP_TEST_RESULT_PASS, "Query result verification passed");
}
return tcu::TestStatus(QP_TEST_RESULT_FAIL, "Query result verification failed");
}
class SecondaryCommandBufferConcurrentTestInstance : public vkt::TestInstance
{
public:
SecondaryCommandBufferConcurrentTestInstance (vkt::Context &context);
~SecondaryCommandBufferConcurrentTestInstance (void);
private:
tcu::TestStatus iterate (void);
enum
{
NUM_QUERIES_IN_POOL = 2,
QUERY_INDEX_CAPTURE_EMPTY = 0,
QUERY_INDEX_CAPTURE_DRAWCALL = 1,
NUM_VERTICES_IN_DRAWCALL = 3
};
std::unique_ptr<StateObjects> m_stateObjects;
vk::Move<vk::VkQueryPool> m_queryPools[NUM_QUERY_POOLS];
deBool m_supportedQueryType[NUM_QUERY_POOLS];
};
SecondaryCommandBufferConcurrentTestInstance::SecondaryCommandBufferConcurrentTestInstance (vkt::Context &context)
: TestInstance (context)
{
// Check support for multiple query types
{
for(deUint32 poolNdx = 0; poolNdx < NUM_QUERY_POOLS; poolNdx++)
m_supportedQueryType[poolNdx] = DE_FALSE;
deUint32 numSupportedQueryTypes = 0;
m_supportedQueryType[QUERY_TYPE_OCCLUSION] = DE_TRUE;
numSupportedQueryTypes++;
if (context.getDeviceFeatures().pipelineStatisticsQuery)
{
m_supportedQueryType[QUERY_TYPE_PIPELINE_STATISTICS] = DE_TRUE;
numSupportedQueryTypes++;
}
// Check support for timestamp queries
{
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
const std::vector<vk::VkQueueFamilyProperties> queueProperties = vk::getPhysicalDeviceQueueFamilyProperties(context.getInstanceInterface(), context.getPhysicalDevice());
DE_ASSERT(queueFamilyIndex < (deUint32)queueProperties.size());
if (queueProperties[queueFamilyIndex].timestampValidBits)
{
m_supportedQueryType[QUERY_TYPE_TIMESTAMP] = DE_TRUE;
numSupportedQueryTypes++;
}
}
if (numSupportedQueryTypes < 2)
throw tcu::NotSupportedError("Device does not support multiple query types");
}
m_stateObjects = std::unique_ptr<StateObjects>(new StateObjects(m_context.getDeviceInterface(), m_context, NUM_VERTICES_IN_DRAWCALL, vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST));
const vk::VkDevice device = m_context.getDevice();
const vk::DeviceInterface& vk = m_context.getDeviceInterface();
for(deUint32 poolNdx = 0; poolNdx < NUM_QUERY_POOLS; poolNdx++)
{
if (!m_supportedQueryType[poolNdx])
continue;
vk::VkQueryPoolCreateInfo queryPoolCreateInfo =
{
vk::VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
DE_NULL,
0u,
static_cast<vk::VkQueryType>(poolNdx),
NUM_QUERIES_IN_POOL,
0u,
};
if (poolNdx == QUERY_TYPE_PIPELINE_STATISTICS)
queryPoolCreateInfo.pipelineStatistics = vk::VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT;
m_queryPools[poolNdx] = createQueryPool(vk, device, &queryPoolCreateInfo, /*pAllocator*/ DE_NULL);
}
std::vector<tcu::Vec4> vertices(NUM_VERTICES_IN_DRAWCALL);
vertices[0] = tcu::Vec4(0.5, 0.5, 0.0, 1.0);
vertices[1] = tcu::Vec4(0.5, 0.0, 0.0, 1.0);
vertices[2] = tcu::Vec4(0.0, 0.5, 0.0, 1.0);
m_stateObjects->setVertices(vk, vertices);
}
SecondaryCommandBufferConcurrentTestInstance::~SecondaryCommandBufferConcurrentTestInstance (void)
{
}
void beginSecondaryCommandBuffer (const vk::DeviceInterface& vk,
const vk::VkCommandBuffer secondaryCmdBuffer,
const vk::VkCommandBufferInheritanceInfo bufferInheritanceInfo)
{
const vk::VkCommandBufferUsageFlags flags = bufferInheritanceInfo.renderPass != DE_NULL
? (vk::VkCommandBufferUsageFlags)vk::VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT
: (vk::VkCommandBufferUsageFlags)0u;
const vk::VkCommandBufferBeginInfo beginInfo =
{
vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // sType
DE_NULL, // pNext
flags, // flags
&bufferInheritanceInfo, // pInheritanceInfo
};
VK_CHECK(vk.beginCommandBuffer(secondaryCmdBuffer, &beginInfo));
}
tcu::TestStatus SecondaryCommandBufferConcurrentTestInstance::iterate (void)
{
tcu::TestLog &log = m_context.getTestContext().getLog();
const vk::VkDevice device = m_context.getDevice();
const vk::VkQueue queue = m_context.getUniversalQueue();
const vk::DeviceInterface& vk = m_context.getDeviceInterface();
const deBool inheritedQueries = m_context.getDeviceFeatures().inheritedQueries;
const CmdPoolCreateInfo cmdPoolCreateInfo (m_context.getUniversalQueueFamilyIndex());
vk::Move<vk::VkCommandPool> cmdPool = vk::createCommandPool(vk, device, &cmdPoolCreateInfo);
vk::Unique<vk::VkCommandBuffer> cmdBufferPrimary (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY));
vk::Unique<vk::VkCommandBuffer> cmdBufferSecondary (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_SECONDARY));
// Secondary command buffer recording.
{
// Begin secondary command buffer
const vk::VkCommandBufferInheritanceInfo secCmdBufInheritInfo =
{
vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO,
DE_NULL,
*m_stateObjects->m_renderPass, // renderPass
0u, // subpass
*m_stateObjects->m_framebuffer, // framebuffer
inheritedQueries ? VK_TRUE : VK_FALSE, // occlusionQueryEnable
(vk::VkQueryControlFlags)0u, // queryFlags
(vk::VkQueryPipelineStatisticFlags)0u, // pipelineStatistics
};
beginSecondaryCommandBuffer(vk, *cmdBufferSecondary, secCmdBufInheritInfo);
vk.cmdBindPipeline(*cmdBufferSecondary, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_stateObjects->m_pipeline);
vk::VkBuffer vertexBuffer = m_stateObjects->m_vertexBuffer->object();
const vk::VkDeviceSize vertexBufferOffset = 0;
vk.cmdBindVertexBuffers(*cmdBufferSecondary, 0, 1, &vertexBuffer, &vertexBufferOffset);
if (!inheritedQueries && m_supportedQueryType[QUERY_TYPE_OCCLUSION])
vk.cmdBeginQuery(*cmdBufferSecondary, *m_queryPools[QUERY_TYPE_OCCLUSION], QUERY_INDEX_CAPTURE_DRAWCALL, 0u);
// Run pipeline statistics queries capture in the second command buffer
if (m_supportedQueryType[QUERY_TYPE_PIPELINE_STATISTICS])
vk.cmdBeginQuery(*cmdBufferSecondary, *m_queryPools[QUERY_TYPE_PIPELINE_STATISTICS], QUERY_INDEX_CAPTURE_DRAWCALL, 0u);
// Timestamp query happening in the secondary command buffer
if (m_supportedQueryType[QUERY_TYPE_TIMESTAMP])
vk.cmdWriteTimestamp(*cmdBufferSecondary, vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, *m_queryPools[QUERY_TYPE_TIMESTAMP], QUERY_INDEX_CAPTURE_DRAWCALL);
vk.cmdDraw(*cmdBufferSecondary, NUM_VERTICES_IN_DRAWCALL, 1, 0, 0);
if (m_supportedQueryType[QUERY_TYPE_PIPELINE_STATISTICS])
vk.cmdEndQuery(*cmdBufferSecondary, *m_queryPools[QUERY_TYPE_PIPELINE_STATISTICS], QUERY_INDEX_CAPTURE_DRAWCALL);
if (!inheritedQueries && m_supportedQueryType[QUERY_TYPE_OCCLUSION])
vk.cmdEndQuery(*cmdBufferSecondary, *m_queryPools[QUERY_TYPE_OCCLUSION], QUERY_INDEX_CAPTURE_DRAWCALL);
endCommandBuffer(vk, *cmdBufferSecondary);
}
// Primary command buffer recording
{
beginCommandBuffer(vk, *cmdBufferPrimary);
initialTransitionColor2DImage(vk, *cmdBufferPrimary, m_stateObjects->m_colorAttachmentImage->object(), vk::VK_IMAGE_LAYOUT_GENERAL,
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
initialTransitionDepth2DImage(vk, *cmdBufferPrimary, m_stateObjects->m_DepthImage->object(), vk::VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
vk::VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, vk::VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | vk::VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
std::vector<vk::VkClearValue> renderPassClearValues(2);
deMemset(&renderPassClearValues[0], 0, static_cast<int>(renderPassClearValues.size()) * sizeof(vk::VkClearValue));
for (deUint32 poolNdx = 0u; poolNdx < NUM_QUERY_POOLS; poolNdx++)
{
if (m_supportedQueryType[poolNdx])
vk.cmdResetQueryPool(*cmdBufferPrimary, *m_queryPools[poolNdx], 0u, NUM_QUERIES_IN_POOL);
}
for (deUint32 poolNdx = 0u; poolNdx < QUERY_TYPE_TIMESTAMP; poolNdx++)
{
if (m_supportedQueryType[poolNdx])
vk.cmdBeginQuery(*cmdBufferPrimary, *m_queryPools[poolNdx], QUERY_INDEX_CAPTURE_EMPTY, 0u);
}
for (deUint32 poolNdx = 0u; poolNdx < QUERY_TYPE_TIMESTAMP; poolNdx++)
{
if (m_supportedQueryType[poolNdx])
vk.cmdEndQuery(*cmdBufferPrimary, *m_queryPools[poolNdx], QUERY_INDEX_CAPTURE_EMPTY);
}
// Run oclussion queries capture in the primary command buffer, inherit the counters for the secondary command buffer
if (inheritedQueries && m_supportedQueryType[QUERY_TYPE_OCCLUSION])
vk.cmdBeginQuery(*cmdBufferPrimary, *m_queryPools[QUERY_TYPE_OCCLUSION], QUERY_INDEX_CAPTURE_DRAWCALL, 0u);
beginRenderPass(vk, *cmdBufferPrimary, *m_stateObjects->m_renderPass, *m_stateObjects->m_framebuffer, vk::makeRect2D(0, 0, StateObjects::WIDTH, StateObjects::HEIGHT), (deUint32)renderPassClearValues.size(), &renderPassClearValues[0], vk::VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
vk.cmdExecuteCommands(*cmdBufferPrimary, 1u, &cmdBufferSecondary.get());
endRenderPass(vk, *cmdBufferPrimary);
if (inheritedQueries && m_supportedQueryType[QUERY_TYPE_OCCLUSION])
vk.cmdEndQuery(*cmdBufferPrimary, *m_queryPools[QUERY_TYPE_OCCLUSION], QUERY_INDEX_CAPTURE_DRAWCALL);
transition2DImage(vk, *cmdBufferPrimary,
m_stateObjects->m_colorAttachmentImage->object(),
vk::VK_IMAGE_ASPECT_COLOR_BIT,
vk::VK_IMAGE_LAYOUT_GENERAL,
vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
vk::VK_ACCESS_TRANSFER_READ_BIT,
vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
vk::VK_PIPELINE_STAGE_TRANSFER_BIT);
endCommandBuffer(vk, *cmdBufferPrimary);
}
submitCommandsAndWait(vk, device, queue, cmdBufferPrimary.get());
deUint64 queryResults[NUM_QUERIES_IN_POOL] = { 0 };
size_t queryResultsSize = sizeof(queryResults);
bool passed = true;
// Occlusion and pipeline statistics queries verification
for (deUint32 poolNdx = 0; poolNdx < QUERY_TYPE_TIMESTAMP; poolNdx++)
{
if (!m_supportedQueryType[poolNdx])
continue;
vk::VkResult queryResult = vk.getQueryPoolResults(device, *m_queryPools[poolNdx], 0, NUM_QUERIES_IN_POOL, queryResultsSize, queryResults, sizeof(queryResults[0]), vk::VK_QUERY_RESULT_64_BIT);
if (queryResult == vk::VK_NOT_READY)
{
TCU_FAIL("Query result not available, but vkWaitIdle() was called.");
}
VK_CHECK(queryResult);
std::string name = (poolNdx == QUERY_TYPE_OCCLUSION) ? "OcclusionQueryResults" : "PipelineStatisticsQueryResults";
std::string desc = (poolNdx == QUERY_TYPE_OCCLUSION) ? "Occlusion query results" : "PipelineStatistics query results";
log << tcu::TestLog::Section(name, desc);
for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(queryResults); ++ndx)
{
log << tcu::TestLog::Message << "query[slot == " << ndx
<< "] result == " << queryResults[ndx] << tcu::TestLog::EndMessage;
}
for (deUint32 queryNdx = 0; queryNdx < DE_LENGTH_OF_ARRAY(queryResults); ++queryNdx)
{
if (queryNdx == QUERY_INDEX_CAPTURE_EMPTY && queryResults[queryNdx] != 0u)
{
log << tcu::TestLog::Message << "vkGetQueryPoolResults returned "
"wrong value of query for index "
<< queryNdx << ", expected any zero value, got "
<< queryResults[0] << "." << tcu::TestLog::EndMessage;
passed = false;
}
if (queryNdx != QUERY_INDEX_CAPTURE_EMPTY && queryResults[queryNdx] == 0)
{
log << tcu::TestLog::Message << "vkGetQueryPoolResults returned "
"wrong value of query for index "
<< queryNdx << ", expected any non-zero value, got "
<< queryResults[0] << "." << tcu::TestLog::EndMessage;
passed = false;
}
}
log << tcu::TestLog::EndSection;
}
// Timestamp query verification
if (m_supportedQueryType[QUERY_TYPE_TIMESTAMP])
{
std::pair<deUint64, deUint64> queryResultsWithAvailabilityBit[NUM_QUERIES_IN_POOL];
size_t queryResultsWithAvailabilityBitSize = sizeof(queryResultsWithAvailabilityBit);
vk::VkResult queryResult = vk.getQueryPoolResults(device, *m_queryPools[QUERY_TYPE_TIMESTAMP], 0, NUM_QUERIES_IN_POOL, queryResultsWithAvailabilityBitSize, &queryResultsWithAvailabilityBit[0], sizeof(queryResultsWithAvailabilityBit[0]), vk::VK_QUERY_RESULT_64_BIT | vk::VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
if (queryResult != vk::VK_NOT_READY)
{
TCU_FAIL("We don't have available one query, it should return VK_NOT_READY");
}
log << tcu::TestLog::Section("TimestampQueryResults",
"Timestamp query results");
for (int ndx = 0; ndx < NUM_QUERIES_IN_POOL; ++ndx)
{
log << tcu::TestLog::Message << "query[slot == " << ndx
<< "] result == " << queryResultsWithAvailabilityBit[ndx].first << tcu::TestLog::EndMessage;
}
for (deUint32 queryNdx = 0; queryNdx < NUM_QUERIES_IN_POOL; ++queryNdx)
{
if (queryNdx == QUERY_INDEX_CAPTURE_EMPTY && (queryResultsWithAvailabilityBit[queryNdx].first != 0u || queryResultsWithAvailabilityBit[queryNdx].second != 0u))
{
log << tcu::TestLog::Message << "vkGetQueryPoolResults returned "
"either wrong value of query for index "
<< queryNdx << " (expected any zero value, got "
<< queryResultsWithAvailabilityBit[queryNdx].first << ") or the result is available (" << queryResultsWithAvailabilityBit[queryNdx].second << ")"
<< tcu::TestLog::EndMessage;
passed = false;
}
if (queryNdx != QUERY_INDEX_CAPTURE_EMPTY && (queryResultsWithAvailabilityBit[queryNdx].first == 0u || queryResultsWithAvailabilityBit[queryNdx].second == 0u))
{
log << tcu::TestLog::Message << "vkGetQueryPoolResults returned "
"either wrong value of query for index "
<< queryNdx << " (expected any non-zero value, got "
<< queryResults[0] << ") or result is unavailable." << tcu::TestLog::EndMessage;
passed = false;
}
}
log << tcu::TestLog::EndSection;
}
if (passed)
{
return tcu::TestStatus(QP_TEST_RESULT_PASS, "Query result verification passed");
}
return tcu::TestStatus(QP_TEST_RESULT_FAIL, "Query result verification failed");
}
template<class Instance>
class QueryPoolConcurrentTest : public vkt::TestCase
{
public:
QueryPoolConcurrentTest (tcu::TestContext &context, const char *name, const char *description)
: TestCase (context, name, description)
{
}
private:
vkt::TestInstance* createInstance (vkt::Context& context) const
{
return new Instance(context);
}
void initPrograms(vk::SourceCollections& programCollection) const
{
const std::string fragSrc = std::string(
"#version 400\n"
"layout(location = 0) out vec4 out_FragColor;\n"
"void main()\n"
"{\n"
" out_FragColor = vec4(0.07, 0.48, 0.75, 1.0);\n"
" if ((int(gl_FragCoord.x) % 2) == (int(gl_FragCoord.y) % 2))\n"
" discard;\n"
"}");
programCollection.glslSources.add("frag") << glu::FragmentSource(fragSrc.c_str());
programCollection.glslSources.add("vert") << glu::VertexSource("#version 430\n"
"layout(location = 0) in vec4 in_Position;\n"
"out gl_PerVertex { vec4 gl_Position; float gl_PointSize; };\n"
"void main() {\n"
" gl_Position = in_Position;\n"
" gl_PointSize = 1.0;\n"
"}\n");
}
};
} //anonymous
QueryPoolConcurrentTests::QueryPoolConcurrentTests (tcu::TestContext &testCtx)
: TestCaseGroup(testCtx, "concurrent_queries", "Tests for concurrent queries")
{
/* Left blank on purpose */
}
QueryPoolConcurrentTests::~QueryPoolConcurrentTests (void)
{
/* Left blank on purpose */
}
void QueryPoolConcurrentTests::init (void)
{
addChild(new QueryPoolConcurrentTest<PrimaryCommandBufferConcurrentTestInstance>(m_testCtx, "primary_command_buffer", ""));
addChild(new QueryPoolConcurrentTest<SecondaryCommandBufferConcurrentTestInstance>(m_testCtx, "secondary_command_buffer", ""));
}
} //QueryPool
} //vkt