/*------------------------------------------------------------------------ * Vulkan Conformance Tests * ------------------------ * * Copyright (c) 2015 The Khronos Group Inc. * Copyright (c) 2015 Intel 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 Occlusion Query Tests *//*--------------------------------------------------------------------*/ #include "vktQueryPoolOcclusionTests.hpp" #include "vktTestCase.hpp" #include "vktDrawImageObjectUtil.hpp" #include "vktDrawBufferObjectUtil.hpp" #include "vktDrawCreateInfoUtil.hpp" #include "vkBuilderUtil.hpp" #include "vkRefUtil.hpp" #include "vkPrograms.hpp" #include "vkTypeUtil.hpp" #include "vkCmdUtil.hpp" #include "tcuTestLog.hpp" #include "tcuResource.hpp" #include "tcuImageCompare.hpp" #include "tcuCommandLine.hpp" namespace vkt { namespace QueryPool { using namespace Draw; namespace { struct StateObjects { StateObjects (const vk::DeviceInterface&vk, vkt::Context &context, const int numVertices, vk::VkPrimitiveTopology primitive, const bool noColorAttachments); void setVertices (const vk::DeviceInterface&vk, std::vector vertices); enum { WIDTH = 128, HEIGHT = 128 }; vkt::Context &m_context; vk::Move m_pipeline; vk::Move m_pipelineLayout; de::SharedPtr m_colorAttachmentImage, m_DepthImage; vk::Move m_attachmentView; vk::Move m_depthView; vk::Move m_renderPass; vk::Move m_framebuffer; de::SharedPtr m_vertexBuffer; vk::VkFormat m_colorAttachmentFormat; }; StateObjects::StateObjects (const vk::DeviceInterface&vk, vkt::Context &context, const int numVertices, vk::VkPrimitiveTopology primitive, const bool noColorAttachments) : 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(); vk::VkExtent3D imageExtent = { WIDTH, // width; HEIGHT, // height; 1 // depth; }; 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_depthView = vk::createImageView(vk, device, &depthViewInfo); // Renderpass and Framebuffer if (noColorAttachments) { RenderPassCreateInfo renderPassCreateInfo; 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 depthAttachmentReference = { 0, // 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 0, // colorCount DE_NULL, // pColorAttachments DE_NULL, // pResolveAttachments depthAttachmentReference, // depthStencilAttachment 0, // preserveCount DE_NULL)); // preserveAttachments m_renderPass = vk::createRenderPass(vk, device, &renderPassCreateInfo); std::vector attachments(1); attachments[0] = *m_depthView; FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, attachments, WIDTH, HEIGHT, 1); m_framebuffer = vk::createFramebuffer(vk, device, &framebufferCreateInfo); } else { 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); 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 attachments(2); attachments[0] = *m_attachmentView; attachments[1] = *m_depthView; FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, attachments, WIDTH, HEIGHT, 1); m_framebuffer = vk::createFramebuffer(vk, device, &framebufferCreateInfo); } { // Pipeline vk::Unique vs(vk::createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0)); vk::Unique 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(1, viewport), std::vector(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 vertices) { const vk::VkDevice device = m_context.getDevice(); tcu::Vec4 *ptr = reinterpret_cast(m_vertexBuffer->getBoundMemory().getHostPtr()); std::copy(vertices.begin(), vertices.end(), ptr); vk::flushAlloc(vk, device, m_vertexBuffer->getBoundMemory()); } enum OcclusionQueryResultSize { RESULT_SIZE_64_BIT, RESULT_SIZE_32_BIT, }; enum OcclusionQueryWait { WAIT_QUEUE, WAIT_QUERY, WAIT_NONE }; enum OcclusionQueryResultsMode { RESULTS_MODE_GET, RESULTS_MODE_GET_RESET, RESULTS_MODE_COPY, RESULTS_MODE_COPY_RESET }; enum OcculusionQueryClearOp { CLEAR_NOOP, CLEAR_COLOR, CLEAR_DEPTH }; struct OcclusionQueryTestVector { vk::VkQueryControlFlags queryControlFlags; OcclusionQueryResultSize queryResultSize; OcclusionQueryWait queryWait; OcclusionQueryResultsMode queryResultsMode; vk::VkDeviceSize queryResultsStride; bool queryResultsAvailability; vk::VkPrimitiveTopology primitiveTopology; bool discardHalf; deBool queryResultsDstOffset; OcculusionQueryClearOp clearOp; bool noColorAttachments; }; class BasicOcclusionQueryTestInstance : public vkt::TestInstance { public: BasicOcclusionQueryTestInstance (vkt::Context &context, const OcclusionQueryTestVector& testVector); ~BasicOcclusionQueryTestInstance (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 }; OcclusionQueryTestVector m_testVector; StateObjects* m_stateObjects; vk::VkQueryPool m_queryPool; }; BasicOcclusionQueryTestInstance::BasicOcclusionQueryTestInstance (vkt::Context &context, const OcclusionQueryTestVector& testVector) : TestInstance (context) , m_testVector (testVector) { DE_ASSERT(testVector.queryResultSize == RESULT_SIZE_64_BIT && testVector.queryWait == WAIT_QUEUE && (testVector.queryResultsMode == RESULTS_MODE_GET || testVector.queryResultsMode == RESULTS_MODE_GET_RESET) && testVector.queryResultsStride == sizeof(deUint64) && testVector.queryResultsAvailability == false && testVector.primitiveTopology == vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST); if ((m_testVector.queryControlFlags & vk::VK_QUERY_CONTROL_PRECISE_BIT) && !m_context.getDeviceFeatures().occlusionQueryPrecise) throw tcu::NotSupportedError("Precise occlusion queries are not supported"); m_stateObjects = new StateObjects(m_context.getDeviceInterface(), m_context, NUM_VERTICES_IN_DRAWCALL, m_testVector.primitiveTopology, m_testVector.noColorAttachments); const vk::VkDevice device = m_context.getDevice(); const vk::DeviceInterface& vk = m_context.getDeviceInterface(); const vk::VkQueryPoolCreateInfo queryPoolCreateInfo = { vk::VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, DE_NULL, 0u, vk::VK_QUERY_TYPE_OCCLUSION, NUM_QUERIES_IN_POOL, 0 }; VK_CHECK(vk.createQueryPool(device, &queryPoolCreateInfo, /*pAllocator*/ DE_NULL, &m_queryPool)); std::vector 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); } BasicOcclusionQueryTestInstance::~BasicOcclusionQueryTestInstance (void) { if (m_stateObjects) delete m_stateObjects; if (m_queryPool != DE_NULL) { #ifndef CTS_USES_VULKANSC const vk::VkDevice device = m_context.getDevice(); const vk::DeviceInterface& vk = m_context.getDeviceInterface(); vk.destroyQueryPool(device, m_queryPool, /*pAllocator*/ DE_NULL); #endif } } tcu::TestStatus BasicOcclusionQueryTestInstance::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(); if (m_testVector.queryResultsMode == RESULTS_MODE_GET_RESET) { // Check VK_EXT_host_query_reset is supported m_context.requireDeviceFunctionality("VK_EXT_host_query_reset"); if(m_context.getHostQueryResetFeatures().hostQueryReset == VK_FALSE) throw tcu::NotSupportedError(std::string("Implementation doesn't support resetting queries from the host").c_str()); } const CmdPoolCreateInfo cmdPoolCreateInfo (m_context.getUniversalQueueFamilyIndex()); vk::Move cmdPool = vk::createCommandPool(vk, device, &cmdPoolCreateInfo); vk::Unique cmdBuffer (vk::allocateCommandBuffer(vk, device, *cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); beginCommandBuffer(vk, *cmdBuffer); if (!m_testVector.noColorAttachments) 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 renderPassClearValues(2); deMemset(&renderPassClearValues[0], 0, static_cast(renderPassClearValues.size()) * sizeof(vk::VkClearValue)); if (m_testVector.queryResultsMode != RESULTS_MODE_GET_RESET) vk.cmdResetQueryPool(*cmdBuffer, m_queryPool, 0, 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); vk.cmdBeginQuery(*cmdBuffer, m_queryPool, QUERY_INDEX_CAPTURE_EMPTY, m_testVector.queryControlFlags); vk.cmdEndQuery(*cmdBuffer, m_queryPool, QUERY_INDEX_CAPTURE_EMPTY); vk.cmdBeginQuery(*cmdBuffer, m_queryPool, QUERY_INDEX_CAPTURE_DRAWCALL, m_testVector.queryControlFlags); vk.cmdDraw(*cmdBuffer, NUM_VERTICES_IN_DRAWCALL, 1, 0, 0); vk.cmdEndQuery(*cmdBuffer, m_queryPool, QUERY_INDEX_CAPTURE_DRAWCALL); endRenderPass(vk, *cmdBuffer); if (!m_testVector.noColorAttachments) 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); if (m_testVector.queryResultsMode == RESULTS_MODE_GET_RESET) vk.resetQueryPool(device, m_queryPool, 0, NUM_QUERIES_IN_POOL); submitCommandsAndWait(vk, device, queue, cmdBuffer.get()); deUint64 queryResults[NUM_QUERIES_IN_POOL] = { 0 }; size_t queryResultsSize = sizeof(queryResults); vk::VkResult queryResult = vk.getQueryPoolResults(device, m_queryPool, 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 avaliable, but vkWaitIdle() was called."); } VK_CHECK(queryResult); log << tcu::TestLog::Section("OcclusionQueryResults", "Occlusion query results"); for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(queryResults); ++ndx) { log << tcu::TestLog::Message << "query[slot == " << ndx << "] result == " << queryResults[ndx] << tcu::TestLog::EndMessage; } bool passed = true; for (int queryNdx = 0; queryNdx < DE_LENGTH_OF_ARRAY(queryResults); ++queryNdx) { deUint64 expectedValue; switch (queryNdx) { case QUERY_INDEX_CAPTURE_EMPTY: expectedValue = 0; break; case QUERY_INDEX_CAPTURE_DRAWCALL: expectedValue = NUM_VERTICES_IN_DRAWCALL; break; } if ((m_testVector.queryControlFlags & vk::VK_QUERY_CONTROL_PRECISE_BIT) || expectedValue == 0) { // require precise value if (queryResults[queryNdx] != expectedValue) { log << tcu::TestLog::Message << "vkGetQueryPoolResults returned " "wrong value of query for index " << queryNdx << ", expected " << expectedValue << ", got " << queryResults[0] << "." << tcu::TestLog::EndMessage; passed = false; } } else { // require imprecize value > 0 if (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; 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 OcclusionQueryTestInstance : public vkt::TestInstance { public: OcclusionQueryTestInstance (vkt::Context &context, const OcclusionQueryTestVector& testVector); ~OcclusionQueryTestInstance (void); private: tcu::TestStatus iterate (void); bool hasSeparateResetCmdBuf (void) const; bool hasSeparateCopyCmdBuf (void) const; void commandClearAttachment (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer); vk::Move recordQueryPoolReset (vk::VkCommandPool commandPool); vk::Move recordRender (vk::VkCommandPool commandPool); vk::Move recordCopyResults (vk::VkCommandPool commandPool); void captureResults (deUint64* retResults, deUint64* retAvailability, bool allowNotReady); void logResults (const deUint64* results, const deUint64* availability); bool validateResults (const deUint64* results, const deUint64* availability, bool allowUnavailable, vk::VkPrimitiveTopology primitiveTopology); enum { NUM_QUERIES_IN_POOL = 3, QUERY_INDEX_CAPTURE_ALL = 0, QUERY_INDEX_CAPTURE_PARTIALLY_OCCLUDED = 1, QUERY_INDEX_CAPTURE_OCCLUDED = 2 }; enum { NUM_VERTICES_IN_DRAWCALL = 3, NUM_VERTICES_IN_PARTIALLY_OCCLUDED_DRAWCALL = 3, NUM_VERTICES_IN_OCCLUDER_DRAWCALL = 3, NUM_VERTICES = NUM_VERTICES_IN_DRAWCALL + NUM_VERTICES_IN_PARTIALLY_OCCLUDED_DRAWCALL + NUM_VERTICES_IN_OCCLUDER_DRAWCALL }; enum { START_VERTEX = 0, START_VERTEX_PARTIALLY_OCCLUDED = START_VERTEX + NUM_VERTICES_IN_DRAWCALL, START_VERTEX_OCCLUDER = START_VERTEX_PARTIALLY_OCCLUDED + NUM_VERTICES_IN_PARTIALLY_OCCLUDED_DRAWCALL }; OcclusionQueryTestVector m_testVector; const vk::VkQueryResultFlags m_queryResultFlags; StateObjects* m_stateObjects; vk::VkQueryPool m_queryPool; de::SharedPtr m_queryPoolResultsBuffer; vk::Move m_commandPool; vk::Move m_queryPoolResetCommandBuffer; vk::Move m_renderCommandBuffer; vk::Move m_copyResultsCommandBuffer; }; OcclusionQueryTestInstance::OcclusionQueryTestInstance (vkt::Context &context, const OcclusionQueryTestVector& testVector) : vkt::TestInstance (context) , m_testVector (testVector) , m_queryResultFlags (((m_testVector.queryWait == WAIT_QUERY && m_testVector.queryResultsMode != RESULTS_MODE_COPY_RESET)? vk::VK_QUERY_RESULT_WAIT_BIT : 0) | (m_testVector.queryResultSize == RESULT_SIZE_64_BIT ? vk::VK_QUERY_RESULT_64_BIT : 0) | (m_testVector.queryResultsAvailability ? vk::VK_QUERY_RESULT_WITH_AVAILABILITY_BIT : 0)) { const vk::VkDevice device = m_context.getDevice(); const vk::DeviceInterface& vk = m_context.getDeviceInterface(); if ((m_testVector.queryControlFlags & vk::VK_QUERY_CONTROL_PRECISE_BIT) && !m_context.getDeviceFeatures().occlusionQueryPrecise) throw tcu::NotSupportedError("Precise occlusion queries are not supported"); m_stateObjects = new StateObjects(m_context.getDeviceInterface(), m_context, NUM_VERTICES_IN_DRAWCALL + NUM_VERTICES_IN_PARTIALLY_OCCLUDED_DRAWCALL + NUM_VERTICES_IN_OCCLUDER_DRAWCALL, m_testVector.primitiveTopology, m_testVector.noColorAttachments); const vk::VkQueryPoolCreateInfo queryPoolCreateInfo = { vk::VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, DE_NULL, 0u, vk::VK_QUERY_TYPE_OCCLUSION, NUM_QUERIES_IN_POOL, 0 }; VK_CHECK(vk.createQueryPool(device, &queryPoolCreateInfo, /*pAllocator*/ DE_NULL, &m_queryPool)); if (m_testVector.queryResultsMode == RESULTS_MODE_COPY || m_testVector.queryResultsMode == RESULTS_MODE_COPY_RESET) { deUint32 numQueriesinPool = NUM_QUERIES_IN_POOL + (m_testVector.queryResultsDstOffset ? 1 : 0); const vk::VkDeviceSize elementSize = m_testVector.queryResultSize == RESULT_SIZE_32_BIT ? sizeof(deUint32) : sizeof(deUint64); const vk::VkDeviceSize resultsBufferSize = m_testVector.queryResultsStride == 0 ? (elementSize + elementSize * m_testVector.queryResultsAvailability) * numQueriesinPool : m_testVector.queryResultsStride * numQueriesinPool; m_queryPoolResultsBuffer = Buffer::createAndAlloc(vk, device, BufferCreateInfo(resultsBufferSize, vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT), m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible); } const CmdPoolCreateInfo cmdPoolCreateInfo (m_context.getUniversalQueueFamilyIndex()); m_commandPool = vk::createCommandPool(vk, device, &cmdPoolCreateInfo); m_renderCommandBuffer = recordRender(*m_commandPool); if (hasSeparateResetCmdBuf()) { m_queryPoolResetCommandBuffer = recordQueryPoolReset(*m_commandPool); } if (hasSeparateCopyCmdBuf()) { m_copyResultsCommandBuffer = recordCopyResults(*m_commandPool); } } OcclusionQueryTestInstance::~OcclusionQueryTestInstance (void) { if (m_stateObjects) delete m_stateObjects; if (m_queryPool != DE_NULL) { #ifndef CTS_USES_VULKANSC const vk::VkDevice device = m_context.getDevice(); const vk::DeviceInterface& vk = m_context.getDeviceInterface(); vk.destroyQueryPool(device, m_queryPool, /*pAllocator*/ DE_NULL); #endif } } tcu::TestStatus OcclusionQueryTestInstance::iterate (void) { const vk::VkQueue queue = m_context.getUniversalQueue(); const vk::DeviceInterface& vk = m_context.getDeviceInterface(); tcu::TestLog& log = m_context.getTestContext().getLog(); std::vector vertices (NUM_VERTICES); if (m_testVector.queryResultsMode == RESULTS_MODE_GET_RESET) { // Check VK_EXT_host_query_reset is supported m_context.requireDeviceFunctionality("VK_EXT_host_query_reset"); if(m_context.getHostQueryResetFeatures().hostQueryReset == VK_FALSE) throw tcu::NotSupportedError(std::string("Implementation doesn't support resetting queries from the host").c_str()); } // 1st triangle vertices[START_VERTEX + 0] = tcu::Vec4( 0.5, 0.5, 0.5, 1.0); vertices[START_VERTEX + 1] = tcu::Vec4( 0.5, -0.5, 0.5, 1.0); vertices[START_VERTEX + 2] = tcu::Vec4(-0.5, 0.5, 0.5, 1.0); // 2nd triangle - partially occluding the scene vertices[START_VERTEX_PARTIALLY_OCCLUDED + 0] = tcu::Vec4(-0.5, -0.5, 1.0, 1.0); vertices[START_VERTEX_PARTIALLY_OCCLUDED + 1] = tcu::Vec4( 0.5, -0.5, 1.0, 1.0); vertices[START_VERTEX_PARTIALLY_OCCLUDED + 2] = tcu::Vec4(-0.5, 0.5, 1.0, 1.0); // 3nd triangle - fully occluding the scene vertices[START_VERTEX_OCCLUDER + 0] = tcu::Vec4( 0.5, 0.5, 1.0, 1.0); vertices[START_VERTEX_OCCLUDER + 1] = tcu::Vec4( 0.5, -0.5, 1.0, 1.0); vertices[START_VERTEX_OCCLUDER + 2] = tcu::Vec4(-0.5, 0.5, 1.0, 1.0); m_stateObjects->setVertices(vk, vertices); if (hasSeparateResetCmdBuf()) { const vk::VkSubmitInfo submitInfoReset = { vk::VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0u, // deUint32 waitSemaphoreCount; DE_NULL, // const VkSemaphore* pWaitSemaphores; (const vk::VkPipelineStageFlags*)DE_NULL, 1u, // deUint32 commandBufferCount; &m_queryPoolResetCommandBuffer.get(), // const VkCommandBuffer* pCommandBuffers; 0u, // deUint32 signalSemaphoreCount; DE_NULL // const VkSemaphore* pSignalSemaphores; }; vk.queueSubmit(queue, 1, &submitInfoReset, DE_NULL); // Trivially wait for reset to complete. This is to ensure the query pool is in reset state before // host accesses, so as to not insert any synchronization before capturing the results needed for WAIT_NONE // variant of test. VK_CHECK(vk.queueWaitIdle(queue)); } { const vk::VkSubmitInfo submitInfoRender = { vk::VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0, // deUint32 waitSemaphoreCount; DE_NULL, // const VkSemaphore* pWaitSemaphores; (const vk::VkPipelineStageFlags*)DE_NULL, 1, // deUint32 commandBufferCount; &m_renderCommandBuffer.get(), // const VkCommandBuffer* pCommandBuffers; 0, // deUint32 signalSemaphoreCount; DE_NULL // const VkSemaphore* pSignalSemaphores; }; if (!hasSeparateResetCmdBuf() && m_testVector.queryResultsMode == RESULTS_MODE_GET_RESET) vk.resetQueryPool(m_context.getDevice(), m_queryPool, 0, NUM_QUERIES_IN_POOL); vk.queueSubmit(queue, 1, &submitInfoRender, DE_NULL); } if (m_testVector.queryWait == WAIT_QUEUE) { VK_CHECK(vk.queueWaitIdle(queue)); } if (hasSeparateCopyCmdBuf()) { // In case of WAIT_QUEUE test variant, the previously submitted m_renderCommandBuffer did not // contain vkCmdCopyQueryResults, so additional cmd buffer is needed. // In the case of WAIT_NONE or WAIT_QUERY, vkCmdCopyQueryResults is stored in m_renderCommandBuffer. const vk::VkSubmitInfo submitInfo = { vk::VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType; DE_NULL, // const void* pNext; 0, // deUint32 waitSemaphoreCount; DE_NULL, // const VkSemaphore* pWaitSemaphores; (const vk::VkPipelineStageFlags*)DE_NULL, 1, // deUint32 commandBufferCount; &m_copyResultsCommandBuffer.get(), // const VkCommandBuffer* pCommandBuffers; 0, // deUint32 signalSemaphoreCount; DE_NULL // const VkSemaphore* pSignalSemaphores; }; vk.queueSubmit(queue, 1, &submitInfo, DE_NULL); } if (m_testVector.queryResultsMode == RESULTS_MODE_COPY || m_testVector.queryResultsMode == RESULTS_MODE_COPY_RESET) { // In case of vkCmdCopyQueryResults is used, test must always wait for it // to complete before we can read the result buffer. VK_CHECK(vk.queueWaitIdle(queue)); } deUint64 queryResults [NUM_QUERIES_IN_POOL]; deUint64 queryAvailability [NUM_QUERIES_IN_POOL]; // Allow not ready results only if nobody waited before getting the query results const bool allowNotReady = (m_testVector.queryWait == WAIT_NONE); captureResults(queryResults, queryAvailability, allowNotReady); log << tcu::TestLog::Section("OcclusionQueryResults", "Occlusion query results"); logResults(queryResults, queryAvailability); bool passed = validateResults(queryResults, queryAvailability, allowNotReady, m_testVector.primitiveTopology); log << tcu::TestLog::EndSection; if (m_testVector.queryResultsMode != RESULTS_MODE_COPY && m_testVector.queryResultsMode != RESULTS_MODE_COPY_RESET) { VK_CHECK(vk.queueWaitIdle(queue)); } if (passed) { return tcu::TestStatus(QP_TEST_RESULT_PASS, "Query result verification passed"); } return tcu::TestStatus(QP_TEST_RESULT_FAIL, "Query result verification failed"); } bool OcclusionQueryTestInstance::hasSeparateResetCmdBuf (void) const { // Determine if resetting query pool should be performed in separate command buffer // to avoid race condition between host query access and device query reset. if (m_testVector.queryResultsMode == RESULTS_MODE_COPY || m_testVector.queryResultsMode == RESULTS_MODE_COPY_RESET) { // We copy query results on device, so there is no race condition between // host and device return false; } if (m_testVector.queryWait == WAIT_QUEUE) { // We wait for queue to be complete before accessing query results return false; } // Separate command buffer with reset must be submitted & completed before // host accesses the query results return true; } bool OcclusionQueryTestInstance::hasSeparateCopyCmdBuf (void) const { // Copy query results must go into separate command buffer, if we want to wait on queue before that return ((m_testVector.queryResultsMode == RESULTS_MODE_COPY || m_testVector.queryResultsMode == RESULTS_MODE_COPY_RESET) && m_testVector.queryWait == WAIT_QUEUE); } vk::Move OcclusionQueryTestInstance::recordQueryPoolReset (vk::VkCommandPool cmdPool) { const vk::VkDevice device = m_context.getDevice(); const vk::DeviceInterface& vk = m_context.getDeviceInterface(); DE_ASSERT(hasSeparateResetCmdBuf()); vk::Move cmdBuffer (vk::allocateCommandBuffer(vk, device, cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); beginCommandBuffer(vk, *cmdBuffer); vk.cmdResetQueryPool(*cmdBuffer, m_queryPool, 0, NUM_QUERIES_IN_POOL); endCommandBuffer(vk, *cmdBuffer); return cmdBuffer; } void OcclusionQueryTestInstance::commandClearAttachment (const vk::DeviceInterface& vk, const vk::VkCommandBuffer commandBuffer) { if (m_testVector.clearOp == CLEAR_NOOP) return; const vk::VkOffset2D offset = vk::makeOffset2D(0, 0); const vk::VkExtent2D extent = vk::makeExtent2D(StateObjects::WIDTH, StateObjects::HEIGHT); const vk::VkClearAttachment attachment = { m_testVector.clearOp == CLEAR_COLOR ? vk::VK_IMAGE_ASPECT_COLOR_BIT : vk::VK_IMAGE_ASPECT_DEPTH_BIT, // VkImageAspectFlags aspectMask; m_testVector.clearOp == CLEAR_COLOR ? 0u : 1u, // uint32_t colorAttachment; m_testVector.clearOp == CLEAR_COLOR ? vk::makeClearValueColor(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f)) : vk::makeClearValueDepthStencil(0.0f, 0u) // VkClearValue clearValue; }; const vk::VkClearRect rect = { { offset, extent }, // VkRect2D rect; 0u, // uint32_t baseArrayLayer; 1u, // uint32_t layerCount; }; vk.cmdClearAttachments(commandBuffer, 1u, &attachment, 1u, &rect); } vk::Move OcclusionQueryTestInstance::recordRender (vk::VkCommandPool cmdPool) { const vk::VkDevice device = m_context.getDevice(); const vk::DeviceInterface& vk = m_context.getDeviceInterface(); vk::Move cmdBuffer (vk::allocateCommandBuffer(vk, device, cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); beginCommandBuffer(vk, *cmdBuffer); if (!m_testVector.noColorAttachments) 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 renderPassClearValues(2); deMemset(&renderPassClearValues[0], 0, static_cast(renderPassClearValues.size()) * sizeof(vk::VkClearValue)); if (!hasSeparateResetCmdBuf() && m_testVector.queryResultsMode != RESULTS_MODE_GET_RESET) { vk.cmdResetQueryPool(*cmdBuffer, m_queryPool, 0, 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); // Draw un-occluded geometry vk.cmdBeginQuery(*cmdBuffer, m_queryPool, QUERY_INDEX_CAPTURE_ALL, m_testVector.queryControlFlags); vk.cmdDraw(*cmdBuffer, NUM_VERTICES_IN_DRAWCALL, 1, START_VERTEX, 0); commandClearAttachment(vk, *cmdBuffer); vk.cmdEndQuery(*cmdBuffer, m_queryPool, QUERY_INDEX_CAPTURE_ALL); endRenderPass(vk, *cmdBuffer); 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); // Draw un-occluded geometry vk.cmdDraw(*cmdBuffer, NUM_VERTICES_IN_DRAWCALL, 1, START_VERTEX, 0); // Partially occlude geometry vk.cmdDraw(*cmdBuffer, NUM_VERTICES_IN_PARTIALLY_OCCLUDED_DRAWCALL, 1, START_VERTEX_PARTIALLY_OCCLUDED, 0); // Draw partially-occluded geometry vk.cmdBeginQuery(*cmdBuffer, m_queryPool, QUERY_INDEX_CAPTURE_PARTIALLY_OCCLUDED, m_testVector.queryControlFlags); vk.cmdDraw(*cmdBuffer, NUM_VERTICES_IN_DRAWCALL, 1, START_VERTEX, 0); commandClearAttachment(vk, *cmdBuffer); vk.cmdEndQuery(*cmdBuffer, m_queryPool, QUERY_INDEX_CAPTURE_PARTIALLY_OCCLUDED); endRenderPass(vk, *cmdBuffer); 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); // Draw un-occluded geometry vk.cmdDraw(*cmdBuffer, NUM_VERTICES_IN_DRAWCALL, 1, START_VERTEX, 0); // Partially occlude geometry vk.cmdDraw(*cmdBuffer, NUM_VERTICES_IN_PARTIALLY_OCCLUDED_DRAWCALL, 1, START_VERTEX_PARTIALLY_OCCLUDED, 0); // Occlude geometry vk.cmdDraw(*cmdBuffer, NUM_VERTICES_IN_OCCLUDER_DRAWCALL, 1, START_VERTEX_OCCLUDER, 0); // Draw occluded geometry vk.cmdBeginQuery(*cmdBuffer, m_queryPool, QUERY_INDEX_CAPTURE_OCCLUDED, m_testVector.queryControlFlags); vk.cmdDraw(*cmdBuffer, NUM_VERTICES_IN_DRAWCALL, 1, START_VERTEX, 0); commandClearAttachment(vk, *cmdBuffer); vk.cmdEndQuery(*cmdBuffer, m_queryPool, QUERY_INDEX_CAPTURE_OCCLUDED); endRenderPass(vk, *cmdBuffer); if (m_testVector.queryResultsMode == RESULTS_MODE_COPY_RESET) { vk.cmdResetQueryPool(*cmdBuffer, m_queryPool, 0, NUM_QUERIES_IN_POOL); } if ((m_testVector.queryResultsMode == RESULTS_MODE_COPY || m_testVector.queryResultsMode == RESULTS_MODE_COPY_RESET) && !hasSeparateCopyCmdBuf()) { vk::VkDeviceSize dstOffset = m_testVector.queryResultsDstOffset ? m_testVector.queryResultsStride : 0u; if (m_testVector.queryResultsStride != 0u) { vk.cmdCopyQueryPoolResults(*cmdBuffer, m_queryPool, 0, NUM_QUERIES_IN_POOL, m_queryPoolResultsBuffer->object(), dstOffset, m_testVector.queryResultsStride, m_queryResultFlags); } else { const vk::VkDeviceSize elementSize = m_testVector.queryResultSize == RESULT_SIZE_32_BIT ? sizeof(deUint32) : sizeof(deUint64); const vk::VkDeviceSize strideSize = elementSize + elementSize * m_testVector.queryResultsAvailability; for (int queryNdx = 0; queryNdx < NUM_QUERIES_IN_POOL; queryNdx++) { vk.cmdCopyQueryPoolResults(*cmdBuffer, m_queryPool, queryNdx, 1, m_queryPoolResultsBuffer->object(), strideSize * queryNdx, 0, m_queryResultFlags); } } bufferBarrier(vk, *cmdBuffer, m_queryPoolResultsBuffer->object(), vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_ACCESS_HOST_READ_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT); } if (!m_testVector.noColorAttachments) 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); return cmdBuffer; } vk::Move OcclusionQueryTestInstance::recordCopyResults (vk::VkCommandPool cmdPool) { const vk::VkDevice device = m_context.getDevice(); const vk::DeviceInterface& vk = m_context.getDeviceInterface(); vk::Move cmdBuffer (vk::allocateCommandBuffer(vk, device, cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); beginCommandBuffer(vk, *cmdBuffer); vk::VkDeviceSize dstOffset = m_testVector.queryResultsDstOffset ? m_testVector.queryResultsStride : 0u; if (m_testVector.queryResultsStride != 0u) { vk.cmdCopyQueryPoolResults(*cmdBuffer, m_queryPool, 0, NUM_QUERIES_IN_POOL, m_queryPoolResultsBuffer->object(), dstOffset, m_testVector.queryResultsStride, m_queryResultFlags); } else { const vk::VkDeviceSize elementSize = m_testVector.queryResultSize == RESULT_SIZE_32_BIT ? sizeof(deUint32) : sizeof(deUint64); const vk::VkDeviceSize strideSize = elementSize + elementSize * m_testVector.queryResultsAvailability; for (int queryNdx = 0; queryNdx < NUM_QUERIES_IN_POOL; queryNdx++) { vk.cmdCopyQueryPoolResults(*cmdBuffer, m_queryPool, queryNdx, 1, m_queryPoolResultsBuffer->object(), strideSize * queryNdx, 0, m_queryResultFlags); } } bufferBarrier(vk, *cmdBuffer, m_queryPoolResultsBuffer->object(), vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_ACCESS_HOST_READ_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT); endCommandBuffer(vk, *cmdBuffer); return cmdBuffer; } void OcclusionQueryTestInstance::captureResults (deUint64* retResults, deUint64* retAvailAbility, bool allowNotReady) { const vk::VkDevice device = m_context.getDevice(); const vk::DeviceInterface& vk = m_context.getDeviceInterface(); const vk::VkDeviceSize elementSize = m_testVector.queryResultSize == RESULT_SIZE_32_BIT ? sizeof(deUint32) : sizeof(deUint64); const vk::VkDeviceSize resultsSize = m_testVector.queryResultsStride == 0 ? elementSize + elementSize * m_testVector.queryResultsAvailability : m_testVector.queryResultsStride; std::vector resultsBuffer (static_cast(resultsSize * NUM_QUERIES_IN_POOL)); if (m_testVector.queryResultsMode == RESULTS_MODE_GET || m_testVector.queryResultsMode == RESULTS_MODE_GET_RESET) { vk::VkResult queryResult = vk.getQueryPoolResults(device, m_queryPool, 0, NUM_QUERIES_IN_POOL, resultsBuffer.size(), &resultsBuffer[0], m_testVector.queryResultsStride, m_queryResultFlags); if (queryResult == vk::VK_NOT_READY && !allowNotReady) { TCU_FAIL("getQueryPoolResults returned VK_NOT_READY, but results should be already available."); } else { VK_CHECK(queryResult); } } else if (m_testVector.queryResultsMode == RESULTS_MODE_COPY || m_testVector.queryResultsMode == RESULTS_MODE_COPY_RESET) { const vk::Allocation& allocation = m_queryPoolResultsBuffer->getBoundMemory(); const deUint8* allocationData = static_cast(allocation.getHostPtr()); const deInt32 indexData = m_testVector.queryResultsDstOffset ? (deInt32)m_testVector.queryResultsStride : 0u; vk::invalidateAlloc(vk, device, allocation); deMemcpy(&resultsBuffer[0], &allocationData[indexData], resultsBuffer.size()); } for (int queryNdx = 0; queryNdx < NUM_QUERIES_IN_POOL; queryNdx++) { const void* srcPtr = &resultsBuffer[queryNdx * static_cast(resultsSize)]; if (m_testVector.queryResultSize == RESULT_SIZE_32_BIT) { const deUint32* srcPtrTyped = static_cast(srcPtr); retResults[queryNdx] = *srcPtrTyped; if (m_testVector.queryResultsAvailability) { retAvailAbility[queryNdx] = *(srcPtrTyped + 1); } } else if (m_testVector.queryResultSize == RESULT_SIZE_64_BIT) { const deUint64* srcPtrTyped = static_cast(srcPtr); retResults[queryNdx] = *srcPtrTyped; if (m_testVector.queryResultsAvailability) { retAvailAbility[queryNdx] = *(srcPtrTyped + 1); } } else { TCU_FAIL("Wrong m_testVector.queryResultSize"); } } if (m_testVector.queryResultsMode == RESULTS_MODE_GET_RESET) { vk.resetQueryPool(device, m_queryPool, 0, NUM_QUERIES_IN_POOL); vk::VkResult queryResult = vk.getQueryPoolResults(device, m_queryPool, 0, NUM_QUERIES_IN_POOL, resultsBuffer.size(), &resultsBuffer[0], m_testVector.queryResultsStride, m_queryResultFlags); if (queryResult != vk::VK_NOT_READY) { TCU_FAIL("getQueryPoolResults did not return VK_NOT_READY"); } /* From Vulkan spec: * * If VK_QUERY_RESULT_WAIT_BIT and VK_QUERY_RESULT_PARTIAL_BIT are both not set then no result values are written to pData * for queries that are in the unavailable state at the time of the call, and vkGetQueryPoolResults returns VK_NOT_READY. * However, availability state is still written to pData for those queries if VK_QUERY_RESULT_WITH_AVAILABILITY_BIT is set. */ for (int queryNdx = 0; queryNdx < NUM_QUERIES_IN_POOL; queryNdx++) { const void* srcPtr = &resultsBuffer[queryNdx * static_cast(resultsSize)]; if (m_testVector.queryResultSize == RESULT_SIZE_32_BIT) { const deUint32* srcPtrTyped = static_cast(srcPtr); if (*srcPtrTyped != retResults[queryNdx]) { TCU_FAIL("getQueryPoolResults returned modified values"); } if (m_testVector.queryResultsAvailability && *(srcPtrTyped + 1) != 0) { TCU_FAIL("resetQueryPool did not disable availability bit"); } } else if (m_testVector.queryResultSize == RESULT_SIZE_64_BIT) { const deUint64* srcPtrTyped = static_cast(srcPtr); if (*srcPtrTyped != retResults[queryNdx]) { TCU_FAIL("getQueryPoolResults returned modified values"); } if (m_testVector.queryResultsAvailability && *(srcPtrTyped + 1) != 0) { TCU_FAIL("resetQueryPool did not disable availability bit"); } } else { TCU_FAIL("Wrong m_testVector.queryResultSize"); } } } } void OcclusionQueryTestInstance::logResults (const deUint64* results, const deUint64* availability) { tcu::TestLog& log = m_context.getTestContext().getLog(); for (int ndx = 0; ndx < NUM_QUERIES_IN_POOL; ++ndx) { if (!m_testVector.queryResultsAvailability) { log << tcu::TestLog::Message << "query[slot == " << ndx << "] result == " << results[ndx] << tcu::TestLog::EndMessage; } else { log << tcu::TestLog::Message << "query[slot == " << ndx << "] result == " << results[ndx] << ", availability == " << availability[ndx] << tcu::TestLog::EndMessage; } } } bool OcclusionQueryTestInstance::validateResults (const deUint64* results , const deUint64* availability, bool allowUnavailable, vk::VkPrimitiveTopology primitiveTopology) { bool passed = true; tcu::TestLog& log = m_context.getTestContext().getLog(); for (int queryNdx = 0; queryNdx < NUM_QUERIES_IN_POOL; ++queryNdx) { deUint64 expectedValueMin = 0; deUint64 expectedValueMax = 0; if (m_testVector.queryResultsMode == RESULTS_MODE_COPY_RESET) { DE_ASSERT(m_testVector.queryResultsAvailability); if (availability[queryNdx] != 0) { // In copy-reset mode results should always be unavailable due to the reset command issued before copying results. log << tcu::TestLog::Message << "query results availability was nonzero for index " << queryNdx << " when resetting the query before copying results" << tcu::TestLog::EndMessage; passed = false; } // Not interested in the actual results. continue; } else if (m_testVector.queryResultsAvailability && availability[queryNdx] == 0) { // query result was not available if (!allowUnavailable) { log << tcu::TestLog::Message << "query results availability was 0 for index " << queryNdx << ", expected any value greater than 0." << tcu::TestLog::EndMessage; passed = false; continue; } } else { // query is available, so expect proper result values if (primitiveTopology == vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST) { switch (queryNdx) { case QUERY_INDEX_CAPTURE_OCCLUDED: expectedValueMin = 0; expectedValueMax = 0; break; case QUERY_INDEX_CAPTURE_PARTIALLY_OCCLUDED: expectedValueMin = 1; expectedValueMax = 1; break; case QUERY_INDEX_CAPTURE_ALL: expectedValueMin = NUM_VERTICES_IN_DRAWCALL; expectedValueMax = NUM_VERTICES_IN_DRAWCALL; break; } } else if (primitiveTopology == vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST) { switch (queryNdx) { case QUERY_INDEX_CAPTURE_OCCLUDED: expectedValueMin = 0; expectedValueMax = 0; break; case QUERY_INDEX_CAPTURE_PARTIALLY_OCCLUDED: case QUERY_INDEX_CAPTURE_ALL: { const int primWidth = StateObjects::WIDTH / 2; const int primHeight = StateObjects::HEIGHT / 2; const int primArea = primWidth * primHeight / 2; if (m_testVector.discardHalf) { expectedValueMin = (int)(0.95f * primArea * 0.5f); expectedValueMax = (int)(1.05f * primArea * 0.5f); } else { expectedValueMin = (int)(0.97f * primArea); expectedValueMax = (int)(1.03f * primArea); } } } } else { TCU_FAIL("Unsupported primitive topology"); } } if ((m_testVector.queryControlFlags & vk::VK_QUERY_CONTROL_PRECISE_BIT) || (expectedValueMin == 0 && expectedValueMax == 0)) { // require precise value if (results[queryNdx] < expectedValueMin || results[queryNdx] > expectedValueMax) { log << tcu::TestLog::Message << "wrong value of query for index " << queryNdx << ", expected the value minimum of " << expectedValueMin << ", maximum of " << expectedValueMax << " got " << results[queryNdx] << "." << tcu::TestLog::EndMessage; passed = false; } } else { // require imprecise value greater than 0 if (results[queryNdx] == 0) { log << tcu::TestLog::Message << "wrong value of query for index " << queryNdx << ", expected any non-zero value, got " << results[queryNdx] << "." << tcu::TestLog::EndMessage; passed = false; } } } return passed; } template class QueryPoolOcclusionTest : public vkt::TestCase { public: QueryPoolOcclusionTest (tcu::TestContext &context, const char *name, const char *description, const OcclusionQueryTestVector& testVector) : TestCase (context, name, description) , m_testVector (testVector) { } private: vkt::TestInstance* createInstance (vkt::Context& context) const { return new Instance(context, m_testVector); } void initPrograms(vk::SourceCollections& programCollection) const { const char* const discard = " if ((int(gl_FragCoord.x) % 2) == (int(gl_FragCoord.y) % 2))\n" " discard;\n"; 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") + std::string(m_testVector.discardHalf ? 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"); } OcclusionQueryTestVector m_testVector; }; } //anonymous QueryPoolOcclusionTests::QueryPoolOcclusionTests (tcu::TestContext &testCtx) : TestCaseGroup(testCtx, "occlusion_query", "Tests for occlusion queries") { /* Left blank on purpose */ } QueryPoolOcclusionTests::~QueryPoolOcclusionTests (void) { /* Left blank on purpose */ } void QueryPoolOcclusionTests::init (void) { OcclusionQueryTestVector baseTestVector; baseTestVector.queryControlFlags = 0; baseTestVector.queryResultSize = RESULT_SIZE_64_BIT; baseTestVector.queryWait = WAIT_QUEUE; baseTestVector.queryResultsMode = RESULTS_MODE_GET; baseTestVector.queryResultsStride = sizeof(deUint64); baseTestVector.queryResultsAvailability = false; baseTestVector.primitiveTopology = vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST; baseTestVector.discardHalf = false; baseTestVector.clearOp = CLEAR_NOOP; baseTestVector.noColorAttachments = false; //Basic tests { OcclusionQueryTestVector testVector = baseTestVector; testVector.queryControlFlags = 0; addChild(new QueryPoolOcclusionTest(m_testCtx, "basic_conservative", "draw with conservative occlusion query", testVector)); testVector.queryControlFlags = vk::VK_QUERY_CONTROL_PRECISE_BIT; addChild(new QueryPoolOcclusionTest(m_testCtx, "basic_precise", "draw with precise occlusion query", testVector)); } // Functional test { const vk::VkQueryControlFlags controlFlags[] = { 0, vk::VK_QUERY_CONTROL_PRECISE_BIT }; const char* const controlFlagsStr[] = { "conservative", "precise" }; for (int controlFlagIdx = 0; controlFlagIdx < DE_LENGTH_OF_ARRAY(controlFlags); ++controlFlagIdx) { const vk::VkPrimitiveTopology primitiveTopology[] = { vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST, vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST }; const char* const primitiveTopologyStr[] = { "points", "triangles" }; for (int primitiveTopologyIdx = 0; primitiveTopologyIdx < DE_LENGTH_OF_ARRAY(primitiveTopology); ++primitiveTopologyIdx) { const OcclusionQueryResultSize resultSize[] = { RESULT_SIZE_32_BIT, RESULT_SIZE_64_BIT }; const char* const resultSizeStr[] = { "32", "64" }; for (int resultSizeIdx = 0; resultSizeIdx < DE_LENGTH_OF_ARRAY(resultSize); ++resultSizeIdx) { const OcclusionQueryWait wait[] = { WAIT_QUEUE, WAIT_QUERY }; const char* const waitStr[] = { "queue", "query" }; for (int waitIdx = 0; waitIdx < DE_LENGTH_OF_ARRAY(wait); ++waitIdx) { const OcclusionQueryResultsMode resultsMode[] = { RESULTS_MODE_GET, RESULTS_MODE_GET_RESET, RESULTS_MODE_COPY, RESULTS_MODE_COPY_RESET }; const char* const resultsModeStr[] = { "get", "get_reset", "copy", "copy_reset" }; for (int resultsModeIdx = 0; resultsModeIdx < DE_LENGTH_OF_ARRAY(resultsMode); ++resultsModeIdx) { if (wait[waitIdx] == WAIT_QUERY && resultsMode[resultsModeIdx] == RESULTS_MODE_GET_RESET) { /* In RESULTS_MODE_GET_RESET we are going to reset the queries and get the query pool results again * without issueing them, in order to check the availability field. In Vulkan spec it mentions that * vkGetQueryPoolResults may not return in finite time. Because of that, we skip those tests. */ continue; } const bool testAvailability[] = { false, true }; const char* const testAvailabilityStr[] = { "without", "with"}; for (int testAvailabilityIdx = 0; testAvailabilityIdx < DE_LENGTH_OF_ARRAY(testAvailability); ++testAvailabilityIdx) { if (resultsMode[resultsModeIdx] == RESULTS_MODE_COPY_RESET && (! testAvailability[testAvailabilityIdx])) { /* In RESULTS_MODE_COPY_RESET mode we will reset queries and make sure the availability flag is * set to zero. It does not make sense to run in this mode without obtaining the availability * flag. */ continue; } const bool discardHalf[] = { false, true }; const char* const discardHalfStr[] = { "", "_discard" }; for (int discardHalfIdx = 0; discardHalfIdx < DE_LENGTH_OF_ARRAY(discardHalf); ++discardHalfIdx) { OcclusionQueryTestVector testVector = baseTestVector; testVector.queryControlFlags = controlFlags[controlFlagIdx]; testVector.queryResultSize = resultSize[resultSizeIdx]; testVector.queryWait = wait[waitIdx]; testVector.queryResultsMode = resultsMode[resultsModeIdx]; testVector.queryResultsStride = testVector.queryResultSize == RESULT_SIZE_32_BIT ? sizeof(deUint32) : sizeof(deUint64); testVector.queryResultsAvailability = testAvailability[testAvailabilityIdx]; testVector.primitiveTopology = primitiveTopology[primitiveTopologyIdx]; testVector.discardHalf = discardHalf[discardHalfIdx]; if (testVector.discardHalf && testVector.primitiveTopology == vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST) continue; // Discarding half of the pixels in fragment shader doesn't make sense with one-pixel-sized points. if (testVector.queryResultsAvailability) { testVector.queryResultsStride *= 2; } std::ostringstream testName; std::ostringstream testDescr; testName << resultsModeStr[resultsModeIdx] << "_results" << "_" << controlFlagsStr[controlFlagIdx] << "_size_" << resultSizeStr[resultSizeIdx] << "_wait_" << waitStr[waitIdx] << "_" << testAvailabilityStr[testAvailabilityIdx] << "_availability" << "_draw_" << primitiveTopologyStr[primitiveTopologyIdx] << discardHalfStr[discardHalfIdx]; testDescr << "draw occluded " << primitiveTopologyStr[primitiveTopologyIdx] << "with " << controlFlagsStr[controlFlagIdx] << ", " << resultsModeStr[resultsModeIdx] << " results " << testAvailabilityStr[testAvailabilityIdx] << " availability bit as " << resultSizeStr[resultSizeIdx] << "bit variables," << (testVector.discardHalf ? " discarding half of the fragments," : "") << "wait for results on" << waitStr[waitIdx]; addChild(new QueryPoolOcclusionTest(m_testCtx, testName.str().c_str(), testDescr.str().c_str(), testVector)); } } } /* Tests for clear operation within a occulusion query activated. * The query shouldn't count internal driver operations relevant to the clear operations. */ const OcculusionQueryClearOp clearOp[] = { CLEAR_COLOR, CLEAR_DEPTH }; const char* const clearOpStr[] = { "clear_color", "clear_depth" }; for (int clearOpIdx = 0; clearOpIdx < DE_LENGTH_OF_ARRAY(clearOp); ++clearOpIdx) { OcclusionQueryTestVector testVector = baseTestVector; testVector.queryControlFlags = controlFlags[controlFlagIdx]; testVector.queryResultSize = resultSize[resultSizeIdx]; testVector.queryWait = wait[waitIdx]; testVector.queryResultsMode = RESULTS_MODE_GET; testVector.queryResultsStride = testVector.queryResultSize == RESULT_SIZE_32_BIT ? sizeof(deUint32) : sizeof(deUint64); testVector.primitiveTopology = primitiveTopology[primitiveTopologyIdx]; testVector.clearOp = clearOp[clearOpIdx]; std::ostringstream testName; std::ostringstream testDescr; testName << "get_results" << "_" << controlFlagsStr[controlFlagIdx] << "_size_" << resultSizeStr[resultSizeIdx] << "_wait_" << waitStr[waitIdx] << "_without_availability" << "_draw_" << primitiveTopologyStr[primitiveTopologyIdx] << "_" << clearOpStr[clearOpIdx]; testDescr << "draw occluded " << primitiveTopologyStr[primitiveTopologyIdx] << "with " << controlFlagsStr[controlFlagIdx] << ", " << "get results without availability bit " << "with " << clearOpStr[clearOpIdx] << " as " << resultSizeStr[resultSizeIdx] << "bit variables," << "wait for results on" << waitStr[waitIdx]; addChild(new QueryPoolOcclusionTest(m_testCtx, testName.str().c_str(), testDescr.str().c_str(), testVector)); } // Tests with no color attachments. { OcclusionQueryTestVector testVector = baseTestVector; testVector.queryControlFlags = controlFlags[controlFlagIdx]; testVector.queryResultSize = resultSize[resultSizeIdx]; testVector.queryWait = wait[waitIdx]; testVector.queryResultsMode = RESULTS_MODE_GET; testVector.queryResultsStride = testVector.queryResultSize == RESULT_SIZE_32_BIT ? sizeof(deUint32) : sizeof(deUint64); testVector.primitiveTopology = primitiveTopology[primitiveTopologyIdx]; testVector.noColorAttachments = true; std::ostringstream testName; std::ostringstream testDescr; testName << "get_results" << "_" << controlFlagsStr[controlFlagIdx] << "_size_" << resultSizeStr[resultSizeIdx] << "_wait_" << waitStr[waitIdx] << "_without_availability" << "_draw_" << primitiveTopologyStr[primitiveTopologyIdx] << "_no_color_attachments"; testDescr << "draw occluded " << primitiveTopologyStr[primitiveTopologyIdx] << "with " << controlFlagsStr[controlFlagIdx] << ", " << "get results without availability bit and attachments as " << resultSizeStr[resultSizeIdx] << "bit variables," << "wait for results on" << waitStr[waitIdx]; addChild(new QueryPoolOcclusionTest(m_testCtx, testName.str().c_str(), testDescr.str().c_str(), testVector)); } } } } } } // Test different strides { const OcclusionQueryResultsMode resultsMode[] = { RESULTS_MODE_GET, RESULTS_MODE_GET_RESET, RESULTS_MODE_COPY, RESULTS_MODE_COPY_RESET }; const char* const resultsModeStr[] = { "get", "get_reset", "copy", "copy_reset" }; for (int resultsModeIdx = 0; resultsModeIdx < DE_LENGTH_OF_ARRAY(resultsMode); ++resultsModeIdx) { const OcclusionQueryResultSize resultSizes[] = { RESULT_SIZE_32_BIT, RESULT_SIZE_64_BIT }; const char* const resultSizeStr[] = { "32", "64" }; const deBool copyQueryDstOffset[] = { DE_TRUE, DE_FALSE }; const char *const copyQueryDstOffsetStr[] = { "_dstoffset", ""}; const bool testAvailability[] = { false, true }; const char* const testAvailabilityStr[] = { "without", "with" }; for (int testAvailabilityIdx = 0; testAvailabilityIdx < DE_LENGTH_OF_ARRAY(testAvailability); ++testAvailabilityIdx) { if (resultsMode[resultsModeIdx] == RESULTS_MODE_COPY_RESET && (! testAvailability[testAvailabilityIdx])) { /* In RESULTS_MODE_COPY_RESET mode we will reset queries and make sure the availability flag is set to zero. It * does not make sense to run in this mode without obtaining the availability flag. */ continue; } for (int resultSizeIdx = 0; resultSizeIdx < DE_LENGTH_OF_ARRAY(resultSizes); ++resultSizeIdx) { const vk::VkDeviceSize resultSize = (resultSizes[resultSizeIdx] == RESULT_SIZE_32_BIT ? sizeof(deUint32) : sizeof(deUint64)); // \todo [2015-12-18 scygan] Ensure only stride values aligned to resultSize are allowed. Otherwise test should be extended. const vk::VkDeviceSize strides[] = { 0u, 1 * resultSize, 2 * resultSize, 3 * resultSize, 4 * resultSize, 5 * resultSize, 13 * resultSize, 1024 * resultSize }; for (int dstOffsetIdx = 0; dstOffsetIdx < DE_LENGTH_OF_ARRAY(copyQueryDstOffset); dstOffsetIdx++) { for (int strideIdx = 0; strideIdx < DE_LENGTH_OF_ARRAY(strides); strideIdx++) { OcclusionQueryTestVector testVector = baseTestVector; testVector.queryResultsMode = resultsMode[resultsModeIdx]; testVector.queryResultSize = resultSizes[resultSizeIdx]; testVector.queryResultsAvailability = testAvailability[testAvailabilityIdx]; testVector.queryResultsStride = strides[strideIdx]; testVector.queryResultsDstOffset = copyQueryDstOffset[dstOffsetIdx]; const vk::VkDeviceSize elementSize = (testVector.queryResultsAvailability ? resultSize * 2 : resultSize); if (elementSize > testVector.queryResultsStride && strides[strideIdx] != 0) { continue; } if (strides[strideIdx] == 0) { // Due to the nature of the test, the dstOffset is tested automatically when stride size is 0. if (testVector.queryResultsDstOffset) { continue; } // We are testing only VkCmdCopyQueryPoolResults with stride 0. if (testVector.queryResultsMode != RESULTS_MODE_COPY) { continue; } } std::ostringstream testName; std::ostringstream testDescr; testName << resultsModeStr[resultsModeIdx] << "_results_size_" << resultSizeStr[resultSizeIdx] << "_stride_" << strides[strideIdx] << "_" << testAvailabilityStr[testAvailabilityIdx] << "_availability" << copyQueryDstOffsetStr[dstOffsetIdx]; testDescr << resultsModeStr[resultsModeIdx] << " results " << testAvailabilityStr[testAvailabilityIdx] << " availability bit as " << resultSizeStr[resultSizeIdx] << "bit variables, with stride" << strides[strideIdx]; addChild(new QueryPoolOcclusionTest(m_testCtx, testName.str().c_str(), testDescr.str().c_str(), testVector)); } } } } } } } } //QueryPool } //vkt