--- /dev/null
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2020 The Khronos Group Inc.
+ * Copyright (c) 2020 Valve Corporation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Test frag shader side effects are not removed by optimizations.
+ *//*--------------------------------------------------------------------*/
+
+#include "vktRasterizationFragShaderSideEffectsTests.hpp"
+#include "vktTestCase.hpp"
+
+#include "vkQueryUtil.hpp"
+#include "vkObjUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkImageWithMemory.hpp"
+#include "vkBufferWithMemory.hpp"
+#include "vkTypeUtil.hpp"
+#include "vkCmdUtil.hpp"
+#include "vkBarrierUtil.hpp"
+#include "vkImageUtil.hpp"
+
+#include "tcuVector.hpp"
+#include "tcuMaybe.hpp"
+#include "tcuTestLog.hpp"
+
+#include "deUniquePtr.hpp"
+
+#include <sstream>
+#include <string>
+#include <memory>
+#include <vector>
+#include <algorithm>
+
+namespace vkt
+{
+namespace rasterization
+{
+
+namespace
+{
+
+enum class CaseType
+{
+ KILL,
+ DEMOTE,
+ SAMPLE_MASK_BEFORE,
+ SAMPLE_MASK_AFTER,
+ ALPHA_COVERAGE_BEFORE,
+ ALPHA_COVERAGE_AFTER,
+ DEPTH_BOUNDS,
+ STENCIL_NEVER,
+ DEPTH_NEVER,
+};
+
+constexpr deUint32 kFramebufferWidth = 32u;
+constexpr deUint32 kFramebufferHeight = 32u;
+constexpr deUint32 kTotalPixels = kFramebufferWidth * kFramebufferHeight;
+
+constexpr vk::VkFormatFeatureFlags kNeededColorFeatures = (vk::VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | vk::VK_FORMAT_FEATURE_TRANSFER_SRC_BIT);
+constexpr vk::VkFormat kColorFormat = vk::VK_FORMAT_R8G8B8A8_UNORM;
+constexpr vk::VkFormatFeatureFlags kNeededDSFeatures = vk::VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
+// VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT must be supported for one of these two, according to the spec.
+const vk::VkFormat kDepthStencilFormats[] = { vk::VK_FORMAT_D32_SFLOAT_S8_UINT, vk::VK_FORMAT_D24_UNORM_S8_UINT };
+
+struct DepthBoundsParameters
+{
+ float minDepthBounds;
+ float maxDepthBounds;
+ float depthValue;
+};
+
+struct TestParams
+{
+ CaseType caseType;
+ tcu::Vec4 clearColor;
+ tcu::Vec4 drawColor;
+ bool colorAtEnd;
+ tcu::Maybe<DepthBoundsParameters> depthBoundsParams;
+
+ TestParams (CaseType type, const tcu::Vec4& clearColor_, const tcu::Vec4& drawColor_, bool colorAtEnd_, const tcu::Maybe<DepthBoundsParameters>& depthBoundsParams_)
+ : caseType (type)
+ , clearColor (clearColor_)
+ , drawColor (drawColor_)
+ , colorAtEnd (colorAtEnd_)
+ , depthBoundsParams (depthBoundsParams_)
+ {
+ if (caseType == CaseType::DEPTH_BOUNDS)
+ DE_ASSERT(static_cast<bool>(depthBoundsParams));
+ }
+};
+
+bool expectClearColor (CaseType caseType)
+{
+ return (caseType != CaseType::ALPHA_COVERAGE_BEFORE && caseType != CaseType::ALPHA_COVERAGE_AFTER);
+}
+
+bool needsDepthStencilAttachment (CaseType caseType)
+{
+ return (caseType == CaseType::DEPTH_BOUNDS || caseType == CaseType::DEPTH_NEVER || caseType == CaseType::STENCIL_NEVER);
+}
+
+vk::VkBool32 makeVkBool32 (bool value)
+{
+ return (value ? VK_TRUE : VK_FALSE);
+}
+
+class FragSideEffectsTestCase : public vkt::TestCase
+{
+public:
+ FragSideEffectsTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params);
+ virtual ~FragSideEffectsTestCase (void) {}
+
+ virtual void checkSupport (Context& context) const;
+ virtual void initPrograms (vk::SourceCollections& programCollection) const;
+ virtual TestInstance* createInstance (Context& context) const;
+
+private:
+ TestParams m_params;
+};
+
+class FragSideEffectsInstance : public vkt::TestInstance
+{
+public:
+ FragSideEffectsInstance (Context& context, const TestParams& params);
+ virtual ~FragSideEffectsInstance (void) {}
+
+ virtual tcu::TestStatus iterate (void);
+
+private:
+ TestParams m_params;
+};
+
+FragSideEffectsTestCase::FragSideEffectsTestCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, const TestParams& params)
+ : vkt::TestCase (testCtx, name, description)
+ , m_params (params)
+{}
+
+void FragSideEffectsTestCase::checkSupport (Context& context) const
+{
+ const auto& vki = context.getInstanceInterface();
+ const auto physicalDevice = context.getPhysicalDevice();
+
+ if (m_params.caseType == CaseType::DEPTH_BOUNDS)
+ {
+ const auto features = vk::getPhysicalDeviceFeatures(vki, physicalDevice);
+ if (!features.depthBounds)
+ TCU_THROW(NotSupportedError, "Depth bounds test not supported");
+ }
+ else if (m_params.caseType == CaseType::DEMOTE)
+ {
+ context.requireDeviceFunctionality("VK_EXT_shader_demote_to_helper_invocation");
+ }
+
+ const auto colorFormatProperties = vk::getPhysicalDeviceFormatProperties(vki, physicalDevice, kColorFormat);
+ if ((colorFormatProperties.optimalTilingFeatures & kNeededColorFeatures) != kNeededColorFeatures)
+ TCU_THROW(NotSupportedError, "Color format lacks required features");
+}
+
+void FragSideEffectsTestCase::initPrograms (vk::SourceCollections& programCollection) const
+{
+ std::ostringstream headers;
+ std::ostringstream before;
+ std::ostringstream after;
+
+ std::ostringstream vert;
+ std::ostringstream frag;
+
+ // Depth should be 0 by default unless provided by the depth bounds parameters.
+ const float meshDepth = (m_params.depthBoundsParams ? m_params.depthBoundsParams.get().depthValue : 0.0f);
+ const auto& drawColor = m_params.drawColor;
+
+ vert
+ << "#version 450\n"
+ << "\n"
+ << "layout (location=0) in vec2 inPos;\n"
+ << "\n"
+ << "void main() {\n"
+ << " gl_Position = vec4(inPos, " << meshDepth << ", 1.0);\n"
+ << "}\n"
+ ;
+
+ // Prepare output color statement to be used before or after SSBO write.
+ std::ostringstream colorStatement;
+ if (m_params.caseType == CaseType::ALPHA_COVERAGE_BEFORE || m_params.caseType == CaseType::ALPHA_COVERAGE_AFTER)
+ {
+ // In the alpha coverage cases the alpha color value is supposed to be 0.
+ DE_ASSERT(m_params.drawColor.w() == 0.0f);
+
+ // Leave out the alpha component for these cases.
+ colorStatement << " outColor.rgb = vec3(" << drawColor.x() << ", " << drawColor.y() << ", " << drawColor.z() << ");\n";
+ }
+ else
+ {
+ colorStatement << " outColor = vec4(" << drawColor.x() << ", " << drawColor.y() << ", " << drawColor.z() << ", " << drawColor.w() << ");\n";
+ }
+
+ switch (m_params.caseType)
+ {
+ case CaseType::KILL:
+ after << " discard;\n";
+ break;
+ case CaseType::DEMOTE:
+ headers << "#extension GL_EXT_demote_to_helper_invocation : enable\n";
+ after << " demote;\n";
+ break;
+ case CaseType::SAMPLE_MASK_BEFORE:
+ before << " gl_SampleMask[0] = 0;\n";
+ break;
+ case CaseType::SAMPLE_MASK_AFTER:
+ after << " gl_SampleMask[0] = 0;\n";
+ break;
+ case CaseType::ALPHA_COVERAGE_BEFORE:
+ before << " outColor.a = float(" << drawColor.w() << ");\n";
+ break;
+ case CaseType::ALPHA_COVERAGE_AFTER:
+ after << " outColor.a = float(" << drawColor.w() << ");\n";
+ break;
+ case CaseType::DEPTH_BOUNDS:
+ case CaseType::STENCIL_NEVER:
+ case CaseType::DEPTH_NEVER:
+ break;
+ default:
+ DE_ASSERT(false); break;
+ }
+
+ frag
+ << "#version 450\n"
+ << "layout(set=0, binding=0, std430) buffer OutputBuffer {\n"
+ << " int val[" << kTotalPixels << "];\n"
+ << "} outBuffer;\n"
+ << "layout (location=0) out vec4 outColor;\n"
+ << headers.str()
+ << "\n"
+ << "void main() {\n"
+ << " const ivec2 fragCoord = ivec2(gl_FragCoord);\n"
+ << " const int bufferIndex = (fragCoord.y * " << kFramebufferWidth << ") + fragCoord.x;\n"
+ << (m_params.colorAtEnd ? "" : colorStatement.str())
+ << before.str()
+ << " outBuffer.val[bufferIndex] = 1;\n"
+ << after.str()
+ << (m_params.colorAtEnd ? colorStatement.str() : "")
+ << "}\n"
+ ;
+
+ programCollection.glslSources.add("vert") << glu::VertexSource(vert.str());
+ programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
+}
+
+TestInstance* FragSideEffectsTestCase::createInstance (Context& context) const
+{
+ return new FragSideEffectsInstance(context, m_params);
+}
+
+FragSideEffectsInstance::FragSideEffectsInstance (Context& context, const TestParams& params)
+ : vkt::TestInstance (context)
+ , m_params (params)
+{}
+
+tcu::TestStatus FragSideEffectsInstance::iterate (void)
+{
+ const auto& vki = m_context.getInstanceInterface();
+ const auto physicalDevice = m_context.getPhysicalDevice();
+ const auto& vkd = m_context.getDeviceInterface();
+ const auto device = m_context.getDevice();
+ auto& alloc = m_context.getDefaultAllocator();
+ const auto queue = m_context.getUniversalQueue();
+ const auto queueIndex = m_context.getUniversalQueueFamilyIndex();
+
+ // Color and depth/stencil images.
+
+ const vk::VkImageCreateInfo colorCreateInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkImageCreateFlags flags;
+ vk::VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ kColorFormat, // VkFormat format;
+ vk::makeExtent3D(kFramebufferWidth, kFramebufferHeight, 1u), // VkExtent3D extent;
+ 1u, // deUint32 mipLevels;
+ 1u, // deUint32 arrayLayers;
+ vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ (vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT), // VkImageUsageFlags usage;
+ vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 0u, // deUint32 queueFamilyIndexCount;
+ nullptr, // const deUint32* pQueueFamilyIndices;
+ vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
+ };
+ vk::ImageWithMemory colorImage(vkd, device, alloc, colorCreateInfo, vk::MemoryRequirement::Any);
+
+ std::unique_ptr<vk::ImageWithMemory> depthStencilImage;
+ vk::VkFormat depthStencilFormat = vk::VK_FORMAT_UNDEFINED;
+
+ if (needsDepthStencilAttachment(m_params.caseType))
+ {
+ // Find available image format first.
+ for (int i = 0; i < DE_LENGTH_OF_ARRAY(kDepthStencilFormats); ++i)
+ {
+ const auto dsFormatProperties = vk::getPhysicalDeviceFormatProperties(vki, physicalDevice, kDepthStencilFormats[i]);
+ if ((dsFormatProperties.optimalTilingFeatures & kNeededDSFeatures) == kNeededDSFeatures)
+ {
+ depthStencilFormat = kDepthStencilFormats[i];
+ break;
+ }
+ }
+
+ if (depthStencilFormat == vk::VK_FORMAT_UNDEFINED)
+ TCU_FAIL("No suitable depth/stencil format found");
+
+ const vk::VkImageCreateInfo depthStencilCreateInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkImageCreateFlags flags;
+ vk::VK_IMAGE_TYPE_2D, // VkImageType imageType;
+ depthStencilFormat, // VkFormat format;
+ vk::makeExtent3D(kFramebufferWidth, kFramebufferHeight, 1u), // VkExtent3D extent;
+ 1u, // deUint32 mipLevels;
+ 1u, // deUint32 arrayLayers;
+ vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
+ vk::VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
+ vk::VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, // VkImageUsageFlags usage;
+ vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
+ 0u, // deUint32 queueFamilyIndexCount;
+ nullptr, // const deUint32* pQueueFamilyIndices;
+ vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
+ };
+
+ depthStencilImage.reset(new vk::ImageWithMemory(vkd, device, alloc, depthStencilCreateInfo, vk::MemoryRequirement::Any));
+ }
+
+ // Image views.
+ const auto colorSubresourceRange = vk::makeImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+ const auto colorImageView = vk::makeImageView(vkd, device, colorImage.get(), vk::VK_IMAGE_VIEW_TYPE_2D, kColorFormat, colorSubresourceRange);
+
+ vk::Move<vk::VkImageView> depthStencilImageView;
+ if (depthStencilImage)
+ {
+ const auto depthStencilSubresourceRange = vk::makeImageSubresourceRange((vk::VK_IMAGE_ASPECT_DEPTH_BIT | vk::VK_IMAGE_ASPECT_STENCIL_BIT), 0u, 1u, 0u, 1u);
+ depthStencilImageView = vk::makeImageView(vkd, device, depthStencilImage.get()->get(), vk::VK_IMAGE_VIEW_TYPE_2D, depthStencilFormat, depthStencilSubresourceRange);
+ }
+
+ // Color image buffer.
+ const auto tcuFormat = vk::mapVkFormat(kColorFormat);
+ const auto colorImageBufferSize = static_cast<vk::VkDeviceSize>(kTotalPixels * tcuFormat.getPixelSize());
+ const auto colorImageBufferInfo = vk::makeBufferCreateInfo(colorImageBufferSize, vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+ vk::BufferWithMemory colorImageBuffer(vkd, device, alloc, colorImageBufferInfo, vk::MemoryRequirement::HostVisible);
+
+ // Vertex buffer.
+ const std::vector<tcu::Vec2> fullScreenQuad =
+ {
+ tcu::Vec2(-1.0f, 1.0f),
+ tcu::Vec2( 1.0f, 1.0f),
+ tcu::Vec2( 1.0f, -1.0f),
+ tcu::Vec2(-1.0f, 1.0f),
+ tcu::Vec2( 1.0f, -1.0f),
+ tcu::Vec2(-1.0f, -1.0f),
+ };
+
+ const auto vertexBufferSize = static_cast<vk::VkDeviceSize>(fullScreenQuad.size() * sizeof(decltype(fullScreenQuad)::value_type));
+ const auto vertexBufferInfo = vk::makeBufferCreateInfo(vertexBufferSize, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
+ const vk::VkDeviceSize vertexBufferOffset = 0ull;
+ vk::BufferWithMemory vertexBuffer (vkd, device, alloc, vertexBufferInfo, vk::MemoryRequirement::HostVisible);
+ const auto& vertexBufferAlloc = vertexBuffer.getAllocation();
+
+ deMemcpy(vertexBufferAlloc.getHostPtr(), fullScreenQuad.data(), static_cast<size_t>(vertexBufferSize));
+ vk::flushAlloc(vkd, device, vertexBufferAlloc);
+
+ // Storage buffer.
+ const auto storageBufferSize = static_cast<vk::VkDeviceSize>(kTotalPixels * sizeof(deInt32));
+ const auto storageBufferInfo = vk::makeBufferCreateInfo(storageBufferSize, (vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | vk::VK_BUFFER_USAGE_TRANSFER_SRC_BIT));
+ vk::BufferWithMemory storageBuffer (vkd, device, alloc, storageBufferInfo, vk::MemoryRequirement::HostVisible);
+ const auto& storageBufferAlloc = storageBuffer.getAllocation();
+
+ deMemset(storageBufferAlloc.getHostPtr(), 0, static_cast<size_t>(storageBufferSize));
+ vk::flushAlloc(vkd, device, storageBufferAlloc);
+
+ // Descriptor set layout.
+ vk::DescriptorSetLayoutBuilder layoutBuilder;
+ layoutBuilder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_FRAGMENT_BIT);
+ const auto descriptorSetLayout = layoutBuilder.build(vkd, device);
+
+ // Pipeline layout.
+ const auto pipelineLayout = vk::makePipelineLayout(vkd, device, descriptorSetLayout.get());
+
+ // Descriptor pool.
+ vk::DescriptorPoolBuilder poolBuilder;
+ poolBuilder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
+ const auto descriptorPool = poolBuilder.build(vkd, device, vk::VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
+
+ // Descriptor set.
+ const auto descriptorSet = vk::makeDescriptorSet(vkd, device, descriptorPool.get(), descriptorSetLayout.get());
+
+ // Update descriptor set.
+ vk::DescriptorSetUpdateBuilder updateBuilder;
+ const auto descriptorBufferInfo = vk::makeDescriptorBufferInfo(storageBuffer.get(), 0u, storageBufferSize);
+ updateBuilder.writeSingle(descriptorSet.get(), vk::DescriptorSetUpdateBuilder::Location::binding(0), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorBufferInfo);
+ updateBuilder.update(vkd, device);
+
+ // Render pass.
+ const auto renderPass = vk::makeRenderPass(vkd, device, kColorFormat, depthStencilFormat);
+
+ // Framebuffer.
+ std::vector<vk::VkImageView> imageViews(1u, colorImageView.get());
+ if (depthStencilImage)
+ imageViews.push_back(depthStencilImageView.get());
+
+ const auto framebuffer = vk::makeFramebuffer(vkd, device, renderPass.get(), static_cast<deUint32>(imageViews.size()), imageViews.data(), kFramebufferWidth, kFramebufferHeight);
+
+ // Shader modules.
+ const auto vertModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("vert"), 0u);
+ const auto fragModule = vk::createShaderModule(vkd, device, m_context.getBinaryCollection().get("frag"), 0u);
+
+ // Vertex input state.
+ const auto vertexBinding = vk::makeVertexInputBindingDescription(0u, static_cast<deUint32>(sizeof(tcu::Vec2)), vk::VK_VERTEX_INPUT_RATE_VERTEX);
+ const auto vertexAttributes = vk::makeVertexInputAttributeDescription(0u, 0u, vk::VK_FORMAT_R32G32_SFLOAT, 0u);
+
+ const vk::VkPipelineVertexInputStateCreateInfo vertexInputInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkPipelineVertexInputStateCreateFlags flags;
+ 1u, // deUint32 vertexBindingDescriptionCount;
+ &vertexBinding, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
+ 1u, // deUint32 vertexAttributeDescriptionCount;
+ &vertexAttributes, // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
+ };
+
+ // Input assembly state.
+ const vk::VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkPipelineInputAssemblyStateCreateFlags flags;
+ vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // VkPrimitiveTopology topology;
+ VK_FALSE, // VkBool32 primitiveRestartEnable;
+ };
+
+ // Viewport state.
+ const auto viewport = vk::makeViewport(kFramebufferWidth, kFramebufferHeight);
+ const auto scissor = vk::makeRect2D(kFramebufferWidth, kFramebufferHeight);
+
+ const vk::VkPipelineViewportStateCreateInfo viewportInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkPipelineViewportStateCreateFlags flags;
+ 1u, // deUint32 viewportCount;
+ &viewport, // const VkViewport* pViewports;
+ 1u, // deUint32 scissorCount;
+ &scissor, // const VkRect2D* pScissors;
+ };
+
+ // Rasterization state.
+ const vk::VkPipelineRasterizationStateCreateInfo rasterizationInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkPipelineRasterizationStateCreateFlags flags;
+ VK_FALSE, // VkBool32 depthClampEnable;
+ VK_FALSE, // VkBool32 rasterizerDiscardEnable;
+ vk::VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode;
+ vk::VK_CULL_MODE_NONE, // VkCullModeFlags cullMode;
+ vk::VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
+ VK_FALSE, // VkBool32 depthBiasEnable;
+ 0.0f, // float depthBiasConstantFactor;
+ 0.0f, // float depthBiasClamp;
+ 0.0f, // float depthBiasSlopeFactor;
+ 1.0f, // float lineWidth;
+ };
+
+ // Multisample state.
+ const bool alphaToCoverageEnable = (m_params.caseType == CaseType::ALPHA_COVERAGE_BEFORE || m_params.caseType == CaseType::ALPHA_COVERAGE_AFTER);
+ const vk::VkPipelineMultisampleStateCreateInfo multisampleInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkPipelineMultisampleStateCreateFlags flags;
+ vk::VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples;
+ VK_FALSE, // VkBool32 sampleShadingEnable;
+ 0.0f, // float minSampleShading;
+ nullptr, // const VkSampleMask* pSampleMask;
+ makeVkBool32(alphaToCoverageEnable), // VkBool32 alphaToCoverageEnable;
+ VK_FALSE, // VkBool32 alphaToOneEnable;
+ };
+
+ // Depth/stencil state.
+ const auto enableDepthBounds = makeVkBool32(m_params.caseType == CaseType::DEPTH_BOUNDS);
+ const auto enableDepthStencilTest = static_cast<bool>(depthStencilImage);
+
+ const auto depthCompareOp = ((m_params.caseType == CaseType::DEPTH_NEVER) ? vk::VK_COMPARE_OP_NEVER : vk::VK_COMPARE_OP_ALWAYS);
+ const auto stencilCompareOp = ((m_params.caseType == CaseType::STENCIL_NEVER) ? vk::VK_COMPARE_OP_NEVER : vk::VK_COMPARE_OP_ALWAYS);
+ const auto stencilOpState = vk::makeStencilOpState(vk::VK_STENCIL_OP_KEEP, vk::VK_STENCIL_OP_KEEP, vk::VK_STENCIL_OP_KEEP, stencilCompareOp, 0xFFu, 0xFFu, 0u);
+
+ const vk::VkPipelineDepthStencilStateCreateInfo depthStencilInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkPipelineDepthStencilStateCreateFlags flags;
+ enableDepthStencilTest, // VkBool32 depthTestEnable;
+ enableDepthStencilTest, // VkBool32 depthWriteEnable;
+ depthCompareOp, // VkCompareOp depthCompareOp;
+ enableDepthBounds, // VkBool32 depthBoundsTestEnable;
+ enableDepthStencilTest, // VkBool32 stencilTestEnable;
+ stencilOpState, // VkStencilOpState front;
+ stencilOpState, // VkStencilOpState back;
+ (enableDepthBounds ? m_params.depthBoundsParams.get().minDepthBounds : 0.0f), // float minDepthBounds;
+ (enableDepthBounds ? m_params.depthBoundsParams.get().maxDepthBounds : 1.0f), // float maxDepthBounds;
+ };
+
+ // Color blend state.
+ const vk::VkPipelineColorBlendAttachmentState colorBlendAttachmentState =
+ {
+ VK_FALSE, // VkBool32 blendEnable
+ vk::VK_BLEND_FACTOR_ZERO, // VkBlendFactor srcColorBlendFactor
+ vk::VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstColorBlendFactor
+ vk::VK_BLEND_OP_ADD, // VkBlendOp colorBlendOp
+ vk::VK_BLEND_FACTOR_ZERO, // VkBlendFactor srcAlphaBlendFactor
+ vk::VK_BLEND_FACTOR_ZERO, // VkBlendFactor dstAlphaBlendFactor
+ vk::VK_BLEND_OP_ADD, // VkBlendOp alphaBlendOp
+ vk::VK_COLOR_COMPONENT_R_BIT // VkColorComponentFlags colorWriteMask
+ | vk::VK_COLOR_COMPONENT_G_BIT
+ | vk::VK_COLOR_COMPONENT_B_BIT
+ | vk::VK_COLOR_COMPONENT_A_BIT
+ };
+
+ const vk::VkPipelineColorBlendStateCreateInfo colorBlendInfo =
+ {
+ vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
+ nullptr, // const void* pNext;
+ 0u, // VkPipelineColorBlendStateCreateFlags flags;
+ VK_FALSE, // VkBool32 logicOpEnable;
+ vk::VK_LOGIC_OP_NO_OP, // VkLogicOp logicOp;
+ 1u, // deUint32 attachmentCount;
+ &colorBlendAttachmentState, // const VkPipelineColorBlendAttachmentState* pAttachments;
+ { .0f, .0f, .0f, .0f }, // float blendConstants[4];
+ };
+
+ // Graphics pipeline.
+ const auto graphicsPipeline = vk::makeGraphicsPipeline(
+ vkd, device, pipelineLayout.get(),
+ vertModule.get(), DE_NULL, DE_NULL, DE_NULL, fragModule.get(),
+ renderPass.get(), 0u,
+ &vertexInputInfo,
+ &inputAssemblyInfo,
+ nullptr,
+ &viewportInfo,
+ &rasterizationInfo,
+ &multisampleInfo,
+ &depthStencilInfo,
+ &colorBlendInfo);
+
+ // Command buffer.
+ const auto cmdPool = vk::makeCommandPool(vkd, device, queueIndex);
+ const auto cmdBufferPtr = vk::allocateCommandBuffer(vkd, device, cmdPool.get(), vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
+ const auto cmdBuffer = cmdBufferPtr.get();
+
+ // Draw full-screen quad.
+ std::vector<vk::VkClearValue> clearValues;
+ clearValues.push_back(vk::makeClearValueColor(m_params.clearColor));
+ clearValues.push_back(vk::makeClearValueDepthStencil(1.0f, 0u));
+
+ vk::beginCommandBuffer(vkd, cmdBuffer);
+ vk::beginRenderPass(vkd, cmdBuffer, renderPass.get(), framebuffer.get(), vk::makeRect2D(kFramebufferWidth, kFramebufferHeight), static_cast<deUint32>(clearValues.size()), clearValues.data());
+ vkd.cmdBindPipeline(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline.get());
+ vkd.cmdBindDescriptorSets(cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout.get(), 0u, 1u, &descriptorSet.get(), 0u, nullptr);
+ vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
+ vkd.cmdDraw(cmdBuffer, static_cast<deUint32>(fullScreenQuad.size()), 1u, 0u, 0u);
+ vk::endRenderPass(vkd, cmdBuffer);
+
+ // Image and buffer barriers.
+
+ // Storage buffer frag-write to host-read barrier.
+ const auto storageBufferBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_SHADER_WRITE_BIT, vk::VK_ACCESS_HOST_READ_BIT, storageBuffer.get(), 0u, VK_WHOLE_SIZE);
+
+ // Color image frag-write to transfer-read barrier.
+ const auto colorImageBarrier = vk::makeImageMemoryBarrier(vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, vk::VK_ACCESS_TRANSFER_READ_BIT, vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorImage.get(), colorSubresourceRange);
+
+ // Color buffer transfer-write to host-read barrier.
+ const auto colorBufferBarrier = vk::makeBufferMemoryBarrier(vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_ACCESS_HOST_READ_BIT, colorImageBuffer.get(), 0u, VK_WHOLE_SIZE);
+
+ vk::cmdPipelineBufferMemoryBarrier(vkd, cmdBuffer, vk::VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT, &storageBufferBarrier);
+ vk::cmdPipelineImageMemoryBarrier(vkd, cmdBuffer, vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, &colorImageBarrier);
+ const auto copyRegion = vk::makeBufferImageCopy(vk::makeExtent3D(kFramebufferWidth, kFramebufferHeight, 1u), vk::makeImageSubresourceLayers(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u));
+ vkd.cmdCopyImageToBuffer(cmdBuffer, colorImage.get(), vk::VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorImageBuffer.get(), 1u, ©Region);
+ vk::cmdPipelineBufferMemoryBarrier(vkd, cmdBuffer, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_PIPELINE_STAGE_HOST_BIT, &colorBufferBarrier);
+
+ vk::endCommandBuffer(vkd, cmdBuffer);
+ vk::submitCommandsAndWait(vkd, device, queue, cmdBuffer);
+
+ // Check output.
+ {
+ // Check SSBO contents.
+ vk::invalidateAlloc(vkd, device, storageBufferAlloc);
+ const auto bufferElements = reinterpret_cast<const deInt32*>(storageBufferAlloc.getHostPtr());
+
+ for (deUint32 i = 0; i < kTotalPixels; ++i)
+ {
+ if (bufferElements[i] != 1)
+ {
+ std::ostringstream msg;
+ msg << "Unexpected value in storage buffer element " << i;
+ return tcu::TestStatus::fail("Fail: " + msg.str());
+ }
+ }
+ }
+
+ {
+ // Check color attachment.
+ std::vector<tcu::Vec4> expectedColors(1u, m_params.clearColor);
+ if (!expectClearColor(m_params.caseType))
+ expectedColors.push_back(m_params.drawColor);
+
+ const auto& colorImageBufferAlloc = colorImageBuffer.getAllocation();
+ vk::invalidateAlloc(vkd, device, colorImageBufferAlloc);
+
+ const auto iWidth = static_cast<int>(kFramebufferWidth);
+ const auto iHeight = static_cast<int>(kFramebufferHeight);
+
+ tcu::ConstPixelBufferAccess colorPixels (tcuFormat, iWidth, iHeight, 1, colorImageBufferAlloc.getHostPtr());
+ std::vector<deUint8> errorMaskBuffer (kTotalPixels * tcuFormat.getPixelSize(), 0u);
+ tcu::PixelBufferAccess errorMask (tcuFormat, iWidth, iHeight, 1, errorMaskBuffer.data());
+ const tcu::Vec4 green (0.0f, 1.0f, 0.0f, 1.0f);
+ const tcu::Vec4 red (1.0f, 0.0f, 0.0f, 1.0f);
+ bool allPixOk = true;
+
+ for (int i = 0; i < iWidth; ++i)
+ for (int j = 0; j < iHeight; ++j)
+ {
+ const auto pixel = colorPixels.getPixel(i, j);
+ const bool pixOk = std::any_of(begin(expectedColors), end(expectedColors), [&pixel](const tcu::Vec4& expected) -> bool { return (pixel == expected); });
+ errorMask.setPixel((pixOk ? green : red), i, j);
+ if (!pixOk)
+ allPixOk = false;
+ }
+
+ if (!allPixOk)
+ {
+ auto& testLog = m_context.getTestContext().getLog();
+ testLog << tcu::TestLog::Image("ColorBuffer", "Result color buffer", colorPixels);
+ testLog << tcu::TestLog::Image("ErrorMask", "Error mask with errors marked in red", errorMask);
+ return tcu::TestStatus::fail("Fail: color buffer with unexpected values; check logged images");
+ }
+ }
+
+ return tcu::TestStatus::pass("Pass");
+}
+
+} // anonymous
+
+tcu::TestCaseGroup* createFragSideEffectsTests (tcu::TestContext& testCtx)
+{
+ de::MovePtr<tcu::TestCaseGroup> fragSideEffectsGroup(new tcu::TestCaseGroup(testCtx, "frag_side_effects", "Test fragment shader side effects are not removed by optimizations"));
+
+ const tcu::Vec4 kDefaultClearColor (0.0f, 0.0f, 0.0f, 1.0f);
+ const tcu::Vec4 kDefaultDrawColor (0.0f, 0.0f, 1.0f, 1.0f);
+ const auto kDefaultDepthBoundsParams = tcu::nothing<DepthBoundsParameters>();
+
+ static const struct
+ {
+ bool colorAtEnd;
+ std::string name;
+ std::string desc;
+ } kColorOrders[] =
+ {
+ { false, "color_at_beginning", "Fragment shader output assignment at the beginning of the shader" },
+ { true, "color_at_end", "Fragment shader output assignment at the end of the shader" },
+ };
+
+ for (int i = 0; i < DE_LENGTH_OF_ARRAY(kColorOrders); ++i)
+ {
+ de::MovePtr<tcu::TestCaseGroup> colorOrderGroup(new tcu::TestCaseGroup(testCtx, kColorOrders[i].name.c_str(), kColorOrders[i].desc.c_str()));
+ const bool colorAtEnd = kColorOrders[i].colorAtEnd;
+
+ {
+ TestParams params(CaseType::KILL, kDefaultClearColor, kDefaultDrawColor, colorAtEnd, kDefaultDepthBoundsParams);
+ colorOrderGroup->addChild(new FragSideEffectsTestCase(testCtx, "kill", "OpKill after SSBO write", params));
+ }
+ {
+ TestParams params(CaseType::DEMOTE, kDefaultClearColor, kDefaultDrawColor, colorAtEnd, kDefaultDepthBoundsParams);
+ colorOrderGroup->addChild(new FragSideEffectsTestCase(testCtx, "demote", "OpDemoteToHelperInvocation after SSBO write", params));
+ }
+ {
+ TestParams params(CaseType::SAMPLE_MASK_BEFORE, kDefaultClearColor, kDefaultDrawColor, colorAtEnd, kDefaultDepthBoundsParams);
+ colorOrderGroup->addChild(new FragSideEffectsTestCase(testCtx, "sample_mask_before", "Set sample mask to zero before SSBO write", params));
+ }
+ {
+ TestParams params(CaseType::SAMPLE_MASK_AFTER, kDefaultClearColor, kDefaultDrawColor, colorAtEnd, kDefaultDepthBoundsParams);
+ colorOrderGroup->addChild(new FragSideEffectsTestCase(testCtx, "sample_mask_after", "Set sample mask to zero after SSBO write", params));
+ }
+ {
+ TestParams params(CaseType::STENCIL_NEVER, kDefaultClearColor, kDefaultDrawColor, colorAtEnd, kDefaultDepthBoundsParams);
+ colorOrderGroup->addChild(new FragSideEffectsTestCase(testCtx, "stencil_never", "SSBO write with stencil test never passes", params));
+ }
+ {
+ TestParams params(CaseType::DEPTH_NEVER, kDefaultClearColor, kDefaultDrawColor, colorAtEnd, kDefaultDepthBoundsParams);
+ colorOrderGroup->addChild(new FragSideEffectsTestCase(testCtx, "depth_never", "SSBO write with depth test never passes", params));
+ }
+ {
+ const tcu::Vec4 drawColor(kDefaultDrawColor.x(), kDefaultDrawColor.y(), kDefaultDrawColor.z(), 0.0f);
+ {
+ TestParams params(CaseType::ALPHA_COVERAGE_BEFORE, kDefaultClearColor, drawColor, colorAtEnd, kDefaultDepthBoundsParams);
+ colorOrderGroup->addChild(new FragSideEffectsTestCase(testCtx, "alpha_coverage_before", "Enable alpha coverage and draw with alpha zero before SSBO write", params));
+ }
+ {
+ TestParams params(CaseType::ALPHA_COVERAGE_AFTER, kDefaultClearColor, drawColor, colorAtEnd, kDefaultDepthBoundsParams);
+ colorOrderGroup->addChild(new FragSideEffectsTestCase(testCtx, "alpha_coverage_after", "Enable alpha coverage and draw with alpha zero after SSBO write", params));
+ }
+ }
+ {
+ DepthBoundsParameters depthBoundsParams = {0.25f, 0.5f, 0.75f}; // min, max, draw depth.
+ TestParams params(CaseType::DEPTH_BOUNDS, kDefaultClearColor, kDefaultDrawColor, colorAtEnd, tcu::just(depthBoundsParams));
+ colorOrderGroup->addChild(new FragSideEffectsTestCase(testCtx, "depth_bounds", "SSBO write with depth bounds test failing", params));
+ }
+
+ fragSideEffectsGroup->addChild(colorOrderGroup.release());
+ }
+
+ return fragSideEffectsGroup.release();
+}
+
+} // rasterization
+} // vkt