| /*------------------------------------------------------------------------ |
| * Vulkan Conformance Tests |
| * ------------------------ |
| * |
| * Copyright (c) 2016 The Khronos Group Inc. |
| * Copyright (c) 2017 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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 Inverted depth ranges tests. |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "vktDrawInvertedDepthRangesTests.hpp" |
| #include "vktDrawCreateInfoUtil.hpp" |
| #include "vktDrawImageObjectUtil.hpp" |
| #include "vktDrawBufferObjectUtil.hpp" |
| #include "vktTestGroupUtil.hpp" |
| #include "vktTestCaseUtil.hpp" |
| |
| #include "vkPrograms.hpp" |
| #include "vkTypeUtil.hpp" |
| #include "vkImageUtil.hpp" |
| #include "vkCmdUtil.hpp" |
| |
| #include "tcuVector.hpp" |
| #include "tcuTextureUtil.hpp" |
| #include "tcuImageCompare.hpp" |
| #include "tcuTestLog.hpp" |
| |
| #include "deSharedPtr.hpp" |
| |
| namespace vkt |
| { |
| namespace Draw |
| { |
| namespace |
| { |
| using namespace vk; |
| using tcu::Vec4; |
| using de::SharedPtr; |
| using de::MovePtr; |
| |
| struct TestParams |
| { |
| VkBool32 depthClampEnable; |
| float minDepth; |
| float maxDepth; |
| }; |
| |
| class InvertedDepthRangesTestInstance : public TestInstance |
| { |
| public: |
| InvertedDepthRangesTestInstance (Context& context, const TestParams& params); |
| tcu::TestStatus iterate (void); |
| tcu::ConstPixelBufferAccess draw (const VkViewport viewport); |
| MovePtr<tcu::TextureLevel> generateReferenceImage (void) const; |
| |
| private: |
| const TestParams m_params; |
| const VkFormat m_colorAttachmentFormat; |
| SharedPtr<Image> m_colorTargetImage; |
| Move<VkImageView> m_colorTargetView; |
| SharedPtr<Buffer> m_vertexBuffer; |
| Move<VkRenderPass> m_renderPass; |
| Move<VkFramebuffer> m_framebuffer; |
| Move<VkPipelineLayout> m_pipelineLayout; |
| Move<VkPipeline> m_pipeline; |
| }; |
| |
| InvertedDepthRangesTestInstance::InvertedDepthRangesTestInstance (Context& context, const TestParams& params) |
| : TestInstance (context) |
| , m_params (params) |
| , m_colorAttachmentFormat (VK_FORMAT_R8G8B8A8_UNORM) |
| { |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkDevice device = m_context.getDevice(); |
| |
| // Vertex data |
| { |
| std::vector<Vec4> vertexData; |
| |
| vertexData.push_back(Vec4(-0.8f, -0.8f, -0.2f, 1.0f)); // 0-----2 |
| vertexData.push_back(Vec4(-0.8f, 0.8f, 0.0f, 1.0f)); // | / |
| vertexData.push_back(Vec4( 0.8f, -0.8f, 1.2f, 1.0f)); // 1|/ |
| |
| const VkDeviceSize dataSize = vertexData.size() * sizeof(Vec4); |
| m_vertexBuffer = Buffer::createAndAlloc(vk, device, BufferCreateInfo(dataSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), |
| m_context.getDefaultAllocator(), MemoryRequirement::HostVisible); |
| |
| deMemcpy(m_vertexBuffer->getBoundMemory().getHostPtr(), &vertexData[0], static_cast<std::size_t>(dataSize)); |
| flushMappedMemoryRange(vk, device, m_vertexBuffer->getBoundMemory().getMemory(), m_vertexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE); |
| } |
| |
| // Render pass |
| { |
| const VkExtent3D targetImageExtent = { 256, 256, 1 }; |
| const VkImageUsageFlags targetImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; |
| |
| const ImageCreateInfo targetImageCreateInfo( |
| VK_IMAGE_TYPE_2D, // imageType, |
| m_colorAttachmentFormat, // format, |
| targetImageExtent, // extent, |
| 1u, // mipLevels, |
| 1u, // arrayLayers, |
| VK_SAMPLE_COUNT_1_BIT, // samples, |
| VK_IMAGE_TILING_OPTIMAL, // tiling, |
| targetImageUsageFlags); // usage, |
| |
| m_colorTargetImage = Image::createAndAlloc(vk, device, targetImageCreateInfo, m_context.getDefaultAllocator(), m_context.getUniversalQueueFamilyIndex()); |
| |
| RenderPassCreateInfo renderPassCreateInfo; |
| renderPassCreateInfo.addAttachment(AttachmentDescription( |
| m_colorAttachmentFormat, // format |
| VK_SAMPLE_COUNT_1_BIT, // samples |
| VK_ATTACHMENT_LOAD_OP_LOAD, // loadOp |
| VK_ATTACHMENT_STORE_OP_STORE, // storeOp |
| VK_ATTACHMENT_LOAD_OP_DONT_CARE, // stencilLoadOp |
| VK_ATTACHMENT_STORE_OP_DONT_CARE, // stencilStoreOp |
| VK_IMAGE_LAYOUT_GENERAL, // initialLayout |
| VK_IMAGE_LAYOUT_GENERAL)); // finalLayout |
| |
| const VkAttachmentReference colorAttachmentReference = |
| { |
| 0u, |
| VK_IMAGE_LAYOUT_GENERAL |
| }; |
| |
| renderPassCreateInfo.addSubpass(SubpassDescription( |
| VK_PIPELINE_BIND_POINT_GRAPHICS, // pipelineBindPoint |
| (VkSubpassDescriptionFlags)0, // flags |
| 0u, // inputAttachmentCount |
| DE_NULL, // inputAttachments |
| 1u, // colorAttachmentCount |
| &colorAttachmentReference, // colorAttachments |
| DE_NULL, // resolveAttachments |
| AttachmentReference(), // depthStencilAttachment |
| 0u, // preserveAttachmentCount |
| DE_NULL)); // preserveAttachments |
| |
| m_renderPass = createRenderPass(vk, device, &renderPassCreateInfo); |
| } |
| |
| // Framebuffer |
| { |
| const ImageViewCreateInfo colorTargetViewInfo (m_colorTargetImage->object(), VK_IMAGE_VIEW_TYPE_2D, m_colorAttachmentFormat); |
| m_colorTargetView = createImageView(vk, device, &colorTargetViewInfo); |
| |
| std::vector<VkImageView> colorAttachments(1); |
| colorAttachments[0] = *m_colorTargetView; |
| |
| const FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, colorAttachments, 256, 256, 1); |
| m_framebuffer = createFramebuffer(vk, device, &framebufferCreateInfo); |
| } |
| |
| // Vertex input |
| |
| const VkVertexInputBindingDescription vertexInputBindingDescription = |
| { |
| 0u, // uint32_t binding; |
| sizeof(Vec4), // uint32_t stride; |
| VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate; |
| }; |
| |
| const VkVertexInputAttributeDescription vertexInputAttributeDescription = |
| { |
| 0u, // uint32_t location; |
| 0u, // uint32_t binding; |
| VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format; |
| 0u // uint32_t offset; |
| }; |
| |
| const PipelineCreateInfo::VertexInputState vertexInputState = PipelineCreateInfo::VertexInputState(1, &vertexInputBindingDescription, |
| 1, &vertexInputAttributeDescription); |
| |
| // Graphics pipeline |
| |
| const VkRect2D scissor = makeRect2D(256u, 256u); |
| |
| std::vector<VkDynamicState> dynamicStates; |
| dynamicStates.push_back(VK_DYNAMIC_STATE_VIEWPORT); |
| |
| const Unique<VkShaderModule> vertexModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0)); |
| const Unique<VkShaderModule> fragmentModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0)); |
| |
| const PipelineLayoutCreateInfo pipelineLayoutCreateInfo; |
| m_pipelineLayout = createPipelineLayout(vk, device, &pipelineLayoutCreateInfo); |
| |
| const PipelineCreateInfo::ColorBlendState::Attachment colorBlendAttachmentState; |
| |
| PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, (VkPipelineCreateFlags)0); |
| pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vertexModule, "main", VK_SHADER_STAGE_VERTEX_BIT)); |
| pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fragmentModule, "main", VK_SHADER_STAGE_FRAGMENT_BIT)); |
| pipelineCreateInfo.addState (PipelineCreateInfo::VertexInputState (vertexInputState)); |
| pipelineCreateInfo.addState (PipelineCreateInfo::InputAssemblerState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)); |
| pipelineCreateInfo.addState (PipelineCreateInfo::ColorBlendState (1, &colorBlendAttachmentState)); |
| pipelineCreateInfo.addState (PipelineCreateInfo::ViewportState (1, std::vector<VkViewport>(), std::vector<VkRect2D>(1, scissor))); |
| pipelineCreateInfo.addState (PipelineCreateInfo::DepthStencilState ()); |
| pipelineCreateInfo.addState (PipelineCreateInfo::RasterizerState ( |
| m_params.depthClampEnable, // depthClampEnable |
| VK_FALSE, // rasterizerDiscardEnable |
| VK_POLYGON_MODE_FILL, // polygonMode |
| VK_CULL_MODE_NONE, // cullMode |
| VK_FRONT_FACE_CLOCKWISE, // frontFace |
| VK_FALSE, // depthBiasEnable |
| 0.0f, // depthBiasConstantFactor |
| 0.0f, // depthBiasClamp |
| 0.0f, // depthBiasSlopeFactor |
| 1.0f)); // lineWidth |
| pipelineCreateInfo.addState (PipelineCreateInfo::MultiSampleState ()); |
| pipelineCreateInfo.addState (PipelineCreateInfo::DynamicState (dynamicStates)); |
| |
| m_pipeline = createGraphicsPipeline(vk, device, DE_NULL, &pipelineCreateInfo); |
| } |
| |
| tcu::ConstPixelBufferAccess InvertedDepthRangesTestInstance::draw (const VkViewport viewport) |
| { |
| const DeviceInterface& vk = m_context.getDeviceInterface(); |
| const VkDevice device = m_context.getDevice(); |
| const VkQueue queue = m_context.getUniversalQueue(); |
| const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex(); |
| |
| // Command buffer |
| |
| const CmdPoolCreateInfo cmdPoolCreateInfo (queueFamilyIndex); |
| const Unique<VkCommandPool> cmdPool (createCommandPool(vk, device, &cmdPoolCreateInfo)); |
| const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY)); |
| |
| // Draw |
| |
| beginCommandBuffer(vk, *cmdBuffer); |
| |
| vk.cmdSetViewport(*cmdBuffer, 0u, 1u, &viewport); |
| |
| { |
| const VkClearColorValue clearColor = makeClearValueColorF32(0.0f, 0.0f, 0.0f, 1.0f).color; |
| const ImageSubresourceRange subresourceRange (VK_IMAGE_ASPECT_COLOR_BIT); |
| |
| initialTransitionColor2DImage(vk, *cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT); |
| vk.cmdClearColorImage(*cmdBuffer, m_colorTargetImage->object(), VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &subresourceRange); |
| } |
| { |
| const VkMemoryBarrier memBarrier = |
| { |
| VK_STRUCTURE_TYPE_MEMORY_BARRIER, // VkStructureType sType; |
| DE_NULL, // const void* pNext; |
| VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask; |
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT // VkAccessFlags dstAccessMask; |
| }; |
| |
| vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL); |
| } |
| |
| beginRenderPass(vk, *cmdBuffer, *m_renderPass, *m_framebuffer, makeRect2D(0, 0, 256u, 256u)); |
| |
| { |
| const VkDeviceSize offset = 0; |
| const VkBuffer buffer = m_vertexBuffer->object(); |
| |
| vk.cmdBindVertexBuffers(*cmdBuffer, 0, 1, &buffer, &offset); |
| } |
| |
| vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline); |
| vk.cmdDraw(*cmdBuffer, 3, 1, 0, 0); |
| endRenderPass(vk, *cmdBuffer); |
| endCommandBuffer(vk, *cmdBuffer); |
| |
| // Submit |
| submitCommandsAndWait(vk, device, queue, cmdBuffer.get()); |
| |
| // Get result |
| { |
| const VkOffset3D zeroOffset = { 0, 0, 0 }; |
| return m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(), VK_IMAGE_LAYOUT_GENERAL, zeroOffset, 256, 256, VK_IMAGE_ASPECT_COLOR_BIT); |
| } |
| } |
| |
| MovePtr<tcu::TextureLevel> InvertedDepthRangesTestInstance::generateReferenceImage (void) const |
| { |
| MovePtr<tcu::TextureLevel> image (new tcu::TextureLevel(mapVkFormat(m_colorAttachmentFormat), 256, 256)); |
| const tcu::PixelBufferAccess access (image->getAccess()); |
| const Vec4 black (0.0f, 0.0f, 0.0f, 1.0f); |
| const int p1 = static_cast<int>(256.0f * 0.2f / 2.0f); |
| const int p2 = static_cast<int>(256.0f * 1.8f / 2.0f); |
| const float delta = 256.0f * 1.6f / 2.0f; |
| const float depthValues[] = { -0.2f, 0.0f, 1.2f }; |
| |
| tcu::clear(access, black); |
| |
| for (int y = p1; y <= p2; ++y) |
| for (int x = p1; x < 256 - y; ++x) |
| { |
| const float a = static_cast<float>(p2 - x + p1 - y) / delta; |
| const float b = static_cast<float>(y - p1) / delta; |
| const float c = 1.0f - a - b; |
| const float depth = a * depthValues[0] + b * depthValues[1] + c * depthValues[2]; |
| const float depthClamped = de::clamp(depth, 0.0f, 1.0f); |
| const float depthFinal = depthClamped * m_params.maxDepth + (1.0f - depthClamped) * m_params.minDepth; |
| |
| if (m_params.depthClampEnable || (depth >= 0.0f && depth <= 1.0f)) |
| access.setPixel(Vec4(depthFinal, 0.5f, 0.5f, 1.0f), x, y); |
| } |
| |
| return image; |
| } |
| |
| tcu::TestStatus InvertedDepthRangesTestInstance::iterate (void) |
| { |
| // Set up the viewport and draw |
| |
| const VkViewport viewport = |
| { |
| 0.0f, // float x; |
| 0.0f, // float y; |
| 256.0f, // float width; |
| 256.0f, // float height; |
| m_params.minDepth, // float minDepth; |
| m_params.maxDepth, // float maxDepth; |
| }; |
| |
| const tcu::ConstPixelBufferAccess resultImage = draw(viewport); |
| |
| // Verify the results |
| |
| tcu::TestLog& log = m_context.getTestContext().getLog(); |
| MovePtr<tcu::TextureLevel> referenceImage = generateReferenceImage(); |
| |
| if (!tcu::fuzzyCompare(log, "Image compare", "Image compare", referenceImage->getAccess(), resultImage, 0.02f, tcu::COMPARE_LOG_RESULT)) |
| return tcu::TestStatus::fail("Rendered image is incorrect"); |
| else |
| return tcu::TestStatus::pass("Pass"); |
| } |
| |
| class InvertedDepthRangesTest : public TestCase |
| { |
| public: |
| InvertedDepthRangesTest (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params) |
| : TestCase (testCtx, name, description) |
| , m_params (params) |
| { |
| } |
| |
| void initPrograms (SourceCollections& programCollection) const |
| { |
| // Vertex shader |
| { |
| std::ostringstream src; |
| src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" |
| << "\n" |
| << "layout(location = 0) in vec4 in_position;\n" |
| << "\n" |
| << "out gl_PerVertex {\n" |
| << " vec4 gl_Position;\n" |
| << "};\n" |
| << "\n" |
| << "void main(void)\n" |
| << "{\n" |
| << " gl_Position = in_position;\n" |
| << "}\n"; |
| |
| programCollection.glslSources.add("vert") << glu::VertexSource(src.str()); |
| } |
| |
| // Fragment shader |
| { |
| std::ostringstream src; |
| src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n" |
| << "\n" |
| << "layout(location = 0) out vec4 out_color;\n" |
| << "\n" |
| << "void main(void)\n" |
| << "{\n" |
| << " out_color = vec4(gl_FragCoord.z, 0.5, 0.5, 1.0);\n" |
| << "}\n"; |
| |
| programCollection.glslSources.add("frag") << glu::FragmentSource(src.str()); |
| } |
| } |
| |
| virtual void checkSupport (Context& context) const |
| { |
| if (m_params.depthClampEnable) |
| context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_DEPTH_CLAMP); |
| |
| if (m_params.minDepth > 1.0f || m_params.minDepth < 0.0f || m_params.maxDepth > 1.0f || m_params.maxDepth < 0.0f) |
| context.requireDeviceFunctionality("VK_EXT_depth_range_unrestricted"); |
| } |
| |
| virtual TestInstance* createInstance (Context& context) const |
| { |
| return new InvertedDepthRangesTestInstance(context, m_params); |
| } |
| |
| private: |
| const TestParams m_params; |
| }; |
| |
| void populateTestGroup (tcu::TestCaseGroup* testGroup) |
| { |
| const struct |
| { |
| const char* const name; |
| VkBool32 depthClamp; |
| } depthClamp[] = |
| { |
| { "depthclamp", VK_TRUE }, |
| { "nodepthclamp", VK_FALSE }, |
| }; |
| |
| const struct |
| { |
| const char* const name; |
| float delta; |
| } delta[] = |
| { |
| { "deltazero", 0.0f }, |
| { "deltasmall", 0.3f }, |
| { "deltaone", 1.0f }, |
| |
| // Range > 1.0 requires VK_EXT_depth_range_unrestricted extension |
| { "depth_range_unrestricted", 2.7f }, |
| }; |
| |
| for (int ndxDepthClamp = 0; ndxDepthClamp < DE_LENGTH_OF_ARRAY(depthClamp); ++ndxDepthClamp) |
| for (int ndxDelta = 0; ndxDelta < DE_LENGTH_OF_ARRAY(delta); ++ndxDelta) |
| { |
| const float minDepth = 0.5f + delta[ndxDelta].delta / 2.0f; |
| const float maxDepth = minDepth - delta[ndxDelta].delta; |
| DE_ASSERT(minDepth >= maxDepth); |
| |
| const TestParams params = |
| { |
| depthClamp[ndxDepthClamp].depthClamp, |
| minDepth, |
| maxDepth |
| }; |
| std::ostringstream name; |
| name << depthClamp[ndxDepthClamp].name << "_" << delta[ndxDelta].name; |
| |
| testGroup->addChild(new InvertedDepthRangesTest(testGroup->getTestContext(), name.str(), "", params)); |
| } |
| } |
| |
| } // anonymous |
| |
| tcu::TestCaseGroup* createInvertedDepthRangesTests (tcu::TestContext& testCtx) |
| { |
| return createTestGroup(testCtx, "inverted_depth_ranges", "Inverted depth ranges", populateTestGroup); |
| } |
| |
| } // Draw |
| } // vkt |