Added pipeline blend tests.
authorDae Kim <dae.kimpark@imgtec.com>
Fri, 23 Oct 2015 09:18:34 +0000 (10:18 +0100)
committerDae Kim <dae.kimpark@imgtec.com>
Fri, 23 Oct 2015 09:18:34 +0000 (10:18 +0100)
12 files changed:
external/vulkancts/framework/vulkan/vkImageUtil.cpp
external/vulkancts/framework/vulkan/vkImageUtil.hpp
external/vulkancts/modules/vulkan/pipeline/CMakeLists.txt
external/vulkancts/modules/vulkan/pipeline/vktPipelineBlendTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/pipeline/vktPipelineBlendTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/pipeline/vktPipelineClearUtil.cpp
external/vulkancts/modules/vulkan/pipeline/vktPipelineClearUtil.hpp
external/vulkancts/modules/vulkan/pipeline/vktPipelineReferenceRenderer.cpp
external/vulkancts/modules/vulkan/pipeline/vktPipelineReferenceRenderer.hpp
external/vulkancts/modules/vulkan/pipeline/vktPipelineTests.cpp
external/vulkancts/modules/vulkan/pipeline/vktPipelineUniqueRandomIterator.hpp [new file with mode: 0644]
framework/referencerenderer/rrFragmentOperations.cpp

index 851178e..70e93f7 100644 (file)
@@ -67,6 +67,9 @@ bool isUintFormat (VkFormat format)
 
 bool isDepthStencilFormat (VkFormat format)
 {
+       if (isCompressedFormat(format))
+               return false;
+
        const tcu::TextureFormat tcuFormat = mapVkFormat(format);
        return tcuFormat.order == tcu::TextureFormat::D || tcuFormat.order == tcu::TextureFormat::S || tcuFormat.order == tcu::TextureFormat::DS;
 }
@@ -440,6 +443,95 @@ tcu::TextureFormat mapVkFormat (VkFormat format)
        }
 }
 
+tcu::CompressedTexFormat mapVkCompressedFormat (VkFormat format)
+{
+       switch (format)
+       {
+               case VK_FORMAT_ETC2_R8G8B8_UNORM:               return tcu::COMPRESSEDTEXFORMAT_ETC2_RGB8;
+               case VK_FORMAT_ETC2_R8G8B8_SRGB:                return tcu::COMPRESSEDTEXFORMAT_ETC2_SRGB8;
+               case VK_FORMAT_ETC2_R8G8B8A1_UNORM:             return tcu::COMPRESSEDTEXFORMAT_ETC2_RGB8_PUNCHTHROUGH_ALPHA1;
+               case VK_FORMAT_ETC2_R8G8B8A1_SRGB:              return tcu::COMPRESSEDTEXFORMAT_ETC2_SRGB8_PUNCHTHROUGH_ALPHA1;
+               case VK_FORMAT_ETC2_R8G8B8A8_UNORM:             return tcu::COMPRESSEDTEXFORMAT_ETC2_EAC_RGBA8;
+               case VK_FORMAT_ETC2_R8G8B8A8_SRGB:              return tcu::COMPRESSEDTEXFORMAT_ETC2_EAC_SRGB8_ALPHA8;
+               case VK_FORMAT_EAC_R11_UNORM:                   return tcu::COMPRESSEDTEXFORMAT_EAC_R11;
+               case VK_FORMAT_EAC_R11_SNORM:                   return tcu::COMPRESSEDTEXFORMAT_EAC_SIGNED_R11;
+               case VK_FORMAT_EAC_R11G11_UNORM:                return tcu::COMPRESSEDTEXFORMAT_EAC_RG11;
+               case VK_FORMAT_EAC_R11G11_SNORM:                return tcu::COMPRESSEDTEXFORMAT_EAC_SIGNED_RG11;
+               case VK_FORMAT_ASTC_4x4_UNORM:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_4x4_RGBA;
+               case VK_FORMAT_ASTC_4x4_SRGB:                   return tcu::COMPRESSEDTEXFORMAT_ASTC_4x4_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_5x4_UNORM:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_5x4_RGBA;
+               case VK_FORMAT_ASTC_5x4_SRGB:                   return tcu::COMPRESSEDTEXFORMAT_ASTC_5x5_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_5x5_UNORM:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_5x5_RGBA;
+               case VK_FORMAT_ASTC_5x5_SRGB:                   return tcu::COMPRESSEDTEXFORMAT_ASTC_5x5_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_6x5_UNORM:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_6x5_RGBA;
+               case VK_FORMAT_ASTC_6x5_SRGB:                   return tcu::COMPRESSEDTEXFORMAT_ASTC_6x5_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_6x6_UNORM:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_6x6_RGBA;
+               case VK_FORMAT_ASTC_6x6_SRGB:                   return tcu::COMPRESSEDTEXFORMAT_ASTC_6x6_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_8x5_UNORM:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_8x5_RGBA;
+               case VK_FORMAT_ASTC_8x5_SRGB:                   return tcu::COMPRESSEDTEXFORMAT_ASTC_8x6_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_8x6_UNORM:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_8x6_RGBA;
+               case VK_FORMAT_ASTC_8x6_SRGB:                   return tcu::COMPRESSEDTEXFORMAT_ASTC_8x6_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_8x8_UNORM:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_8x8_RGBA;
+               case VK_FORMAT_ASTC_8x8_SRGB:                   return tcu::COMPRESSEDTEXFORMAT_ASTC_8x8_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_10x5_UNORM:                 return tcu::COMPRESSEDTEXFORMAT_ASTC_10x5_RGBA;
+               case VK_FORMAT_ASTC_10x5_SRGB:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_10x5_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_10x6_UNORM:                 return tcu::COMPRESSEDTEXFORMAT_ASTC_10x6_RGBA;
+               case VK_FORMAT_ASTC_10x6_SRGB:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_10x6_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_10x8_UNORM:                 return tcu::COMPRESSEDTEXFORMAT_ASTC_10x8_RGBA;
+               case VK_FORMAT_ASTC_10x8_SRGB:                  return tcu::COMPRESSEDTEXFORMAT_ASTC_10x8_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_10x10_UNORM:                return tcu::COMPRESSEDTEXFORMAT_ASTC_10x10_RGBA;
+               case VK_FORMAT_ASTC_10x10_SRGB:                 return tcu::COMPRESSEDTEXFORMAT_ASTC_10x10_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_12x10_UNORM:                return tcu::COMPRESSEDTEXFORMAT_ASTC_12x10_RGBA;
+               case VK_FORMAT_ASTC_12x10_SRGB:                 return tcu::COMPRESSEDTEXFORMAT_ASTC_12x10_SRGB8_ALPHA8;
+               case VK_FORMAT_ASTC_12x12_UNORM:                return tcu::COMPRESSEDTEXFORMAT_ASTC_12x12_RGBA;
+               case VK_FORMAT_ASTC_12x12_SRGB:                 return tcu::COMPRESSEDTEXFORMAT_ASTC_12x12_SRGB8_ALPHA8;
+               default:
+                       break;
+       }
+
+       return tcu::COMPRESSEDTEXFORMAT_LAST;
+}
+
+VkChannelMapping getFormatChannelMapping (VkFormat format)
+{
+       using tcu::TextureFormat;
+
+       static const VkChannelMapping   R               = {     VK_CHANNEL_SWIZZLE_R,           VK_CHANNEL_SWIZZLE_ZERO,        VK_CHANNEL_SWIZZLE_ZERO,        VK_CHANNEL_SWIZZLE_ONE  };
+       static const VkChannelMapping   RG              = {     VK_CHANNEL_SWIZZLE_R,           VK_CHANNEL_SWIZZLE_G,           VK_CHANNEL_SWIZZLE_ZERO,        VK_CHANNEL_SWIZZLE_ONE  };
+       static const VkChannelMapping   RGB             = {     VK_CHANNEL_SWIZZLE_R,           VK_CHANNEL_SWIZZLE_G,           VK_CHANNEL_SWIZZLE_B,           VK_CHANNEL_SWIZZLE_ONE  };
+       static const VkChannelMapping   RGBA    = {     VK_CHANNEL_SWIZZLE_R,           VK_CHANNEL_SWIZZLE_G,           VK_CHANNEL_SWIZZLE_B,           VK_CHANNEL_SWIZZLE_A    };
+       static const VkChannelMapping   S               = { VK_CHANNEL_SWIZZLE_ZERO,    VK_CHANNEL_SWIZZLE_ZERO,        VK_CHANNEL_SWIZZLE_ZERO,        VK_CHANNEL_SWIZZLE_A    };
+       static const VkChannelMapping   DS              = {     VK_CHANNEL_SWIZZLE_R,           VK_CHANNEL_SWIZZLE_ZERO,        VK_CHANNEL_SWIZZLE_ZERO,        VK_CHANNEL_SWIZZLE_A    };
+       static const VkChannelMapping   BGRA    = {     VK_CHANNEL_SWIZZLE_B,           VK_CHANNEL_SWIZZLE_G,           VK_CHANNEL_SWIZZLE_R,           VK_CHANNEL_SWIZZLE_A    };
+
+       if (format == VK_FORMAT_UNDEFINED)
+               return RGBA;
+
+       const tcu::TextureFormat tcuFormat = (isCompressedFormat(format)) ? tcu::getUncompressedFormat(mapVkCompressedFormat(format))
+                                                                                                                                         : mapVkFormat(format);
+
+       switch (tcuFormat.order)
+       {
+               case TextureFormat::R:          return R;
+               case TextureFormat::RG:         return RG;
+               case TextureFormat::RGB:        return RGB;
+               case TextureFormat::RGBA:       return RGBA;
+               case TextureFormat::BGRA:       return BGRA;
+               case TextureFormat::sR:         return R;
+               case TextureFormat::sRG:        return RG;
+               case TextureFormat::sRGB:       return RGB;
+               case TextureFormat::sRGBA:      return RGBA;
+               case TextureFormat::D:          return R;
+               case TextureFormat::S:          return S;
+               case TextureFormat::DS:         return DS;
+               default:
+                       break;
+       }
+
+       DE_ASSERT(false);
+       return RGBA;
+}
+
 static bool isScaledFormat (VkFormat format)
 {
        // update this mapping if VkFormat changes
index b10a193..f8e668d 100644 (file)
 
 #include "vkDefs.hpp"
 #include "tcuTexture.hpp"
+#include "tcuCompressedTexture.hpp"
 
 namespace vk
 {
 
-bool                           isFloatFormat                   (VkFormat format);
-bool                           isUnormFormat                   (VkFormat format);
-bool                           isSnormFormat                   (VkFormat format);
-bool                           isIntFormat                             (VkFormat format);
-bool                           isUintFormat                    (VkFormat format);
-bool                           isDepthStencilFormat    (VkFormat format);
-bool                           isCompressedFormat              (VkFormat format);
+bool                                           isFloatFormat                   (VkFormat format);
+bool                                           isUnormFormat                   (VkFormat format);
+bool                                           isSnormFormat                   (VkFormat format);
+bool                                           isIntFormat                             (VkFormat format);
+bool                                           isUintFormat                    (VkFormat format);
+bool                                           isDepthStencilFormat    (VkFormat format);
+bool                                           isCompressedFormat              (VkFormat format);
 
-tcu::TextureFormat     mapVkFormat                             (VkFormat format);
-VkFormat                       mapTextureFormat                (const tcu::TextureFormat& format);
+tcu::TextureFormat                     mapVkFormat                             (VkFormat format);
+tcu::CompressedTexFormat       mapVkCompressedFormat   (VkFormat format);
+VkChannelMapping                       getFormatChannelMapping (VkFormat format);
 
-void                           imageUtilSelfTest               (void);
+VkFormat                                       mapTextureFormat                (const tcu::TextureFormat& format);
+
+void                                           imageUtilSelfTest               (void);
 
 } // vk
 
index c4f5547..d993528 100644 (file)
@@ -4,6 +4,8 @@ include_directories(
        )
 
 set(DEQP_VK_PIPELINE_SRCS
+       vktPipelineBlendTests.cpp
+       vktPipelineBlendTests.hpp
        vktPipelineClearUtil.cpp
        vktPipelineClearUtil.hpp
        vktPipelineDepthTests.cpp
diff --git a/external/vulkancts/modules/vulkan/pipeline/vktPipelineBlendTests.cpp b/external/vulkancts/modules/vulkan/pipeline/vktPipelineBlendTests.cpp
new file mode 100644 (file)
index 0000000..d9d029f
--- /dev/null
@@ -0,0 +1,1148 @@
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Imagination Technologies Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Blend Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktPipelineBlendTests.hpp"
+#include "vktPipelineClearUtil.hpp"
+#include "vktPipelineImageUtil.hpp"
+#include "vktPipelineVertexUtil.hpp"
+#include "vktPipelineUniqueRandomIterator.hpp"
+#include "vktPipelineReferenceRenderer.hpp"
+#include "vktTestCase.hpp"
+#include "vkImageUtil.hpp"
+#include "vkMemUtil.hpp"
+#include "vkPlatform.hpp"
+#include "vkPrograms.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkRef.hpp"
+#include "vkRefUtil.hpp"
+#include "tcuImageCompare.hpp"
+#include "tcuPlatform.hpp"
+#include "tcuTextureUtil.hpp"
+#include "deRandom.hpp"
+#include "deStringUtil.hpp"
+#include "deUniquePtr.hpp"
+#include <cstring>
+#include <set>
+#include <sstream>
+#include <vector>
+
+namespace vkt
+{
+namespace pipeline
+{
+
+using namespace vk;
+
+namespace
+{
+
+bool isSupportedBlendFormat (const InstanceInterface& instanceInterface, VkPhysicalDevice device, VkFormat format)
+{
+       VkFormatProperties formatProps;
+
+       VK_CHECK(instanceInterface.getPhysicalDeviceFormatProperties(device, format, &formatProps));
+
+       return (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) &&
+                  (formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT);
+}
+
+class BlendStateUniqueRandomIterator : public UniqueRandomIterator<VkPipelineColorBlendAttachmentState>
+{
+public:
+                                                                                       BlendStateUniqueRandomIterator          (deUint32 numberOfCombinations, int seed);
+       virtual                                                                 ~BlendStateUniqueRandomIterator         (void) {}
+       VkPipelineColorBlendAttachmentState             getIndexedValue (deUint32 index);
+
+private:
+       const static VkBlend                                    m_blendFactors[];
+       const static VkBlendOp                                  m_blendOps[];
+
+       // Pre-calculated constants
+       const static deUint32                                   m_blendFactorsLength;
+       const static deUint32                                   m_blendFactorsLength2;
+       const static deUint32                                   m_blendFactorsLength3;
+       const static deUint32                                   m_blendFactorsLength4;
+       const static deUint32                                   m_blendOpsLength;
+
+       // Total number of cross-combinations of (srcBlendColor x destBlendColor x blendOpColor x srcBlendAlpha x destBlendAlpha x blendOpAlpha)
+       const static deUint32                                   m_totalBlendStates;
+};
+
+class BlendTest : public vkt::TestCase
+{
+public:
+       enum
+       {
+               QUAD_COUNT = 4
+       };
+
+       const static VkChannelFlags                     s_channelWriteMasks[QUAD_COUNT];
+       const static tcu::Vec4                          s_blendConst;
+
+                                                                               BlendTest                               (tcu::TestContext&                                                      testContext,
+                                                                                                                                const std::string&                                                     name,
+                                                                                                                                const std::string&                                                     description,
+                                                                                                                                const VkFormat                                                         colorFormat,
+                                                                                                                                const VkPipelineColorBlendAttachmentState      blendStates[QUAD_COUNT]);
+       virtual                                                         ~BlendTest                              (void);
+       virtual void                                            initPrograms                    (SourceCollections& sourceCollections) const;
+       virtual TestInstance*                           createInstance                  (Context& context) const;
+
+private:
+       const VkFormat                                          m_colorFormat;
+       VkPipelineColorBlendAttachmentState     m_blendStates[QUAD_COUNT];
+};
+
+class BlendTestInstance : public vkt::TestInstance
+{
+public:
+                                                                               BlendTestInstance               (Context& context, const VkFormat colorFormat, const VkPipelineColorBlendAttachmentState blendStates[BlendTest::QUAD_COUNT]);
+       virtual                                                         ~BlendTestInstance              (void);
+       virtual tcu::TestStatus                         iterate                                 (void);
+
+private:
+       static float                                            getNormChannelThreshold (const tcu::TextureFormat& format, int numBits);
+       static tcu::Vec4                                        getFormatThreshold              (const tcu::TextureFormat& format);
+       tcu::TestStatus                                         verifyImage                             (void);
+
+       VkPipelineColorBlendAttachmentState     m_blendStates[BlendTest::QUAD_COUNT];
+
+       const tcu::IVec2                                        m_renderSize;
+       const VkFormat                                          m_colorFormat;
+
+       VkImageCreateInfo                                       m_colorImageCreateInfo;
+       Move<VkImage>                                           m_colorImage;
+       de::MovePtr<Allocation>                         m_colorImageAlloc;
+       Move<VkImageView>                                       m_colorAttachmentView;
+       Move<VkRenderPass>                                      m_renderPass;
+       Move<VkFramebuffer>                                     m_framebuffer;
+
+       Move<VkShaderModule>                            m_vertexShaderModule;
+       Move<VkShaderModule>                            m_fragmentShaderModule;
+       Move<VkShader>                                          m_vertexShader;
+       Move<VkShader>                                          m_fragmentShader;
+
+       Move<VkBuffer>                                          m_vertexBuffer;
+       std::vector<Vertex4RGBA>                        m_vertices;
+       de::MovePtr<Allocation>                         m_vertexBufferAlloc;
+
+       Move<VkPipelineLayout>                          m_pipelineLayout;
+       Move<VkPipeline>                                        m_graphicsPipelines[BlendTest::QUAD_COUNT];
+
+       Move<VkCmdPool>                                         m_cmdPool;
+       Move<VkCmdBuffer>                                       m_cmdBuffer;
+
+       Move<VkFence>                                           m_fence;
+};
+
+
+// BlendStateUniqueRandomIterator
+
+const VkBlend BlendStateUniqueRandomIterator::m_blendFactors[] =
+{
+       VK_BLEND_ZERO,
+       VK_BLEND_ONE,
+       VK_BLEND_SRC_COLOR,
+       VK_BLEND_ONE_MINUS_SRC_COLOR,
+       VK_BLEND_DEST_COLOR,
+       VK_BLEND_ONE_MINUS_DEST_COLOR,
+       VK_BLEND_SRC_ALPHA,
+       VK_BLEND_ONE_MINUS_SRC_ALPHA,
+       VK_BLEND_DEST_ALPHA,
+       VK_BLEND_ONE_MINUS_DEST_ALPHA,
+       VK_BLEND_CONSTANT_COLOR,
+       VK_BLEND_ONE_MINUS_CONSTANT_COLOR,
+       VK_BLEND_CONSTANT_ALPHA,
+       VK_BLEND_ONE_MINUS_CONSTANT_ALPHA,
+       VK_BLEND_SRC_ALPHA_SATURATE
+};
+
+const VkBlendOp BlendStateUniqueRandomIterator::m_blendOps[] =
+{
+       VK_BLEND_OP_ADD,
+       VK_BLEND_OP_SUBTRACT,
+       VK_BLEND_OP_REVERSE_SUBTRACT,
+       VK_BLEND_OP_MIN,
+       VK_BLEND_OP_MAX
+};
+
+const deUint32 BlendStateUniqueRandomIterator::m_blendFactorsLength            = DE_LENGTH_OF_ARRAY(m_blendFactors);
+const deUint32 BlendStateUniqueRandomIterator::m_blendFactorsLength2   = m_blendFactorsLength * m_blendFactorsLength;
+const deUint32 BlendStateUniqueRandomIterator::m_blendFactorsLength3   = m_blendFactorsLength2 * m_blendFactorsLength;
+const deUint32 BlendStateUniqueRandomIterator::m_blendFactorsLength4   = m_blendFactorsLength3 * m_blendFactorsLength;
+const deUint32 BlendStateUniqueRandomIterator::m_blendOpsLength                        = DE_LENGTH_OF_ARRAY(m_blendOps);
+const deUint32 BlendStateUniqueRandomIterator::m_totalBlendStates              = m_blendFactorsLength4 * m_blendOpsLength * m_blendOpsLength;
+
+
+BlendStateUniqueRandomIterator::BlendStateUniqueRandomIterator (deUint32 numberOfCombinations, int seed)
+       : UniqueRandomIterator<VkPipelineColorBlendAttachmentState>(numberOfCombinations, m_totalBlendStates, seed)
+{
+}
+
+VkPipelineColorBlendAttachmentState BlendStateUniqueRandomIterator::getIndexedValue (deUint32 index)
+{
+       const deUint32          blendOpAlphaIndex                       = index / (m_blendFactorsLength4 * m_blendOpsLength);
+       const deUint32          blendOpAlphaSeqIndex            = blendOpAlphaIndex * (m_blendFactorsLength4 * m_blendOpsLength);
+
+       const deUint32          destBlendAlphaIndex                     = (index - blendOpAlphaSeqIndex) / (m_blendFactorsLength3 * m_blendOpsLength);
+       const deUint32          destBlendAlphaSeqIndex          = destBlendAlphaIndex * (m_blendFactorsLength3 * m_blendOpsLength);
+
+       const deUint32          srcBlendAlphaIndex                      = (index - blendOpAlphaSeqIndex - destBlendAlphaSeqIndex) / (m_blendFactorsLength2 * m_blendOpsLength);
+       const deUint32          srcBlendAlphaSeqIndex           = srcBlendAlphaIndex * (m_blendFactorsLength2 * m_blendOpsLength);
+
+       const deUint32          blendOpColorIndex                       = (index - blendOpAlphaSeqIndex - destBlendAlphaSeqIndex - srcBlendAlphaSeqIndex) / m_blendFactorsLength2;
+       const deUint32          blendOpColorSeqIndex            = blendOpColorIndex * m_blendFactorsLength2;
+
+       const deUint32          destBlendColorIndex                     = (index - blendOpAlphaSeqIndex - destBlendAlphaSeqIndex - srcBlendAlphaSeqIndex - blendOpColorSeqIndex) / m_blendFactorsLength;
+       const deUint32          destBlendColorSeqIndex          = destBlendColorIndex * m_blendFactorsLength;
+
+       const deUint32          srcBlendColorIndex                      = index - blendOpAlphaSeqIndex - destBlendAlphaSeqIndex - srcBlendAlphaSeqIndex - blendOpColorSeqIndex - destBlendColorSeqIndex;
+
+       const VkPipelineColorBlendAttachmentState blendAttachmentState =
+       {
+               true,                                                                                                                                                   // VkBool32                     blendEnable;
+               m_blendFactors[srcBlendColorIndex],                                                                                             // VkBlend                      srcBlendColor;
+               m_blendFactors[destBlendColorIndex],                                                                                    // VkBlend                      destBlendColor;
+               m_blendOps[blendOpColorIndex],                                                                                                  // VkBlendOp            blendOpColor;
+               m_blendFactors[srcBlendAlphaIndex],                                                                                             // VkBlend                      srcBlendAlpha;
+               m_blendFactors[destBlendAlphaIndex],                                                                                    // VkBlend                      destBlendAlpha;
+               m_blendOps[blendOpAlphaIndex],                                                                                                  // VkBlendOp            blendOpAlpha;
+               VK_CHANNEL_R_BIT | VK_CHANNEL_G_BIT | VK_CHANNEL_B_BIT | VK_CHANNEL_A_BIT               // VkChannelFlags       channelWriteMask;
+       };
+
+       return blendAttachmentState;
+}
+
+
+// BlendTest
+
+const VkChannelFlags   BlendTest::s_channelWriteMasks[BlendTest::QUAD_COUNT] = { VK_CHANNEL_R_BIT | VK_CHANNEL_G_BIT,  // Pair of channels: R & G
+                                                                                                                                                                 VK_CHANNEL_G_BIT | VK_CHANNEL_B_BIT,  // Pair of channels: G & B
+                                                                                                                                                                 VK_CHANNEL_B_BIT | VK_CHANNEL_A_BIT,  // Pair of channels: B & A
+                                                                                                                                                                 VK_CHANNEL_R_BIT | VK_CHANNEL_G_BIT | VK_CHANNEL_B_BIT | VK_CHANNEL_A_BIT };  // All channels
+
+const tcu::Vec4                        BlendTest::s_blendConst = tcu::Vec4(0.1f, 0.2f, 0.3f, 0.4f);
+
+BlendTest::BlendTest (tcu::TestContext&                                                                testContext,
+                                         const std::string&                                                    name,
+                                         const std::string&                                                    description,
+                                         const VkFormat                                                                colorFormat,
+                                         const VkPipelineColorBlendAttachmentState             blendStates[QUAD_COUNT])
+       : vkt::TestCase (testContext, name, description)
+       , m_colorFormat(colorFormat)
+{
+       deMemcpy(m_blendStates, blendStates, sizeof(VkPipelineColorBlendAttachmentState) * QUAD_COUNT);
+}
+
+BlendTest::~BlendTest (void)
+{
+}
+
+TestInstance* BlendTest::createInstance(Context& context) const
+{
+       return new BlendTestInstance(context, m_colorFormat, m_blendStates);
+}
+
+void BlendTest::initPrograms (SourceCollections& sourceCollections) const
+{
+       std::ostringstream fragmentSource;
+
+       sourceCollections.glslSources.add("color_vert") << glu::VertexSource(
+               "#version 310 es\n"
+               "layout(location = 0) in highp vec4 position;\n"
+               "layout(location = 1) in highp vec4 color;\n"
+               "layout(location = 0) out highp vec4 vtxColor;\n"
+               "void main (void)\n"
+               "{\n"
+               "       gl_Position = position;\n"
+               "       vtxColor = color;\n"
+               "}\n");
+
+       fragmentSource << "#version 310 es\n"
+               "layout(location = 0) in highp vec4 vtxColor;\n"
+               "layout(location = 0) out highp vec4 fragColor;\n"
+               "void main (void)\n"
+               "{\n"
+               "       fragColor = vtxColor;\n"
+               "}\n";
+
+       sourceCollections.glslSources.add("color_frag") << glu::FragmentSource(fragmentSource.str());
+}
+
+
+// BlendTestInstance
+
+BlendTestInstance::BlendTestInstance (Context&                                                                 context,
+                                                                         const VkFormat                                                        colorFormat,
+                                                                         const VkPipelineColorBlendAttachmentState     blendStates[BlendTest::QUAD_COUNT])
+       : vkt::TestInstance     (context)
+       , m_renderSize          (32, 32)
+       , m_colorFormat         (colorFormat)
+{
+       const DeviceInterface&          vk                                      = m_context.getDeviceInterface();
+       const VkDevice                          vkDevice                        = m_context.getDevice();
+       const deUint32                          queueFamilyIndex        = m_context.getUniversalQueueFamilyIndex();
+       SimpleAllocator                         memAlloc                        (vk, vkDevice, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
+
+       // Copy depth operators
+       deMemcpy(m_blendStates, blendStates, sizeof(VkPipelineColorBlendAttachmentState) * BlendTest::QUAD_COUNT);
+
+       // Create color image
+       {
+               if (!isSupportedBlendFormat(context.getInstanceInterface(), context.getPhysicalDevice(), m_colorFormat))
+                       throw tcu::NotSupportedError(std::string("Unsupported color blending format: ") + getFormatName(m_colorFormat));
+
+               const VkImageCreateInfo colorImageParams =
+               {
+                       VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,                                                                            // VkStructureType              sType;
+                       DE_NULL,                                                                                                                                        // const void*                  pNext;
+                       VK_IMAGE_TYPE_2D,                                                                                                                       // VkImageType                  imageType;
+                       m_colorFormat,                                                                                                                          // VkFormat                             format;
+                       { m_renderSize.x(), m_renderSize.y(), 1u },                                                                     // VkExtent3D                   extent;
+                       1u,                                                                                                                                                     // deUint32                             mipLevels;
+                       1u,                                                                                                                                                     // deUint32                             arraySize;
+                       1u,                                                                                                                                                     // deUint32                             samples;
+                       VK_IMAGE_TILING_OPTIMAL,                                                                                                        // VkImageTiling                tiling;
+                       VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SOURCE_BIT,       // VkImageUsageFlags    usage;
+                       0u,                                                                                                                                                     // VkImageCreateFlags   flags;
+                       VK_SHARING_MODE_EXCLUSIVE,                                                                                                      // VkSharingMode                sharingMode;
+                       1u,                                                                                                                                                     // deUint32                             queueFamilyCount;
+                       &queueFamilyIndex,                                                                                                                      // const deUint32*              pQueueFamilyIndices;
+                       VK_IMAGE_LAYOUT_UNDEFINED                                                                                                       // VkImageLayout                initialLayout;
+               };
+
+               m_colorImageCreateInfo  = colorImageParams;
+               m_colorImage                    = createImage(vk, vkDevice, &m_colorImageCreateInfo);
+
+               // Allocate and bind color image memory
+               m_colorImageAlloc               = memAlloc.allocate(getImageMemoryRequirements(vk, vkDevice, *m_colorImage), MemoryRequirement::Any);
+               VK_CHECK(vk.bindImageMemory(vkDevice, *m_colorImage, m_colorImageAlloc->getMemory(), m_colorImageAlloc->getOffset()));
+       }
+
+       // Create color attachment view
+       {
+               const VkImageViewCreateInfo colorAttachmentViewParams =
+               {
+                       VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,                       // VkStructureType                              sType;
+                       DE_NULL,                                                                                        // const void*                                  pNext;
+                       *m_colorImage,                                                                          // VkImage                                              image;
+                       VK_IMAGE_VIEW_TYPE_2D,                                                          // VkImageViewType                              viewType;
+                       m_colorFormat,                                                                          // VkFormat                                             format;
+                       getFormatChannelMapping(m_colorFormat),                         // VkChannelMapping                             channels;
+                       { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u },          // VkImageSubresourceRange              subresourceRange;
+                       0u                                                                                                      // VkImageViewCreateFlags               flags;
+               };
+
+               m_colorAttachmentView = createImageView(vk, vkDevice, &colorAttachmentViewParams);
+       }
+
+       // Create render pass
+       {
+               const VkAttachmentDescription colorAttachmentDescription =
+               {
+                       VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION,                       // VkStructureType                              sType;
+                       DE_NULL,                                                                                        // const void*                                  pNext;
+                       m_colorFormat,                                                                          // VkFormat                                             format;
+                       1u,                                                                                                     // deUint32                                             samples;
+                       VK_ATTACHMENT_LOAD_OP_CLEAR,                                            // VkAttachmentLoadOp                   loadOp;
+                       VK_ATTACHMENT_STORE_OP_STORE,                                           // VkAttachmentStoreOp                  storeOp;
+                       VK_ATTACHMENT_LOAD_OP_DONT_CARE,                                        // VkAttachmentLoadOp                   stencilLoadOp;
+                       VK_ATTACHMENT_STORE_OP_DONT_CARE,                                       // VkAttachmentStoreOp                  stencilStoreOp;
+                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,                       // VkImageLayout                                initialLayout;
+                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,                       // VkImageLayout                                finalLayout;
+                       0u,                                                                                                     // VkAttachmentDescriptionFlags flags;
+               };
+
+               const VkAttachmentReference colorAttachmentReference =
+               {
+                       0u,                                                                                                     // deUint32                     attachment;
+                       VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL                        // VkImageLayout        layout;
+               };
+
+               const VkAttachmentReference depthStencilAttachmentReference =
+               {
+                       VK_ATTACHMENT_UNUSED,                                                           // deUint32                     attachment;
+                       VK_IMAGE_LAYOUT_UNDEFINED                                                       // VkImageLayout        layout;
+               };
+
+               const VkSubpassDescription subpassDescription =
+               {
+                       VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION,                          // VkStructureType                              sType;
+                       DE_NULL,                                                                                        // const void*                                  pNext;
+                       VK_PIPELINE_BIND_POINT_GRAPHICS,                                        // VkPipelineBindPoint                  pipelineBindPoint;
+                       0u,                                                                                                     // VkSubpassDescriptionFlags    flags;
+                       0u,                                                                                                     // deUint32                                             inputCount;
+                       DE_NULL,                                                                                        // constVkAttachmentReference*  inputAttachments;
+                       1u,                                                                                                     // deUint32                                             colorCount;
+                       &colorAttachmentReference,                                                      // constVkAttachmentReference*  pColorAttachments;
+                       DE_NULL,                                                                                        // constVkAttachmentReference*  pResolveAttachments;
+                       depthStencilAttachmentReference,                                        // VkAttachmentReference                depthStencilAttachment;
+                       0u,                                                                                                     // deUint32                                             preserveCount;
+                       DE_NULL                                                                                         // constVkAttachmentReference*  pPreserveAttachments;
+               };
+
+               const VkRenderPassCreateInfo renderPassParams =
+               {
+                       VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,                      // VkStructureType                                      sType;
+                       DE_NULL,                                                                                        // const void*                                          pNext;
+                       1u,                                                                                                     // deUint32                                                     attachmentCount;
+                       &colorAttachmentDescription,                                            // const VkAttachmentDescription*       pAttachments;
+                       1u,                                                                                                     // deUint32                                                     subpassCount;
+                       &subpassDescription,                                                            // const VkSubpassDescription*          pSubpasses;
+                       0u,                                                                                                     // deUint32                                                     dependencyCount;
+                       DE_NULL                                                                                         // const VkSubpassDependency*           pDependencies;
+               };
+
+               m_renderPass = createRenderPass(vk, vkDevice, &renderPassParams);
+       }
+
+       // Create framebuffer
+       {
+               const VkFramebufferCreateInfo framebufferParams =
+               {
+                       VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,                      // VkStructureType              sType;
+                       DE_NULL,                                                                                        // const void*                  pNext;
+                       *m_renderPass,                                                                          // VkRenderPass                 renderPass;
+                       1u,                                                                                                     // deUint32                             attachmentCount;
+                       &m_colorAttachmentView.get(),                                           // const VkImageView*   pAttachments;
+                       (deUint32)m_renderSize.x(),                                                     // deUint32                             width;
+                       (deUint32)m_renderSize.y(),                                                     // deUint32                             height;
+                       1u                                                                                                      // deUint32                             layers;
+               };
+
+               m_framebuffer = createFramebuffer(vk, vkDevice, &framebufferParams);
+       }
+
+       // Create shaders
+       {
+               m_vertexShaderModule    = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("color_vert"), 0);
+               m_fragmentShaderModule  = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("color_frag"), 0);
+
+               const VkShaderCreateInfo vertexShaderParams =
+               {
+                       VK_STRUCTURE_TYPE_SHADER_CREATE_INFO,                   // VkStructureType              sType;
+                       DE_NULL,                                                                                // const void*                  pNext;
+                       *m_vertexShaderModule,                                                  // VkShaderModule               module;
+                       "main",                                                                                 // const char*                  pName;
+                       0u,                                                                                             // VkShaderCreateFlags  flags;
+                       VK_SHADER_STAGE_VERTEX                                                  // VkShaderStage                stage;
+               };
+
+               const VkShaderCreateInfo fragmentShaderParams =
+               {
+                       VK_STRUCTURE_TYPE_SHADER_CREATE_INFO,                   // VkStructureType              sType;
+                       DE_NULL,                                                                                // const void*                  pNext;
+                       *m_fragmentShaderModule,                                                // VkShaderModule               module;
+                       "main",                                                                                 // const char*                  pName;
+                       0u,                                                                                             // VkShaderCreateFlags  flags;
+                       VK_SHADER_STAGE_FRAGMENT                                                // VkShaderStage                stage;
+               };
+
+               m_vertexShader          = createShader(vk, vkDevice, &vertexShaderParams);
+               m_fragmentShader        = createShader(vk, vkDevice, &fragmentShaderParams);
+       }
+
+       // Create pipeline layout
+       {
+               const VkPipelineLayoutCreateInfo pipelineLayoutParams =
+               {
+                       VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,          // VkStructureType                              sType;
+                       DE_NULL,                                                                                        // const void*                                  pNext;
+                       0u,                                                                                                     // deUint32                                             descriptorSetCount;
+                       DE_NULL,                                                                                        // const VkDescriptorSetLayout* pSetLayouts;
+                       0u,                                                                                                     // deUint32                                             pushConstantRangeCount;
+                       DE_NULL                                                                                         // const VkPushConstantRange*   pPushConstantRanges;
+               };
+
+               m_pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
+       }
+
+       // Create pipeline
+       {
+               const VkPipelineShaderStageCreateInfo shaderStageParams[2] =
+               {
+                       {
+                               VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,            // VkStructureType                              sType;
+                               DE_NULL,                                                                                                        // const void*                                  pNext;
+                               VK_SHADER_STAGE_VERTEX,                                                                         // VkShaderStage                                stage;
+                               *m_vertexShader,                                                                                        // VkShader                                             shader;
+                               DE_NULL                                                                                                         // const VkSpecializationInfo*  pSpecializationInfo;
+                       },
+                       {
+                               VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,            // VkStructureType                              sType;
+                               DE_NULL,                                                                                                        // const void*                                  pNext;
+                               VK_SHADER_STAGE_FRAGMENT,                                                                       // VkShaderStage                                stage;
+                               *m_fragmentShader,                                                                                      // VkShader                                             shader;
+                               DE_NULL                                                                                                         // const VkSpecializationInfo*  pSpecializationInfo;
+                       }
+               };
+
+               const VkVertexInputBindingDescription vertexInputBindingDescription =
+               {
+                       0u,                                                                     // deUint32                                     binding;
+                       sizeof(Vertex4RGBA),                            // deUint32                                     strideInBytes;
+                       VK_VERTEX_INPUT_STEP_RATE_VERTEX        // VkVertexInputStepRate        stepRate;
+               };
+
+               const VkVertexInputAttributeDescription vertexInputAttributeDescriptions[2] =
+               {
+                       {
+                               0u,                                                             // deUint32     location;
+                               0u,                                                             // deUint32     binding;
+                               VK_FORMAT_R32G32B32A32_SFLOAT,  // VkFormat     format;
+                               0u                                                              // deUint32     offsetInBytes;
+                       },
+                       {
+                               1u,                                                             // deUint32     location;
+                               0u,                                                             // deUint32     binding;
+                               VK_FORMAT_R32G32B32A32_SFLOAT,  // VkFormat     format;
+                               (deUint32)(sizeof(float) * 4),  // deUint32     offsetInBytes;
+                       }
+               };
+
+               const VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
+               {
+                       VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,              // VkStructureType                                                      sType;
+                       DE_NULL,                                                                                                                // const void*                                                          pNext;
+                       1u,                                                                                                                             // deUint32                                                                     bindingCount;
+                       &vertexInputBindingDescription,                                                                 // const VkVertexInputBindingDescription*       pVertexBindingDescriptions;
+                       2u,                                                                                                                             // deUint32                                                                     attributeCount;
+                       vertexInputAttributeDescriptions                                                                // const VkVertexInputAttributeDescription*     pVertexAttributeDescriptions;
+               };
+
+               const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateParams =
+               {
+                       VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,    // VkStructureType              sType;
+                       DE_NULL,                                                                                                                // const void*                  pNext;
+                       VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,                                                    // VkPrimitiveTopology  topology;
+                       false                                                                                                                   // VkBool32                             primitiveRestartEnable;
+               };
+
+               const VkViewport viewport =
+               {
+                       0.0f,                                           // float        originX;
+                       0.0f,                                           // float        originY;
+                       (float)m_renderSize.x(),        // float        width;
+                       (float)m_renderSize.y(),        // float        height;
+                       0.0f,                                           // float        minDepth;
+                       1.0f                                            // float        maxDepth;
+               };
+
+               const VkRect2D scissor = { { 0, 0 }, { m_renderSize.x(), m_renderSize.y() } };
+
+               const VkPipelineViewportStateCreateInfo viewportStateParams =
+               {
+                       VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,                  // VkStructureType              sType;
+                       DE_NULL,                                                                                                                // const void*                  pNext;
+                       1u,                                                                                                                             // deUint32                             viewportCount;
+                       &viewport,                                                                                                              // const VkViewport*    pViewports;
+                       1u,                                                                                                                             // deUint32                             scissorCount;
+                       &scissor                                                                                                                // const VkRect2D*              pScissors;
+               };
+
+               const VkPipelineRasterStateCreateInfo rasterStateParams =
+               {
+                       VK_STRUCTURE_TYPE_PIPELINE_RASTER_STATE_CREATE_INFO,                    // VkStructureType      sType;
+                       DE_NULL,                                                                                                                // const void*          pNext;
+                       false,                                                                                                                  // VkBool32                     depthClipEnable;
+                       false,                                                                                                                  // VkBool32                     rasterizerDiscardEnable;
+                       VK_FILL_MODE_SOLID,                                                                                             // VkFillMode           fillMode;
+                       VK_CULL_MODE_NONE,                                                                                              // VkCullMode           cullMode;
+                       VK_FRONT_FACE_CCW,                                                                                              // VkFrontFace          frontFace;
+                       false,                                                                                                                  // VkBool32                     depthBiasEnable;
+                       0.0f,                                                                                                                   // float                        depthBias;
+                       0.0f,                                                                                                                   // float                        depthBiasClamp;
+                       0.0f,                                                                                                                   // float                        slopeScaledDepthBias;
+                       1.0f                                                                                                                    // float                        lineWidth;
+               };
+
+               const VkPipelineMultisampleStateCreateInfo multisampleStateParams =
+               {
+                       VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,       // VkStructureType              sType;
+                       DE_NULL,                                                                                                        // const void*                  pNext;
+                       1u,                                                                                                                     // deUint32                             rasterSamples;
+                       false,                                                                                                          // VkBool32                             sampleShadingEnable;
+                       0.0f,                                                                                                           // float                                minSampleShading;
+                       DE_NULL                                                                                                         // const VkSampleMask*  sampleMask;
+               };
+
+               const VkPipelineDepthStencilStateCreateInfo depthStencilStateParams =
+               {
+                       VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,     // VkStructureType      sType;
+                       DE_NULL,                                                                                                        // const void*          pNext;
+                       false,                                                                                                          // VkBool32                     depthTestEnable;
+                       false,                                                                                                          // VkBool32                     depthWriteEnable;
+                       VK_COMPARE_OP_LESS,                                                                                     // VkCompareOp          depthCompareOp;
+                       false,                                                                                                          // VkBool32                     depthBoundsTestEnable;
+                       false,                                                                                                          // VkBool32                     stencilTestEnable;
+                       // VkStencilOpState     front;
+                       {
+                               VK_STENCIL_OP_KEEP,             // VkStencilOp  stencilFailOp;
+                               VK_STENCIL_OP_KEEP,             // VkStencilOp  stencilPassOp;
+                               VK_STENCIL_OP_KEEP,             // VkStencilOp  stencilDepthFailOp;
+                               VK_COMPARE_OP_NEVER,    // VkCompareOp  stencilCompareOp;
+                               0u,                                             // deUint32             stencilCompareMask;
+                               0u,                                             // deUint32             stencilWriteMask;
+                               0u                                              // deUint32             stencilReference;
+                       },
+                       // VkStencilOpState     back;
+                       {
+                               VK_STENCIL_OP_KEEP,             // VkStencilOp  stencilFailOp;
+                               VK_STENCIL_OP_KEEP,             // VkStencilOp  stencilPassOp;
+                               VK_STENCIL_OP_KEEP,             // VkStencilOp  stencilDepthFailOp;
+                               VK_COMPARE_OP_NEVER,    // VkCompareOp  stencilCompareOp;
+                               0u,                                             // deUint32             stencilCompareMask;
+                               0u,                                             // deUint32             stencilWriteMask;
+                               0u                                              // deUint32             stencilReference;
+                       },
+                       -1.0f,                                                                                                          // float                        minDepthBounds;
+                       +1.0f                                                                                                           // float                        maxDepthBounds;
+               };
+
+               // The color blend attachment will be set up before creating the graphics pipeline.
+               VkPipelineColorBlendStateCreateInfo colorBlendStateParams =
+               {
+                       VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,       // VkStructureType                                                              sType;
+                       DE_NULL,                                                                                                        // const void*                                                                  pNext;
+                       false,                                                                                                          // VkBool32                                                                             alphaToCoverageEnable;
+                       false,                                                                                                          // VkBool32                                                                             alphaToOneEnable;
+                       false,                                                                                                          // VkBool32                                                                             logicOpEnable;
+                       VK_LOGIC_OP_COPY,                                                                                       // VkLogicOp                                                                    logicOp;
+                       1u,                                                                                                                     // deUint32                                                                             attachmentCount;
+                       DE_NULL,                                                                                                        // const VkPipelineColorBlendAttachmentState*   pAttachments;
+                       {                                                                                                                       // float                                                                                blendConst[4];
+                               BlendTest::s_blendConst.x(),
+                               BlendTest::s_blendConst.y(),
+                               BlendTest::s_blendConst.z(),
+                               BlendTest::s_blendConst.w()
+                       }
+               };
+
+               const VkPipelineDynamicStateCreateInfo dynamicStateParams =
+               {
+                       VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,           // VkStructureType                      sType;
+                       DE_NULL,                                                                                                        // const void*                          pNext;
+                       0u,                                                                                                                     // deUint32                                     dynamicStateCount;
+                       DE_NULL                                                                                                         // const VkDynamicState*        pDynamicStates;
+               };
+
+               const VkGraphicsPipelineCreateInfo graphicsPipelineParams =
+               {
+                       VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,        // VkStructureType                                                                      sType;
+                       DE_NULL,                                                                                        // const void*                                                                          pNext;
+                       2u,                                                                                                     // deUint32                                                                                     stageCount;
+                       shaderStageParams,                                                                      // const VkPipelineShaderStageCreateInfo*                       pStages;
+                       &vertexInputStateParams,                                                        // const VkPipelineVertexInputStateCreateInfo*          pVertexInputState;
+                       &inputAssemblyStateParams,                                                      // const VkPipelineInputAssemblyStateCreateInfo*        pInputAssemblyState;
+                       DE_NULL,                                                                                        // const VkPipelineTessellationStateCreateInfo*         pTessellationState;
+                       &viewportStateParams,                                                           // const VkPipelineViewportStateCreateInfo*                     pViewportState;
+                       &rasterStateParams,                                                                     // const VkPipelineRasterStateCreateInfo*                       pRasterState;
+                       &multisampleStateParams,                                                        // const VkPipelineMultisampleStateCreateInfo*          pMultisampleState;
+                       &depthStencilStateParams,                                                       // const VkPipelineDepthStencilStateCreateInfo*         pDepthStencilState;
+                       &colorBlendStateParams,                                                         // const VkPipelineColorBlendStateCreateInfo*           pColorBlendState;
+                       &dynamicStateParams,                                                            // const VkPipelineDynamicStateCreateInfo*                      pDynamicState;
+                       0u,                                                                                                     // VkPipelineCreateFlags                                                        flags;
+                       *m_pipelineLayout,                                                                      // VkPipelineLayout                                                                     layout;
+                       *m_renderPass,                                                                          // VkRenderPass                                                                         renderPass;
+                       0u,                                                                                                     // deUint32                                                                                     subpass;
+                       0u,                                                                                                     // VkPipeline                                                                           basePipelineHandle;
+                       0u                                                                                                      // deInt32                                                                                      basePipelineIndex;
+               };
+
+               for (int quadNdx = 0; quadNdx < BlendTest::QUAD_COUNT; quadNdx++)
+               {
+                       colorBlendStateParams.pAttachments      = &m_blendStates[quadNdx];
+                       m_graphicsPipelines[quadNdx]            = createGraphicsPipeline(vk, vkDevice, DE_NULL, &graphicsPipelineParams);
+               }
+       }
+
+       // Create vertex buffer
+       {
+               const VkBufferCreateInfo vertexBufferParams =
+               {
+                       VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,           // VkStructureType              sType;
+                       DE_NULL,                                                                        // const void*                  pNext;
+                       1024u,                                                                          // VkDeviceSize                 size;
+                       VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,                      // VkBufferUsageFlags   usage;
+                       0u,                                                                                     // VkBufferCreateFlags  flags;
+                       VK_SHARING_MODE_EXCLUSIVE,                                      // VkSharingMode                sharingMode;
+                       1u,                                                                                     // deUint32                             queueFamilyCount;
+                       &queueFamilyIndex                                                       // const deUint32*              pQueueFamilyIndices;
+               };
+
+               m_vertices                      = createOverlappingQuads();
+               m_vertexBuffer          = createBuffer(vk, vkDevice, &vertexBufferParams);
+               m_vertexBufferAlloc     = memAlloc.allocate(getBufferMemoryRequirements(vk, vkDevice, *m_vertexBuffer), MemoryRequirement::HostVisible);
+
+               VK_CHECK(vk.bindBufferMemory(vkDevice, *m_vertexBuffer, m_vertexBufferAlloc->getMemory(), m_vertexBufferAlloc->getOffset()));
+
+               // Adjust vertex colors
+               if (!isFloatFormat(m_colorFormat))
+               {
+                       const tcu::TextureFormatInfo formatInfo = tcu::getTextureFormatInfo(mapVkFormat(m_colorFormat));
+                       for (size_t vertexNdx = 0; vertexNdx < m_vertices.size(); vertexNdx++)
+                               m_vertices[vertexNdx].color = (m_vertices[vertexNdx].color - formatInfo.lookupBias) / formatInfo.lookupScale;
+               }
+
+               // Upload vertex data
+               deMemcpy(m_vertexBufferAlloc->getHostPtr(), m_vertices.data(), m_vertices.size() * sizeof(Vertex4RGBA));
+
+               const VkMappedMemoryRange flushRange =
+               {
+                       VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,  // VkStructureType      sType;
+                       DE_NULL,                                                                // const void*          pNext;
+                       m_vertexBufferAlloc->getMemory(),               // VkDeviceMemory       mem;
+                       m_vertexBufferAlloc->getOffset(),               // VkDeviceSize         offset;
+                       vertexBufferParams.size                                 // VkDeviceSize         size;
+               };
+
+               vk.flushMappedMemoryRanges(vkDevice, 1, &flushRange);
+       }
+
+       // Create command pool
+       {
+               const VkCmdPoolCreateInfo cmdPoolParams =
+               {
+                       VK_STRUCTURE_TYPE_CMD_POOL_CREATE_INFO,         // VkStructureType              sType;
+                       DE_NULL,                                                                        // const void*                  pNext;
+                       queueFamilyIndex,                                                       // deUint32                             queueFamilyIndex;
+                       VK_CMD_POOL_CREATE_TRANSIENT_BIT                        // VkCmdPoolCreateFlags flags;
+               };
+
+               m_cmdPool = createCommandPool(vk, vkDevice, &cmdPoolParams);
+       }
+
+       // Create command buffer
+       {
+               const VkCmdBufferCreateInfo cmdBufferParams =
+               {
+                       VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,       // VkStructureType                      sType;
+                       DE_NULL,                                                                        // const void*                          pNext;
+                       *m_cmdPool,                                                                     // VkCmdPool                            cmdPool;
+                       VK_CMD_BUFFER_LEVEL_PRIMARY,                            // VkCmdBufferLevel                     level;
+                       0u                                                                                      // VkCmdBufferCreateFlags       flags;
+               };
+
+               const VkCmdBufferBeginInfo cmdBufferBeginInfo =
+               {
+                       VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,        // VkStructureType                      sType;
+                       DE_NULL,                                                                        // const void*                          pNext;
+                       0u,                                                                                     // VkCmdBufferOptimizeFlags     flags;
+                       0u,                                                                                     // deUint32                                     subpass;
+                       DE_NULL,                                                                        // VkRenderPass                         renderPass;
+                       DE_NULL                                                                         // VkFramebuffer                        framebuffer;
+               };
+
+               const VkClearValue attachmentClearValue = defaultClearValue(m_colorFormat);
+
+               const VkRenderPassBeginInfo renderPassBeginInfo =
+               {
+                       VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,                               // VkStructureType              sType;
+                       DE_NULL,                                                                                                // const void*                  pNext;
+                       *m_renderPass,                                                                                  // VkRenderPass                 renderPass;
+                       *m_framebuffer,                                                                                 // VkFramebuffer                framebuffer;
+                       { { 0, 0 }, { m_renderSize.x(), m_renderSize.y() } },   // VkRect2D                             renderArea;
+                       1,                                                                                                              // deUint32                             clearValueCount;
+                       &attachmentClearValue                                                                   // const VkClearValue*  pClearValues;
+               };
+
+               m_cmdBuffer = createCommandBuffer(vk, vkDevice, &cmdBufferParams);
+
+               VK_CHECK(vk.beginCommandBuffer(*m_cmdBuffer, &cmdBufferBeginInfo));
+               vk.cmdBeginRenderPass(*m_cmdBuffer, &renderPassBeginInfo, VK_RENDER_PASS_CONTENTS_INLINE);
+
+               const VkDeviceSize quadOffset = (m_vertices.size() / BlendTest::QUAD_COUNT) * sizeof(Vertex4RGBA);
+
+               for (int quadNdx = 0; quadNdx < BlendTest::QUAD_COUNT; quadNdx++)
+               {
+                       VkDeviceSize vertexBufferOffset = quadOffset * quadNdx;
+
+                       vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_graphicsPipelines[quadNdx]);
+                       vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &m_vertexBuffer.get(), &vertexBufferOffset);
+                       vk.cmdDraw(*m_cmdBuffer, (deUint32)(m_vertices.size() / BlendTest::QUAD_COUNT), 1, 0, 0);
+               }
+
+               vk.cmdEndRenderPass(*m_cmdBuffer);
+               VK_CHECK(vk.endCommandBuffer(*m_cmdBuffer));
+       }
+
+       // Create fence
+       {
+               const VkFenceCreateInfo fenceParams =
+               {
+                       VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,    // VkStructureType              sType;
+                       DE_NULL,                                                                // const void*                  pNext;
+                       0u                                                                              // VkFenceCreateFlags   flags;
+               };
+
+               m_fence = createFence(vk, vkDevice, &fenceParams);
+       }
+}
+
+BlendTestInstance::~BlendTestInstance (void)
+{
+}
+
+tcu::TestStatus BlendTestInstance::iterate (void)
+{
+       const DeviceInterface&          vk                      = m_context.getDeviceInterface();
+       const VkDevice                          vkDevice        = m_context.getDevice();
+       const VkQueue                           queue           = m_context.getUniversalQueue();
+
+       VK_CHECK(vk.resetFences(vkDevice, 1, &m_fence.get()));
+       VK_CHECK(vk.queueSubmit(queue, 1, &m_cmdBuffer.get(), *m_fence));
+       VK_CHECK(vk.waitForFences(vkDevice, 1, &m_fence.get(), true, ~(0ull) /* infinity */));
+
+       return verifyImage();
+}
+
+float BlendTestInstance::getNormChannelThreshold (const tcu::TextureFormat& format, int numBits)
+{
+       switch (tcu::getTextureChannelClass(format.type))
+       {
+               case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:     return BlendTest::QUAD_COUNT / static_cast<float>((1 << numBits) - 1);
+               case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:       return BlendTest::QUAD_COUNT / static_cast<float>((1 << (numBits - 1)) - 1);
+               default:
+                       break;
+       }
+
+       DE_ASSERT(false);
+       return 0.0f;
+}
+
+tcu::Vec4 BlendTestInstance::getFormatThreshold (const tcu::TextureFormat& format)
+{
+       using tcu::Vec4;
+       using tcu::TextureFormat;
+
+       Vec4 threshold(0.01f);
+
+       switch (format.type)
+       {
+               case TextureFormat::UNORM_BYTE_44:
+                       threshold = Vec4(getNormChannelThreshold(format, 4), getNormChannelThreshold(format, 4), 1.0f, 1.0f);
+                       break;
+
+               case TextureFormat::UNORM_SHORT_565:
+                       threshold = Vec4(getNormChannelThreshold(format, 5), getNormChannelThreshold(format, 6), getNormChannelThreshold(format, 5), 1.0f);
+                       break;
+
+               case TextureFormat::UNORM_SHORT_555:
+                       threshold = Vec4(getNormChannelThreshold(format, 5), getNormChannelThreshold(format, 5), getNormChannelThreshold(format, 5), 1.0f);
+                       break;
+
+               case TextureFormat::UNORM_SHORT_4444:
+                       threshold = Vec4(getNormChannelThreshold(format, 4));
+                       break;
+
+               case TextureFormat::UNORM_SHORT_5551:
+                       threshold = Vec4(getNormChannelThreshold(format, 5), getNormChannelThreshold(format, 5), getNormChannelThreshold(format, 5), 0.1f);
+                       break;
+
+               case TextureFormat::UNORM_INT_1010102_REV:
+               case TextureFormat::SNORM_INT_1010102_REV:
+                       threshold = Vec4(getNormChannelThreshold(format, 10), getNormChannelThreshold(format, 10), getNormChannelThreshold(format, 10), 0.1f);
+                       break;
+
+               case TextureFormat::UNORM_INT8:
+               case TextureFormat::SNORM_INT8:
+                       threshold = Vec4(getNormChannelThreshold(format, 8));
+                       break;
+
+               case TextureFormat::UNORM_INT16:
+               case TextureFormat::SNORM_INT16:
+                       threshold = Vec4(getNormChannelThreshold(format, 16));
+                       break;
+
+               case TextureFormat::UNORM_INT32:
+               case TextureFormat::SNORM_INT32:
+                       threshold = Vec4(getNormChannelThreshold(format, 32));
+                       break;
+
+               case TextureFormat::HALF_FLOAT:
+                       threshold = Vec4(0.005f);
+                       break;
+
+               case TextureFormat::FLOAT:
+                       threshold = Vec4(0.00001f);
+                       break;
+
+               case TextureFormat::UNSIGNED_INT_11F_11F_10F_REV:
+                       threshold = Vec4(0.02f, 0.02f, 0.02f, 1.0f);
+                       break;
+
+               case TextureFormat::UNSIGNED_INT_999_E5_REV:
+                       threshold = Vec4(0.05f, 0.05f, 0.05f, 1.0f);
+                       break;
+
+               default:
+                       DE_ASSERT(false);
+       }
+
+       // Return value matching the channel order specified by the format
+       if (format.order == tcu::TextureFormat::BGR || format.order == tcu::TextureFormat::BGRA)
+               return threshold.swizzle(2, 1, 0, 3);
+       else
+               return threshold;
+}
+
+tcu::TestStatus BlendTestInstance::verifyImage (void)
+{
+       const tcu::TextureFormat        tcuColorFormat  = mapVkFormat(m_colorFormat);
+       const tcu::TextureFormat        tcuDepthFormat  = tcu::TextureFormat(); // Undefined depth/stencil format
+       const ColorVertexShader         vertexShader;
+       const ColorFragmentShader       fragmentShader  (tcuColorFormat, tcuDepthFormat);
+       const rr::Program                       program                 (&vertexShader, &fragmentShader);
+       ReferenceRenderer                       refRenderer             (m_renderSize.x(), m_renderSize.y(), 1, tcuColorFormat, tcuDepthFormat, &program);
+       bool                                            compareOk               = false;
+
+       // Render reference image
+       {
+               for (int quadNdx = 0; quadNdx < BlendTest::QUAD_COUNT; quadNdx++)
+               {
+                       const VkPipelineColorBlendAttachmentState& blendState = m_blendStates[quadNdx];
+
+                       // Set blend state
+                       rr::RenderState renderState                                     (refRenderer.getViewportState());
+                       renderState.fragOps.blendMode                           = rr::BLENDMODE_STANDARD;
+                       renderState.fragOps.blendRGBState.srcFunc       = mapVkBlend(blendState.srcBlendColor);
+                       renderState.fragOps.blendRGBState.dstFunc       = mapVkBlend(blendState.destBlendColor);
+                       renderState.fragOps.blendRGBState.equation      = mapVkBlendOp(blendState.blendOpColor);
+                       renderState.fragOps.blendAState.srcFunc         = mapVkBlend(blendState.srcBlendAlpha);
+                       renderState.fragOps.blendAState.dstFunc         = mapVkBlend(blendState.destBlendAlpha);
+                       renderState.fragOps.blendAState.equation        = mapVkBlendOp(blendState.blendOpAlpha);
+                       renderState.fragOps.blendColor                          = BlendTest::s_blendConst;
+                       renderState.fragOps.colorMask                           = mapVkChannelFlags(BlendTest::s_channelWriteMasks[quadNdx]);
+
+                       refRenderer.draw(renderState,
+                                                        rr::PRIMITIVETYPE_TRIANGLES,
+                                                        std::vector<Vertex4RGBA>(m_vertices.begin() + quadNdx * 6,
+                                                                                                         m_vertices.begin() + (quadNdx + 1) * 6));
+               }
+       }
+
+
+       // Compare result with reference image
+       {
+               const DeviceInterface&                          vk                                                      = m_context.getDeviceInterface();
+               const VkDevice                                          vkDevice                                        = m_context.getDevice();
+               const VkQueue                                           queue                                           = m_context.getUniversalQueue();
+               const deUint32                                          queueFamilyIndex                        = m_context.getUniversalQueueFamilyIndex();
+               SimpleAllocator                                         allocator                                       (vk, vkDevice, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
+               de::UniquePtr<tcu::TextureLevel>        result                                          (readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, *m_colorImage, m_colorFormat, m_renderSize).release());
+               const tcu::Vec4                                         threshold                                       (getFormatThreshold(tcuColorFormat));
+
+               compareOk = tcu::floatThresholdCompare(m_context.getTestContext().getLog(),
+                                                                                          "FloatImageCompare",
+                                                                                          "Image comparison",
+                                                                                          refRenderer.getAccess(),
+                                                                                          result->getAccess(),
+                                                                                          threshold,
+                                                                                          tcu::COMPARE_LOG_RESULT);
+       }
+
+       if (compareOk)
+               return tcu::TestStatus::pass("Result image matches reference");
+       else
+               return tcu::TestStatus::fail("Image mismatch");
+}
+
+} // anonymous
+
+std::string getBlendStateName (const VkPipelineColorBlendAttachmentState& blendState)
+{
+       const char* shortBlendFactorNames[] =
+       {
+               "z",            // VK_BLEND_ZERO
+               "o",            // VK_BLEND_ONE
+               "sc",           // VK_BLEND_SRC_COLOR
+               "1msc",         // VK_BLEND_ONE_MINUS_SRC_COLOR
+               "dc",           // VK_BLEND_DEST_COLOR
+               "1mdc",         // VK_BLEND_ONE_MINUS_DEST_COLOR
+               "sa",           // VK_BLEND_SRC_ALPHA
+               "1msa",         // VK_BLEND_ONE_MINUS_SRC_ALPHA
+               "da",           // VK_BLEND_DEST_ALPHA
+               "1mda",         // VK_BLEND_ONE_MINUS_DEST_ALPHA
+               "cc",           // VK_BLEND_CONSTANT_COLOR
+               "1mcc",         // VK_BLEND_ONE_MINUS_CONSTANT_COLOR
+               "ca",           // VK_BLEND_CONSTANT_ALPHA
+               "1mca",         // VK_BLEND_ONE_MINUS_CONSTANT_ALPHA
+               "sas"           // VK_BLEND_SRC_ALPHA_SATURATE
+       };
+
+       const char* blendOpNames[] =
+       {
+               "add",          // VK_BLEND_OP_ADD
+               "sub",          // VK_BLEND_OP_SUBTRACT
+               "rsub",         // VK_BLEND_OP_REVERSE_SUBTRACT
+               "min",          // VK_BLEND_OP_MIN
+               "max",          // VK_BLEND_OP_MAX
+       };
+
+       std::ostringstream shortName;
+
+       shortName << "color_" << shortBlendFactorNames[blendState.srcBlendColor] << "_" << shortBlendFactorNames[blendState.destBlendColor] << "_" << blendOpNames[blendState.blendOpColor];
+       shortName << "_alpha_" << shortBlendFactorNames[blendState.srcBlendAlpha] << "_" << shortBlendFactorNames[blendState.destBlendAlpha] << "_" << blendOpNames[blendState.blendOpAlpha];
+
+       return shortName.str();
+}
+
+std::string getBlendStateSetName (const VkPipelineColorBlendAttachmentState blendStates[BlendTest::QUAD_COUNT])
+{
+       std::ostringstream name;
+
+       for (int quadNdx = 0; quadNdx < BlendTest::QUAD_COUNT; quadNdx++)
+       {
+               name << getBlendStateName(blendStates[quadNdx]);
+
+               if (quadNdx < BlendTest::QUAD_COUNT - 1)
+                       name << "-";
+       }
+
+       return name.str();
+}
+
+std::string getBlendStateSetDescription (const VkPipelineColorBlendAttachmentState blendStates[BlendTest::QUAD_COUNT])
+{
+       std::ostringstream description;
+
+       description << "Draws " << BlendTest::QUAD_COUNT << " quads with the following blend states:\n";
+
+       for (int quadNdx = 0; quadNdx < BlendTest::QUAD_COUNT; quadNdx++)
+               description << blendStates[quadNdx] << "\n";
+
+       return description.str();
+}
+
+std::string getFormatCaseName (VkFormat format)
+{
+       const std::string fullName = getFormatName(format);
+
+       DE_ASSERT(de::beginsWith(fullName, "VK_FORMAT_"));
+
+       return de::toLower(fullName.substr(10));
+}
+
+tcu::TestCaseGroup* createBlendTests (tcu::TestContext& testCtx)
+{
+       const deUint32 blendStatesPerFormat = 100 * BlendTest::QUAD_COUNT;
+
+       // Formats that are dEQP-compatible, non-integer and uncompressed
+       const VkFormat blendFormats[] =
+       {
+               VK_FORMAT_R4G4_UNORM,
+               VK_FORMAT_R4G4B4A4_UNORM,
+               VK_FORMAT_R5G6B5_UNORM,
+               VK_FORMAT_R5G5B5A1_UNORM,
+               VK_FORMAT_R8_UNORM,
+               VK_FORMAT_R8_SNORM,
+               VK_FORMAT_R8_SRGB,
+               VK_FORMAT_R8G8_UNORM,
+               VK_FORMAT_R8G8_SNORM,
+               VK_FORMAT_R8G8_SRGB,
+               VK_FORMAT_R8G8B8_UNORM,
+               VK_FORMAT_R8G8B8_SNORM,
+               VK_FORMAT_R8G8B8_SRGB,
+               VK_FORMAT_R8G8B8A8_UNORM,
+               VK_FORMAT_R8G8B8A8_SNORM,
+               VK_FORMAT_R8G8B8A8_SRGB,
+               VK_FORMAT_R10G10B10A2_UNORM,
+               VK_FORMAT_R16_UNORM,
+               VK_FORMAT_R16_SNORM,
+               VK_FORMAT_R16_SFLOAT,
+               VK_FORMAT_R16G16_UNORM,
+               VK_FORMAT_R16G16_SNORM,
+               VK_FORMAT_R16G16_SFLOAT,
+               VK_FORMAT_R16G16B16_UNORM,
+               VK_FORMAT_R16G16B16_SNORM,
+               VK_FORMAT_R16G16B16_SFLOAT,
+               VK_FORMAT_R16G16B16A16_UNORM,
+               VK_FORMAT_R16G16B16A16_SNORM,
+               VK_FORMAT_R16G16B16A16_SFLOAT,
+               VK_FORMAT_R32_SFLOAT,
+               VK_FORMAT_R32G32_SFLOAT,
+               VK_FORMAT_R32G32B32_SFLOAT,
+               VK_FORMAT_R32G32B32A32_SFLOAT,
+               VK_FORMAT_R11G11B10_UFLOAT,
+               VK_FORMAT_R9G9B9E5_UFLOAT,
+               VK_FORMAT_B4G4R4A4_UNORM,
+               VK_FORMAT_B5G5R5A1_UNORM,
+       };
+
+       de::MovePtr<tcu::TestCaseGroup>         blendTests              (new tcu::TestCaseGroup(testCtx, "blend", "Blend tests"));
+       de::MovePtr<tcu::TestCaseGroup>         formatTests             (new tcu::TestCaseGroup(testCtx, "format", "Uses different blend formats"));
+       BlendStateUniqueRandomIterator          blendStateItr   (blendStatesPerFormat, 123);
+
+       for (size_t formatNdx = 0; formatNdx < DE_LENGTH_OF_ARRAY(blendFormats); formatNdx++)
+       {
+               const VkFormat                                  format                  = blendFormats[formatNdx];
+               de::MovePtr<tcu::TestCaseGroup> formatTest              (new tcu::TestCaseGroup(testCtx,
+                                                                                                                                                               getFormatCaseName(format).c_str(),
+                                                                                                                                                               (std::string("Uses format ") + getFormatName(format)).c_str()));
+               de::MovePtr<tcu::TestCaseGroup> blendStateTests;
+               {
+                       std::ostringstream blendStateDescription;
+                       blendStateDescription << "Combines blend factors, operators and channel write masks. The constant color used in all tests is " << BlendTest::s_blendConst;
+                       blendStateTests = de::MovePtr<tcu::TestCaseGroup>(new tcu::TestCaseGroup(testCtx, "states", blendStateDescription.str().c_str()));
+               }
+
+               blendStateItr.reset();
+
+               while (blendStateItr.hasNext())
+               {
+                       VkPipelineColorBlendAttachmentState quadBlendConfigs[BlendTest::QUAD_COUNT];
+
+                       for (int quadNdx = 0; quadNdx < BlendTest::QUAD_COUNT; quadNdx++)
+                       {
+                               quadBlendConfigs[quadNdx] = blendStateItr.next();
+                               quadBlendConfigs[quadNdx].channelWriteMask = BlendTest::s_channelWriteMasks[quadNdx];
+                       }
+
+                       blendStateTests->addChild(new BlendTest(testCtx,
+                                                                                                       getBlendStateSetName(quadBlendConfigs),
+                                                                                                       getBlendStateSetDescription(quadBlendConfigs),
+                                                                                                       format,
+                                                                                                       quadBlendConfigs));
+               }
+               formatTest->addChild(blendStateTests.release());
+               formatTests->addChild(formatTest.release());
+       }
+       blendTests->addChild(formatTests.release());
+
+       return blendTests.release();
+}
+
+} // pipeline
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/pipeline/vktPipelineBlendTests.hpp b/external/vulkancts/modules/vulkan/pipeline/vktPipelineBlendTests.hpp
new file mode 100644 (file)
index 0000000..7585e95
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef _VKTPIPELINEBLENDTESTS_HPP
+#define _VKTPIPELINEBLENDTESTS_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Imagination Technologies Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Blend Tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktTestCase.hpp"
+
+namespace vkt
+{
+namespace pipeline
+{
+
+tcu::TestCaseGroup* createBlendTests (tcu::TestContext& testCtx);
+
+} // pipeline
+} // vkt
+
+#endif // _VKTPIPELINEBLENDTESTS_HPP
index 0326809..32bc5c4 100644 (file)
@@ -44,10 +44,15 @@ namespace pipeline
 
 using namespace vk;
 
-tcu::Vec4 defaultClearColorFloat (const tcu::TextureFormat& format)
+tcu::Vec4 defaultClearColor (const tcu::TextureFormat& format)
 {
-       const tcu::TextureFormatInfo    formatInfo      = tcu::getTextureFormatInfo(format);
-       return (defaultClearColorUnorm() - formatInfo.lookupBias) / formatInfo.lookupScale;
+   if (tcu::getTextureChannelClass(format.type) == tcu::TEXTURECHANNELCLASS_FLOATING_POINT)
+       return defaultClearColorUnorm();
+   else
+   {
+       const tcu::TextureFormatInfo formatInfo = tcu::getTextureFormatInfo(format);
+       return (defaultClearColorUnorm() - formatInfo.lookupBias) / formatInfo.lookupScale;
+   }
 }
 
 tcu::IVec4 defaultClearColorInt (const tcu::TextureFormat& format)
@@ -127,7 +132,7 @@ VkClearValue defaultClearValue (VkFormat clearFormat)
                }
                else
                {
-                       const tcu::Vec4 defaultColor    = defaultClearColorFloat(tcuClearFormat);
+                       const tcu::Vec4 defaultColor    = defaultClearColor(tcuClearFormat);
                        clearValue.color.float32[0]                     = defaultColor.x();
                        clearValue.color.float32[1]                     = defaultColor.y();
                        clearValue.color.float32[2]                     = defaultColor.z();
index 48649f4..6383aca 100644 (file)
@@ -44,7 +44,7 @@ namespace vkt
 namespace pipeline
 {
 
-tcu::Vec4                                              defaultClearColorFloat                  (const tcu::TextureFormat& format);
+tcu::Vec4                                              defaultClearColor                               (const tcu::TextureFormat& format);
 tcu::IVec4                                             defaultClearColorInt                    (const tcu::TextureFormat& format);
 tcu::UVec4                                             defaultClearColorUint                   (const tcu::TextureFormat& format);
 tcu::Vec4                                              defaultClearColorUnorm                  (void);
index 9f3ff18..fe522d0 100644 (file)
@@ -45,6 +45,58 @@ namespace pipeline
 
 using namespace vk;
 
+rr::BlendFunc mapVkBlend (VkBlend blend)
+{
+       switch (blend)
+       {
+               case VK_BLEND_ZERO:                                     return rr::BLENDFUNC_ZERO;
+               case VK_BLEND_ONE:                                              return rr::BLENDFUNC_ONE;
+               case VK_BLEND_SRC_COLOR:                                return rr::BLENDFUNC_SRC_COLOR;
+               case VK_BLEND_ONE_MINUS_SRC_COLOR:              return rr::BLENDFUNC_ONE_MINUS_SRC_COLOR;
+               case VK_BLEND_DEST_COLOR:                               return rr::BLENDFUNC_DST_COLOR;
+               case VK_BLEND_ONE_MINUS_DEST_COLOR:             return rr::BLENDFUNC_ONE_MINUS_DST_COLOR;
+               case VK_BLEND_SRC_ALPHA:                                return rr::BLENDFUNC_SRC_ALPHA;
+               case VK_BLEND_ONE_MINUS_SRC_ALPHA:              return rr::BLENDFUNC_ONE_MINUS_SRC_ALPHA;
+               case VK_BLEND_DEST_ALPHA:                               return rr::BLENDFUNC_DST_ALPHA;
+               case VK_BLEND_ONE_MINUS_DEST_ALPHA:             return rr::BLENDFUNC_ONE_MINUS_DST_ALPHA;
+               case VK_BLEND_CONSTANT_COLOR:                   return rr::BLENDFUNC_CONSTANT_COLOR;
+               case VK_BLEND_ONE_MINUS_CONSTANT_COLOR: return rr::BLENDFUNC_ONE_MINUS_CONSTANT_COLOR;
+               case VK_BLEND_CONSTANT_ALPHA:                   return rr::BLENDFUNC_CONSTANT_ALPHA;
+               case VK_BLEND_ONE_MINUS_CONSTANT_ALPHA: return rr::BLENDFUNC_ONE_MINUS_CONSTANT_ALPHA;
+               case VK_BLEND_SRC_ALPHA_SATURATE:               return rr::BLENDFUNC_SRC_ALPHA_SATURATE;
+               case VK_BLEND_SRC1_COLOR:                               return rr::BLENDFUNC_SRC1_COLOR;
+               case VK_BLEND_ONE_MINUS_SRC1_COLOR:             return rr::BLENDFUNC_ONE_MINUS_SRC1_COLOR;
+               case VK_BLEND_SRC1_ALPHA:                               return rr::BLENDFUNC_SRC1_ALPHA;
+               case VK_BLEND_ONE_MINUS_SRC1_ALPHA:             return rr::BLENDFUNC_ONE_MINUS_SRC1_ALPHA;
+               default:
+                       DE_ASSERT(false);
+       }
+       return rr::BLENDFUNC_LAST;
+}
+
+rr::BlendEquation mapVkBlendOp (VkBlendOp blendOp)
+{
+       switch (blendOp)
+       {
+               case VK_BLEND_OP_ADD:                                   return rr::BLENDEQUATION_ADD;
+               case VK_BLEND_OP_SUBTRACT:                              return rr::BLENDEQUATION_SUBTRACT;
+               case VK_BLEND_OP_REVERSE_SUBTRACT:              return rr::BLENDEQUATION_REVERSE_SUBTRACT;
+               case VK_BLEND_OP_MIN:                                   return rr::BLENDEQUATION_MIN;
+               case VK_BLEND_OP_MAX:                                   return rr::BLENDEQUATION_MAX;
+               default:
+                       DE_ASSERT(false);
+       }
+       return rr::BLENDEQUATION_LAST;
+}
+
+tcu::BVec4 mapVkChannelFlags (VkChannelFlags flags)
+{
+       return tcu::BVec4(flags & VK_CHANNEL_R_BIT,
+                                         flags & VK_CHANNEL_G_BIT,
+                                         flags & VK_CHANNEL_B_BIT,
+                                         flags & VK_CHANNEL_A_BIT);
+}
+
 rr::TestFunc mapVkCompareOp (VkCompareOp compareFunc)
 {
        switch (compareFunc)
@@ -117,8 +169,13 @@ ReferenceRenderer::ReferenceRenderer(int                                           surfaceWidth,
        }
        else
        {
-               tcu::clear(m_colorBuffer.getAccess(), defaultClearColorFloat(m_colorFormat));
-               tcu::clear(m_resolveColorBuffer.getAccess(), defaultClearColorFloat(m_colorFormat));
+               tcu::Vec4 clearColor = defaultClearColor(m_colorFormat);
+
+               if (isSRGB(m_colorFormat))
+                       clearColor = tcu::linearToSRGB(clearColor);
+
+               tcu::clear(m_colorBuffer.getAccess(), clearColor);
+               tcu::clear(m_resolveColorBuffer.getAccess(), clearColor);
        }
 
        if (hasDepthStencil)
index a4de408..c2a7412 100644 (file)
@@ -90,7 +90,6 @@ class ColorFragmentShader : public rr::FragmentShader
 {
 private:
        const tcu::TextureFormat                m_colorFormat;
-       const tcu::TextureFormatInfo    m_colorFormatInfo;
        const tcu::TextureFormat                m_depthStencilFormat;
 
 public:
@@ -98,7 +97,6 @@ public:
                                                 const tcu::TextureFormat& depthStencilFormat)
                : rr::FragmentShader    (2, 1)
                , m_colorFormat                 (colorFormat)
-               , m_colorFormatInfo             (tcu::getTextureFormatInfo(colorFormat))
                , m_depthStencilFormat  (depthStencilFormat)
        {
                const tcu::TextureChannelClass channelClass = tcu::getTextureChannelClass(m_colorFormat.type);
@@ -132,12 +130,7 @@ public:
                        for (int fragNdx = 0; fragNdx < 4; fragNdx++)
                        {
                                const tcu::Vec4 vtxColor = rr::readVarying<float>(packet, context, 1, fragNdx);
-
-                               rr::writeFragmentOutput(context,
-                                                                               packetNdx,
-                                                                               fragNdx,
-                                                                               0,
-                                                                               (vtxColor - m_colorFormatInfo.lookupBias) / m_colorFormatInfo.lookupScale);
+                               rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, vtxColor);
                        }
                }
        }
@@ -179,6 +172,9 @@ private:
        const rr::Program*                      m_program;
 };
 
+rr::BlendFunc                                  mapVkBlend                              (vk::VkBlend blend);
+rr::BlendEquation                              mapVkBlendOp                    (vk::VkBlendOp blendOp);
+tcu::BVec4                                             mapVkChannelFlags               (vk::VkChannelFlags flags);
 rr::TestFunc                                   mapVkCompareOp                  (vk::VkCompareOp compareFunc);
 rr::PrimitiveType                              mapVkPrimitiveTopology  (vk::VkPrimitiveTopology primitiveTopology);
 
index 6971d57..0da7774 100644 (file)
@@ -34,6 +34,7 @@
  *//*--------------------------------------------------------------------*/
 
 #include "vktPipelineTests.hpp"
+#include "vktPipelineBlendTests.hpp"
 #include "vktPipelineDepthTests.hpp"
 #include "deUniquePtr.hpp"
 
@@ -46,6 +47,7 @@ tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx)
 {
        de::MovePtr<tcu::TestCaseGroup> pipelineTests (new tcu::TestCaseGroup(testCtx, "pipeline", "Pipeline Tests"));
 
+       pipelineTests->addChild(createBlendTests(testCtx));
        pipelineTests->addChild(createDepthTests(testCtx));
 
        return pipelineTests.release();
diff --git a/external/vulkancts/modules/vulkan/pipeline/vktPipelineUniqueRandomIterator.hpp b/external/vulkancts/modules/vulkan/pipeline/vktPipelineUniqueRandomIterator.hpp
new file mode 100644 (file)
index 0000000..7ad6b30
--- /dev/null
@@ -0,0 +1,140 @@
+#ifndef _VKTPIPELINEUNIQUERANDOMITERATOR_HPP
+#define _VKTPIPELINEUNIQUERANDOMITERATOR_HPP
+/*------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 The Khronos Group Inc.
+ * Copyright (c) 2015 Imagination Technologies Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be included
+ * in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by Khronos,
+ * at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Iterator over a unique sequence of items
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "deRandom.hpp"
+#include <set>
+#include <vector>
+
+namespace vkt
+{
+namespace pipeline
+{
+
+template <typename T>
+class UniqueRandomIterator
+{
+public:
+                                                       UniqueRandomIterator    (deUint32 numItems, deUint32 numValues, int seed);
+       virtual                                 ~UniqueRandomIterator   (void) {}
+       bool                                    hasNext                                 (void) const;
+       T                                               next                                    (void);
+       void                                    reset                                   (void);
+
+protected:
+       virtual T                               getIndexedValue                 (deUint32 index) = 0;
+
+private:
+       std::vector<deUint32>   m_indices;
+       size_t                                  m_currentIndex;
+};
+
+class RandomFunction
+{
+private:
+       de::Random m_random;
+
+public:
+       RandomFunction(int seed)
+               : m_random(seed)
+       {}
+
+       virtual ~RandomFunction() {}
+
+       deUint32 operator()(deUint32 max)
+       {
+               return m_random.getUint32() % max;
+       }
+};
+
+template <typename T>
+UniqueRandomIterator<T>::UniqueRandomIterator (deUint32 numItems, deUint32 numValues, int seed)
+{
+       DE_ASSERT(numItems <= numValues);
+
+       RandomFunction randomFunc(seed);
+
+       if (numItems == numValues)
+       {
+               // Fast way to populate the index sequence
+               m_indices = std::vector<deUint32>(numItems);
+
+               for (deUint32 itemNdx = 0; itemNdx < numItems; itemNdx++)
+                       m_indices[itemNdx] = itemNdx;
+       }
+       else
+       {
+               std::set<deUint32> uniqueIndices;
+
+               // Populate set with "numItems" unique values between 0 and numValues - 1
+               while (uniqueIndices.size() < numItems)
+                       uniqueIndices.insert(randomFunc(numValues));
+
+               // Copy set into index sequence
+               m_indices = std::vector<deUint32>(uniqueIndices.begin(), uniqueIndices.end());
+       }
+
+       // Scramble the indices
+       std::random_shuffle(m_indices.begin(), m_indices.end(), randomFunc);
+
+       reset();
+}
+
+template <typename T>
+bool UniqueRandomIterator<T>::hasNext (void) const
+{
+       return m_currentIndex < m_indices.size();
+}
+
+template <typename T>
+T UniqueRandomIterator<T>::next (void)
+{
+       DE_ASSERT(m_currentIndex < m_indices.size());
+
+       return getIndexedValue(m_indices[m_currentIndex++]);
+}
+
+template <typename T>
+void UniqueRandomIterator<T>::reset (void)
+{
+       m_currentIndex = 0;
+}
+
+} // pipeline
+} // vkt
+
+#endif // _VKTPIPELINEUNIQUERANDOMITERATOR_HPP
index 6d0f1f9..d0fb3c3 100644 (file)
@@ -24,6 +24,7 @@
 #include "rrFragmentOperations.hpp"
 #include "tcuVectorUtil.hpp"
 #include "tcuTextureUtil.hpp"
+#include <limits>
 
 using tcu::IVec2;
 using tcu::Vec3;
@@ -303,20 +304,20 @@ void FragmentProcessor::executeStencilDpFailAndPass (int fragNdxOffset, int numS
 
 void FragmentProcessor::executeBlendFactorComputeRGB (const Vec4& blendColor, const BlendState& blendRGBState)
 {
-#define SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, FACTOR_EXPRESSION)                                                                                   \
-       for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)                                                         \
-       {                                                                                                                                                                                                                       \
-               if (m_sampleRegister[regSampleNdx].isAlive)                                                                                                                             \
-               {                                                                                                                                                                                                               \
-                       const Vec4& src         = m_sampleRegister[regSampleNdx].clampedBlendSrcColor;                                                  \
-                       const Vec4& src1        = m_sampleRegister[regSampleNdx].clampedBlendSrc1Color;                                                 \
-                       const Vec4& dst         = m_sampleRegister[regSampleNdx].clampedBlendDstColor;                                                  \
-                       DE_UNREF(src);                                                                                                                                                                          \
-                       DE_UNREF(src1);                                                                                                                                                                         \
-                       DE_UNREF(dst);                                                                                                                                                                          \
-                                                                                                                                                                                                                               \
-                       m_sampleRegister[regSampleNdx].FACTOR_NAME = clamp((FACTOR_EXPRESSION), Vec3(0.0f), Vec3(1.0f));        \
-               }                                                                                                                                                                                                               \
+#define SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, FACTOR_EXPRESSION)                                                                                                                                                           \
+       for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)                                                                                                                                 \
+       {                                                                                                                                                                                                                                                                                               \
+               if (m_sampleRegister[regSampleNdx].isAlive)                                                                                                                                                                                                     \
+               {                                                                                                                                                                                                                                                                                       \
+                       const Vec4& src         = m_sampleRegister[regSampleNdx].clampedBlendSrcColor;                                                                                                                          \
+                       const Vec4& src1        = m_sampleRegister[regSampleNdx].clampedBlendSrc1Color;                                                                                                                         \
+                       const Vec4& dst         = m_sampleRegister[regSampleNdx].clampedBlendDstColor;                                                                                                                          \
+                       DE_UNREF(src);                                                                                                                                                                                                                                                  \
+                       DE_UNREF(src1);                                                                                                                                                                                                                                                 \
+                       DE_UNREF(dst);                                                                                                                                                                                                                                                  \
+                                                                                                                                                                                                                                                                                                       \
+                       m_sampleRegister[regSampleNdx].FACTOR_NAME = (FACTOR_EXPRESSION);                                                                                                                                               \
+               }                                                                                                                                                                                                                                                                                       \
        }
 
 #define SWITCH_SRC_OR_DST_FACTOR_RGB(FUNC_NAME, FACTOR_NAME)                                                                                                                                                                   \
@@ -354,20 +355,20 @@ void FragmentProcessor::executeBlendFactorComputeRGB (const Vec4& blendColor, co
 
 void FragmentProcessor::executeBlendFactorComputeA (const Vec4& blendColor, const BlendState& blendAState)
 {
-#define SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, FACTOR_EXPRESSION)                                                           \
-       for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)                                 \
-       {                                                                                                                                                                                               \
-               if (m_sampleRegister[regSampleNdx].isAlive)                                                                                                     \
-               {                                                                                                                                                                                       \
-                       const Vec4& src         = m_sampleRegister[regSampleNdx].clampedBlendSrcColor;                          \
-                       const Vec4& src1        = m_sampleRegister[regSampleNdx].clampedBlendSrc1Color;                         \
-                       const Vec4& dst         = m_sampleRegister[regSampleNdx].clampedBlendDstColor;                          \
-                       DE_UNREF(src);                                                                                                                                                  \
-                       DE_UNREF(src1);                                                                                                                                                 \
-                       DE_UNREF(dst);                                                                                                                                                  \
-                                                                                                                                                                                                       \
-                       m_sampleRegister[regSampleNdx].FACTOR_NAME = clamp((FACTOR_EXPRESSION), 0.0f, 1.0f);    \
-               }                                                                                                                                                                                       \
+#define SAMPLE_REGISTER_BLEND_FACTOR(FACTOR_NAME, FACTOR_EXPRESSION)                                                                                                           \
+       for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)                                                                                 \
+       {                                                                                                                                                                                                                                               \
+               if (m_sampleRegister[regSampleNdx].isAlive)                                                                                                                                                     \
+               {                                                                                                                                                                                                                                       \
+                       const Vec4& src         = m_sampleRegister[regSampleNdx].clampedBlendSrcColor;                                                                          \
+                       const Vec4& src1        = m_sampleRegister[regSampleNdx].clampedBlendSrc1Color;                                                                         \
+                       const Vec4& dst         = m_sampleRegister[regSampleNdx].clampedBlendDstColor;                                                                          \
+                       DE_UNREF(src);                                                                                                                                                                                                  \
+                       DE_UNREF(src1);                                                                                                                                                                                                 \
+                       DE_UNREF(dst);                                                                                                                                                                                                  \
+                                                                                                                                                                                                                                                       \
+                       m_sampleRegister[regSampleNdx].FACTOR_NAME = (FACTOR_EXPRESSION);                                                                                               \
+               }                                                                                                                                                                                                                                       \
        }
 
 #define SWITCH_SRC_OR_DST_FACTOR_A(FUNC_NAME, FACTOR_NAME)                                                                                                                                             \
@@ -843,6 +844,28 @@ void FragmentProcessor::render (const rr::MultisamplePixelBufferAccess&            msColor
                switch (fragmentDataType)
                {
                        case rr::GENERICVECTYPE_FLOAT:
+                       {
+                               // Select min/max clamping values for blending factors and operands
+                               Vec4 minClampValue;
+                               Vec4 maxClampValue;
+
+                               if (colorbufferClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
+                               {
+                                       minClampValue = Vec4(0.0f);
+                                       maxClampValue = Vec4(1.0f);
+                               }
+                               else if (colorbufferClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT)
+                               {
+                                       minClampValue = Vec4(-1.0f);
+                                       maxClampValue = Vec4(1.0f);
+                               }
+                               else
+                               {
+                                       // No clamping
+                                       minClampValue = Vec4(-std::numeric_limits<float>::infinity());
+                                       maxClampValue = Vec4(std::numeric_limits<float>::infinity());
+                               }
+
                                // Blend calculation - only if using blend.
                                if (state.blendMode == BLENDMODE_STANDARD)
                                {
@@ -855,9 +878,9 @@ void FragmentProcessor::render (const rr::MultisamplePixelBufferAccess&             msColor
                                                        const Fragment&         frag                    = inputFragments[groupFirstFragNdx + regSampleNdx/numSamplesPerFragment];
                                                        Vec4                            dstColor                = colorBuffer.getPixel(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
 
-                                                       m_sampleRegister[regSampleNdx].clampedBlendSrcColor             = clamp(frag.value.get<float>(), Vec4(0.0f), Vec4(1.0f));
-                                                       m_sampleRegister[regSampleNdx].clampedBlendSrc1Color    = clamp(frag.value1.get<float>(), Vec4(0.0f), Vec4(1.0f));
-                                                       m_sampleRegister[regSampleNdx].clampedBlendDstColor             = clamp(sRGBTarget ? tcu::sRGBToLinear(dstColor) : dstColor, Vec4(0.0f), Vec4(1.0f));
+                                                       m_sampleRegister[regSampleNdx].clampedBlendSrcColor             = clamp(frag.value.get<float>(), minClampValue, maxClampValue);
+                                                       m_sampleRegister[regSampleNdx].clampedBlendSrc1Color    = clamp(frag.value1.get<float>(), minClampValue, maxClampValue);
+                                                       m_sampleRegister[regSampleNdx].clampedBlendDstColor             = clamp(sRGBTarget ? tcu::sRGBToLinear(dstColor) : dstColor, minClampValue, maxClampValue);
                                                }
                                        }
 
@@ -881,8 +904,8 @@ void FragmentProcessor::render (const rr::MultisamplePixelBufferAccess&             msColor
                                                        const Vec4                      srcColor                = frag.value.get<float>();
                                                        const Vec4                      dstColor                = colorBuffer.getPixel(fragSampleNdx, frag.pixelCoord.x(), frag.pixelCoord.y());
 
-                                                       m_sampleRegister[regSampleNdx].clampedBlendSrcColor             = unpremultiply(clamp(srcColor, Vec4(0.0f), Vec4(1.0f)));
-                                                       m_sampleRegister[regSampleNdx].clampedBlendDstColor             = unpremultiply(clamp(sRGBTarget ? tcu::sRGBToLinear(dstColor) : dstColor, Vec4(0.0f), Vec4(1.0f)));
+                                                       m_sampleRegister[regSampleNdx].clampedBlendSrcColor             = unpremultiply(clamp(srcColor, minClampValue, maxClampValue));
+                                                       m_sampleRegister[regSampleNdx].clampedBlendDstColor             = unpremultiply(clamp(sRGBTarget ? tcu::sRGBToLinear(dstColor) : dstColor, minClampValue, maxClampValue));
                                                }
                                        }
 
@@ -905,6 +928,19 @@ void FragmentProcessor::render (const rr::MultisamplePixelBufferAccess&            msColor
                                        }
                                }
 
+                               // Clamp result values in sample register
+                               if (colorbufferClass != tcu::TEXTURECHANNELCLASS_FLOATING_POINT)
+                               {
+                                       for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)
+                                       {
+                                               if (m_sampleRegister[regSampleNdx].isAlive)
+                                               {
+                                                       m_sampleRegister[regSampleNdx].blendedRGB       = clamp(m_sampleRegister[regSampleNdx].blendedRGB, minClampValue.swizzle(0, 1, 2), maxClampValue.swizzle(0, 1, 2));
+                                                       m_sampleRegister[regSampleNdx].blendedA         = clamp(m_sampleRegister[regSampleNdx].blendedA, minClampValue.w(), maxClampValue.w());
+                                               }
+                                       }
+                               }
+
                                // Finally, write the colors to the color buffer.
 
                                if (state.colorMask[0] && state.colorMask[1] && state.colorMask[2] && state.colorMask[3])
@@ -917,7 +953,7 @@ void FragmentProcessor::render (const rr::MultisamplePixelBufferAccess&             msColor
                                else if (state.colorMask[0] || state.colorMask[1] || state.colorMask[2] || state.colorMask[3])
                                        executeMaskedColorWrite(groupFirstFragNdx, numSamplesPerFragment, inputFragments, colorMaskFactor, colorMaskNegationFactor, sRGBTarget, colorBuffer);
                                break;
-
+                       }
                        case rr::GENERICVECTYPE_INT32:
                                // Write fragments
                                for (int regSampleNdx = 0; regSampleNdx < SAMPLE_REGISTER_SIZE; regSampleNdx++)