| /*------------------------------------------------------------------------ |
| * 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); |
| 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()); |
| } |
| |
| 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 |
| }; |
| |
| struct OcclusionQueryTestVector |
| { |
| vk::VkQueryControlFlags queryControlFlags; |
| OcclusionQueryResultSize queryResultSize; |
| OcclusionQueryWait queryWait; |
| OcclusionQueryResultsMode queryResultsMode; |
| vk::VkDeviceSize queryResultsStride; |
| bool queryResultsAvailability; |
| vk::VkPrimitiveTopology primitiveTopology; |
| bool discardHalf; |
| }; |
| |
| 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); |
| |
| 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<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); |
| } |
| |
| BasicOcclusionQueryTestInstance::~BasicOcclusionQueryTestInstance (void) |
| { |
| if (m_stateObjects) |
| delete m_stateObjects; |
| |
| if (m_queryPool != DE_NULL) |
| { |
| const vk::VkDevice device = m_context.getDevice(); |
| const vk::DeviceInterface& vk = m_context.getDeviceInterface(); |
| |
| vk.destroyQueryPool(device, m_queryPool, /*pAllocator*/ DE_NULL); |
| } |
| } |
| |
| 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<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)); |
| |
| 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); |
| |
| 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; |
| |
| vk::Move<vk::VkCommandBuffer> recordQueryPoolReset (vk::VkCommandPool commandPool); |
| vk::Move<vk::VkCommandBuffer> recordRender (vk::VkCommandPool commandPool); |
| vk::Move<vk::VkCommandBuffer> 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<Buffer> m_queryPoolResultsBuffer; |
| |
| vk::Move<vk::VkCommandPool> m_commandPool; |
| vk::Move<vk::VkCommandBuffer> m_queryPoolResetCommandBuffer; |
| vk::Move<vk::VkCommandBuffer> m_renderCommandBuffer; |
| vk::Move<vk::VkCommandBuffer> 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); |
| |
| 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) |
| { |
| const vk::VkDeviceSize resultsBufferSize = m_testVector.queryResultsStride * NUM_QUERIES_IN_POOL; |
| 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) |
| { |
| const vk::VkDevice device = m_context.getDevice(); |
| |
| if (m_stateObjects) |
| delete m_stateObjects; |
| |
| if (m_queryPool != DE_NULL) |
| { |
| const vk::DeviceInterface& vk = m_context.getDeviceInterface(); |
| vk.destroyQueryPool(device, m_queryPool, /*pAllocator*/ DE_NULL); |
| } |
| } |
| |
| 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<tcu::Vec4> 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<vk::VkCommandBuffer> OcclusionQueryTestInstance::recordQueryPoolReset (vk::VkCommandPool cmdPool) |
| { |
| const vk::VkDevice device = m_context.getDevice(); |
| const vk::DeviceInterface& vk = m_context.getDeviceInterface(); |
| |
| DE_ASSERT(hasSeparateResetCmdBuf()); |
| |
| vk::Move<vk::VkCommandBuffer> 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; |
| } |
| |
| vk::Move<vk::VkCommandBuffer> OcclusionQueryTestInstance::recordRender (vk::VkCommandPool cmdPool) |
| { |
| const vk::VkDevice device = m_context.getDevice(); |
| const vk::DeviceInterface& vk = m_context.getDeviceInterface(); |
| |
| vk::Move<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)); |
| |
| 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); |
| 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); |
| 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); |
| 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.cmdCopyQueryPoolResults(*cmdBuffer, m_queryPool, 0, NUM_QUERIES_IN_POOL, m_queryPoolResultsBuffer->object(), /*dstOffset*/ 0, m_testVector.queryResultsStride, 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); |
| } |
| |
| 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<vk::VkCommandBuffer> OcclusionQueryTestInstance::recordCopyResults (vk::VkCommandPool cmdPool) |
| { |
| const vk::VkDevice device = m_context.getDevice(); |
| const vk::DeviceInterface& vk = m_context.getDeviceInterface(); |
| |
| vk::Move<vk::VkCommandBuffer> cmdBuffer (vk::allocateCommandBuffer(vk, device, cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY)); |
| |
| beginCommandBuffer(vk, *cmdBuffer); |
| vk.cmdCopyQueryPoolResults(*cmdBuffer, m_queryPool, 0, NUM_QUERIES_IN_POOL, m_queryPoolResultsBuffer->object(), /*dstOffset*/ 0, m_testVector.queryResultsStride, 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(); |
| std::vector<deUint8> resultsBuffer (static_cast<size_t>(m_testVector.queryResultsStride) * 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 void* allocationData = allocation.getHostPtr(); |
| |
| vk::invalidateAlloc(vk, device, allocation); |
| |
| deMemcpy(&resultsBuffer[0], allocationData, resultsBuffer.size()); |
| } |
| |
| for (int queryNdx = 0; queryNdx < NUM_QUERIES_IN_POOL; queryNdx++) |
| { |
| const void* srcPtr = &resultsBuffer[queryNdx * static_cast<size_t>(m_testVector.queryResultsStride)]; |
| if (m_testVector.queryResultSize == RESULT_SIZE_32_BIT) |
| { |
| const deUint32* srcPtrTyped = static_cast<const deUint32*>(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<const deUint64*>(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<size_t>(m_testVector.queryResultsStride)]; |
| if (m_testVector.queryResultSize == RESULT_SIZE_32_BIT) |
| { |
| const deUint32* srcPtrTyped = static_cast<const deUint32*>(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<const deUint64*>(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 Instance> |
| 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; |
| |
| //Basic tests |
| { |
| OcclusionQueryTestVector testVector = baseTestVector; |
| testVector.queryControlFlags = 0; |
| addChild(new QueryPoolOcclusionTest<BasicOcclusionQueryTestInstance>(m_testCtx, "basic_conservative", "draw with conservative occlusion query", testVector)); |
| testVector.queryControlFlags = vk::VK_QUERY_CONTROL_PRECISE_BIT; |
| addChild(new QueryPoolOcclusionTest<BasicOcclusionQueryTestInstance>(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<OcclusionQueryTestInstance>(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 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[] = |
| { |
| 1 * resultSize, |
| 2 * resultSize, |
| 3 * resultSize, |
| 4 * resultSize, |
| 5 * resultSize, |
| 13 * resultSize, |
| 1024 * resultSize |
| }; |
| |
| 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]; |
| |
| const vk::VkDeviceSize elementSize = (testVector.queryResultsAvailability ? resultSize * 2 : resultSize); |
| |
| if (elementSize > testVector.queryResultsStride) |
| { |
| continue; |
| } |
| |
| std::ostringstream testName; |
| std::ostringstream testDescr; |
| |
| testName << resultsModeStr[resultsModeIdx] |
| << "_results_size_" << resultSizeStr[resultSizeIdx] |
| << "_stride_" << strides[strideIdx] |
| << "_" << testAvailabilityStr[testAvailabilityIdx] << "_availability"; |
| |
| testDescr << resultsModeStr[resultsModeIdx] << " results " |
| << testAvailabilityStr[testAvailabilityIdx] << " availability bit as " |
| << resultSizeStr[resultSizeIdx] << "bit variables, with stride" << strides[strideIdx]; |
| |
| addChild(new QueryPoolOcclusionTest<OcclusionQueryTestInstance>(m_testCtx, testName.str().c_str(), testDescr.str().c_str(), testVector)); |
| } |
| } |
| } |
| } |
| |
| } |
| } |
| |
| } //QueryPool |
| } //vkt |