blob: f3e0cebe6bf45a099dff1156f31c456124c874a8 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2019 Valve Corporation.
* Copyright (c) 2019 The Khronos Group Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*//*!
* \file
* \brief VK_EXT_blend_operation_advanced tests
*//*--------------------------------------------------------------------*/
#include "vktPipelineBlendOperationAdvancedTests.hpp"
#include "vktPipelineImageUtil.hpp"
#include "vktPipelineReferenceRenderer.hpp"
#include "vktTestCaseUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkImageUtil.hpp"
#include "vkRefUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkObjUtil.hpp"
#include "tcuTestLog.hpp"
#include "tcuImageCompare.hpp"
namespace vkt
{
namespace pipeline
{
using namespace vk;
namespace
{
using tcu::Vec3;
using tcu::Vec4;
const deUint32 widthArea = 32u;
const deUint32 heightArea = 32u;
static const float A1 = 0.750f; // Between 1 and 0.5
static const float A2 = 0.375f; // Between 0.5 and 0.25
static const float A3 = 0.125f; // Between 0.25 and 0.0
const Vec4 srcColors[] = {
// Test that pre-multiplied is converted correctly.
// Should not test invalid premultiplied colours (1, 1, 1, 0).
{ 1.000f, 0.750f, 0.500f, 1.00f },
{ 0.250f, 0.125f, 0.000f, 1.00f },
// Test clamping.
{ 1.000f, 0.750f, 0.500f, 1.00f },
{ 0.250f, 0.125f, 0.000f, 1.00f },
{ 1.000f, 0.750f, 0.500f, 1.00f },
{ 0.250f, 0.125f, 0.000f, 1.00f },
// Combinations that test other branches of blend equations.
{ 1.000f, 0.750f, 0.500f, 1.00f },
{ 0.250f, 0.125f, 0.000f, 1.00f },
{ 1.000f, 0.750f, 0.500f, 1.00f },
{ 0.250f, 0.125f, 0.000f, 1.00f },
{ 1.000f, 0.750f, 0.500f, 1.00f },
{ 0.250f, 0.125f, 0.000f, 1.00f },
{ 1.000f, 0.750f, 0.500f, 1.00f },
{ 0.250f, 0.125f, 0.000f, 1.00f },
{ 1.000f, 0.750f, 0.500f, 1.00f },
{ 0.250f, 0.125f, 0.000f, 1.00f },
// Above block with few different pre-multiplied alpha values.
{ 1.000f * A1, 0.750f * A1, 0.500f * A1, 1.00f * A1},
{ 0.250f * A1, 0.125f * A1, 0.000f * A1, 1.00f * A1},
{ 1.000f * A1, 0.750f * A1, 0.500f * A1, 1.00f * A1},
{ 0.250f * A1, 0.125f * A1, 0.000f * A1, 1.00f * A1},
{ 1.000f * A1, 0.750f * A1, 0.500f * A1, 1.00f * A1},
{ 0.250f * A1, 0.125f * A1, 0.000f * A1, 1.00f * A1},
{ 1.000f * A1, 0.750f * A1, 0.500f * A1, 1.00f * A1},
{ 0.250f * A1, 0.125f * A1, 0.000f * A1, 1.00f * A1},
{ 1.000f * A1, 0.750f * A1, 0.500f * A1, 1.00f * A1},
{ 0.250f * A1, 0.125f * A1, 0.000f * A1, 1.00f * A1},
{ 1.000f * A2, 0.750f * A2, 0.500f * A2, 1.00f * A2},
{ 0.250f * A2, 0.125f * A2, 0.000f * A2, 1.00f * A2},
{ 1.000f * A2, 0.750f * A2, 0.500f * A2, 1.00f * A2},
{ 0.250f * A2, 0.125f * A2, 0.000f * A2, 1.00f * A2},
{ 1.000f * A2, 0.750f * A2, 0.500f * A2, 1.00f * A2},
{ 0.250f * A2, 0.125f * A2, 0.000f * A2, 1.00f * A2},
{ 1.000f * A2, 0.750f * A2, 0.500f * A2, 1.00f * A2},
{ 0.250f * A2, 0.125f * A2, 0.000f * A2, 1.00f * A2},
{ 1.000f * A2, 0.750f * A2, 0.500f * A2, 1.00f * A2},
{ 0.250f * A2, 0.125f * A2, 0.000f * A2, 1.00f * A2},
{ 1.000f * A3, 0.750f * A3, 0.500f * A3, 1.00f * A3},
{ 0.250f * A3, 0.125f * A3, 0.000f * A3, 1.00f * A3},
{ 1.000f * A3, 0.750f * A3, 0.500f * A3, 1.00f * A3},
{ 0.250f * A3, 0.125f * A3, 0.000f * A3, 1.00f * A3},
{ 1.000f * A3, 0.750f * A3, 0.500f * A3, 1.00f * A3},
{ 0.250f * A3, 0.125f * A3, 0.000f * A3, 1.00f * A3},
{ 1.000f * A3, 0.750f * A3, 0.500f * A3, 1.00f * A3},
{ 0.250f * A3, 0.125f * A3, 0.000f * A3, 1.00f * A3},
{ 1.000f * A3, 0.750f * A3, 0.500f * A3, 1.00f * A3},
{ 0.250f * A3, 0.125f * A3, 0.000f * A3, 1.00f * A3},
// Add some source colors with alpha component that is different than the respective destination color
{ 0.750f, 0.750f, 0.500f, 0.750f },
{ 0.250f, 0.500f, 0.500f, 0.750f },
{ 0.250f, 0.125f, 0.000f, 0.500f },
{ 0.250f, 0.250f, 0.500f, 0.500f },
{ 0.250f, 0.125f, 0.000f, 0.250f },
{ 0.125f, 0.125f, 0.125f, 0.250f }};
const Vec4 dstColors[] = {
// Test that pre-multiplied is converted correctly.
// Should not test invalid premultiplied colours (1, 1, 1, 0).
{ 0.000f, 0.000f, 0.000f, 0.00f },
{ 0.000f, 0.000f, 0.000f, 0.00f },
// Test clamping.
{ -0.125f, -0.125f, -0.125f, 1.00f },
{ -0.125f, -0.125f, -0.125f, 1.00f },
{ 1.125f, 1.125f, 1.125f, 1.00f },
{ 1.125f, 1.125f, 1.125f, 1.00f },
// Combinations that test other branches of blend equations.
{ 1.000f, 1.000f, 1.000f, 1.00f },
{ 1.000f, 1.000f, 1.000f, 1.00f },
{ 0.500f, 0.500f, 0.500f, 1.00f },
{ 0.500f, 0.500f, 0.500f, 1.00f },
{ 0.250f, 0.250f, 0.250f, 1.00f },
{ 0.250f, 0.250f, 0.250f, 1.00f },
{ 0.125f, 0.125f, 0.125f, 1.00f },
{ 0.125f, 0.125f, 0.125f, 1.00f },
{ 0.000f, 0.000f, 0.000f, 1.00f },
{ 0.000f, 0.000f, 0.000f, 1.00f },
// Above block with few different pre-multiplied alpha values.
{ 1.000f * A1, 1.000f * A1, 1.000f * A1, 1.00f * A1},
{ 1.000f * A1, 1.000f * A1, 1.000f * A1, 1.00f * A1},
{ 0.500f * A1, 0.500f * A1, 0.500f * A1, 1.00f * A1},
{ 0.500f * A1, 0.500f * A1, 0.500f * A1, 1.00f * A1},
{ 0.250f * A1, 0.250f * A1, 0.250f * A1, 1.00f * A1},
{ 0.250f * A1, 0.250f * A1, 0.250f * A1, 1.00f * A1},
{ 0.125f * A1, 0.125f * A1, 0.125f * A1, 1.00f * A1},
{ 0.125f * A1, 0.125f * A1, 0.125f * A1, 1.00f * A1},
{ 0.000f * A1, 0.000f * A1, 0.000f * A1, 1.00f * A1},
{ 0.000f * A1, 0.000f * A1, 0.000f * A1, 1.00f * A1},
{ 1.000f * A2, 1.000f * A2, 1.000f * A2, 1.00f * A2},
{ 1.000f * A2, 1.000f * A2, 1.000f * A2, 1.00f * A2},
{ 0.500f * A2, 0.500f * A2, 0.500f * A2, 1.00f * A2},
{ 0.500f * A2, 0.500f * A2, 0.500f * A2, 1.00f * A2},
{ 0.250f * A2, 0.250f * A2, 0.250f * A2, 1.00f * A2},
{ 0.250f * A2, 0.250f * A2, 0.250f * A2, 1.00f * A2},
{ 0.125f * A2, 0.125f * A2, 0.125f * A2, 1.00f * A2},
{ 0.125f * A2, 0.125f * A2, 0.125f * A2, 1.00f * A2},
{ 0.000f * A2, 0.000f * A2, 0.000f * A2, 1.00f * A2},
{ 0.000f * A2, 0.000f * A2, 0.000f * A2, 1.00f * A2},
{ 1.000f * A3, 1.000f * A3, 1.000f * A3, 1.00f * A3},
{ 1.000f * A3, 1.000f * A3, 1.000f * A3, 1.00f * A3},
{ 0.500f * A3, 0.500f * A3, 0.500f * A3, 1.00f * A3},
{ 0.500f * A3, 0.500f * A3, 0.500f * A3, 1.00f * A3},
{ 0.250f * A3, 0.250f * A3, 0.250f * A3, 1.00f * A3 },
{ 0.250f * A3, 0.250f * A3, 0.250f * A3, 1.00f * A3 },
{ 0.125f * A3, 0.125f * A3, 0.125f * A3, 1.00f * A3 },
{ 0.125f * A3, 0.125f * A3, 0.125f * A3, 1.00f * A3 },
{ 0.000f * A3, 0.000f * A3, 0.000f * A3, 1.00f * A3 },
{ 0.000f * A3, 0.000f * A3, 0.000f * A3, 1.00f * A3 },
// Add some source colors with alpha component that is different than the respective source color
{ 1.000f, 1.000f, 1.000f, 1.000f },
{ 0.250f, 0.250f, 0.250f, 0.500f },
{ 0.500f, 0.500f, 0.500f, 0.750f },
{ 0.250f, 0.250f, 0.250f, 0.250f },
{ 0.250f, 0.250f, 0.250f, 0.500f },
{ 0.125f, 0.125f, 0.125f, 0.125f }};
const Vec4 clearColorVec4 (1.0f, 1.0f, 1.0f, 1.0f);
enum TestMode
{
TEST_MODE_GENERIC = 0,
TEST_MODE_COHERENT = 1,
};
struct BlendOperationAdvancedParam
{
TestMode testMode;
deUint32 testNumber;
std::vector<VkBlendOp> blendOps;
deBool coherentOperations;
deBool independentBlend;
deUint32 colorAttachmentsCount;
VkBool32 premultipliedSrcColor;
VkBool32 premultipliedDstColor;
VkBlendOverlapEXT overlap;
};
// helper functions
const std::string generateTestName (struct BlendOperationAdvancedParam param)
{
std::ostringstream result;
result << ((param.testMode == TEST_MODE_COHERENT && !param.coherentOperations) ? "barrier_" : "");
result << "color_attachments_" << param.colorAttachmentsCount;
result << "_" << de::toLower(getBlendOverlapEXTStr(param.overlap).toString().substr(3));
result << (!param.premultipliedSrcColor ? "_nonpremultipliedsrc" : "");
result << (!param.premultipliedDstColor ? "_nonpremultiplieddst" : "");
result << "_" << param.testNumber;
return result.str();
}
const std::string generateTestDescription ()
{
std::string result("Test advanced blend operations");
return result;
}
Vec3 calculateWeightingFactors(BlendOperationAdvancedParam param,
float alphaSrc, float alphaDst)
{
Vec3 p = Vec3(0.0f, 0.0f, 0.0f);
switch(param.overlap)
{
case VK_BLEND_OVERLAP_UNCORRELATED_EXT:
p.x() = alphaSrc * alphaDst;
p.y() = alphaSrc * (1.0f - alphaDst);
p.z() = alphaDst * (1.0f - alphaSrc);
break;
case VK_BLEND_OVERLAP_CONJOINT_EXT:
p.x() = deFloatMin(alphaSrc, alphaDst);
p.y() = deFloatMax(alphaSrc - alphaDst, 0.0f);
p.z() = deFloatMax(alphaDst - alphaSrc, 0.0f);
break;
case VK_BLEND_OVERLAP_DISJOINT_EXT:
p.x() = deFloatMax(alphaSrc + alphaDst - 1.0f, 0.0f);
p.y() = deFloatMin(alphaSrc, 1.0f - alphaDst);
p.z() = deFloatMin(alphaDst, 1.0f - alphaSrc);
break;
default:
DE_FATAL("Unsupported Advanced Blend Overlap Mode");
};
return p;
}
Vec3 calculateXYZFactors(VkBlendOp op)
{
Vec3 xyz = Vec3(0.0f, 0.0f, 0.0f);
switch (op)
{
case VK_BLEND_OP_ZERO_EXT:
xyz = Vec3(0.0f, 0.0f, 0.0f);
break;
case VK_BLEND_OP_DST_ATOP_EXT:
case VK_BLEND_OP_SRC_EXT:
xyz = Vec3(1.0f, 1.0f, 0.0f);
break;
case VK_BLEND_OP_DST_EXT:
xyz = Vec3(1.0f, 0.0f, 1.0f);
break;
case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
case VK_BLEND_OP_HSL_COLOR_EXT:
case VK_BLEND_OP_HSL_SATURATION_EXT:
case VK_BLEND_OP_HSL_HUE_EXT:
case VK_BLEND_OP_HARDMIX_EXT:
case VK_BLEND_OP_PINLIGHT_EXT:
case VK_BLEND_OP_LINEARLIGHT_EXT:
case VK_BLEND_OP_VIVIDLIGHT_EXT:
case VK_BLEND_OP_LINEARBURN_EXT:
case VK_BLEND_OP_LINEARDODGE_EXT:
case VK_BLEND_OP_EXCLUSION_EXT:
case VK_BLEND_OP_DIFFERENCE_EXT:
case VK_BLEND_OP_SOFTLIGHT_EXT:
case VK_BLEND_OP_HARDLIGHT_EXT:
case VK_BLEND_OP_COLORBURN_EXT:
case VK_BLEND_OP_COLORDODGE_EXT:
case VK_BLEND_OP_LIGHTEN_EXT:
case VK_BLEND_OP_DARKEN_EXT:
case VK_BLEND_OP_OVERLAY_EXT:
case VK_BLEND_OP_SCREEN_EXT:
case VK_BLEND_OP_MULTIPLY_EXT:
case VK_BLEND_OP_SRC_OVER_EXT:
case VK_BLEND_OP_DST_OVER_EXT:
xyz = Vec3(1.0f, 1.0f, 1.0f);
break;
case VK_BLEND_OP_SRC_IN_EXT:
case VK_BLEND_OP_DST_IN_EXT:
xyz = Vec3(1.0f, 0.0f, 0.0f);
break;
case VK_BLEND_OP_SRC_OUT_EXT:
xyz = Vec3(0.0f, 1.0f, 0.0f);
break;
case VK_BLEND_OP_DST_OUT_EXT:
xyz = Vec3(0.0f, 0.0f, 1.0f);
break;
case VK_BLEND_OP_INVERT_RGB_EXT:
case VK_BLEND_OP_INVERT_EXT:
case VK_BLEND_OP_SRC_ATOP_EXT:
xyz = Vec3(1.0f, 0.0f, 1.0f);
break;
case VK_BLEND_OP_XOR_EXT:
xyz = Vec3(0.0f, 1.0f, 1.0f);
break;
default:
DE_FATAL("Unsupported f/X/Y/Z Advanced Blend Operations Mode");
};
return xyz;
}
float blendOpOverlay(float src, float dst)
{
if (dst <= 0.5f)
return (2.0f * src * dst);
else
return (1.0f - (2.0f * (1.0f - src) * (1.0f - dst)));
}
float blendOpColorDodge(float src, float dst)
{
if (dst <= 0.0f)
return 0.0f;
else if (src < 1.0f)
return deFloatMin(1.0f, (dst / (1.0f - src)));
else
return 1.0f;
}
float blendOpColorBurn(float src, float dst)
{
if (dst >= 1.0f)
return 1.0f;
else if (src > 0.0f)
return 1.0f - deFloatMin(1.0f, (1.0f - dst) / src);
else
return 0.0f;
}
float blendOpHardlight(float src, float dst)
{
if (src <= 0.5f)
return 2.0f * src * dst;
else
return 1.0f - (2.0f * (1.0f - src) * (1.0f - dst));
}
float blendOpSoftlight(float src, float dst)
{
if (src <= 0.5f)
return dst - ((1.0f - (2.0f * src)) * dst * (1.0f - dst));
else if (dst <= 0.25f)
return dst + (((2.0f * src) - 1.0f) * dst * ((((16.0f * dst) - 12.0f) * dst) + 3.0f));
else
return dst + (((2.0f * src) - 1.0f) * (deFloatSqrt(dst) - dst));
}
float blendOpLinearDodge(float src, float dst)
{
if ((src + dst) <= 1.0f)
return src + dst;
else
return 1.0f;
}
float blendOpLinearBurn(float src, float dst)
{
if ((src + dst) > 1.0f)
return src + dst - 1.0f;
else
return 0.0f;
}
float blendOpVividLight(float src, float dst)
{
if (src <= 0.0f)
return 0.0f;
if (src < 0.5f)
return 1.0f - (deFloatMin(1.0f, (1.0f - dst) / (2.0f * src)));
if (src < 1.0f)
return deFloatMin(1.0f, dst / (2.0f * (1.0f - src)));
else
return 1.0f;
}
float blendOpLinearLight(float src, float dst)
{
if ((2.0f * src + dst) > 2.0f)
return 1.0f;
if ((2.0f * src + dst) <= 1.0f)
return 0.0f;
return (2.0f * src) + dst - 1.0f;
}
float blendOpPinLight(float src, float dst)
{
if (((2.0f * src - 1.0f) > dst) && src < 0.5f)
return 0.0f;
if (((2.0f * src - 1.0f) > dst) && src >= 0.5f)
return 2.0f * src - 1.0f;
if (((2.0f * src - 1.0f) <= dst) && src < (0.5f * dst))
return 2.0f * src;
if (((2.0f * src - 1.0f) <= dst) && src >= (0.5f * dst))
return dst;
return 0.0f;
}
float blendOpHardmix(float src, float dst)
{
if ((src + dst) < 1.0f)
return 0.0f;
else
return 1.0f;
}
float minv3(Vec3 c)
{
return deFloatMin(deFloatMin(c.x(), c.y()), c.z());
}
float maxv3(Vec3 c)
{
return deFloatMax(deFloatMax(c.x(), c.y()), c.z());
}
float lumv3(Vec3 c)
{
return dot(c, Vec3(0.3f, 0.59f, 0.11f));
}
float satv3(Vec3 c)
{
return maxv3(c) - minv3(c);
}
// If any color components are outside [0,1], adjust the color to
// get the components in range.
Vec3 clipColor(Vec3 color)
{
float lum = lumv3(color);
float mincol = minv3(color);
float maxcol = maxv3(color);
if (mincol < 0.0)
{
color = lum + ((color - lum) * lum) / (lum - mincol);
}
if (maxcol > 1.0)
{
color = lum + ((color - lum) * (1.0f - lum)) / (maxcol - lum);
}
return color;
}
// Take the base RGB color <cbase> and override its luminosity
// with that of the RGB color <clum>.
Vec3 setLum(Vec3 cbase, Vec3 clum)
{
float lbase = lumv3(cbase);
float llum = lumv3(clum);
float ldiff = llum - lbase;
Vec3 color = cbase + Vec3(ldiff);
return clipColor(color);
}
// Take the base RGB color <cbase> and override its saturation with
// that of the RGB color <csat>. The override the luminosity of the
// result with that of the RGB color <clum>.
Vec3 setLumSat(Vec3 cbase, Vec3 csat, Vec3 clum)
{
float minbase = minv3(cbase);
float sbase = satv3(cbase);
float ssat = satv3(csat);
Vec3 color;
if (sbase > 0)
{
// Equivalent (modulo rounding errors) to setting the
// smallest (R,G,B) component to 0, the largest to <ssat>,
// and interpolating the "middle" component based on its
// original value relative to the smallest/largest.
color = (cbase - minbase) * ssat / sbase;
} else {
color = Vec3(0.0f);
}
return setLum(color, clum);
}
Vec3 calculateFFunction(VkBlendOp op,
Vec3 src, Vec3 dst)
{
Vec3 f = Vec3(0.0f, 0.0f, 0.0f);
switch (op)
{
case VK_BLEND_OP_XOR_EXT:
case VK_BLEND_OP_SRC_OUT_EXT:
case VK_BLEND_OP_DST_OUT_EXT:
case VK_BLEND_OP_ZERO_EXT:
f = Vec3(0.0f, 0.0f, 0.0f);
break;
case VK_BLEND_OP_SRC_ATOP_EXT:
case VK_BLEND_OP_SRC_IN_EXT:
case VK_BLEND_OP_SRC_OVER_EXT:
case VK_BLEND_OP_SRC_EXT:
f = src;
break;
case VK_BLEND_OP_DST_ATOP_EXT:
case VK_BLEND_OP_DST_IN_EXT:
case VK_BLEND_OP_DST_OVER_EXT:
case VK_BLEND_OP_DST_EXT:
f = dst;
break;
case VK_BLEND_OP_MULTIPLY_EXT:
f = src * dst;
break;
case VK_BLEND_OP_SCREEN_EXT:
f = src + dst - (src*dst);
break;
case VK_BLEND_OP_OVERLAY_EXT:
f.x() = blendOpOverlay(src.x(), dst.x());
f.y() = blendOpOverlay(src.y(), dst.y());
f.z() = blendOpOverlay(src.z(), dst.z());
break;
case VK_BLEND_OP_DARKEN_EXT:
f.x() = deFloatMin(src.x(), dst.x());
f.y() = deFloatMin(src.y(), dst.y());
f.z() = deFloatMin(src.z(), dst.z());
break;
case VK_BLEND_OP_LIGHTEN_EXT:
f.x() = deFloatMax(src.x(), dst.x());
f.y() = deFloatMax(src.y(), dst.y());
f.z() = deFloatMax(src.z(), dst.z());
break;
case VK_BLEND_OP_COLORDODGE_EXT:
f.x() = blendOpColorDodge(src.x(), dst.x());
f.y() = blendOpColorDodge(src.y(), dst.y());
f.z() = blendOpColorDodge(src.z(), dst.z());
break;
case VK_BLEND_OP_COLORBURN_EXT:
f.x() = blendOpColorBurn(src.x(), dst.x());
f.y() = blendOpColorBurn(src.y(), dst.y());
f.z() = blendOpColorBurn(src.z(), dst.z());
break;
case VK_BLEND_OP_HARDLIGHT_EXT:
f.x() = blendOpHardlight(src.x(), dst.x());
f.y() = blendOpHardlight(src.y(), dst.y());
f.z() = blendOpHardlight(src.z(), dst.z());
break;
case VK_BLEND_OP_SOFTLIGHT_EXT:
f.x() = blendOpSoftlight(src.x(), dst.x());
f.y() = blendOpSoftlight(src.y(), dst.y());
f.z() = blendOpSoftlight(src.z(), dst.z());
break;
case VK_BLEND_OP_DIFFERENCE_EXT:
f.x() = deFloatAbs(dst.x() - src.x());
f.y() = deFloatAbs(dst.y() - src.y());
f.z() = deFloatAbs(dst.z() - src.z());
break;
case VK_BLEND_OP_EXCLUSION_EXT:
f = src + dst - (2.0f * src * dst);
break;
case VK_BLEND_OP_INVERT_EXT:
f = 1.0f - dst;
break;
case VK_BLEND_OP_INVERT_RGB_EXT:
f = src * (1.0f - dst);
break;
case VK_BLEND_OP_LINEARDODGE_EXT:
f.x() = blendOpLinearDodge(src.x(), dst.x());
f.y() = blendOpLinearDodge(src.y(), dst.y());
f.z() = blendOpLinearDodge(src.z(), dst.z());
break;
case VK_BLEND_OP_LINEARBURN_EXT:
f.x() = blendOpLinearBurn(src.x(), dst.x());
f.y() = blendOpLinearBurn(src.y(), dst.y());
f.z() = blendOpLinearBurn(src.z(), dst.z());
break;
case VK_BLEND_OP_VIVIDLIGHT_EXT:
f.x() = blendOpVividLight(src.x(), dst.x());
f.y() = blendOpVividLight(src.y(), dst.y());
f.z() = blendOpVividLight(src.z(), dst.z());
break;
case VK_BLEND_OP_LINEARLIGHT_EXT:
f.x() = blendOpLinearLight(src.x(), dst.x());
f.y() = blendOpLinearLight(src.y(), dst.y());
f.z() = blendOpLinearLight(src.z(), dst.z());
break;
case VK_BLEND_OP_PINLIGHT_EXT:
f.x() = blendOpPinLight(src.x(), dst.x());
f.y() = blendOpPinLight(src.y(), dst.y());
f.z() = blendOpPinLight(src.z(), dst.z());
break;
case VK_BLEND_OP_HARDMIX_EXT:
f.x() = blendOpHardmix(src.x(), dst.x());
f.y() = blendOpHardmix(src.y(), dst.y());
f.z() = blendOpHardmix(src.z(), dst.z());
break;
case VK_BLEND_OP_HSL_HUE_EXT:
f = setLumSat(src, dst, dst);
break;
case VK_BLEND_OP_HSL_SATURATION_EXT:
f = setLumSat(dst, src, dst);
break;
case VK_BLEND_OP_HSL_COLOR_EXT:
f = setLum(src, dst);
break;
case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
f = setLum(dst, src);
break;
default:
DE_FATAL("Unsupported f/X/Y/Z Advanced Blend Operations Mode");
};
return f;
}
Vec4 additionalRGBBlendOperations(VkBlendOp op,
Vec4 src, Vec4 dst)
{
Vec4 res = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
switch (op)
{
case VK_BLEND_OP_PLUS_EXT:
res = src + dst;
break;
case VK_BLEND_OP_PLUS_CLAMPED_EXT:
res.x() = deFloatMin(1.0f, src.x() + dst.x());
res.y() = deFloatMin(1.0f, src.y() + dst.y());
res.z() = deFloatMin(1.0f, src.z() + dst.z());
res.w() = deFloatMin(1.0f, src.w() + dst.w());
break;
case VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT:
res.x() = deFloatMin(deFloatMin(1.0f, src.w() + dst.w()), src.x() + dst.x());
res.y() = deFloatMin(deFloatMin(1.0f, src.w() + dst.w()), src.y() + dst.y());
res.z() = deFloatMin(deFloatMin(1.0f, src.w() + dst.w()), src.z() + dst.z());
res.w() = deFloatMin(1.0f, src.w() + dst.w());
break;
case VK_BLEND_OP_PLUS_DARKER_EXT:
res.x() = deFloatMax(0.0f, deFloatMin(1.0f, src.w() + dst.w()) - ((src.w() - src.x()) + (dst.w() - dst.x())));
res.y() = deFloatMax(0.0f, deFloatMin(1.0f, src.w() + dst.w()) - ((src.w() - src.y()) + (dst.w() - dst.y())));
res.z() = deFloatMax(0.0f, deFloatMin(1.0f, src.w() + dst.w()) - ((src.w() - src.z()) + (dst.w() - dst.z())));
res.w() = deFloatMin(1.0f, src.w() + dst.w());
break;
case VK_BLEND_OP_MINUS_EXT:
res = dst - src;
break;
case VK_BLEND_OP_MINUS_CLAMPED_EXT:
res.x() = deFloatMax(0.0f, dst.x() - src.x());
res.y() = deFloatMax(0.0f, dst.y() - src.y());
res.z() = deFloatMax(0.0f, dst.z() - src.z());
res.w() = deFloatMax(0.0f, dst.w() - src.w());
break;
case VK_BLEND_OP_CONTRAST_EXT:
res.x() = (dst.w() / 2.0f) + 2.0f * (dst.x() - (dst.w() / 2.0f)) * (src.x() - (src.w() / 2.0f));
res.y() = (dst.w() / 2.0f) + 2.0f * (dst.y() - (dst.w() / 2.0f)) * (src.y() - (src.w() / 2.0f));
res.z() = (dst.w() / 2.0f) + 2.0f * (dst.z() - (dst.w() / 2.0f)) * (src.z() - (src.w() / 2.0f));
res.w() = dst.w();
break;
case VK_BLEND_OP_INVERT_OVG_EXT:
res.x() = src.w() * (1.0f - dst.x()) + (1.0f - src.w()) * dst.x();
res.y() = src.w() * (1.0f - dst.y()) + (1.0f - src.w()) * dst.y();
res.z() = src.w() * (1.0f - dst.z()) + (1.0f - src.w()) * dst.z();
res.w() = src.w() + dst.w() - src.w() * dst.w();
break;
case VK_BLEND_OP_RED_EXT:
res = dst;
res.x() = src.x();
break;
case VK_BLEND_OP_GREEN_EXT:
res = dst;
res.y() = src.y();
break;
case VK_BLEND_OP_BLUE_EXT:
res = dst;
res.z() = src.z();
break;
default:
DE_FATAL("Unsupported blend operation");
};
return res;
}
Vec4 calculateFinalColor(BlendOperationAdvancedParam param, VkBlendOp op,
Vec4 source, Vec4 destination)
{
Vec4 result = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
Vec3 srcColor = source.xyz();
Vec3 dstColor = destination.xyz();
// Calculate weighting factors
Vec3 p = calculateWeightingFactors(param, source.w(), destination.w());
if (op > VK_BLEND_OP_MAX && op < VK_BLEND_OP_PLUS_EXT)
{
{
// If srcPremultiplied is set to VK_TRUE, the fragment color components
// are considered to have been premultiplied by the A component prior to
// blending. The base source color (Rs',Gs',Bs') is obtained by dividing
// through by the A component.
if (param.premultipliedSrcColor)
{
if (source.w() != 0.0f)
srcColor = srcColor / source.w();
else
srcColor = Vec3(0.0f, 0.0f, 0.0f);
}
// If dstPremultiplied is set to VK_TRUE, the destination components are
// considered to have been premultiplied by the A component prior to
// blending. The base destination color (Rd',Gd',Bd') is obtained by dividing
// through by the A component.
if (param.premultipliedDstColor)
{
if (destination.w() != 0.0f)
dstColor = dstColor / destination.w();
else
dstColor = Vec3(0.0f, 0.0f, 0.0f);
}
}
// Calculate X, Y, Z terms of the equation
Vec3 xyz = calculateXYZFactors(op);
Vec3 fSrcDst = calculateFFunction(op, srcColor, dstColor);
result.x() = fSrcDst.x() * p.x() + xyz.y() * srcColor.x() * p.y() + xyz.z() * dstColor.x() * p.z();
result.y() = fSrcDst.y() * p.x() + xyz.y() * srcColor.y() * p.y() + xyz.z() * dstColor.y() * p.z();
result.z() = fSrcDst.z() * p.x() + xyz.y() * srcColor.z() * p.y() + xyz.z() * dstColor.z() * p.z();
result.w() = xyz.x() * p.x() + xyz.y() * p.y() + xyz.z() * p.z();
}
else if (op >= VK_BLEND_OP_PLUS_EXT && op < VK_BLEND_OP_MAX_ENUM)
{
// Premultiply colors for additional RGB blend operations. The formula is different than the rest of operations.
{
if (!param.premultipliedSrcColor)
{
srcColor = srcColor * source.w();
}
if (!param.premultipliedDstColor)
{
dstColor = dstColor * destination.w();
}
}
Vec4 src = Vec4(srcColor.x(), srcColor.y(), srcColor.z(), source.w());
Vec4 dst = Vec4(dstColor.x(), dstColor.y(), dstColor.z(), destination.w());
result = additionalRGBBlendOperations(op, src, dst);
}
else
{
DE_FATAL("Unsupported Blend Operation");
}
return result;
}
static inline void getCoordinates (deUint32 index, deInt32 &x, deInt32 &y)
{
x = index % widthArea;
y = index / heightArea;
}
static inline std::vector<Vec4> createPoints (void)
{
std::vector<Vec4> vertices;
vertices.push_back(Vec4(-1.0f, -1.0f, 0.0f, 1.0f));
vertices.push_back(Vec4( 1.0f, 1.0f, 0.0f, 1.0f));
vertices.push_back(Vec4(-1.0f, 1.0f, 0.0f, 1.0f));
vertices.push_back(Vec4(-1.0f, -1.0f, 0.0f, 1.0f));
vertices.push_back(Vec4( 1.0f, 1.0f, 0.0f, 1.0f));
vertices.push_back(Vec4( 1.0f, -1.0f, 0.0f, 1.0f));
return vertices;
}
template <class Test>
vkt::TestCase* newTestCase (tcu::TestContext& testContext,
const BlendOperationAdvancedParam testParam)
{
return new Test(testContext,
generateTestName(testParam).c_str(),
generateTestDescription().c_str(),
testParam);
}
Move<VkRenderPass> makeTestRenderPass (BlendOperationAdvancedParam param,
const DeviceInterface& vk,
const VkDevice device,
const VkFormat colorFormat,
VkAttachmentLoadOp colorLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR)
{
const VkAttachmentDescription colorAttachmentDescription =
{
(VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags
colorFormat, // VkFormat format
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
colorLoadOp, // 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
(colorLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD) ?
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL :
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout
};
std::vector<VkAttachmentDescription> attachmentDescriptions;
std::vector<VkAttachmentReference> colorAttachmentRefs;
for (deUint32 i = 0; i < param.colorAttachmentsCount; i++)
{
attachmentDescriptions.push_back(colorAttachmentDescription);
const VkAttachmentReference colorAttachmentRef =
{
i, // deUint32 attachment
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout
};
colorAttachmentRefs.push_back(colorAttachmentRef);
}
const VkSubpassDescription subpassDescription =
{
(VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags
VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint
0u, // deUint32 inputAttachmentCount
DE_NULL, // const VkAttachmentReference* pInputAttachments
param.colorAttachmentsCount, // deUint32 colorAttachmentCount
colorAttachmentRefs.data(), // const VkAttachmentReference* pColorAttachments
DE_NULL, // const VkAttachmentReference* pResolveAttachments
DE_NULL, // const VkAttachmentReference* pDepthStencilAttachment
0u, // deUint32 preserveAttachmentCount
DE_NULL // const deUint32* pPreserveAttachments
};
const VkRenderPassCreateInfo renderPassInfo =
{
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType
DE_NULL, // const void* pNext
(VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags
(deUint32)attachmentDescriptions.size(), // deUint32 attachmentCount
attachmentDescriptions.data(), // const VkAttachmentDescription* pAttachments
1u, // deUint32 subpassCount
&subpassDescription, // const VkSubpassDescription* pSubpasses
0u, // deUint32 dependencyCount
DE_NULL // const VkSubpassDependency* pDependencies
};
return createRenderPass(vk, device, &renderPassInfo, DE_NULL);
}
Move<VkBuffer> createBufferAndBindMemory (Context& context, VkDeviceSize size, VkBufferUsageFlags usage, de::MovePtr<Allocation>* pAlloc)
{
const DeviceInterface& vk = context.getDeviceInterface();
const VkDevice vkDevice = context.getDevice();
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
const VkBufferCreateInfo vertexBufferParams =
{
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkBufferCreateFlags flags;
size, // VkDeviceSize size;
usage, // VkBufferUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyCount;
&queueFamilyIndex // const deUint32* pQueueFamilyIndices;
};
Move<VkBuffer> vertexBuffer = createBuffer(vk, vkDevice, &vertexBufferParams);
*pAlloc = context.getDefaultAllocator().allocate(getBufferMemoryRequirements(vk, vkDevice, *vertexBuffer), MemoryRequirement::HostVisible);
VK_CHECK(vk.bindBufferMemory(vkDevice, *vertexBuffer, (*pAlloc)->getMemory(), (*pAlloc)->getOffset()));
return vertexBuffer;
}
Move<VkImage> createImage2DAndBindMemory (Context& context,
VkFormat format,
deUint32 width,
deUint32 height,
VkImageUsageFlags usage,
VkSampleCountFlagBits sampleCount,
de::details::MovePtr<Allocation>* pAlloc)
{
const DeviceInterface& vk = context.getDeviceInterface();
const VkDevice vkDevice = context.getDevice();
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
const VkImageCreateInfo colorImageParams =
{
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageCreateFlags flags;
VK_IMAGE_TYPE_2D, // VkImageType imageType;
format, // VkFormat format;
{ width, height, 1u }, // VkExtent3D extent;
1u, // deUint32 mipLevels;
1u, // deUint32 arraySize;
sampleCount, // deUint32 samples;
VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
usage, // VkImageUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyCount;
&queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
};
Move<VkImage> image = createImage(vk, vkDevice, &colorImageParams);
*pAlloc = context.getDefaultAllocator().allocate(getImageMemoryRequirements(vk, vkDevice, *image), MemoryRequirement::Any);
VK_CHECK(vk.bindImageMemory(vkDevice, *image, (*pAlloc)->getMemory(), (*pAlloc)->getOffset()));
return image;
}
// Test Classes
class BlendOperationAdvancedTestInstance : public vkt::TestInstance
{
public:
BlendOperationAdvancedTestInstance (Context& context,
const BlendOperationAdvancedParam param);
virtual ~BlendOperationAdvancedTestInstance (void);
virtual tcu::TestStatus iterate (void);
protected:
void prepareRenderPass (VkFramebuffer framebuffer, VkPipeline pipeline) const;
void prepareCommandBuffer (void) const;
void buildPipeline (VkBool32 premultiplySrc, VkBool32 premultiplyDst);
void bindShaderStage (VkShaderStageFlagBits stage,
const char* sourceName,
const char* entryName);
deBool verifyTestResult (void);
protected:
const BlendOperationAdvancedParam m_param;
const tcu::UVec2 m_renderSize;
const VkFormat m_colorFormat;
Move<VkPipelineLayout> m_pipelineLayout;
Move<VkBuffer> m_vertexBuffer;
de::MovePtr<Allocation> m_vertexBufferMemory;
std::vector<Vec4> m_vertices;
Move<VkRenderPass> m_renderPass;
Move<VkCommandPool> m_cmdPool;
Move<VkCommandBuffer> m_cmdBuffer;
std::vector<Move<VkImage>> m_colorImages;
std::vector<Move<VkImageView>> m_colorAttachmentViews;
std::vector<de::MovePtr<Allocation>> m_colorImageAllocs;
std::vector<VkImageMemoryBarrier> m_imageLayoutBarriers;
Move<VkFramebuffer> m_framebuffer;
Move<VkPipeline> m_pipeline;
Move<VkShaderModule> m_shaderModules[2];
deUint32 m_shaderStageCount;
VkPipelineShaderStageCreateInfo m_shaderStageInfo[2];
};
void BlendOperationAdvancedTestInstance::bindShaderStage (VkShaderStageFlagBits stage,
const char* sourceName,
const char* entryName)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
// Create shader module
deUint32* code = (deUint32*)m_context.getBinaryCollection().get(sourceName).getBinary();
deUint32 codeSize = (deUint32)m_context.getBinaryCollection().get(sourceName).getSize();
const VkShaderModuleCreateInfo moduleCreateInfo =
{
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkShaderModuleCreateFlags flags;
codeSize, // deUintptr codeSize;
code, // const deUint32* pCode;
};
m_shaderModules[m_shaderStageCount] = createShaderModule(vk, vkDevice, &moduleCreateInfo);
// Prepare shader stage info
m_shaderStageInfo[m_shaderStageCount].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
m_shaderStageInfo[m_shaderStageCount].pNext = DE_NULL;
m_shaderStageInfo[m_shaderStageCount].flags = 0u;
m_shaderStageInfo[m_shaderStageCount].stage = stage;
m_shaderStageInfo[m_shaderStageCount].module = *m_shaderModules[m_shaderStageCount];
m_shaderStageInfo[m_shaderStageCount].pName = entryName;
m_shaderStageInfo[m_shaderStageCount].pSpecializationInfo = DE_NULL;
m_shaderStageCount++;
}
void BlendOperationAdvancedTestInstance::buildPipeline (VkBool32 srcPremultiplied,
VkBool32 dstPremultiplied)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
// Create pipeline
const VkVertexInputBindingDescription vertexInputBindingDescription =
{
0u, // deUint32 binding;
sizeof(Vec4), // deUint32 strideInBytes;
VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
};
const VkVertexInputAttributeDescription vertexInputAttributeDescription =
{
0u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
0u // deUint32 offsetInBytes;
};
const VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineVertexInputStateCreateFlags flags;
1u, // deUint32 vertexBindingDescriptionCount;
&vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
1u, // deUint32 vertexAttributeDescriptionCount;
&vertexInputAttributeDescription, // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
};
const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineInputAssemblyStateCreateFlags flags;
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // VkPrimitiveTopology topology;
VK_FALSE, // VkBool32 primitiveRestartEnable;
};
const VkRect2D scissor = makeRect2D(m_renderSize);
VkViewport viewport = makeViewport(m_renderSize);
const VkPipelineViewportStateCreateInfo viewportStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineViewportStateCreateFlags flags;
1u, // deUint32 viewportCount;
&viewport, // const VkViewport* pViewports;
1u, // deUint32 scissorCount;
&scissor // const VkRect2D* pScissors;
};
const VkPipelineRasterizationStateCreateInfo rasterStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineRasterizationStateCreateFlags flags;
VK_FALSE, // VkBool32 depthClampEnable;
VK_FALSE, // VkBool32 rasterizerDiscardEnable;
VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode;
VK_CULL_MODE_NONE, // VkCullModeFlags cullMode;
VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
VK_FALSE, // VkBool32 depthBiasEnable;
0.0f, // float depthBiasConstantFactor;
0.0f, // float depthBiasClamp;
0.0f, // float depthBiasSlopeFactor;
1.0f, // float lineWidth;
};
const VkPipelineColorBlendAdvancedStateCreateInfoEXT blendAdvancedStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT, // VkStructureType sType;
DE_NULL, // const void* pNext;
srcPremultiplied, // VkBool32 srcPremultiplied;
dstPremultiplied, // VkBool32 dstPremultiplied;
m_param.overlap, // VkBlendOverlapEXT blendOverlap;
};
std::vector<VkPipelineColorBlendAttachmentState> colorBlendAttachmentStates;
for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
{
const VkPipelineColorBlendAttachmentState colorBlendAttachmentState =
{
VK_TRUE, // VkBool32 blendEnable;
VK_BLEND_FACTOR_ONE, // VkBlendFactor srcColorBlendFactor;
VK_BLEND_FACTOR_ONE, // VkBlendFactor dstColorBlendFactor;
m_param.blendOps[i], // VkBlendOp colorBlendOp;
VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor;
VK_BLEND_FACTOR_ONE, // VkBlendFactor dstAlphaBlendFactor;
m_param.blendOps[i], // VkBlendOp alphaBlendOp;
VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT // VkColorComponentFlags colorWriteMask;
};
colorBlendAttachmentStates.emplace_back(colorBlendAttachmentState);
}
const VkPipelineColorBlendStateCreateInfo colorBlendStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
&blendAdvancedStateParams, // const void* pNext;
0u, // VkPipelineColorBlendStateCreateFlags flags;
VK_FALSE, // VkBool32 logicOpEnable;
VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
(deUint32)colorBlendAttachmentStates.size(), // deUint32 attachmentCount;
colorBlendAttachmentStates.data(), // const VkPipelineColorBlendAttachmentState* pAttachments;
{ 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConst[4];
};
const VkPipelineMultisampleStateCreateInfo multisampleStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineMultisampleStateCreateFlags flags;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples;
VK_FALSE, // VkBool32 sampleShadingEnable;
0.0f, // float minSampleShading;
DE_NULL, // const VkSampleMask* pSampleMask;
VK_FALSE, // VkBool32 alphaToCoverageEnable;
VK_FALSE, // VkBool32 alphaToOneEnable;
};
VkPipelineDepthStencilStateCreateInfo depthStencilStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineDepthStencilStateCreateFlags flags;
VK_FALSE, // VkBool32 depthTestEnable;
VK_FALSE, // VkBool32 depthWriteEnable;
VK_COMPARE_OP_NEVER, // VkCompareOp depthCompareOp;
VK_FALSE, // VkBool32 depthBoundsTestEnable;
VK_FALSE, // VkBool32 stencilTestEnable;
// VkStencilOpState front;
{
VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
0u, // deUint32 compareMask;
0u, // deUint32 writeMask;
0u, // deUint32 reference;
},
// VkStencilOpState back;
{
VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
0u, // deUint32 compareMask;
0u, // deUint32 writeMask;
0u, // deUint32 reference;
},
0.0f, // float minDepthBounds;
1.0f, // float maxDepthBounds;
};
const VkDynamicState dynamicState = VK_DYNAMIC_STATE_SCISSOR;
const VkPipelineDynamicStateCreateInfo dynamicStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineDynamicStateCreateFlags flags;
1u, // uint32_t dynamicStateCount;
&dynamicState // const VkDynamicState* pDynamicStates;
};
const VkGraphicsPipelineCreateInfo graphicsPipelineParams =
{
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineCreateFlags flags;
m_shaderStageCount, // deUint32 stageCount;
m_shaderStageInfo, // const VkPipelineShaderStageCreateInfo* pStages;
&vertexInputStateParams, // const VkPipelineVertexInputStateCreateInfo* pVertexInputState;
&inputAssemblyStateParams, // const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState;
DE_NULL, // const VkPipelineTessellationStateCreateInfo* pTessellationState;
&viewportStateParams, // const VkPipelineViewportStateCreateInfo* pViewportState;
&rasterStateParams, // const VkPipelineRasterizationStateCreateInfo* pRasterState;
&multisampleStateParams, // const VkPipelineMultisampleStateCreateInfo* pMultisampleState;
&depthStencilStateParams, // const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState;
&colorBlendStateParams, // const VkPipelineColorBlendStateCreateInfo* pColorBlendState;
&dynamicStateParams, // const VkPipelineDynamicStateCreateInfo* pDynamicState;
*m_pipelineLayout, // VkPipelineLayout layout;
*m_renderPass, // VkRenderPass renderPass;
0u, // deUint32 subpass;
DE_NULL, // VkPipeline basePipelineHandle;
0u, // deInt32 basePipelineIndex;
};
m_pipeline = createGraphicsPipeline(vk, vkDevice, DE_NULL, &graphicsPipelineParams);
}
void BlendOperationAdvancedTestInstance::prepareRenderPass (VkFramebuffer framebuffer, VkPipeline pipeline) const
{
const DeviceInterface& vk = m_context.getDeviceInterface();
std::vector<VkClearValue> attachmentClearValues;
for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
attachmentClearValues.emplace_back(makeClearValueColor(clearColorVec4));
beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, framebuffer, makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()),
m_param.colorAttachmentsCount, attachmentClearValues.data());
vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkDeviceSize offsets = 0u;
vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &offsets);
// Draw all colors
deUint32 skippedColors = 0u;
for (deUint32 color = 0; color < DE_LENGTH_OF_ARRAY(srcColors); color++)
{
// Skip ill-formed colors when we have non-premultiplied destination colors.
if (m_param.premultipliedDstColor == VK_FALSE)
{
deBool skipColor = false;
for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
{
Vec4 calculatedColor = calculateFinalColor(m_param, m_param.blendOps[i], srcColors[color], dstColors[color]);
if (calculatedColor.w() <= 0.0f && calculatedColor != Vec4(0.0f))
{
// Skip ill-formed colors, because the spec says the result is undefined.
skippedColors++;
skipColor = true;
break;
}
}
if (skipColor)
continue;
}
deInt32 x = 0;
deInt32 y = 0;
getCoordinates(color, x, y);
// Set source color as push constant
vk.cmdPushConstants(*m_cmdBuffer, *m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0u, sizeof(Vec4), &srcColors[color]);
VkRect2D scissor = makeRect2D(x, y, 1u, 1u);
vk.cmdSetScissor(*m_cmdBuffer, 0u, 1u, &scissor);
// To set destination color, we do clear attachment restricting the area to the respective pixel of each color attachment.
{
// Set destination color as push constant.
std::vector<VkClearAttachment> attachments;
VkClearValue clearValue = vk::makeClearValueColorVec4(dstColors[color]);
for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
{
VkClearAttachment attachment =
{
VK_IMAGE_ASPECT_COLOR_BIT,
i,
clearValue
};
attachments.emplace_back(attachment);
}
const VkClearRect rect =
{
scissor,
0u,
1u
};
vk.cmdClearAttachments(*m_cmdBuffer, (deUint32)attachments.size(), attachments.data(), 1u, &rect);
}
// Draw
vk.cmdDraw(*m_cmdBuffer, (deUint32)m_vertices.size(), 1u, 0u, 0u);
}
// If we break this assert, then we are not testing anything in this test.
DE_ASSERT(skippedColors < DE_LENGTH_OF_ARRAY(srcColors));
// Log number of skipped colors
if (skippedColors != 0u)
{
tcu::TestLog& log = m_context.getTestContext().getLog();
log << tcu::TestLog::Message << "Skipped " << skippedColors << " out of " << DE_LENGTH_OF_ARRAY(srcColors) << " color cases due to ill-formed colors" << tcu::TestLog::EndMessage;
}
endRenderPass(vk, *m_cmdBuffer);
}
void BlendOperationAdvancedTestInstance::prepareCommandBuffer () const
{
const DeviceInterface& vk = m_context.getDeviceInterface();
beginCommandBuffer(vk, *m_cmdBuffer, 0u);
vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, (VkDependencyFlags)0,
0u, DE_NULL, 0u, DE_NULL, (deUint32)m_imageLayoutBarriers.size(), m_imageLayoutBarriers.data());
prepareRenderPass(*m_framebuffer, *m_pipeline);
endCommandBuffer(vk, *m_cmdBuffer);
}
BlendOperationAdvancedTestInstance::BlendOperationAdvancedTestInstance (Context& context,
const BlendOperationAdvancedParam param)
: TestInstance (context)
, m_param (param)
, m_renderSize (tcu::UVec2(widthArea, heightArea))
, m_colorFormat (VK_FORMAT_R16G16B16A16_SFLOAT)
, m_shaderStageCount (0)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
// Create vertex buffer and upload data
{
// Load vertices into vertex buffer
m_vertices = createPoints();
DE_ASSERT((deUint32)m_vertices.size() == 6);
m_vertexBuffer = createBufferAndBindMemory(m_context, m_vertices.size() * sizeof(Vec4), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, &m_vertexBufferMemory);
deMemcpy(m_vertexBufferMemory->getHostPtr(), m_vertices.data(), m_vertices.size() * sizeof(Vec4));
flushAlloc(vk, vkDevice, *m_vertexBufferMemory);
}
// Create render pass
m_renderPass = makeTestRenderPass(param, vk, vkDevice, m_colorFormat);
const VkComponentMapping componentMappingRGBA = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
// Create color images
for (deUint32 i = 0; i < param.colorAttachmentsCount; i++)
{
de::MovePtr<Allocation> colorImageAlloc;
m_colorImageAllocs.emplace_back(colorImageAlloc);
Move<VkImage> colorImage = createImage2DAndBindMemory(m_context,
m_colorFormat,
m_renderSize.x(),
m_renderSize.y(),
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_SAMPLE_COUNT_1_BIT,
&m_colorImageAllocs.back());
m_colorImages.emplace_back(colorImage);
// Set up image layout transition barriers
{
VkImageMemoryBarrier colorImageBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkAccessFlags srcAccessMask;
(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT), // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex;
*m_colorImages.back(), // VkImage image;
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }, // VkImageSubresourceRange subresourceRange;
};
m_imageLayoutBarriers.emplace_back(colorImageBarrier);
}
// Create color attachment view
{
VkImageViewCreateInfo colorAttachmentViewParams =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageViewCreateFlags flags;
*m_colorImages.back(), // VkImage image;
VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
m_colorFormat, // VkFormat format;
componentMappingRGBA, // VkComponentMapping components;
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }, // VkImageSubresourceRange subresourceRange;
};
m_colorAttachmentViews.emplace_back(createImageView(vk, vkDevice, &colorAttachmentViewParams));
}
}
// Create framebuffer
{
std::vector<VkImageView> imageViews;
for (auto& movePtr : m_colorAttachmentViews)
imageViews.push_back(movePtr.get());
const VkFramebufferCreateInfo framebufferParams =
{
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkFramebufferCreateFlags flags;
*m_renderPass, // VkRenderPass renderPass;
(deUint32)imageViews.size(), // deUint32 attachmentCount;
imageViews.data(), // const VkImageView* pAttachments;
(deUint32)m_renderSize.x(), // deUint32 width;
(deUint32)m_renderSize.y(), // deUint32 height;
1u, // deUint32 layers;
};
m_framebuffer = createFramebuffer(vk, vkDevice, &framebufferParams);
}
// Bind shader stages
{
bindShaderStage(VK_SHADER_STAGE_VERTEX_BIT, "vert", "main");
bindShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, "frag", "main");
}
// Create pipeline layout
{
const VkPushConstantRange pushConstantRange =
{
VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlags stageFlags
0, // deUint32 offset
sizeof(Vec4) // deUint32 size
};
const VkPipelineLayoutCreateInfo pipelineLayoutParams =
{
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineLayoutCreateFlags flags;
0u, // deUint32 setLayoutCount;
DE_NULL, // const VkDescriptorSetLayout* pSetLayouts;
1u, // deUint32 pushConstantRangeCount;
&pushConstantRange // const VkPushConstantRange* pPushConstantRanges;
};
m_pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
}
// Create pipeline
buildPipeline(m_param.premultipliedSrcColor, m_param.premultipliedDstColor);
// Create command pool
m_cmdPool = createCommandPool(vk, vkDevice, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
// Create command buffer
m_cmdBuffer = allocateCommandBuffer(vk, vkDevice, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
}
BlendOperationAdvancedTestInstance::~BlendOperationAdvancedTestInstance (void)
{
}
tcu::TestStatus BlendOperationAdvancedTestInstance::iterate (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const VkQueue queue = m_context.getUniversalQueue();
tcu::TestLog& log = m_context.getTestContext().getLog();
// Log the blend operations to test
{
if (m_param.independentBlend)
{
for (deUint32 i = 0; (i < m_param.colorAttachmentsCount); i++)
log << tcu::TestLog::Message << "Color attachment " << i << " uses depth op: "<< de::toLower(getBlendOpStr(m_param.blendOps[i]).toString().substr(3)) << tcu::TestLog::EndMessage;
}
else
{
log << tcu::TestLog::Message << "All color attachments use depth op: " << de::toLower(getBlendOpStr(m_param.blendOps[0]).toString().substr(3)) << tcu::TestLog::EndMessage;
}
}
prepareCommandBuffer();
submitCommandsAndWait(vk, vkDevice, queue, m_cmdBuffer.get());
if (verifyTestResult() == DE_FALSE)
return tcu::TestStatus::fail("Image mismatch");
return tcu::TestStatus::pass("Result images matches references");
}
deBool BlendOperationAdvancedTestInstance::verifyTestResult ()
{
deBool compareOk = DE_TRUE;
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const VkQueue queue = m_context.getUniversalQueue();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
Allocator& allocator = m_context.getDefaultAllocator();
std::vector<tcu::TextureLevel> referenceImages;
for (deUint32 colorAtt = 0; colorAtt < m_param.colorAttachmentsCount; colorAtt++)
{
tcu::TextureLevel refImage (vk::mapVkFormat(m_colorFormat), 32, 32);
tcu::clear(refImage.getAccess(), clearColorVec4);
referenceImages.emplace_back(refImage);
}
for (deUint32 color = 0; color < DE_LENGTH_OF_ARRAY(srcColors); color++)
{
deBool skipColor = DE_FALSE;
// Check if any color attachment will generate an ill-formed color. If that's the case, skip that color in the verification.
for (deUint32 colorAtt = 0; colorAtt < m_param.colorAttachmentsCount; colorAtt++)
{
Vec4 rectColor = calculateFinalColor(m_param, m_param.blendOps[colorAtt], srcColors[color], dstColors[color]);
if (m_param.premultipliedDstColor == VK_FALSE)
{
if (rectColor.w() > 0.0f)
{
rectColor.x() = rectColor.x() / rectColor.w();
rectColor.y() = rectColor.y() / rectColor.w();
rectColor.z() = rectColor.z() / rectColor.w();
}
else
{
// Skip the color check if it is ill-formed.
if (rectColor != Vec4(0.0f))
{
skipColor = DE_TRUE;
break;
}
}
}
}
// Skip ill-formed colors that appears in any color attachment.
if (skipColor)
continue;
// If we reach this point, the final color for all color attachment is not ill-formed.
for (deUint32 colorAtt = 0; colorAtt < m_param.colorAttachmentsCount; colorAtt++)
{
Vec4 rectColor = calculateFinalColor(m_param, m_param.blendOps[colorAtt], srcColors[color], dstColors[color]);
if (m_param.premultipliedDstColor == VK_FALSE)
{
if (rectColor.w() > 0.0f)
{
rectColor.x() = rectColor.x() / rectColor.w();
rectColor.y() = rectColor.y() / rectColor.w();
rectColor.z() = rectColor.z() / rectColor.w();
}
else
{
// Ill-formed colors were already skipped
DE_ASSERT(rectColor == Vec4(0.0f));
}
}
deInt32 x = 0;
deInt32 y = 0;
getCoordinates(color, x, y);
tcu::clear(tcu::getSubregion(referenceImages[colorAtt].getAccess(), x, y, 1u, 1u), rectColor);
}
}
for (deUint32 colorAtt = 0; colorAtt < m_param.colorAttachmentsCount; colorAtt++)
{
// Compare image
de::MovePtr<tcu::TextureLevel> result = vkt::pipeline::readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, *m_colorImages[colorAtt], m_colorFormat, m_renderSize);
std::ostringstream name;
name << "Image comparison. Color attachment: " << colorAtt << ". Depth op: " << de::toLower(getBlendOpStr(m_param.blendOps[colorAtt]).toString().substr(3));
compareOk = tcu::floatThresholdCompare(m_context.getTestContext().getLog(),
"FloatImageCompare",
name.str().c_str(),
referenceImages[colorAtt].getAccess(),
result->getAccess(),
Vec4(0.01f, 0.01f, 0.01f, 0.01f),
tcu::COMPARE_LOG_RESULT);
if (!compareOk)
return DE_FALSE;
}
return DE_TRUE;
}
class BlendOperationAdvancedTest : public vkt::TestCase
{
public:
BlendOperationAdvancedTest (tcu::TestContext& testContext,
const std::string& name,
const std::string& description,
const BlendOperationAdvancedParam param)
: vkt::TestCase (testContext, name, description)
, m_param (param)
{ }
virtual ~BlendOperationAdvancedTest (void) { }
virtual void initPrograms (SourceCollections& programCollection) const;
virtual TestInstance* createInstance (Context& context) const;
virtual void checkSupport (Context& context) const;
protected:
const BlendOperationAdvancedParam m_param;
};
void BlendOperationAdvancedTest::checkSupport(Context& context) const
{
const InstanceInterface& vki = context.getInstanceInterface();
context.requireDeviceFunctionality("VK_EXT_blend_operation_advanced");
VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT blendProperties;
blendProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT;
blendProperties.pNext = DE_NULL;
VkPhysicalDeviceProperties2 properties2;
properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
properties2.pNext = &blendProperties;
vki.getPhysicalDeviceProperties2(context.getPhysicalDevice(), &properties2);
if (!blendProperties.advancedBlendAllOperations)
{
for (deUint32 index = 0u; index < m_param.blendOps.size(); index++)
{
switch (m_param.blendOps[index])
{
case VK_BLEND_OP_MULTIPLY_EXT:
case VK_BLEND_OP_SCREEN_EXT:
case VK_BLEND_OP_OVERLAY_EXT:
case VK_BLEND_OP_DARKEN_EXT:
case VK_BLEND_OP_LIGHTEN_EXT:
case VK_BLEND_OP_COLORDODGE_EXT:
case VK_BLEND_OP_COLORBURN_EXT:
case VK_BLEND_OP_HARDLIGHT_EXT:
case VK_BLEND_OP_SOFTLIGHT_EXT:
case VK_BLEND_OP_DIFFERENCE_EXT:
case VK_BLEND_OP_EXCLUSION_EXT:
case VK_BLEND_OP_HSL_HUE_EXT:
case VK_BLEND_OP_HSL_SATURATION_EXT:
case VK_BLEND_OP_HSL_COLOR_EXT:
case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
break;
default:
throw tcu::NotSupportedError("Unsupported all advanced blend operations and unsupported advanced blend operation");
}
}
}
if (m_param.colorAttachmentsCount > blendProperties.advancedBlendMaxColorAttachments)
{
std::ostringstream error;
error << "Unsupported number of color attachments (" << blendProperties.advancedBlendMaxColorAttachments << " < " << m_param.colorAttachmentsCount;
throw tcu::NotSupportedError(error.str().c_str());
}
if (m_param.overlap != VK_BLEND_OVERLAP_UNCORRELATED_EXT && !blendProperties.advancedBlendCorrelatedOverlap)
{
throw tcu::NotSupportedError("Unsupported blend correlated overlap");
}
if (m_param.colorAttachmentsCount > 1 && m_param.independentBlend && !blendProperties.advancedBlendIndependentBlend)
{
throw tcu::NotSupportedError("Unsupported independent blend");
}
if (!m_param.premultipliedSrcColor && !blendProperties.advancedBlendNonPremultipliedSrcColor)
{
throw tcu::NotSupportedError("Unsupported non-premultiplied source color");
}
if (!m_param.premultipliedDstColor && !blendProperties.advancedBlendNonPremultipliedDstColor)
{
throw tcu::NotSupportedError("Unsupported non-premultiplied destination color");
}
const VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT blendFeatures = context.getBlendOperationAdvancedFeaturesEXT();
if (m_param.coherentOperations && !blendFeatures.advancedBlendCoherentOperations)
{
throw tcu::NotSupportedError("Unsupported required coherent operations");
}
}
void BlendOperationAdvancedTest::initPrograms (SourceCollections& programCollection) const
{
programCollection.glslSources.add("vert") << glu::VertexSource(
"#version 310 es\n"
"layout(location = 0) in vec4 position;\n"
"void main (void)\n"
"{\n"
" gl_Position = position;\n"
"}\n");
std::ostringstream fragmentSource;
fragmentSource << "#version 310 es\n";
fragmentSource << "layout(push_constant) uniform Color { highp vec4 color; };\n";
for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
fragmentSource << "layout(location = "<< i <<") out highp vec4 fragColor" << i <<";\n";
fragmentSource << "void main (void)\n";
fragmentSource << "{\n";
for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
fragmentSource << " fragColor" << i <<" = color;\n";
fragmentSource << "}\n";
programCollection.glslSources.add("frag") << glu::FragmentSource(fragmentSource.str().c_str());
}
class BlendOperationAdvancedTestCoherentInstance : public vkt::TestInstance
{
public:
BlendOperationAdvancedTestCoherentInstance (Context& context,
const BlendOperationAdvancedParam param);
virtual ~BlendOperationAdvancedTestCoherentInstance (void);
virtual tcu::TestStatus iterate (void);
protected:
void prepareRenderPass (VkFramebuffer framebuffer, VkPipeline pipeline,
VkRenderPass renderpass, deBool secondDraw);
virtual void prepareCommandBuffer (void);
virtual void buildPipeline (void);
virtual void bindShaderStage (VkShaderStageFlagBits stage,
const char* sourceName,
const char* entryName);
virtual tcu::TestStatus verifyTestResult (void);
protected:
const BlendOperationAdvancedParam m_param;
const tcu::UVec2 m_renderSize;
const VkFormat m_colorFormat;
Move<VkPipelineLayout> m_pipelineLayout;
Move<VkBuffer> m_vertexBuffer;
de::MovePtr<Allocation> m_vertexBufferMemory;
std::vector<Vec4> m_vertices;
std::vector<Move<VkRenderPass>> m_renderPasses;
Move<VkCommandPool> m_cmdPool;
Move<VkCommandBuffer> m_cmdBuffer;
Move<VkImage> m_colorImage;
Move<VkImageView> m_colorAttachmentView;
de::MovePtr<Allocation> m_colorImageAlloc;
std::vector<VkImageMemoryBarrier> m_imageLayoutBarriers;
std::vector<Move<VkFramebuffer>> m_framebuffers;
std::vector<Move<VkPipeline>> m_pipelines;
Move<VkShaderModule> m_shaderModules[2];
deUint32 m_shaderStageCount;
VkPipelineShaderStageCreateInfo m_shaderStageInfo[2];
};
BlendOperationAdvancedTestCoherentInstance::~BlendOperationAdvancedTestCoherentInstance (void)
{
}
void BlendOperationAdvancedTestCoherentInstance::bindShaderStage (VkShaderStageFlagBits stage,
const char* sourceName,
const char* entryName)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
// Create shader module
deUint32* code = (deUint32*)m_context.getBinaryCollection().get(sourceName).getBinary();
deUint32 codeSize = (deUint32)m_context.getBinaryCollection().get(sourceName).getSize();
const VkShaderModuleCreateInfo moduleCreateInfo =
{
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkShaderModuleCreateFlags flags;
codeSize, // deUintptr codeSize;
code, // const deUint32* pCode;
};
m_shaderModules[m_shaderStageCount] = createShaderModule(vk, vkDevice, &moduleCreateInfo);
// Prepare shader stage info
m_shaderStageInfo[m_shaderStageCount].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
m_shaderStageInfo[m_shaderStageCount].pNext = DE_NULL;
m_shaderStageInfo[m_shaderStageCount].flags = 0u;
m_shaderStageInfo[m_shaderStageCount].stage = stage;
m_shaderStageInfo[m_shaderStageCount].module = *m_shaderModules[m_shaderStageCount];
m_shaderStageInfo[m_shaderStageCount].pName = entryName;
m_shaderStageInfo[m_shaderStageCount].pSpecializationInfo = DE_NULL;
m_shaderStageCount++;
}
void BlendOperationAdvancedTestCoherentInstance::buildPipeline ()
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
// Create pipeline
const VkVertexInputBindingDescription vertexInputBindingDescription =
{
0u, // deUint32 binding;
sizeof(Vec4) , // deUint32 strideInBytes;
VK_VERTEX_INPUT_RATE_VERTEX, // VkVertexInputRate inputRate;
};
const VkVertexInputAttributeDescription vertexInputAttributeDescription =
{
0u, // deUint32 location;
0u, // deUint32 binding;
VK_FORMAT_R32G32B32A32_SFLOAT, // VkFormat format;
0u // deUint32 offsetInBytes;
};
const VkPipelineVertexInputStateCreateInfo vertexInputStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineVertexInputStateCreateFlags flags;
1u, // deUint32 vertexBindingDescriptionCount;
&vertexInputBindingDescription, // const VkVertexInputBindingDescription* pVertexBindingDescriptions;
1u, // deUint32 vertexAttributeDescriptionCount;
&vertexInputAttributeDescription, // const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
};
const VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineInputAssemblyStateCreateFlags flags;
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, // VkPrimitiveTopology topology;
VK_FALSE, // VkBool32 primitiveRestartEnable;
};
const VkRect2D scissor = makeRect2D(m_renderSize);
VkViewport viewport = makeViewport(m_renderSize);
const VkPipelineViewportStateCreateInfo viewportStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineViewportStateCreateFlags flags;
1u, // deUint32 viewportCount;
&viewport, // const VkViewport* pViewports;
1u, // deUint32 scissorCount;
&scissor // const VkRect2D* pScissors;
};
const VkPipelineRasterizationStateCreateInfo rasterStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineRasterizationStateCreateFlags flags;
VK_FALSE, // VkBool32 depthClampEnable;
VK_FALSE, // VkBool32 rasterizerDiscardEnable;
VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode;
VK_CULL_MODE_NONE, // VkCullModeFlags cullMode;
VK_FRONT_FACE_COUNTER_CLOCKWISE, // VkFrontFace frontFace;
VK_FALSE, // VkBool32 depthBiasEnable;
0.0f, // float depthBiasConstantFactor;
0.0f, // float depthBiasClamp;
0.0f, // float depthBiasSlopeFactor;
1.0f, // float lineWidth;
};
const VkPipelineColorBlendAdvancedStateCreateInfoEXT blendAdvancedStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT, // VkStructureType sType;
DE_NULL, // const void* pNext;
VK_TRUE, // VkBool32 srcPremultiplied;
VK_TRUE, // VkBool32 dstPremultiplied;
m_param.overlap, // VkBlendOverlapEXT blendOverlap;
};
std::vector<VkPipelineColorBlendAttachmentState> colorBlendAttachmentStates;
// One VkPipelineColorBlendAttachmentState for each pipeline, we only have one color attachment.
for (deUint32 i = 0; i < 2; i++)
{
const VkPipelineColorBlendAttachmentState colorBlendAttachmentState =
{
VK_TRUE, // VkBool32 blendEnable;
VK_BLEND_FACTOR_ONE, // VkBlendFactor srcColorBlendFactor;
VK_BLEND_FACTOR_ONE, // VkBlendFactor dstColorBlendFactor;
m_param.blendOps[i], // VkBlendOp colorBlendOp;
VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor;
VK_BLEND_FACTOR_ONE, // VkBlendFactor dstAlphaBlendFactor;
m_param.blendOps[i], // VkBlendOp alphaBlendOp;
VK_COLOR_COMPONENT_R_BIT |
VK_COLOR_COMPONENT_G_BIT |
VK_COLOR_COMPONENT_B_BIT |
VK_COLOR_COMPONENT_A_BIT // VkColorComponentFlags colorWriteMask;
};
colorBlendAttachmentStates.emplace_back(colorBlendAttachmentState);
}
std::vector<VkPipelineColorBlendStateCreateInfo> colorBlendStateParams;
VkPipelineColorBlendStateCreateInfo colorBlendStateParam =
{
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
&blendAdvancedStateParams, // const void* pNext;
0u, // VkPipelineColorBlendStateCreateFlags flags;
VK_FALSE, // VkBool32 logicOpEnable;
VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
1u, // deUint32 attachmentCount;
&colorBlendAttachmentStates[0], // const VkPipelineColorBlendAttachmentState* pAttachments;
{ 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConst[4];
};
colorBlendStateParams.emplace_back(colorBlendStateParam);
// For the second pipeline, the blendOp changed.
colorBlendStateParam.pAttachments = &colorBlendAttachmentStates[1];
colorBlendStateParams.emplace_back(colorBlendStateParam);
const VkPipelineMultisampleStateCreateInfo multisampleStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineMultisampleStateCreateFlags flags;
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples;
VK_FALSE, // VkBool32 sampleShadingEnable;
0.0f, // float minSampleShading;
DE_NULL, // const VkSampleMask* pSampleMask;
VK_FALSE, // VkBool32 alphaToCoverageEnable;
VK_FALSE, // VkBool32 alphaToOneEnable;
};
VkPipelineDepthStencilStateCreateInfo depthStencilStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineDepthStencilStateCreateFlags flags;
VK_FALSE, // VkBool32 depthTestEnable;
VK_FALSE, // VkBool32 depthWriteEnable;
VK_COMPARE_OP_NEVER, // VkCompareOp depthCompareOp;
VK_FALSE, // VkBool32 depthBoundsTestEnable;
VK_FALSE, // VkBool32 stencilTestEnable;
// VkStencilOpState front;
{
VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
0u, // deUint32 compareMask;
0u, // deUint32 writeMask;
0u, // deUint32 reference;
},
// VkStencilOpState back;
{
VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
0u, // deUint32 compareMask;
0u, // deUint32 writeMask;
0u, // deUint32 reference;
},
0.0f, // float minDepthBounds;
1.0f, // float maxDepthBounds;
};
const VkDynamicState dynamicState = VK_DYNAMIC_STATE_SCISSOR;
const VkPipelineDynamicStateCreateInfo dynamicStateParams =
{
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineDynamicStateCreateFlags flags;
1u, // uint32_t dynamicStateCount;
&dynamicState // const VkDynamicState* pDynamicStates;
};
VkGraphicsPipelineCreateInfo graphicsPipelineParams =
{
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineCreateFlags flags;
m_shaderStageCount, // deUint32 stageCount;
m_shaderStageInfo, // const VkPipelineShaderStageCreateInfo* pStages;
&vertexInputStateParams, // const VkPipelineVertexInputStateCreateInfo* pVertexInputState;
&inputAssemblyStateParams, // const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState;
DE_NULL, // const VkPipelineTessellationStateCreateInfo* pTessellationState;
&viewportStateParams, // const VkPipelineViewportStateCreateInfo* pViewportState;
&rasterStateParams, // const VkPipelineRasterizationStateCreateInfo* pRasterState;
&multisampleStateParams, // const VkPipelineMultisampleStateCreateInfo* pMultisampleState;
&depthStencilStateParams, // const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState;
&colorBlendStateParams[0], // const VkPipelineColorBlendStateCreateInfo* pColorBlendState;
&dynamicStateParams, // const VkPipelineDynamicStateCreateInfo* pDynamicState;
*m_pipelineLayout, // VkPipelineLayout layout;
m_renderPasses[0].get(), // VkRenderPass renderPass;
0u, // deUint32 subpass;
DE_NULL, // VkPipeline basePipelineHandle;
0u, // deInt32 basePipelineIndex;
};
// Create first pipeline
m_pipelines.emplace_back(createGraphicsPipeline(vk, vkDevice, DE_NULL, &graphicsPipelineParams));
// Create second pipeline
graphicsPipelineParams.pColorBlendState = &colorBlendStateParams[1];
graphicsPipelineParams.renderPass = m_renderPasses[1].get();
m_pipelines.emplace_back(createGraphicsPipeline(vk, vkDevice, DE_NULL, &graphicsPipelineParams));
}
void BlendOperationAdvancedTestCoherentInstance::prepareRenderPass (VkFramebuffer framebuffer, VkPipeline pipeline, VkRenderPass renderpass, deBool secondDraw)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
VkClearValue attachmentClearValue = makeClearValueColor(clearColorVec4);
beginRenderPass(vk, *m_cmdBuffer, renderpass, framebuffer, makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()),
(secondDraw ? 0u : 1u),
(secondDraw ? DE_NULL : &attachmentClearValue));
vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
VkDeviceSize offsets = 0u;
vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &offsets);
// There are two different renderpasses, each of them draw
// one half of the colors.
deBool skippedColors = 0u;
for (deUint32 color = 0; color < DE_LENGTH_OF_ARRAY(srcColors)/2; color++)
{
// Skip ill-formed colors when we have non-premultiplied destination colors.
if (m_param.premultipliedDstColor == VK_FALSE)
{
deBool skipColor = false;
for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
{
Vec4 calculatedColor = calculateFinalColor(m_param, m_param.blendOps[i], srcColors[color], dstColors[color]);
if (calculatedColor.w() <= 0.0f && calculatedColor != Vec4(0.0f))
{
// Skip ill-formed colors, because the spec says the result is undefined.
skippedColors++;
skipColor = true;
break;
}
}
if (skipColor)
continue;
}
deInt32 x = 0;
deInt32 y = 0;
getCoordinates(color, x, y);
deUint32 index = secondDraw ? (color + DE_LENGTH_OF_ARRAY(srcColors) / 2) : color;
// Set source color as push constant
vk.cmdPushConstants(*m_cmdBuffer, *m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0u, sizeof(Vec4), &srcColors[index]);
VkRect2D scissor = makeRect2D(x, y, 1u, 1u);
vk.cmdSetScissor(*m_cmdBuffer, 0u, 1u, &scissor);
// To set destination color, we do clear attachment restricting the area to the respective pixel of each color attachment.
// Only clear in the first draw, for the second draw the destination color is the result of the first draw's blend.
if (secondDraw == DE_FALSE)
{
std::vector<VkClearAttachment> attachments;
VkClearValue clearValue = vk::makeClearValueColorVec4(dstColors[index]);
const VkClearAttachment attachment =
{
VK_IMAGE_ASPECT_COLOR_BIT,
0u,
clearValue
};
const VkClearRect rect =
{
scissor,
0u,
1u
};
vk.cmdClearAttachments(*m_cmdBuffer, 1u, &attachment, 1u, &rect);
}
// Draw
vk.cmdDraw(*m_cmdBuffer, (deUint32)m_vertices.size(), 1u, 0u, 0u);
}
// If we break this assert, then we are not testing anything in this test.
DE_ASSERT(skippedColors < (DE_LENGTH_OF_ARRAY(srcColors) / 2));
// Log number of skipped colors
if (skippedColors != 0u)
{
tcu::TestLog& log = m_context.getTestContext().getLog();
log << tcu::TestLog::Message << "Skipped " << skippedColors << " out of " << (DE_LENGTH_OF_ARRAY(srcColors) / 2) << " color cases due to ill-formed colors" << tcu::TestLog::EndMessage;
}
endRenderPass(vk, *m_cmdBuffer);
}
void BlendOperationAdvancedTestCoherentInstance::prepareCommandBuffer ()
{
const DeviceInterface& vk = m_context.getDeviceInterface();
beginCommandBuffer(vk, *m_cmdBuffer, 0u);
vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, (VkDependencyFlags)0,
0u, DE_NULL, 0u, DE_NULL, (deUint32)m_imageLayoutBarriers.size(), m_imageLayoutBarriers.data());
prepareRenderPass(m_framebuffers[0].get(), m_pipelines[0].get(), m_renderPasses[0].get(), false);
if (m_param.coherentOperations == DE_FALSE)
{
const VkImageMemoryBarrier colorImageBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT), // VkAccessFlags srcAccessMask;
(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT), // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex;
*m_colorImage, // VkImage image;
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }, // VkImageSubresourceRange subresourceRange;
};
vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, (VkDependencyFlags)0,
0u, DE_NULL, 0u, DE_NULL, 1u, &colorImageBarrier);
}
prepareRenderPass(m_framebuffers[1].get(), m_pipelines[1].get(), m_renderPasses[1].get(), true);
endCommandBuffer(vk, *m_cmdBuffer);
}
BlendOperationAdvancedTestCoherentInstance::BlendOperationAdvancedTestCoherentInstance (Context& context,
const BlendOperationAdvancedParam param)
: TestInstance (context)
, m_param (param)
, m_renderSize (tcu::UVec2(widthArea, heightArea))
, m_colorFormat (VK_FORMAT_R16G16B16A16_SFLOAT)
, m_shaderStageCount (0)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
// Create vertex buffer
{
m_vertices = createPoints();
DE_ASSERT((deUint32)m_vertices.size() == 6);
m_vertexBuffer = createBufferAndBindMemory(m_context, m_vertices.size() * sizeof(Vec4), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, &m_vertexBufferMemory);
// Load vertices into vertex buffer
deMemcpy(m_vertexBufferMemory->getHostPtr(), m_vertices.data(), m_vertices.size() * sizeof(Vec4));
flushAlloc(vk, vkDevice, *m_vertexBufferMemory);
}
// Create render passes
m_renderPasses.emplace_back(makeTestRenderPass(param, vk, vkDevice, m_colorFormat, VK_ATTACHMENT_LOAD_OP_CLEAR));
m_renderPasses.emplace_back(makeTestRenderPass(param, vk, vkDevice, m_colorFormat, VK_ATTACHMENT_LOAD_OP_LOAD));
const VkComponentMapping componentMappingRGBA = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
// Create color image
m_colorImage = createImage2DAndBindMemory(m_context,
m_colorFormat,
m_renderSize.x(),
m_renderSize.y(),
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
VK_SAMPLE_COUNT_1_BIT,
&m_colorImageAlloc);
// Set up image layout transition barriers
{
VkImageMemoryBarrier colorImageBarrier =
{
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkAccessFlags srcAccessMask;
(VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT), // VkAccessFlags dstAccessMask;
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout;
VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex;
*m_colorImage, // VkImage image;
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }, // VkImageSubresourceRange subresourceRange;
};
m_imageLayoutBarriers.emplace_back(colorImageBarrier);
}
// Create color attachment view
{
VkImageViewCreateInfo colorAttachmentViewParams =
{
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkImageViewCreateFlags flags;
*m_colorImage, // VkImage image;
VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
m_colorFormat, // VkFormat format;
componentMappingRGBA, // VkComponentMapping components;
{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }, // VkImageSubresourceRange subresourceRange;
};
m_colorAttachmentView = createImageView(vk, vkDevice, &colorAttachmentViewParams);
}
// Create framebuffers
{
VkFramebufferCreateInfo framebufferParams =
{
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkFramebufferCreateFlags flags;
m_renderPasses[0].get(), // 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_framebuffers.emplace_back(createFramebuffer(vk, vkDevice, &framebufferParams));
framebufferParams.renderPass = m_renderPasses[1].get();
m_framebuffers.emplace_back(createFramebuffer(vk, vkDevice, &framebufferParams));
}
// Bind shader stages
{
bindShaderStage(VK_SHADER_STAGE_VERTEX_BIT, "vert", "main");
bindShaderStage(VK_SHADER_STAGE_FRAGMENT_BIT, "frag", "main");
}
// Create pipeline layout
{
const VkPushConstantRange pushConstantRange =
{
VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlags stageFlags
0, // deUint32 offset
sizeof(Vec4) // deUint32 size
};
const VkPipelineLayoutCreateInfo pipelineLayoutParams =
{
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkPipelineLayoutCreateFlags flags;
0u, // deUint32 setLayoutCount;
DE_NULL, // const VkDescriptorSetLayout* pSetLayouts;
1u, // deUint32 pushConstantRangeCount;
&pushConstantRange // const VkPushConstantRange* pPushConstantRanges;
};
m_pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
}
// Create pipeline
buildPipeline();
// Create command pool
m_cmdPool = createCommandPool(vk, vkDevice, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
// Create command buffer
m_cmdBuffer = allocateCommandBuffer(vk, vkDevice, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
}
tcu::TestStatus BlendOperationAdvancedTestCoherentInstance::iterate (void)
{
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const VkQueue queue = m_context.getUniversalQueue();
tcu::TestLog& log = m_context.getTestContext().getLog();
// Log the blend operations to test
{
DE_ASSERT(m_param.blendOps.size() == 2u);
log << tcu::TestLog::Message << "First depth op: " << de::toLower(getBlendOpStr(m_param.blendOps[0]).toString().substr(3)) << tcu::TestLog::EndMessage;
log << tcu::TestLog::Message << "Second depth op: " << de::toLower(getBlendOpStr(m_param.blendOps[1]).toString().substr(3)) << tcu::TestLog::EndMessage;
}
prepareCommandBuffer();
submitCommandsAndWait(vk, vkDevice, queue, m_cmdBuffer.get());
return verifyTestResult();
}
tcu::TestStatus BlendOperationAdvancedTestCoherentInstance::verifyTestResult (void)
{
deBool compareOk = DE_TRUE;
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkDevice vkDevice = m_context.getDevice();
const VkQueue queue = m_context.getUniversalQueue();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
Allocator& allocator = m_context.getDefaultAllocator();
tcu::TextureLevel refImage (vk::mapVkFormat(m_colorFormat), 32, 32);
tcu::clear(refImage.getAccess(), clearColorVec4);
// Generate reference image
for (deUint32 color = 0; color < DE_LENGTH_OF_ARRAY(srcColors)/2; color++)
{
deUint32 secondDrawColorIndex = color + DE_LENGTH_OF_ARRAY(srcColors)/2;
// Calculate first draw final color
Vec4 rectColorTmp = calculateFinalColor(m_param, m_param.blendOps[0], srcColors[color], dstColors[color]);
if (m_param.premultipliedDstColor == VK_FALSE)
{
if (rectColorTmp.w() > 0.0f)
{
rectColorTmp.x() = rectColorTmp.x() / rectColorTmp.w();
rectColorTmp.y() = rectColorTmp.y() / rectColorTmp.w();
rectColorTmp.z() = rectColorTmp.z() / rectColorTmp.w();
}
else
{
// Skip the color check if it is ill-formed.
if (rectColorTmp != Vec4(0.0f))
continue;
}
}
// Calculate second draw final color
Vec4 rectColor = calculateFinalColor(m_param, m_param.blendOps[1], srcColors[secondDrawColorIndex], rectColorTmp);
if (m_param.premultipliedDstColor == VK_FALSE)
{
if (rectColor.w() > 0.0f)
{
rectColor.x() = rectColor.x() / rectColor.w();
rectColor.y() = rectColor.y() / rectColor.w();
rectColor.z() = rectColor.z() / rectColor.w();
}
else
{
// Skip the color check if it is ill-formed.
if (rectColor != Vec4(0.0f))
continue;
}
}
deInt32 x = 0;
deInt32 y = 0;
getCoordinates(color, x, y);
tcu::clear(tcu::getSubregion(refImage.getAccess(), x, y, 1u, 1u), rectColor);
}
de::MovePtr<tcu::TextureLevel> result = vkt::pipeline::readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, *m_colorImage, m_colorFormat, m_renderSize);
std::ostringstream name;
name << "Image comparison. Depth ops: " << de::toLower(getBlendOpStr(m_param.blendOps[0]).toString().substr(3)) << " and " << de::toLower(getBlendOpStr(m_param.blendOps[1]).toString().substr(3));
compareOk = tcu::floatThresholdCompare(m_context.getTestContext().getLog(),
"FloatImageCompare",
name.str().c_str(),
refImage.getAccess(),
result->getAccess(),
Vec4(0.01f, 0.01f, 0.01f, 0.01f),
tcu::COMPARE_LOG_RESULT);
if (!compareOk)
return tcu::TestStatus::fail("Image mismatch");
return tcu::TestStatus::pass("Result images matches references");
}
TestInstance* BlendOperationAdvancedTest::createInstance (Context& context) const
{
if (m_param.testMode == TEST_MODE_GENERIC)
return new BlendOperationAdvancedTestInstance(context, m_param);
else
return new BlendOperationAdvancedTestCoherentInstance(context, m_param);
}
} // anonymous
tcu::TestCaseGroup* createBlendOperationAdvancedTests (tcu::TestContext& testCtx)
{
enum nonpremultiplyEnum
{
PREMULTIPLY_SRC = 1u,
PREMULTIPLY_DST = 2u
};
deUint32 premultiplyModes[] = { 0u, PREMULTIPLY_SRC, PREMULTIPLY_DST, PREMULTIPLY_SRC | PREMULTIPLY_DST };
deUint32 colorAttachmentCounts[] = { 1u, 2u, 4u, 8u, 16u };
deBool coherentOps[] = { DE_FALSE, DE_TRUE };
VkBlendOp blendOps[] =
{
VK_BLEND_OP_ZERO_EXT, VK_BLEND_OP_SRC_EXT, VK_BLEND_OP_DST_EXT, VK_BLEND_OP_SRC_OVER_EXT, VK_BLEND_OP_DST_OVER_EXT,
VK_BLEND_OP_SRC_IN_EXT, VK_BLEND_OP_DST_IN_EXT, VK_BLEND_OP_SRC_OUT_EXT, VK_BLEND_OP_DST_OUT_EXT, VK_BLEND_OP_SRC_ATOP_EXT,
VK_BLEND_OP_DST_ATOP_EXT, VK_BLEND_OP_XOR_EXT, VK_BLEND_OP_MULTIPLY_EXT, VK_BLEND_OP_SCREEN_EXT, VK_BLEND_OP_OVERLAY_EXT,
VK_BLEND_OP_DARKEN_EXT, VK_BLEND_OP_LIGHTEN_EXT, VK_BLEND_OP_COLORDODGE_EXT, VK_BLEND_OP_COLORBURN_EXT, VK_BLEND_OP_HARDLIGHT_EXT,
VK_BLEND_OP_SOFTLIGHT_EXT, VK_BLEND_OP_DIFFERENCE_EXT, VK_BLEND_OP_EXCLUSION_EXT, VK_BLEND_OP_INVERT_EXT, VK_BLEND_OP_INVERT_RGB_EXT,
VK_BLEND_OP_LINEARDODGE_EXT, VK_BLEND_OP_LINEARBURN_EXT, VK_BLEND_OP_VIVIDLIGHT_EXT, VK_BLEND_OP_LINEARLIGHT_EXT, VK_BLEND_OP_PINLIGHT_EXT,
VK_BLEND_OP_HARDMIX_EXT, VK_BLEND_OP_HSL_HUE_EXT, VK_BLEND_OP_HSL_SATURATION_EXT, VK_BLEND_OP_HSL_COLOR_EXT, VK_BLEND_OP_HSL_LUMINOSITY_EXT,
VK_BLEND_OP_PLUS_EXT, VK_BLEND_OP_PLUS_CLAMPED_EXT, VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT, VK_BLEND_OP_PLUS_DARKER_EXT, VK_BLEND_OP_MINUS_EXT,
VK_BLEND_OP_MINUS_CLAMPED_EXT, VK_BLEND_OP_CONTRAST_EXT, VK_BLEND_OP_INVERT_OVG_EXT, VK_BLEND_OP_RED_EXT, VK_BLEND_OP_GREEN_EXT, VK_BLEND_OP_BLUE_EXT,
};
de::MovePtr<tcu::TestCaseGroup> tests (new tcu::TestCaseGroup(testCtx, "blend_operation_advanced", "VK_EXT_blend_operation_advanced tests"));
de::Random rnd (deStringHash(tests->getName()));
de::MovePtr<tcu::TestCaseGroup> opsTests (new tcu::TestCaseGroup(testCtx, "ops", "Test each blend operation advance op"));
for (deUint32 colorAttachmentCount = 0u; colorAttachmentCount < DE_LENGTH_OF_ARRAY(colorAttachmentCounts); colorAttachmentCount++)
{
for (deUint32 overlap = 0; overlap <= VK_BLEND_OVERLAP_CONJOINT_EXT; overlap++)
{
for (deUint32 premultiply = 0u; premultiply < DE_LENGTH_OF_ARRAY(premultiplyModes); premultiply++)
{
deUint32 testNumber = 0u;
for (deUint64 blendOp = 0u; blendOp < DE_LENGTH_OF_ARRAY(blendOps); blendOp++)
{
deBool isAdditionalRGBBlendOp = blendOps[blendOp] >= VK_BLEND_OP_PLUS_EXT && blendOps[blendOp] < VK_BLEND_OP_MAX_ENUM;
// Additional RGB Blend operations are not affected by the blend overlap modes
if (isAdditionalRGBBlendOp && overlap != VK_BLEND_OVERLAP_UNCORRELATED_EXT)
continue;
BlendOperationAdvancedParam testParams;
testParams.testMode = TEST_MODE_GENERIC;
testParams.overlap = (VkBlendOverlapEXT) overlap;
testParams.coherentOperations = DE_FALSE;
testParams.colorAttachmentsCount = colorAttachmentCounts[colorAttachmentCount];
testParams.independentBlend = DE_FALSE;
testParams.premultipliedSrcColor = (premultiplyModes[premultiply] & PREMULTIPLY_SRC) ? VK_TRUE : VK_FALSE;
testParams.premultipliedDstColor = (premultiplyModes[premultiply] & PREMULTIPLY_DST) ? VK_TRUE : VK_FALSE;
testParams.testNumber = testNumber++;
for (deUint32 numColorAtt = 0; numColorAtt < colorAttachmentCounts[colorAttachmentCount]; numColorAtt++)
testParams.blendOps.push_back(blendOps[blendOp]);
opsTests->addChild(newTestCase<BlendOperationAdvancedTest>(testCtx, testParams));
}
}
}
}
tests->addChild(opsTests.release());
// Independent Blend Tests: test more than one color attachment.
de::MovePtr<tcu::TestCaseGroup> independentTests (new tcu::TestCaseGroup(testCtx, "independent", "Test independent blend feature"));
deUint32 testNumber = 0u;
for (deUint32 colorAttachmentCount = 1u; colorAttachmentCount < DE_LENGTH_OF_ARRAY(colorAttachmentCounts); colorAttachmentCount++)
{
BlendOperationAdvancedParam testParams;
testParams.testMode = TEST_MODE_GENERIC;
testParams.overlap = VK_BLEND_OVERLAP_UNCORRELATED_EXT;
testParams.coherentOperations = DE_FALSE;
testParams.colorAttachmentsCount = colorAttachmentCounts[colorAttachmentCount];
testParams.independentBlend = DE_TRUE;
testParams.premultipliedSrcColor = VK_TRUE;
testParams.premultipliedDstColor = VK_TRUE;
testParams.testNumber = testNumber++;
for (deUint32 numColorAtt = 0; numColorAtt < colorAttachmentCounts[colorAttachmentCount]; numColorAtt++)
{
deUint32 i = de::randomScalar<deUint32>(rnd, 0, DE_LENGTH_OF_ARRAY(blendOps) - 1);
testParams.blendOps.push_back(blendOps[i]);
}
independentTests->addChild(newTestCase<BlendOperationAdvancedTest>(testCtx, testParams));
}
tests->addChild(independentTests.release());
// Coherent tests, do two consecutive advanced blending operations on the same color attachment.
de::MovePtr<tcu::TestCaseGroup> coherentTests (new tcu::TestCaseGroup(testCtx, "coherent", "Test coherent memory"));
testNumber = 0u;
for (deUint32 coherent = 0u; coherent < DE_LENGTH_OF_ARRAY(coherentOps); coherent++)
{
BlendOperationAdvancedParam testParams;
testParams.testMode = TEST_MODE_COHERENT;
testParams.overlap = VK_BLEND_OVERLAP_UNCORRELATED_EXT;
testParams.coherentOperations = coherentOps[coherent];
testParams.colorAttachmentsCount = 1u;
testParams.independentBlend = DE_FALSE;
testParams.premultipliedSrcColor = VK_TRUE;
testParams.premultipliedDstColor = VK_TRUE;
testParams.testNumber = testNumber++;
// We do two consecutive advanced blending operations
deUint32 i = de::randomScalar<deUint32>(rnd, 0, DE_LENGTH_OF_ARRAY(blendOps) - 1);
testParams.blendOps.push_back(blendOps[i]);
i = de::randomScalar<deUint32>(rnd, 0, DE_LENGTH_OF_ARRAY(blendOps) - 1);
testParams.blendOps.push_back(blendOps[i]);
coherentTests->addChild(newTestCase<BlendOperationAdvancedTest>(testCtx, testParams));
}
tests->addChild(coherentTests.release());
return tests.release();
}
} // pipeline
} // vkt