1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2019 Valve Corporation.
6 * Copyright (c) 2019 The Khronos Group Inc.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 *//*!
21 * \file
22 * \brief VK_EXT_blend_operation_advanced tests
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktPipelineBlendOperationAdvancedTests.hpp"
26 #include "vktPipelineImageUtil.hpp"
27 #include "vktPipelineReferenceRenderer.hpp"
28 #include "vktTestCaseUtil.hpp"
29 #include "vkCmdUtil.hpp"
30 #include "vkImageUtil.hpp"
31 #include "vkRefUtil.hpp"
32 #include "vkQueryUtil.hpp"
33 #include "vkTypeUtil.hpp"
34 #include "vkBuilderUtil.hpp"
35 #include "vkObjUtil.hpp"
36
37 #include "tcuTestLog.hpp"
38 #include "tcuImageCompare.hpp"
39 #include "tcuCommandLine.hpp"
40
41 #include <cmath>
42
43 namespace vkt
44 {
45 namespace pipeline
46 {
47
48 using namespace vk;
49
50 namespace
51 {
52 using tcu::Vec3;
53 using tcu::Vec4;
54
55 const deUint32 widthArea = 32u;
56 const deUint32 heightArea = 32u;
57
58 static const float A1 = 0.750f; // Between 1 and 0.5
59 static const float A2 = 0.375f; // Between 0.5 and 0.25
60 static const float A3 = 0.125f; // Between 0.25 and 0.0
61
62 const Vec4 srcColors[] = {
63 // Test that pre-multiplied is converted correctly.
64 // Should not test invalid premultiplied colours (1, 1, 1, 0).
65 { 1.000f, 0.750f, 0.500f, 1.00f },
66 { 0.250f, 0.125f, 0.000f, 1.00f },
67
68 // Test clamping.
69 { 1.000f, 0.750f, 0.500f, 1.00f },
70 { 0.250f, 0.125f, 0.000f, 1.00f },
71 { 1.000f, 0.750f, 0.500f, 1.00f },
72 { 0.250f, 0.125f, 0.000f, 1.00f },
73
74 // Combinations that test other branches of blend equations.
75 { 1.000f, 0.750f, 0.500f, 1.00f },
76 { 0.250f, 0.125f, 0.000f, 1.00f },
77 { 1.000f, 0.750f, 0.500f, 1.00f },
78 { 0.250f, 0.125f, 0.000f, 1.00f },
79 { 1.000f, 0.750f, 0.500f, 1.00f },
80 { 0.250f, 0.125f, 0.000f, 1.00f },
81 { 1.000f, 0.750f, 0.500f, 1.00f },
82 { 0.250f, 0.125f, 0.000f, 1.00f },
83 { 1.000f, 0.750f, 0.500f, 1.00f },
84 { 0.250f, 0.125f, 0.000f, 1.00f },
85
86 // Above block with few different pre-multiplied alpha values.
87 { 1.000f * A1, 0.750f * A1, 0.500f * A1, 1.00f * A1},
88 { 0.250f * A1, 0.125f * A1, 0.000f * A1, 1.00f * A1},
89 { 1.000f * A1, 0.750f * A1, 0.500f * A1, 1.00f * A1},
90 { 0.250f * A1, 0.125f * A1, 0.000f * A1, 1.00f * A1},
91 { 1.000f * A1, 0.750f * A1, 0.500f * A1, 1.00f * A1},
92 { 0.250f * A1, 0.125f * A1, 0.000f * A1, 1.00f * A1},
93 { 1.000f * A1, 0.750f * A1, 0.500f * A1, 1.00f * A1},
94 { 0.250f * A1, 0.125f * A1, 0.000f * A1, 1.00f * A1},
95 { 1.000f * A1, 0.750f * A1, 0.500f * A1, 1.00f * A1},
96 { 0.250f * A1, 0.125f * A1, 0.000f * A1, 1.00f * A1},
97
98 { 1.000f * A2, 0.750f * A2, 0.500f * A2, 1.00f * A2},
99 { 0.250f * A2, 0.125f * A2, 0.000f * A2, 1.00f * A2},
100 { 1.000f * A2, 0.750f * A2, 0.500f * A2, 1.00f * A2},
101 { 0.250f * A2, 0.125f * A2, 0.000f * A2, 1.00f * A2},
102 { 1.000f * A2, 0.750f * A2, 0.500f * A2, 1.00f * A2},
103 { 0.250f * A2, 0.125f * A2, 0.000f * A2, 1.00f * A2},
104 { 1.000f * A2, 0.750f * A2, 0.500f * A2, 1.00f * A2},
105 { 0.250f * A2, 0.125f * A2, 0.000f * A2, 1.00f * A2},
106 { 1.000f * A2, 0.750f * A2, 0.500f * A2, 1.00f * A2},
107 { 0.250f * A2, 0.125f * A2, 0.000f * A2, 1.00f * A2},
108
109 { 1.000f * A3, 0.750f * A3, 0.500f * A3, 1.00f * A3},
110 { 0.250f * A3, 0.125f * A3, 0.000f * A3, 1.00f * A3},
111 { 1.000f * A3, 0.750f * A3, 0.500f * A3, 1.00f * A3},
112 { 0.250f * A3, 0.125f * A3, 0.000f * A3, 1.00f * A3},
113 { 1.000f * A3, 0.750f * A3, 0.500f * A3, 1.00f * A3},
114 { 0.250f * A3, 0.125f * A3, 0.000f * A3, 1.00f * A3},
115 { 1.000f * A3, 0.750f * A3, 0.500f * A3, 1.00f * A3},
116 { 0.250f * A3, 0.125f * A3, 0.000f * A3, 1.00f * A3},
117 { 1.000f * A3, 0.750f * A3, 0.500f * A3, 1.00f * A3},
118 { 0.250f * A3, 0.125f * A3, 0.000f * A3, 1.00f * A3},
119
120 // Add some source colors with alpha component that is different than the respective destination color
121 { 0.750f, 0.750f, 0.500f, 0.750f },
122 { 0.250f, 0.500f, 0.500f, 0.750f },
123 { 0.250f, 0.125f, 0.000f, 0.500f },
124 { 0.250f, 0.250f, 0.500f, 0.500f },
125 { 0.250f, 0.125f, 0.000f, 0.250f },
126 { 0.125f, 0.125f, 0.125f, 0.250f }};
127
128 const Vec4 dstColors[] = {
129 // Test that pre-multiplied is converted correctly.
130 // Should not test invalid premultiplied colours (1, 1, 1, 0).
131 { 0.000f, 0.000f, 0.000f, 0.00f },
132 { 0.000f, 0.000f, 0.000f, 0.00f },
133
134 // Test clamping.
135 { -0.125f, -0.125f, -0.125f, 1.00f },
136 { -0.125f, -0.125f, -0.125f, 1.00f },
137 { 1.125f, 1.125f, 1.125f, 1.00f },
138 { 1.125f, 1.125f, 1.125f, 1.00f },
139
140 // Combinations that test other branches of blend equations.
141 { 1.000f, 1.000f, 1.000f, 1.00f },
142 { 1.000f, 1.000f, 1.000f, 1.00f },
143 { 0.500f, 0.500f, 0.500f, 1.00f },
144 { 0.500f, 0.500f, 0.500f, 1.00f },
145 { 0.250f, 0.250f, 0.250f, 1.00f },
146 { 0.250f, 0.250f, 0.250f, 1.00f },
147 { 0.125f, 0.125f, 0.125f, 1.00f },
148 { 0.125f, 0.125f, 0.125f, 1.00f },
149 { 0.000f, 0.000f, 0.000f, 1.00f },
150 { 0.000f, 0.000f, 0.000f, 1.00f },
151
152 // Above block with few different pre-multiplied alpha values.
153 { 1.000f * A1, 1.000f * A1, 1.000f * A1, 1.00f * A1},
154 { 1.000f * A1, 1.000f * A1, 1.000f * A1, 1.00f * A1},
155 { 0.500f * A1, 0.500f * A1, 0.500f * A1, 1.00f * A1},
156 { 0.500f * A1, 0.500f * A1, 0.500f * A1, 1.00f * A1},
157 { 0.250f * A1, 0.250f * A1, 0.250f * A1, 1.00f * A1},
158 { 0.250f * A1, 0.250f * A1, 0.250f * A1, 1.00f * A1},
159 { 0.125f * A1, 0.125f * A1, 0.125f * A1, 1.00f * A1},
160 { 0.125f * A1, 0.125f * A1, 0.125f * A1, 1.00f * A1},
161 { 0.000f * A1, 0.000f * A1, 0.000f * A1, 1.00f * A1},
162 { 0.000f * A1, 0.000f * A1, 0.000f * A1, 1.00f * A1},
163
164 { 1.000f * A2, 1.000f * A2, 1.000f * A2, 1.00f * A2},
165 { 1.000f * A2, 1.000f * A2, 1.000f * A2, 1.00f * A2},
166 { 0.500f * A2, 0.500f * A2, 0.500f * A2, 1.00f * A2},
167 { 0.500f * A2, 0.500f * A2, 0.500f * A2, 1.00f * A2},
168 { 0.250f * A2, 0.250f * A2, 0.250f * A2, 1.00f * A2},
169 { 0.250f * A2, 0.250f * A2, 0.250f * A2, 1.00f * A2},
170 { 0.125f * A2, 0.125f * A2, 0.125f * A2, 1.00f * A2},
171 { 0.125f * A2, 0.125f * A2, 0.125f * A2, 1.00f * A2},
172 { 0.000f * A2, 0.000f * A2, 0.000f * A2, 1.00f * A2},
173 { 0.000f * A2, 0.000f * A2, 0.000f * A2, 1.00f * A2},
174
175 { 1.000f * A3, 1.000f * A3, 1.000f * A3, 1.00f * A3},
176 { 1.000f * A3, 1.000f * A3, 1.000f * A3, 1.00f * A3},
177 { 0.500f * A3, 0.500f * A3, 0.500f * A3, 1.00f * A3},
178 { 0.500f * A3, 0.500f * A3, 0.500f * A3, 1.00f * A3},
179 { 0.250f * A3, 0.250f * A3, 0.250f * A3, 1.00f * A3 },
180 { 0.250f * A3, 0.250f * A3, 0.250f * A3, 1.00f * A3 },
181 { 0.125f * A3, 0.125f * A3, 0.125f * A3, 1.00f * A3 },
182 { 0.125f * A3, 0.125f * A3, 0.125f * A3, 1.00f * A3 },
183 { 0.000f * A3, 0.000f * A3, 0.000f * A3, 1.00f * A3 },
184 { 0.000f * A3, 0.000f * A3, 0.000f * A3, 1.00f * A3 },
185
186 // Add some source colors with alpha component that is different than the respective source color
187 { 1.000f, 1.000f, 1.000f, 1.000f },
188 { 0.250f, 0.250f, 0.250f, 0.500f },
189 { 0.500f, 0.500f, 0.500f, 0.750f },
190 { 0.250f, 0.250f, 0.250f, 0.250f },
191 { 0.250f, 0.250f, 0.250f, 0.500f },
192 { 0.125f, 0.125f, 0.125f, 0.125f }};
193
194 const Vec4 clearColorVec4 (1.0f, 1.0f, 1.0f, 1.0f);
195
196 enum TestMode
197 {
198 TEST_MODE_GENERIC = 0,
199 TEST_MODE_COHERENT = 1,
200 };
201
202 struct BlendOperationAdvancedParam
203 {
204 PipelineConstructionType pipelineConstructionType;
205 TestMode testMode;
206 deUint32 testNumber;
207 std::vector<VkBlendOp> blendOps;
208 deBool coherentOperations;
209 deBool independentBlend;
210 deUint32 colorAttachmentsCount;
211 VkBool32 premultipliedSrcColor;
212 VkBool32 premultipliedDstColor;
213 VkBlendOverlapEXT overlap;
214 VkFormat format;
215 };
216
217 // helper functions
generateTestName(struct BlendOperationAdvancedParam param)218 const std::string generateTestName (struct BlendOperationAdvancedParam param)
219 {
220 std::ostringstream result;
221
222 result << ((param.testMode == TEST_MODE_COHERENT && !param.coherentOperations) ? "barrier_" : "");
223 result << "color_attachments_" << param.colorAttachmentsCount;
224 result << "_" << de::toLower(getBlendOverlapEXTStr(param.overlap).toString().substr(3));
225 result << (!param.premultipliedSrcColor ? "_nonpremultipliedsrc" : "");
226 result << (!param.premultipliedDstColor ? "_nonpremultiplieddst" : "");
227 result << "_" << param.testNumber;
228 if (param.format == VK_FORMAT_R8G8B8A8_UNORM)
229 result << "_r8g8b8a8_unorm";
230 return result.str();
231 }
232
generateTestDescription()233 const std::string generateTestDescription ()
234 {
235 std::string result("Test advanced blend operations");
236 return result;
237 }
238
calculateWeightingFactors(BlendOperationAdvancedParam param, float alphaSrc, float alphaDst)239 Vec3 calculateWeightingFactors(BlendOperationAdvancedParam param,
240 float alphaSrc, float alphaDst)
241 {
242 Vec3 p = Vec3(0.0f, 0.0f, 0.0f);
243 switch(param.overlap)
244 {
245 case VK_BLEND_OVERLAP_UNCORRELATED_EXT:
246 p.x() = alphaSrc * alphaDst;
247 p.y() = alphaSrc * (1.0f - alphaDst);
248 p.z() = alphaDst * (1.0f - alphaSrc);
249 break;
250 case VK_BLEND_OVERLAP_CONJOINT_EXT:
251 p.x() = deFloatMin(alphaSrc, alphaDst);
252 p.y() = deFloatMax(alphaSrc - alphaDst, 0.0f);
253 p.z() = deFloatMax(alphaDst - alphaSrc, 0.0f);
254 break;
255 case VK_BLEND_OVERLAP_DISJOINT_EXT:
256 p.x() = deFloatMax(alphaSrc + alphaDst - 1.0f, 0.0f);
257 p.y() = deFloatMin(alphaSrc, 1.0f - alphaDst);
258 p.z() = deFloatMin(alphaDst, 1.0f - alphaSrc);
259 break;
260 default:
261 DE_FATAL("Unsupported Advanced Blend Overlap Mode");
262 }
263 return p;
264 }
265
calculateXYZFactors(VkBlendOp op)266 Vec3 calculateXYZFactors(VkBlendOp op)
267 {
268 Vec3 xyz = Vec3(0.0f, 0.0f, 0.0f);
269 switch (op)
270 {
271 case VK_BLEND_OP_ZERO_EXT:
272 xyz = Vec3(0.0f, 0.0f, 0.0f);
273 break;
274
275 case VK_BLEND_OP_DST_ATOP_EXT:
276 case VK_BLEND_OP_SRC_EXT:
277 xyz = Vec3(1.0f, 1.0f, 0.0f);
278 break;
279
280 case VK_BLEND_OP_DST_EXT:
281 xyz = Vec3(1.0f, 0.0f, 1.0f);
282 break;
283
284 case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
285 case VK_BLEND_OP_HSL_COLOR_EXT:
286 case VK_BLEND_OP_HSL_SATURATION_EXT:
287 case VK_BLEND_OP_HSL_HUE_EXT:
288 case VK_BLEND_OP_HARDMIX_EXT:
289 case VK_BLEND_OP_PINLIGHT_EXT:
290 case VK_BLEND_OP_LINEARLIGHT_EXT:
291 case VK_BLEND_OP_VIVIDLIGHT_EXT:
292 case VK_BLEND_OP_LINEARBURN_EXT:
293 case VK_BLEND_OP_LINEARDODGE_EXT:
294 case VK_BLEND_OP_EXCLUSION_EXT:
295 case VK_BLEND_OP_DIFFERENCE_EXT:
296 case VK_BLEND_OP_SOFTLIGHT_EXT:
297 case VK_BLEND_OP_HARDLIGHT_EXT:
298 case VK_BLEND_OP_COLORBURN_EXT:
299 case VK_BLEND_OP_COLORDODGE_EXT:
300 case VK_BLEND_OP_LIGHTEN_EXT:
301 case VK_BLEND_OP_DARKEN_EXT:
302 case VK_BLEND_OP_OVERLAY_EXT:
303 case VK_BLEND_OP_SCREEN_EXT:
304 case VK_BLEND_OP_MULTIPLY_EXT:
305 case VK_BLEND_OP_SRC_OVER_EXT:
306 case VK_BLEND_OP_DST_OVER_EXT:
307 xyz = Vec3(1.0f, 1.0f, 1.0f);
308 break;
309
310 case VK_BLEND_OP_SRC_IN_EXT:
311 case VK_BLEND_OP_DST_IN_EXT:
312 xyz = Vec3(1.0f, 0.0f, 0.0f);
313 break;
314
315 case VK_BLEND_OP_SRC_OUT_EXT:
316 xyz = Vec3(0.0f, 1.0f, 0.0f);
317 break;
318
319 case VK_BLEND_OP_DST_OUT_EXT:
320 xyz = Vec3(0.0f, 0.0f, 1.0f);
321 break;
322
323 case VK_BLEND_OP_INVERT_RGB_EXT:
324 case VK_BLEND_OP_INVERT_EXT:
325 case VK_BLEND_OP_SRC_ATOP_EXT:
326 xyz = Vec3(1.0f, 0.0f, 1.0f);
327 break;
328
329 case VK_BLEND_OP_XOR_EXT:
330 xyz = Vec3(0.0f, 1.0f, 1.0f);
331 break;
332
333 default:
334 DE_FATAL("Unsupported f/X/Y/Z Advanced Blend Operations Mode");
335 }
336
337 return xyz;
338 }
339
blendOpOverlay(float src, float dst)340 float blendOpOverlay(float src, float dst)
341 {
342 if (dst <= 0.5f)
343 return (2.0f * src * dst);
344 else
345 return (1.0f - (2.0f * (1.0f - src) * (1.0f - dst)));
346 }
347
blendOpColorDodge(float src, float dst)348 float blendOpColorDodge(float src, float dst)
349 {
350 if (dst <= 0.0f)
351 return 0.0f;
352 else if (src < 1.0f)
353 return deFloatMin(1.0f, (dst / (1.0f - src)));
354 else
355 return 1.0f;
356 }
357
blendOpColorBurn(float src, float dst)358 float blendOpColorBurn(float src, float dst)
359 {
360 if (dst >= 1.0f)
361 return 1.0f;
362 else if (src > 0.0f)
363 return 1.0f - deFloatMin(1.0f, (1.0f - dst) / src);
364 else
365 return 0.0f;
366 }
367
blendOpHardlight(float src, float dst)368 float blendOpHardlight(float src, float dst)
369 {
370 if (src <= 0.5f)
371 return 2.0f * src * dst;
372 else
373 return 1.0f - (2.0f * (1.0f - src) * (1.0f - dst));
374 }
375
blendOpSoftlight(float src, float dst)376 float blendOpSoftlight(float src, float dst)
377 {
378 if (src <= 0.5f)
379 return dst - ((1.0f - (2.0f * src)) * dst * (1.0f - dst));
380 else if (dst <= 0.25f)
381 return dst + (((2.0f * src) - 1.0f) * dst * ((((16.0f * dst) - 12.0f) * dst) + 3.0f));
382 else
383 return dst + (((2.0f * src) - 1.0f) * (deFloatSqrt(dst) - dst));
384 }
385
blendOpLinearDodge(float src, float dst)386 float blendOpLinearDodge(float src, float dst)
387 {
388 if ((src + dst) <= 1.0f)
389 return src + dst;
390 else
391 return 1.0f;
392 }
393
blendOpLinearBurn(float src, float dst)394 float blendOpLinearBurn(float src, float dst)
395 {
396 if ((src + dst) > 1.0f)
397 return src + dst - 1.0f;
398 else
399 return 0.0f;
400 }
401
blendOpVividLight(float src, float dst)402 float blendOpVividLight(float src, float dst)
403 {
404 if (src <= 0.0f)
405 return 0.0f;
406 if (src < 0.5f)
407 return 1.0f - (deFloatMin(1.0f, (1.0f - dst) / (2.0f * src)));
408 if (src < 1.0f)
409 return deFloatMin(1.0f, dst / (2.0f * (1.0f - src)));
410 else
411 return 1.0f;
412 }
413
blendOpLinearLight(float src, float dst)414 float blendOpLinearLight(float src, float dst)
415 {
416 if ((2.0f * src + dst) > 2.0f)
417 return 1.0f;
418 if ((2.0f * src + dst) <= 1.0f)
419 return 0.0f;
420 return (2.0f * src) + dst - 1.0f;
421 }
422
blendOpPinLight(float src, float dst)423 float blendOpPinLight(float src, float dst)
424 {
425 if (((2.0f * src - 1.0f) > dst) && src < 0.5f)
426 return 0.0f;
427 if (((2.0f * src - 1.0f) > dst) && src >= 0.5f)
428 return 2.0f * src - 1.0f;
429 if (((2.0f * src - 1.0f) <= dst) && src < (0.5f * dst))
430 return 2.0f * src;
431 if (((2.0f * src - 1.0f) <= dst) && src >= (0.5f * dst))
432 return dst;
433 return 0.0f;
434 }
435
blendOpHardmix(float src, float dst)436 float blendOpHardmix(float src, float dst)
437 {
438 if ((src + dst) < 1.0f)
439 return 0.0f;
440 else
441 return 1.0f;
442 }
443
minv3(Vec3 c)444 float minv3(Vec3 c)
445 {
446 return deFloatMin(deFloatMin(c.x(), c.y()), c.z());
447 }
448
maxv3(Vec3 c)449 float maxv3(Vec3 c)
450 {
451 return deFloatMax(deFloatMax(c.x(), c.y()), c.z());
452 }
453
lumv3(Vec3 c)454 float lumv3(Vec3 c)
455 {
456 return dot(c, Vec3(0.3f, 0.59f, 0.11f));
457 }
458
satv3(Vec3 c)459 float satv3(Vec3 c)
460 {
461 return maxv3(c) - minv3(c);
462 }
463
464 // If any color components are outside [0,1], adjust the color to
465 // get the components in range.
clipColor(Vec3 color)466 Vec3 clipColor(Vec3 color)
467 {
468 float lum = lumv3(color);
469 float mincol = minv3(color);
470 float maxcol = maxv3(color);
471
472 if (mincol < 0.0)
473 {
474 color = lum + ((color - lum) * lum) / (lum - mincol);
475 }
476 if (maxcol > 1.0)
477 {
478 color = lum + ((color - lum) * (1.0f - lum)) / (maxcol - lum);
479 }
480 return color;
481 }
482
483 // Take the base RGB color <cbase> and override its luminosity
484 // with that of the RGB color <clum>.
setLum(Vec3 cbase, Vec3 clum)485 Vec3 setLum(Vec3 cbase, Vec3 clum)
486 {
487 float lbase = lumv3(cbase);
488 float llum = lumv3(clum);
489 float ldiff = llum - lbase;
490
491 Vec3 color = cbase + Vec3(ldiff);
492 return clipColor(color);
493 }
494
495 // Take the base RGB color <cbase> and override its saturation with
496 // that of the RGB color <csat>. The override the luminosity of the
497 // result with that of the RGB color <clum>.
setLumSat(Vec3 cbase, Vec3 csat, Vec3 clum)498 Vec3 setLumSat(Vec3 cbase, Vec3 csat, Vec3 clum)
499 {
500 float minbase = minv3(cbase);
501 float sbase = satv3(cbase);
502 float ssat = satv3(csat);
503 Vec3 color;
504
505 if (sbase > 0)
506 {
507 // Equivalent (modulo rounding errors) to setting the
508 // smallest (R,G,B) component to 0, the largest to <ssat>,
509 // and interpolating the "middle" component based on its
510 // original value relative to the smallest/largest.
511 color = (cbase - minbase) * ssat / sbase;
512 } else {
513 color = Vec3(0.0f);
514 }
515 return setLum(color, clum);
516 }
517
calculateFFunction(VkBlendOp op, Vec3 src, Vec3 dst)518 Vec3 calculateFFunction(VkBlendOp op,
519 Vec3 src, Vec3 dst)
520 {
521 Vec3 f = Vec3(0.0f, 0.0f, 0.0f);
522
523 switch (op)
524 {
525 case VK_BLEND_OP_XOR_EXT:
526 case VK_BLEND_OP_SRC_OUT_EXT:
527 case VK_BLEND_OP_DST_OUT_EXT:
528 case VK_BLEND_OP_ZERO_EXT:
529 f = Vec3(0.0f, 0.0f, 0.0f);
530 break;
531
532 case VK_BLEND_OP_SRC_ATOP_EXT:
533 case VK_BLEND_OP_SRC_IN_EXT:
534 case VK_BLEND_OP_SRC_OVER_EXT:
535 case VK_BLEND_OP_SRC_EXT:
536 f = src;
537 break;
538
539 case VK_BLEND_OP_DST_ATOP_EXT:
540 case VK_BLEND_OP_DST_IN_EXT:
541 case VK_BLEND_OP_DST_OVER_EXT:
542 case VK_BLEND_OP_DST_EXT:
543 f = dst;
544 break;
545
546 case VK_BLEND_OP_MULTIPLY_EXT:
547 f = src * dst;
548 break;
549
550 case VK_BLEND_OP_SCREEN_EXT:
551 f = src + dst - (src*dst);
552 break;
553
554 case VK_BLEND_OP_OVERLAY_EXT:
555 f.x() = blendOpOverlay(src.x(), dst.x());
556 f.y() = blendOpOverlay(src.y(), dst.y());
557 f.z() = blendOpOverlay(src.z(), dst.z());
558 break;
559
560 case VK_BLEND_OP_DARKEN_EXT:
561 f.x() = deFloatMin(src.x(), dst.x());
562 f.y() = deFloatMin(src.y(), dst.y());
563 f.z() = deFloatMin(src.z(), dst.z());
564 break;
565
566 case VK_BLEND_OP_LIGHTEN_EXT:
567 f.x() = deFloatMax(src.x(), dst.x());
568 f.y() = deFloatMax(src.y(), dst.y());
569 f.z() = deFloatMax(src.z(), dst.z());
570 break;
571
572 case VK_BLEND_OP_COLORDODGE_EXT:
573 f.x() = blendOpColorDodge(src.x(), dst.x());
574 f.y() = blendOpColorDodge(src.y(), dst.y());
575 f.z() = blendOpColorDodge(src.z(), dst.z());
576 break;
577
578 case VK_BLEND_OP_COLORBURN_EXT:
579 f.x() = blendOpColorBurn(src.x(), dst.x());
580 f.y() = blendOpColorBurn(src.y(), dst.y());
581 f.z() = blendOpColorBurn(src.z(), dst.z());
582 break;
583
584 case VK_BLEND_OP_HARDLIGHT_EXT:
585 f.x() = blendOpHardlight(src.x(), dst.x());
586 f.y() = blendOpHardlight(src.y(), dst.y());
587 f.z() = blendOpHardlight(src.z(), dst.z());
588 break;
589
590 case VK_BLEND_OP_SOFTLIGHT_EXT:
591 f.x() = blendOpSoftlight(src.x(), dst.x());
592 f.y() = blendOpSoftlight(src.y(), dst.y());
593 f.z() = blendOpSoftlight(src.z(), dst.z());
594 break;
595
596 case VK_BLEND_OP_DIFFERENCE_EXT:
597 f.x() = deFloatAbs(dst.x() - src.x());
598 f.y() = deFloatAbs(dst.y() - src.y());
599 f.z() = deFloatAbs(dst.z() - src.z());
600 break;
601
602
603 case VK_BLEND_OP_EXCLUSION_EXT:
604 f = src + dst - (2.0f * src * dst);
605 break;
606
607 case VK_BLEND_OP_INVERT_EXT:
608 f = 1.0f - dst;
609 break;
610
611 case VK_BLEND_OP_INVERT_RGB_EXT:
612 f = src * (1.0f - dst);
613 break;
614
615 case VK_BLEND_OP_LINEARDODGE_EXT:
616 f.x() = blendOpLinearDodge(src.x(), dst.x());
617 f.y() = blendOpLinearDodge(src.y(), dst.y());
618 f.z() = blendOpLinearDodge(src.z(), dst.z());
619 break;
620
621 case VK_BLEND_OP_LINEARBURN_EXT:
622 f.x() = blendOpLinearBurn(src.x(), dst.x());
623 f.y() = blendOpLinearBurn(src.y(), dst.y());
624 f.z() = blendOpLinearBurn(src.z(), dst.z());
625 break;
626
627 case VK_BLEND_OP_VIVIDLIGHT_EXT:
628 f.x() = blendOpVividLight(src.x(), dst.x());
629 f.y() = blendOpVividLight(src.y(), dst.y());
630 f.z() = blendOpVividLight(src.z(), dst.z());
631 break;
632
633 case VK_BLEND_OP_LINEARLIGHT_EXT:
634 f.x() = blendOpLinearLight(src.x(), dst.x());
635 f.y() = blendOpLinearLight(src.y(), dst.y());
636 f.z() = blendOpLinearLight(src.z(), dst.z());
637 break;
638
639 case VK_BLEND_OP_PINLIGHT_EXT:
640 f.x() = blendOpPinLight(src.x(), dst.x());
641 f.y() = blendOpPinLight(src.y(), dst.y());
642 f.z() = blendOpPinLight(src.z(), dst.z());
643 break;
644
645 case VK_BLEND_OP_HARDMIX_EXT:
646 f.x() = blendOpHardmix(src.x(), dst.x());
647 f.y() = blendOpHardmix(src.y(), dst.y());
648 f.z() = blendOpHardmix(src.z(), dst.z());
649 break;
650
651 case VK_BLEND_OP_HSL_HUE_EXT:
652 f = setLumSat(src, dst, dst);
653 break;
654
655 case VK_BLEND_OP_HSL_SATURATION_EXT:
656 f = setLumSat(dst, src, dst);
657 break;
658
659 case VK_BLEND_OP_HSL_COLOR_EXT:
660 f = setLum(src, dst);
661 break;
662
663 case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
664 f = setLum(dst, src);
665 break;
666
667 default:
668 DE_FATAL("Unsupported f/X/Y/Z Advanced Blend Operations Mode");
669 }
670
671 return f;
672 }
673
additionalRGBBlendOperations(VkBlendOp op, Vec4 src, Vec4 dst)674 Vec4 additionalRGBBlendOperations(VkBlendOp op,
675 Vec4 src, Vec4 dst)
676 {
677 Vec4 res = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
678
679 switch (op)
680 {
681 case VK_BLEND_OP_PLUS_EXT:
682 res = src + dst;
683 break;
684
685 case VK_BLEND_OP_PLUS_CLAMPED_EXT:
686 res.x() = deFloatMin(1.0f, src.x() + dst.x());
687 res.y() = deFloatMin(1.0f, src.y() + dst.y());
688 res.z() = deFloatMin(1.0f, src.z() + dst.z());
689 res.w() = deFloatMin(1.0f, src.w() + dst.w());
690 break;
691
692 case VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT:
693 res.x() = deFloatMin(deFloatMin(1.0f, src.w() + dst.w()), src.x() + dst.x());
694 res.y() = deFloatMin(deFloatMin(1.0f, src.w() + dst.w()), src.y() + dst.y());
695 res.z() = deFloatMin(deFloatMin(1.0f, src.w() + dst.w()), src.z() + dst.z());
696 res.w() = deFloatMin(1.0f, src.w() + dst.w());
697 break;
698
699 case VK_BLEND_OP_PLUS_DARKER_EXT:
700 res.x() = deFloatMax(0.0f, deFloatMin(1.0f, src.w() + dst.w()) - ((src.w() - src.x()) + (dst.w() - dst.x())));
701 res.y() = deFloatMax(0.0f, deFloatMin(1.0f, src.w() + dst.w()) - ((src.w() - src.y()) + (dst.w() - dst.y())));
702 res.z() = deFloatMax(0.0f, deFloatMin(1.0f, src.w() + dst.w()) - ((src.w() - src.z()) + (dst.w() - dst.z())));
703 res.w() = deFloatMin(1.0f, src.w() + dst.w());
704 break;
705
706 case VK_BLEND_OP_MINUS_EXT:
707 res = dst - src;
708 break;
709
710 case VK_BLEND_OP_MINUS_CLAMPED_EXT:
711 res.x() = deFloatMax(0.0f, dst.x() - src.x());
712 res.y() = deFloatMax(0.0f, dst.y() - src.y());
713 res.z() = deFloatMax(0.0f, dst.z() - src.z());
714 res.w() = deFloatMax(0.0f, dst.w() - src.w());
715 break;
716
717 case VK_BLEND_OP_CONTRAST_EXT:
718 res.x() = (dst.w() / 2.0f) + 2.0f * (dst.x() - (dst.w() / 2.0f)) * (src.x() - (src.w() / 2.0f));
719 res.y() = (dst.w() / 2.0f) + 2.0f * (dst.y() - (dst.w() / 2.0f)) * (src.y() - (src.w() / 2.0f));
720 res.z() = (dst.w() / 2.0f) + 2.0f * (dst.z() - (dst.w() / 2.0f)) * (src.z() - (src.w() / 2.0f));
721 res.w() = dst.w();
722 break;
723
724 case VK_BLEND_OP_INVERT_OVG_EXT:
725 res.x() = src.w() * (1.0f - dst.x()) + (1.0f - src.w()) * dst.x();
726 res.y() = src.w() * (1.0f - dst.y()) + (1.0f - src.w()) * dst.y();
727 res.z() = src.w() * (1.0f - dst.z()) + (1.0f - src.w()) * dst.z();
728 res.w() = src.w() + dst.w() - src.w() * dst.w();
729 break;
730
731 case VK_BLEND_OP_RED_EXT:
732 res = dst;
733 res.x() = src.x();
734 break;
735
736 case VK_BLEND_OP_GREEN_EXT:
737 res = dst;
738 res.y() = src.y();
739 break;
740
741 case VK_BLEND_OP_BLUE_EXT:
742 res = dst;
743 res.z() = src.z();
744 break;
745
746 default:
747 DE_FATAL("Unsupported blend operation");
748 }
749 return res;
750 }
751
calculateFinalColor(BlendOperationAdvancedParam param, VkBlendOp op, Vec4 source, Vec4 destination)752 Vec4 calculateFinalColor(BlendOperationAdvancedParam param, VkBlendOp op,
753 Vec4 source, Vec4 destination)
754 {
755 Vec4 result = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
756 Vec3 srcColor = source.xyz();
757 Vec3 dstColor = destination.xyz();
758
759 // Calculate weighting factors
760 Vec3 p = calculateWeightingFactors(param, source.w(), destination.w());
761
762 if (op > VK_BLEND_OP_MAX && op < VK_BLEND_OP_PLUS_EXT)
763 {
764 {
765 // If srcPremultiplied is set to VK_TRUE, the fragment color components
766 // are considered to have been premultiplied by the A component prior to
767 // blending. The base source color (Rs',Gs',Bs') is obtained by dividing
768 // through by the A component.
769 if (param.premultipliedSrcColor)
770 {
771 if (source.w() != 0.0f)
772 srcColor = srcColor / source.w();
773 else
774 srcColor = Vec3(0.0f, 0.0f, 0.0f);
775 }
776 // If dstPremultiplied is set to VK_TRUE, the destination components are
777 // considered to have been premultiplied by the A component prior to
778 // blending. The base destination color (Rd',Gd',Bd') is obtained by dividing
779 // through by the A component.
780 if (param.premultipliedDstColor)
781 {
782 if (destination.w() != 0.0f)
783 dstColor = dstColor / destination.w();
784 else
785 dstColor = Vec3(0.0f, 0.0f, 0.0f);
786 }
787 }
788
789 // Calculate X, Y, Z terms of the equation
790 Vec3 xyz = calculateXYZFactors(op);
791 Vec3 fSrcDst = calculateFFunction(op, srcColor, dstColor);
792
793 result.x() = fSrcDst.x() * p.x() + xyz.y() * srcColor.x() * p.y() + xyz.z() * dstColor.x() * p.z();
794 result.y() = fSrcDst.y() * p.x() + xyz.y() * srcColor.y() * p.y() + xyz.z() * dstColor.y() * p.z();
795 result.z() = fSrcDst.z() * p.x() + xyz.y() * srcColor.z() * p.y() + xyz.z() * dstColor.z() * p.z();
796 result.w() = xyz.x() * p.x() + xyz.y() * p.y() + xyz.z() * p.z();
797 }
798 else if (op >= VK_BLEND_OP_PLUS_EXT && op < VK_BLEND_OP_MAX_ENUM)
799 {
800 // Premultiply colors for additional RGB blend operations. The formula is different than the rest of operations.
801 {
802 if (!param.premultipliedSrcColor)
803 {
804 srcColor = srcColor * source.w();
805 }
806
807 if (!param.premultipliedDstColor)
808 {
809 dstColor = dstColor * destination.w();
810 }
811
812 }
813 Vec4 src = Vec4(srcColor.x(), srcColor.y(), srcColor.z(), source.w());
814 Vec4 dst = Vec4(dstColor.x(), dstColor.y(), dstColor.z(), destination.w());
815 result = additionalRGBBlendOperations(op, src, dst);
816 }
817 else
818 {
819 DE_FATAL("Unsupported Blend Operation");
820 }
821 return result;
822 }
823
getCoordinates(deUint32 index, deInt32 &x, deInt32 &y)824 static inline void getCoordinates (deUint32 index, deInt32 &x, deInt32 &y)
825 {
826 x = index % widthArea;
827 y = index / heightArea;
828 }
829
createPoints(void)830 static inline std::vector<Vec4> createPoints (void)
831 {
832 std::vector<Vec4> vertices;
833 vertices.push_back(Vec4(-1.0f, -1.0f, 0.0f, 1.0f));
834 vertices.push_back(Vec4( 1.0f, 1.0f, 0.0f, 1.0f));
835 vertices.push_back(Vec4(-1.0f, 1.0f, 0.0f, 1.0f));
836 vertices.push_back(Vec4(-1.0f, -1.0f, 0.0f, 1.0f));
837 vertices.push_back(Vec4( 1.0f, 1.0f, 0.0f, 1.0f));
838 vertices.push_back(Vec4( 1.0f, -1.0f, 0.0f, 1.0f));
839 return vertices;
840 }
841
842 template <class Test>
newTestCase(tcu::TestContext& testContext, const BlendOperationAdvancedParam testParam)843 vkt::TestCase* newTestCase (tcu::TestContext& testContext,
844 const BlendOperationAdvancedParam testParam)
845 {
846 return new Test(testContext,
847 generateTestName(testParam).c_str(),
848 generateTestDescription().c_str(),
849 testParam);
850 }
851
makeTestRenderPass(BlendOperationAdvancedParam param, const DeviceInterface& vk, const VkDevice device, const VkFormat colorFormat, VkAttachmentLoadOp colorLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR)852 Move<VkRenderPass> makeTestRenderPass (BlendOperationAdvancedParam param,
853 const DeviceInterface& vk,
854 const VkDevice device,
855 const VkFormat colorFormat,
856 VkAttachmentLoadOp colorLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR)
857 {
858 const VkAttachmentDescription colorAttachmentDescription =
859 {
860 (VkAttachmentDescriptionFlags)0, // VkAttachmentDescriptionFlags flags
861 colorFormat, // VkFormat format
862 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
863 colorLoadOp, // VkAttachmentLoadOp loadOp
864 VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp
865 VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp
866 VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp
867 (colorLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD) ?
868 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL :
869 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout
870 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout
871 };
872
873 std::vector<VkAttachmentDescription> attachmentDescriptions;
874 std::vector<VkAttachmentReference> colorAttachmentRefs;
875
876
877 for (deUint32 i = 0; i < param.colorAttachmentsCount; i++)
878 {
879 attachmentDescriptions.push_back(colorAttachmentDescription);
880 const VkAttachmentReference colorAttachmentRef =
881 {
882 i, // deUint32 attachment
883 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout layout
884 };
885
886 colorAttachmentRefs.push_back(colorAttachmentRef);
887 }
888
889 const VkSubpassDescription subpassDescription =
890 {
891 (VkSubpassDescriptionFlags)0, // VkSubpassDescriptionFlags flags
892 VK_PIPELINE_BIND_POINT_GRAPHICS, // VkPipelineBindPoint pipelineBindPoint
893 0u, // deUint32 inputAttachmentCount
894 DE_NULL, // const VkAttachmentReference* pInputAttachments
895 param.colorAttachmentsCount, // deUint32 colorAttachmentCount
896 colorAttachmentRefs.data(), // const VkAttachmentReference* pColorAttachments
897 DE_NULL, // const VkAttachmentReference* pResolveAttachments
898 DE_NULL, // const VkAttachmentReference* pDepthStencilAttachment
899 0u, // deUint32 preserveAttachmentCount
900 DE_NULL // const deUint32* pPreserveAttachments
901 };
902
903 const VkRenderPassCreateInfo renderPassInfo =
904 {
905 VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType
906 DE_NULL, // const void* pNext
907 (VkRenderPassCreateFlags)0, // VkRenderPassCreateFlags flags
908 (deUint32)attachmentDescriptions.size(), // deUint32 attachmentCount
909 attachmentDescriptions.data(), // const VkAttachmentDescription* pAttachments
910 1u, // deUint32 subpassCount
911 &subpassDescription, // const VkSubpassDescription* pSubpasses
912 0u, // deUint32 dependencyCount
913 DE_NULL // const VkSubpassDependency* pDependencies
914 };
915
916 return createRenderPass(vk, device, &renderPassInfo, DE_NULL);
917 }
918
createBufferAndBindMemory(Context& context, VkDeviceSize size, VkBufferUsageFlags usage, de::MovePtr<Allocation>* pAlloc)919 Move<VkBuffer> createBufferAndBindMemory (Context& context, VkDeviceSize size, VkBufferUsageFlags usage, de::MovePtr<Allocation>* pAlloc)
920 {
921 const DeviceInterface& vk = context.getDeviceInterface();
922 const VkDevice vkDevice = context.getDevice();
923 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
924
925 const VkBufferCreateInfo vertexBufferParams =
926 {
927 VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
928 DE_NULL, // const void* pNext;
929 0u, // VkBufferCreateFlags flags;
930 size, // VkDeviceSize size;
931 usage, // VkBufferUsageFlags usage;
932 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
933 1u, // deUint32 queueFamilyCount;
934 &queueFamilyIndex // const deUint32* pQueueFamilyIndices;
935 };
936
937 Move<VkBuffer> vertexBuffer = createBuffer(vk, vkDevice, &vertexBufferParams);
938
939 *pAlloc = context.getDefaultAllocator().allocate(getBufferMemoryRequirements(vk, vkDevice, *vertexBuffer), MemoryRequirement::HostVisible);
940 VK_CHECK(vk.bindBufferMemory(vkDevice, *vertexBuffer, (*pAlloc)->getMemory(), (*pAlloc)->getOffset()));
941
942 return vertexBuffer;
943 }
944
createImage2DAndBindMemory(Context& context, VkFormat format, deUint32 width, deUint32 height, VkImageUsageFlags usage, VkSampleCountFlagBits sampleCount, de::details::MovePtr<Allocation>* pAlloc)945 Move<VkImage> createImage2DAndBindMemory (Context& context,
946 VkFormat format,
947 deUint32 width,
948 deUint32 height,
949 VkImageUsageFlags usage,
950 VkSampleCountFlagBits sampleCount,
951 de::details::MovePtr<Allocation>* pAlloc)
952 {
953 const DeviceInterface& vk = context.getDeviceInterface();
954 const VkDevice vkDevice = context.getDevice();
955 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
956
957 const VkImageCreateInfo colorImageParams =
958 {
959 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
960 DE_NULL, // const void* pNext;
961 0u, // VkImageCreateFlags flags;
962 VK_IMAGE_TYPE_2D, // VkImageType imageType;
963 format, // VkFormat format;
964 { width, height, 1u }, // VkExtent3D extent;
965 1u, // deUint32 mipLevels;
966 1u, // deUint32 arraySize;
967 sampleCount, // deUint32 samples;
968 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
969 usage, // VkImageUsageFlags usage;
970 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
971 1u, // deUint32 queueFamilyCount;
972 &queueFamilyIndex, // const deUint32* pQueueFamilyIndices;
973 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
974 };
975
976 Move<VkImage> image = createImage(vk, vkDevice, &colorImageParams);
977
978 *pAlloc = context.getDefaultAllocator().allocate(getImageMemoryRequirements(vk, vkDevice, *image), MemoryRequirement::Any);
979 VK_CHECK(vk.bindImageMemory(vkDevice, *image, (*pAlloc)->getMemory(), (*pAlloc)->getOffset()));
980
981 return image;
982 }
983
984 // Test Classes
985 class BlendOperationAdvancedTestInstance : public vkt::TestInstance
986 {
987 public:
988 BlendOperationAdvancedTestInstance (Context& context,
989 const BlendOperationAdvancedParam param);
990 virtual ~BlendOperationAdvancedTestInstance (void);
991 virtual tcu::TestStatus iterate (void);
992 protected:
993 void prepareRenderPass (VkFramebuffer framebuffer, VkPipeline pipeline) const;
994 void prepareCommandBuffer (void) const;
995 void buildPipeline (VkBool32 premultiplySrc, VkBool32 premultiplyDst);
996 deBool verifyTestResult (void);
997 protected:
998 const BlendOperationAdvancedParam m_param;
999 const tcu::UVec2 m_renderSize;
1000 const VkFormat m_colorFormat;
1001 Move<VkPipelineLayout> m_pipelineLayout;
1002
1003 Move<VkBuffer> m_vertexBuffer;
1004 de::MovePtr<Allocation> m_vertexBufferMemory;
1005 std::vector<Vec4> m_vertices;
1006
1007 Move<VkRenderPass> m_renderPass;
1008 Move<VkCommandPool> m_cmdPool;
1009 Move<VkCommandBuffer> m_cmdBuffer;
1010 std::vector<Move<VkImage>> m_colorImages;
1011 std::vector<Move<VkImageView>> m_colorAttachmentViews;
1012 std::vector<de::MovePtr<Allocation>> m_colorImageAllocs;
1013 std::vector<VkImageMemoryBarrier> m_imageLayoutBarriers;
1014 Move<VkFramebuffer> m_framebuffer;
1015 GraphicsPipelineWrapper m_pipeline;
1016
1017 Move<VkShaderModule> m_shaderModules[2];
1018 };
1019
buildPipeline(VkBool32 srcPremultiplied, VkBool32 dstPremultiplied)1020 void BlendOperationAdvancedTestInstance::buildPipeline (VkBool32 srcPremultiplied,
1021 VkBool32 dstPremultiplied)
1022 {
1023 const DeviceInterface& vk = m_context.getDeviceInterface();
1024 const VkDevice vkDevice = m_context.getDevice();
1025
1026 const std::vector<VkRect2D> scissor { makeRect2D(m_renderSize) };
1027 const std::vector<VkViewport> viewport { makeViewport(m_renderSize) };
1028
1029 const VkPipelineColorBlendAdvancedStateCreateInfoEXT blendAdvancedStateParams =
1030 {
1031 VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT, // VkStructureType sType;
1032 DE_NULL, // const void* pNext;
1033 srcPremultiplied, // VkBool32 srcPremultiplied;
1034 dstPremultiplied, // VkBool32 dstPremultiplied;
1035 m_param.overlap, // VkBlendOverlapEXT blendOverlap;
1036 };
1037
1038 std::vector<VkPipelineColorBlendAttachmentState> colorBlendAttachmentStates;
1039
1040 for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
1041 {
1042 const VkPipelineColorBlendAttachmentState colorBlendAttachmentState =
1043 {
1044 VK_TRUE, // VkBool32 blendEnable;
1045 VK_BLEND_FACTOR_ONE, // VkBlendFactor srcColorBlendFactor;
1046 VK_BLEND_FACTOR_ONE, // VkBlendFactor dstColorBlendFactor;
1047 m_param.blendOps[i], // VkBlendOp colorBlendOp;
1048 VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor;
1049 VK_BLEND_FACTOR_ONE, // VkBlendFactor dstAlphaBlendFactor;
1050 m_param.blendOps[i], // VkBlendOp alphaBlendOp;
1051 VK_COLOR_COMPONENT_R_BIT |
1052 VK_COLOR_COMPONENT_G_BIT |
1053 VK_COLOR_COMPONENT_B_BIT |
1054 VK_COLOR_COMPONENT_A_BIT // VkColorComponentFlags colorWriteMask;
1055 };
1056 colorBlendAttachmentStates.emplace_back(colorBlendAttachmentState);
1057 }
1058
1059 const VkPipelineColorBlendStateCreateInfo colorBlendStateParams =
1060 {
1061 VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
1062 &blendAdvancedStateParams, // const void* pNext;
1063 0u, // VkPipelineColorBlendStateCreateFlags flags;
1064 VK_FALSE, // VkBool32 logicOpEnable;
1065 VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
1066 (deUint32)colorBlendAttachmentStates.size(), // deUint32 attachmentCount;
1067 colorBlendAttachmentStates.data(), // const VkPipelineColorBlendAttachmentState* pAttachments;
1068 { 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConst[4];
1069 };
1070
1071 const VkPipelineMultisampleStateCreateInfo multisampleStateParams =
1072 {
1073 VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
1074 DE_NULL, // const void* pNext;
1075 0u, // VkPipelineMultisampleStateCreateFlags flags;
1076 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples;
1077 VK_FALSE, // VkBool32 sampleShadingEnable;
1078 0.0f, // float minSampleShading;
1079 DE_NULL, // const VkSampleMask* pSampleMask;
1080 VK_FALSE, // VkBool32 alphaToCoverageEnable;
1081 VK_FALSE, // VkBool32 alphaToOneEnable;
1082 };
1083
1084 VkPipelineDepthStencilStateCreateInfo depthStencilStateParams =
1085 {
1086 VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
1087 DE_NULL, // const void* pNext;
1088 0u, // VkPipelineDepthStencilStateCreateFlags flags;
1089 VK_FALSE, // VkBool32 depthTestEnable;
1090 VK_FALSE, // VkBool32 depthWriteEnable;
1091 VK_COMPARE_OP_NEVER, // VkCompareOp depthCompareOp;
1092 VK_FALSE, // VkBool32 depthBoundsTestEnable;
1093 VK_FALSE, // VkBool32 stencilTestEnable;
1094 // VkStencilOpState front;
1095 {
1096 VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
1097 VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
1098 VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
1099 VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
1100 0u, // deUint32 compareMask;
1101 0u, // deUint32 writeMask;
1102 0u, // deUint32 reference;
1103 },
1104 // VkStencilOpState back;
1105 {
1106 VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
1107 VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
1108 VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
1109 VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
1110 0u, // deUint32 compareMask;
1111 0u, // deUint32 writeMask;
1112 0u, // deUint32 reference;
1113 },
1114 0.0f, // float minDepthBounds;
1115 1.0f, // float maxDepthBounds;
1116 };
1117
1118 const VkDynamicState dynamicState = VK_DYNAMIC_STATE_SCISSOR;
1119 const VkPipelineDynamicStateCreateInfo dynamicStateParams =
1120 {
1121 VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
1122 DE_NULL, // const void* pNext;
1123 0u, // VkPipelineDynamicStateCreateFlags flags;
1124 1u, // uint32_t dynamicStateCount;
1125 &dynamicState // const VkDynamicState* pDynamicStates;
1126 };
1127
1128 m_shaderModules[0] = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("vert"), 0);
1129 m_shaderModules[1] = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("frag"), 0);
1130
1131 m_pipeline.setDynamicState(&dynamicStateParams)
1132 .setDefaultRasterizationState()
1133 .setupVertexInputState()
1134 .setupPreRasterizationShaderState(viewport, scissor, *m_pipelineLayout, *m_renderPass, 0u, m_shaderModules[0].get())
1135 .setupFragmentShaderState(*m_pipelineLayout, *m_renderPass, 0u, m_shaderModules[1].get(), &depthStencilStateParams, &multisampleStateParams)
1136 .setupFragmentOutputState(*m_renderPass, 0u, &colorBlendStateParams, &multisampleStateParams)
1137 .setMonolithicPipelineLayout(*m_pipelineLayout)
1138 .buildPipeline();
1139 }
1140
prepareRenderPass(VkFramebuffer framebuffer, VkPipeline pipeline) const1141 void BlendOperationAdvancedTestInstance::prepareRenderPass (VkFramebuffer framebuffer, VkPipeline pipeline) const
1142 {
1143 const DeviceInterface& vk = m_context.getDeviceInterface();
1144
1145 std::vector<VkClearValue> attachmentClearValues;
1146
1147 for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
1148 attachmentClearValues.emplace_back(makeClearValueColor(clearColorVec4));
1149
1150 beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, framebuffer, makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()),
1151 m_param.colorAttachmentsCount, attachmentClearValues.data());
1152 vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
1153 VkDeviceSize offsets = 0u;
1154 vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &offsets);
1155
1156 // Draw all colors
1157 deUint32 skippedColors = 0u;
1158 for (deUint32 color = 0; color < DE_LENGTH_OF_ARRAY(srcColors); color++)
1159 {
1160 // Skip ill-formed colors when we have non-premultiplied destination colors.
1161 if (m_param.premultipliedDstColor == VK_FALSE)
1162 {
1163 deBool skipColor = false;
1164 for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
1165 {
1166 Vec4 calculatedColor = calculateFinalColor(m_param, m_param.blendOps[i], srcColors[color], dstColors[color]);
1167 if (calculatedColor.w() <= 0.0f && calculatedColor != Vec4(0.0f))
1168 {
1169 // Skip ill-formed colors, because the spec says the result is undefined.
1170 skippedColors++;
1171 skipColor = true;
1172 break;
1173 }
1174 }
1175 if (skipColor)
1176 continue;
1177 }
1178
1179 deInt32 x = 0;
1180 deInt32 y = 0;
1181 getCoordinates(color, x, y);
1182
1183 // Set source color as push constant
1184 vk.cmdPushConstants(*m_cmdBuffer, *m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0u, sizeof(Vec4), &srcColors[color]);
1185
1186 VkRect2D scissor = makeRect2D(x, y, 1u, 1u);
1187 vk.cmdSetScissor(*m_cmdBuffer, 0u, 1u, &scissor);
1188
1189 // To set destination color, we do clear attachment restricting the area to the respective pixel of each color attachment.
1190 {
1191 // Set destination color as push constant.
1192 std::vector<VkClearAttachment> attachments;
1193 VkClearValue clearValue = vk::makeClearValueColorVec4(dstColors[color]);
1194
1195 for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
1196 {
1197 VkClearAttachment attachment =
1198 {
1199 VK_IMAGE_ASPECT_COLOR_BIT,
1200 i,
1201 clearValue
1202 };
1203 attachments.emplace_back(attachment);
1204 }
1205
1206 const VkClearRect rect =
1207 {
1208 scissor,
1209 0u,
1210 1u
1211 };
1212 vk.cmdClearAttachments(*m_cmdBuffer, (deUint32)attachments.size(), attachments.data(), 1u, &rect);
1213 }
1214
1215 // Draw
1216 vk.cmdDraw(*m_cmdBuffer, (deUint32)m_vertices.size(), 1u, 0u, 0u);
1217 }
1218
1219 // If we break this assert, then we are not testing anything in this test.
1220 DE_ASSERT(skippedColors < DE_LENGTH_OF_ARRAY(srcColors));
1221
1222 // Log number of skipped colors
1223 if (skippedColors != 0u)
1224 {
1225 tcu::TestLog& log = m_context.getTestContext().getLog();
1226 log << tcu::TestLog::Message << "Skipped " << skippedColors << " out of " << DE_LENGTH_OF_ARRAY(srcColors) << " color cases due to ill-formed colors" << tcu::TestLog::EndMessage;
1227 }
1228 endRenderPass(vk, *m_cmdBuffer);
1229 }
1230
1231 void BlendOperationAdvancedTestInstance::prepareCommandBuffer () const
1232 {
1233 const DeviceInterface& vk = m_context.getDeviceInterface();
1234
1235
1236 beginCommandBuffer(vk, *m_cmdBuffer, 0u);
1237
1238 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,
1239 0u, DE_NULL, 0u, DE_NULL, (deUint32)m_imageLayoutBarriers.size(), m_imageLayoutBarriers.data());
1240
1241 prepareRenderPass(*m_framebuffer, m_pipeline.getPipeline());
1242
1243 endCommandBuffer(vk, *m_cmdBuffer);
1244 }
1245
1246 BlendOperationAdvancedTestInstance::BlendOperationAdvancedTestInstance (Context& context,
1247 const BlendOperationAdvancedParam param)
1248 : TestInstance (context)
1249 , m_param (param)
1250 , m_renderSize (tcu::UVec2(widthArea, heightArea))
1251 , m_colorFormat (param.format)
1252 , m_pipeline (m_context.getDeviceInterface(), m_context.getDevice(), param.pipelineConstructionType)
1253 {
1254 const DeviceInterface& vk = m_context.getDeviceInterface();
1255 const VkDevice vkDevice = m_context.getDevice();
1256 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
1257
1258 // Create vertex buffer and upload data
1259 {
1260 // Load vertices into vertex buffer
1261 m_vertices = createPoints();
1262 DE_ASSERT((deUint32)m_vertices.size() == 6);
1263
1264 m_vertexBuffer = createBufferAndBindMemory(m_context, m_vertices.size() * sizeof(Vec4), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, &m_vertexBufferMemory);
1265 deMemcpy(m_vertexBufferMemory->getHostPtr(), m_vertices.data(), m_vertices.size() * sizeof(Vec4));
1266 flushAlloc(vk, vkDevice, *m_vertexBufferMemory);
1267 }
1268
1269 // Create render pass
1270 m_renderPass = makeTestRenderPass(param, vk, vkDevice, m_colorFormat);
1271
1272 const VkComponentMapping componentMappingRGBA = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
1273
1274 // Create color images
1275 for (deUint32 i = 0; i < param.colorAttachmentsCount; i++)
1276 {
1277 de::MovePtr<Allocation> colorImageAlloc;
1278 m_colorImageAllocs.emplace_back(colorImageAlloc);
1279
1280 Move<VkImage> colorImage = createImage2DAndBindMemory(m_context,
1281 m_colorFormat,
1282 m_renderSize.x(),
1283 m_renderSize.y(),
1284 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
1285 VK_SAMPLE_COUNT_1_BIT,
1286 &m_colorImageAllocs.back());
1287 m_colorImages.emplace_back(colorImage);
1288
1289 // Set up image layout transition barriers
1290 {
1291 VkImageMemoryBarrier colorImageBarrier =
1292 {
1293 VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
1294 DE_NULL, // const void* pNext;
1295 0u, // VkAccessFlags srcAccessMask;
1296 (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
1297 VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT), // VkAccessFlags dstAccessMask;
1298 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
1299 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout;
1300 VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
1301 VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex;
1302 *m_colorImages.back(), // VkImage image;
1303 { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }, // VkImageSubresourceRange subresourceRange;
1304 };
1305
1306 m_imageLayoutBarriers.emplace_back(colorImageBarrier);
1307 }
1308
1309 // Create color attachment view
1310 {
1311 VkImageViewCreateInfo colorAttachmentViewParams =
1312 {
1313 VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
1314 DE_NULL, // const void* pNext;
1315 0u, // VkImageViewCreateFlags flags;
1316 *m_colorImages.back(), // VkImage image;
1317 VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
1318 m_colorFormat, // VkFormat format;
1319 componentMappingRGBA, // VkComponentMapping components;
1320 { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }, // VkImageSubresourceRange subresourceRange;
1321 };
1322
1323 m_colorAttachmentViews.emplace_back(createImageView(vk, vkDevice, &colorAttachmentViewParams));
1324 }
1325 }
1326
1327 // Create framebuffer
1328 {
1329 std::vector<VkImageView> imageViews;
1330
1331 for (auto& movePtr : m_colorAttachmentViews)
1332 imageViews.push_back(movePtr.get());
1333
1334 const VkFramebufferCreateInfo framebufferParams =
1335 {
1336 VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
1337 DE_NULL, // const void* pNext;
1338 0u, // VkFramebufferCreateFlags flags;
1339 *m_renderPass, // VkRenderPass renderPass;
1340 (deUint32)imageViews.size(), // deUint32 attachmentCount;
1341 imageViews.data(), // const VkImageView* pAttachments;
1342 (deUint32)m_renderSize.x(), // deUint32 width;
1343 (deUint32)m_renderSize.y(), // deUint32 height;
1344 1u, // deUint32 layers;
1345 };
1346
1347 m_framebuffer = createFramebuffer(vk, vkDevice, &framebufferParams);
1348 }
1349
1350
1351 // Create pipeline layout
1352 {
1353 const VkPushConstantRange pushConstantRange =
1354 {
1355 VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlags stageFlags
1356 0, // deUint32 offset
1357 sizeof(Vec4) // deUint32 size
1358 };
1359
1360 const VkPipelineLayoutCreateInfo pipelineLayoutParams =
1361 {
1362 VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
1363 DE_NULL, // const void* pNext;
1364 0u, // VkPipelineLayoutCreateFlags flags;
1365 0u, // deUint32 setLayoutCount;
1366 DE_NULL, // const VkDescriptorSetLayout* pSetLayouts;
1367 1u, // deUint32 pushConstantRangeCount;
1368 &pushConstantRange // const VkPushConstantRange* pPushConstantRanges;
1369 };
1370
1371 m_pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
1372 }
1373
1374 // Create pipeline
1375 buildPipeline(m_param.premultipliedSrcColor, m_param.premultipliedDstColor);
1376
1377 // Create command pool
1378 m_cmdPool = createCommandPool(vk, vkDevice, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
1379
1380 // Create command buffer
1381 m_cmdBuffer = allocateCommandBuffer(vk, vkDevice, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
1382 }
1383
1384 BlendOperationAdvancedTestInstance::~BlendOperationAdvancedTestInstance (void)
1385 {
1386 }
1387
1388 tcu::TestStatus BlendOperationAdvancedTestInstance::iterate (void)
1389 {
1390 const DeviceInterface& vk = m_context.getDeviceInterface();
1391 const VkDevice vkDevice = m_context.getDevice();
1392 const VkQueue queue = m_context.getUniversalQueue();
1393 tcu::TestLog& log = m_context.getTestContext().getLog();
1394
1395 // Log the blend operations to test
1396 {
1397 if (m_param.independentBlend)
1398 {
1399 for (deUint32 i = 0; (i < m_param.colorAttachmentsCount); i++)
1400 log << tcu::TestLog::Message << "Color attachment " << i << " uses depth op: "<< de::toLower(getBlendOpStr(m_param.blendOps[i]).toString().substr(3)) << tcu::TestLog::EndMessage;
1401
1402 }
1403 else
1404 {
1405 log << tcu::TestLog::Message << "All color attachments use depth op: " << de::toLower(getBlendOpStr(m_param.blendOps[0]).toString().substr(3)) << tcu::TestLog::EndMessage;
1406
1407 }
1408 }
1409 prepareCommandBuffer();
1410 submitCommandsAndWait(vk, vkDevice, queue, m_cmdBuffer.get());
1411
1412 if (verifyTestResult() == DE_FALSE)
1413 return tcu::TestStatus::fail("Image mismatch");
1414
1415 return tcu::TestStatus::pass("Result images matches references");
1416 }
1417
1418 deBool BlendOperationAdvancedTestInstance::verifyTestResult ()
1419 {
1420 deBool compareOk = DE_TRUE;
1421 const DeviceInterface& vk = m_context.getDeviceInterface();
1422 const VkDevice vkDevice = m_context.getDevice();
1423 const VkQueue queue = m_context.getUniversalQueue();
1424 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
1425 Allocator& allocator = m_context.getDefaultAllocator();
1426 std::vector<tcu::TextureLevel> referenceImages;
1427
1428 for (deUint32 colorAtt = 0; colorAtt < m_param.colorAttachmentsCount; colorAtt++)
1429 {
1430 tcu::TextureLevel refImage (vk::mapVkFormat(m_colorFormat), 32, 32);
1431 tcu::clear(refImage.getAccess(), clearColorVec4);
1432 referenceImages.emplace_back(refImage);
1433 }
1434
1435 for (deUint32 color = 0; color < DE_LENGTH_OF_ARRAY(srcColors); color++)
1436 {
1437 deBool skipColor = DE_FALSE;
1438
1439 // Check if any color attachment will generate an ill-formed color. If that's the case, skip that color in the verification.
1440 for (deUint32 colorAtt = 0; colorAtt < m_param.colorAttachmentsCount; colorAtt++)
1441 {
1442 Vec4 rectColor = calculateFinalColor(m_param, m_param.blendOps[colorAtt], srcColors[color], dstColors[color]);
1443
1444 if (m_param.premultipliedDstColor == VK_FALSE)
1445 {
1446 if (rectColor.w() > 0.0f)
1447 {
1448 rectColor.x() = rectColor.x() / rectColor.w();
1449 rectColor.y() = rectColor.y() / rectColor.w();
1450 rectColor.z() = rectColor.z() / rectColor.w();
1451 }
1452 else
1453 {
1454 // Skip the color check if it is ill-formed.
1455 if (rectColor != Vec4(0.0f))
1456 {
1457 skipColor = DE_TRUE;
1458 break;
1459 }
1460 }
1461 }
1462
1463 // If pixel value is not normal (inf, nan, denorm), skip it
1464 if (!std::isnormal(rectColor.x()) ||
1465 !std::isnormal(rectColor.y()) ||
1466 !std::isnormal(rectColor.z()) ||
1467 !std::isnormal(rectColor.w()))
1468 skipColor = DE_TRUE;
1469 }
1470
1471 // Skip ill-formed colors that appears in any color attachment.
1472 if (skipColor)
1473 continue;
1474
1475 // If we reach this point, the final color for all color attachment is not ill-formed.
1476 for (deUint32 colorAtt = 0; colorAtt < m_param.colorAttachmentsCount; colorAtt++)
1477 {
1478 Vec4 rectColor = calculateFinalColor(m_param, m_param.blendOps[colorAtt], srcColors[color], dstColors[color]);
1479 if (m_param.premultipliedDstColor == VK_FALSE)
1480 {
1481 if (rectColor.w() > 0.0f)
1482 {
1483 rectColor.x() = rectColor.x() / rectColor.w();
1484 rectColor.y() = rectColor.y() / rectColor.w();
1485 rectColor.z() = rectColor.z() / rectColor.w();
1486 }
1487 else
1488 {
1489 // Ill-formed colors were already skipped
1490 DE_ASSERT(rectColor == Vec4(0.0f));
1491 }
1492 }
1493 deInt32 x = 0;
1494 deInt32 y = 0;
1495 getCoordinates(color, x, y);
1496 tcu::clear(tcu::getSubregion(referenceImages[colorAtt].getAccess(), x, y, 1u, 1u), rectColor);
1497 }
1498 }
1499
1500 for (deUint32 colorAtt = 0; colorAtt < m_param.colorAttachmentsCount; colorAtt++)
1501 {
1502 // Compare image
1503 de::MovePtr<tcu::TextureLevel> result = vkt::pipeline::readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, *m_colorImages[colorAtt], m_colorFormat, m_renderSize);
1504 std::ostringstream name;
1505 name << "Image comparison. Color attachment: " << colorAtt << ". Depth op: " << de::toLower(getBlendOpStr(m_param.blendOps[colorAtt]).toString().substr(3));
1506
1507 // R8G8B8A8 threshold was derived experimentally.
1508 compareOk = tcu::floatThresholdCompare(m_context.getTestContext().getLog(),
1509 "FloatImageCompare",
1510 name.str().c_str(),
1511 referenceImages[colorAtt].getAccess(),
1512 result->getAccess(),
1513 clearColorVec4,
1514 m_colorFormat == VK_FORMAT_R8G8B8A8_UNORM ? Vec4(0.15f, 0.15f, 0.15f, 0.13f) : Vec4(0.01f, 0.01f, 0.01f, 0.01f),
1515 tcu::COMPARE_LOG_RESULT);
1516 #ifdef CTS_USES_VULKANSC
1517 if (m_context.getTestContext().getCommandLine().isSubProcess())
1518 #endif // CTS_USES_VULKANSC
1519 {
1520 if (!compareOk)
1521 return DE_FALSE;
1522 }
1523 }
1524 return DE_TRUE;
1525 }
1526
1527 class BlendOperationAdvancedTest : public vkt::TestCase
1528 {
1529 public:
1530 BlendOperationAdvancedTest (tcu::TestContext& testContext,
1531 const std::string& name,
1532 const std::string& description,
1533 const BlendOperationAdvancedParam param)
1534 : vkt::TestCase (testContext, name, description)
1535 , m_param (param)
1536 { }
1537 virtual ~BlendOperationAdvancedTest (void) { }
1538 virtual void initPrograms (SourceCollections& programCollection) const;
1539 virtual TestInstance* createInstance (Context& context) const;
1540 virtual void checkSupport (Context& context) const;
1541
1542 protected:
1543 const BlendOperationAdvancedParam m_param;
1544 };
1545
1546 void BlendOperationAdvancedTest::checkSupport(Context& context) const
1547 {
1548 const InstanceInterface& vki = context.getInstanceInterface();
1549
1550 context.requireDeviceFunctionality("VK_EXT_blend_operation_advanced");
1551
1552 VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT blendProperties;
1553 blendProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT;
1554 blendProperties.pNext = DE_NULL;
1555
1556 VkPhysicalDeviceProperties2 properties2;
1557 properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
1558 properties2.pNext = &blendProperties;
1559 vki.getPhysicalDeviceProperties2(context.getPhysicalDevice(), &properties2);
1560
1561 if (!blendProperties.advancedBlendAllOperations)
1562 {
1563 for (deUint32 index = 0u; index < m_param.blendOps.size(); index++)
1564 {
1565 switch (m_param.blendOps[index])
1566 {
1567 case VK_BLEND_OP_MULTIPLY_EXT:
1568 case VK_BLEND_OP_SCREEN_EXT:
1569 case VK_BLEND_OP_OVERLAY_EXT:
1570 case VK_BLEND_OP_DARKEN_EXT:
1571 case VK_BLEND_OP_LIGHTEN_EXT:
1572 case VK_BLEND_OP_COLORDODGE_EXT:
1573 case VK_BLEND_OP_COLORBURN_EXT:
1574 case VK_BLEND_OP_HARDLIGHT_EXT:
1575 case VK_BLEND_OP_SOFTLIGHT_EXT:
1576 case VK_BLEND_OP_DIFFERENCE_EXT:
1577 case VK_BLEND_OP_EXCLUSION_EXT:
1578 case VK_BLEND_OP_HSL_HUE_EXT:
1579 case VK_BLEND_OP_HSL_SATURATION_EXT:
1580 case VK_BLEND_OP_HSL_COLOR_EXT:
1581 case VK_BLEND_OP_HSL_LUMINOSITY_EXT:
1582 break;
1583 default:
1584 throw tcu::NotSupportedError("Unsupported all advanced blend operations and unsupported advanced blend operation");
1585 }
1586 }
1587 }
1588
1589 if (m_param.colorAttachmentsCount > blendProperties.advancedBlendMaxColorAttachments)
1590 {
1591 std::ostringstream error;
1592 error << "Unsupported number of color attachments (" << blendProperties.advancedBlendMaxColorAttachments << " < " << m_param.colorAttachmentsCount;
1593 throw tcu::NotSupportedError(error.str().c_str());
1594 }
1595
1596 if (m_param.overlap != VK_BLEND_OVERLAP_UNCORRELATED_EXT && !blendProperties.advancedBlendCorrelatedOverlap)
1597 {
1598 throw tcu::NotSupportedError("Unsupported blend correlated overlap");
1599 }
1600
1601 if (m_param.colorAttachmentsCount > 1 && m_param.independentBlend && !blendProperties.advancedBlendIndependentBlend)
1602 {
1603 throw tcu::NotSupportedError("Unsupported independent blend");
1604 }
1605
1606 if (!m_param.premultipliedSrcColor && !blendProperties.advancedBlendNonPremultipliedSrcColor)
1607 {
1608 throw tcu::NotSupportedError("Unsupported non-premultiplied source color");
1609 }
1610
1611 if (!m_param.premultipliedDstColor && !blendProperties.advancedBlendNonPremultipliedDstColor)
1612 {
1613 throw tcu::NotSupportedError("Unsupported non-premultiplied destination color");
1614 }
1615
1616 const VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT blendFeatures = context.getBlendOperationAdvancedFeaturesEXT();
1617 if (m_param.coherentOperations && !blendFeatures.advancedBlendCoherentOperations)
1618 {
1619 throw tcu::NotSupportedError("Unsupported required coherent operations");
1620 }
1621 checkPipelineLibraryRequirements(context.getInstanceInterface(), context.getPhysicalDevice(), m_param.pipelineConstructionType);
1622 }
1623
1624 void BlendOperationAdvancedTest::initPrograms (SourceCollections& programCollection) const
1625 {
1626 programCollection.glslSources.add("vert") << glu::VertexSource(
1627 "#version 310 es\n"
1628 "layout(location = 0) in vec4 position;\n"
1629 "void main (void)\n"
1630 "{\n"
1631 " gl_Position = position;\n"
1632 "}\n");
1633
1634 std::ostringstream fragmentSource;
1635 fragmentSource << "#version 310 es\n";
1636 fragmentSource << "layout(push_constant) uniform Color { highp vec4 color; };\n";
1637 for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
1638 fragmentSource << "layout(location = "<< i <<") out highp vec4 fragColor" << i <<";\n";
1639 fragmentSource << "void main (void)\n";
1640 fragmentSource << "{\n";
1641 for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
1642 fragmentSource << " fragColor" << i <<" = color;\n";
1643 fragmentSource << "}\n";
1644 programCollection.glslSources.add("frag") << glu::FragmentSource(fragmentSource.str().c_str());
1645 }
1646
1647 class BlendOperationAdvancedTestCoherentInstance : public vkt::TestInstance
1648 {
1649 public:
1650 BlendOperationAdvancedTestCoherentInstance (Context& context,
1651 const BlendOperationAdvancedParam param);
1652 virtual ~BlendOperationAdvancedTestCoherentInstance (void);
1653 virtual tcu::TestStatus iterate (void);
1654 protected:
1655 void prepareRenderPass (VkFramebuffer framebuffer, VkPipeline pipeline,
1656 VkRenderPass renderpass, deBool secondDraw);
1657 virtual void prepareCommandBuffer (void);
1658 virtual void buildPipeline (void);
1659 virtual tcu::TestStatus verifyTestResult (void);
1660
1661 protected:
1662 const BlendOperationAdvancedParam m_param;
1663 const tcu::UVec2 m_renderSize;
1664 const VkFormat m_colorFormat;
1665 Move<VkPipelineLayout> m_pipelineLayout;
1666
1667 Move<VkBuffer> m_vertexBuffer;
1668 de::MovePtr<Allocation> m_vertexBufferMemory;
1669 std::vector<Vec4> m_vertices;
1670
1671 std::vector<Move<VkRenderPass>> m_renderPasses;
1672 Move<VkCommandPool> m_cmdPool;
1673 Move<VkCommandBuffer> m_cmdBuffer;
1674 Move<VkImage> m_colorImage;
1675 Move<VkImageView> m_colorAttachmentView;
1676 de::MovePtr<Allocation> m_colorImageAlloc;
1677 std::vector<VkImageMemoryBarrier> m_imageLayoutBarriers;
1678 std::vector<Move<VkFramebuffer>> m_framebuffers;
1679 std::vector<GraphicsPipelineWrapper> m_pipelines;
1680
1681 Move<VkShaderModule> m_shaderModules[2];
1682 deUint32 m_shaderStageCount;
1683 VkPipelineShaderStageCreateInfo m_shaderStageInfo[2];
1684 };
1685
1686 BlendOperationAdvancedTestCoherentInstance::~BlendOperationAdvancedTestCoherentInstance (void)
1687 {
1688 }
1689
1690 void BlendOperationAdvancedTestCoherentInstance::buildPipeline ()
1691 {
1692 const DeviceInterface& vk = m_context.getDeviceInterface();
1693 const VkDevice vkDevice = m_context.getDevice();
1694
1695 const std::vector<VkRect2D> scissor { makeRect2D(m_renderSize) };
1696 const std::vector<VkViewport> viewport { makeViewport(m_renderSize) };
1697
1698 const VkPipelineColorBlendAdvancedStateCreateInfoEXT blendAdvancedStateParams =
1699 {
1700 VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT, // VkStructureType sType;
1701 DE_NULL, // const void* pNext;
1702 VK_TRUE, // VkBool32 srcPremultiplied;
1703 VK_TRUE, // VkBool32 dstPremultiplied;
1704 m_param.overlap, // VkBlendOverlapEXT blendOverlap;
1705 };
1706
1707 std::vector<VkPipelineColorBlendAttachmentState> colorBlendAttachmentStates;
1708
1709 // One VkPipelineColorBlendAttachmentState for each pipeline, we only have one color attachment.
1710 for (deUint32 i = 0; i < 2; i++)
1711 {
1712 const VkPipelineColorBlendAttachmentState colorBlendAttachmentState =
1713 {
1714 VK_TRUE, // VkBool32 blendEnable;
1715 VK_BLEND_FACTOR_ONE, // VkBlendFactor srcColorBlendFactor;
1716 VK_BLEND_FACTOR_ONE, // VkBlendFactor dstColorBlendFactor;
1717 m_param.blendOps[i], // VkBlendOp colorBlendOp;
1718 VK_BLEND_FACTOR_ONE, // VkBlendFactor srcAlphaBlendFactor;
1719 VK_BLEND_FACTOR_ONE, // VkBlendFactor dstAlphaBlendFactor;
1720 m_param.blendOps[i], // VkBlendOp alphaBlendOp;
1721 VK_COLOR_COMPONENT_R_BIT |
1722 VK_COLOR_COMPONENT_G_BIT |
1723 VK_COLOR_COMPONENT_B_BIT |
1724 VK_COLOR_COMPONENT_A_BIT // VkColorComponentFlags colorWriteMask;
1725 };
1726 colorBlendAttachmentStates.emplace_back(colorBlendAttachmentState);
1727 }
1728
1729 std::vector<VkPipelineColorBlendStateCreateInfo> colorBlendStateParams;
1730 VkPipelineColorBlendStateCreateInfo colorBlendStateParam =
1731 {
1732 VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType;
1733 &blendAdvancedStateParams, // const void* pNext;
1734 0u, // VkPipelineColorBlendStateCreateFlags flags;
1735 VK_FALSE, // VkBool32 logicOpEnable;
1736 VK_LOGIC_OP_COPY, // VkLogicOp logicOp;
1737 1u, // deUint32 attachmentCount;
1738 &colorBlendAttachmentStates[0], // const VkPipelineColorBlendAttachmentState* pAttachments;
1739 { 0.0f, 0.0f, 0.0f, 0.0f }, // float blendConst[4];
1740 };
1741 colorBlendStateParams.emplace_back(colorBlendStateParam);
1742
1743 // For the second pipeline, the blendOp changed.
1744 colorBlendStateParam.pAttachments = &colorBlendAttachmentStates[1];
1745 colorBlendStateParams.emplace_back(colorBlendStateParam);
1746
1747 const VkPipelineMultisampleStateCreateInfo multisampleStateParams =
1748 {
1749 VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, // VkStructureType sType;
1750 DE_NULL, // const void* pNext;
1751 0u, // VkPipelineMultisampleStateCreateFlags flags;
1752 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits rasterizationSamples;
1753 VK_FALSE, // VkBool32 sampleShadingEnable;
1754 0.0f, // float minSampleShading;
1755 DE_NULL, // const VkSampleMask* pSampleMask;
1756 VK_FALSE, // VkBool32 alphaToCoverageEnable;
1757 VK_FALSE, // VkBool32 alphaToOneEnable;
1758 };
1759
1760 VkPipelineDepthStencilStateCreateInfo depthStencilStateParams =
1761 {
1762 VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, // VkStructureType sType;
1763 DE_NULL, // const void* pNext;
1764 0u, // VkPipelineDepthStencilStateCreateFlags flags;
1765 VK_FALSE, // VkBool32 depthTestEnable;
1766 VK_FALSE, // VkBool32 depthWriteEnable;
1767 VK_COMPARE_OP_NEVER, // VkCompareOp depthCompareOp;
1768 VK_FALSE, // VkBool32 depthBoundsTestEnable;
1769 VK_FALSE, // VkBool32 stencilTestEnable;
1770 // VkStencilOpState front;
1771 {
1772 VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
1773 VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
1774 VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
1775 VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
1776 0u, // deUint32 compareMask;
1777 0u, // deUint32 writeMask;
1778 0u, // deUint32 reference;
1779 },
1780 // VkStencilOpState back;
1781 {
1782 VK_STENCIL_OP_KEEP, // VkStencilOp failOp;
1783 VK_STENCIL_OP_KEEP, // VkStencilOp passOp;
1784 VK_STENCIL_OP_KEEP, // VkStencilOp depthFailOp;
1785 VK_COMPARE_OP_NEVER, // VkCompareOp compareOp;
1786 0u, // deUint32 compareMask;
1787 0u, // deUint32 writeMask;
1788 0u, // deUint32 reference;
1789 },
1790 0.0f, // float minDepthBounds;
1791 1.0f, // float maxDepthBounds;
1792 };
1793
1794 const VkDynamicState dynamicState = VK_DYNAMIC_STATE_SCISSOR;
1795 const VkPipelineDynamicStateCreateInfo dynamicStateParams =
1796 {
1797 VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, // VkStructureType sType;
1798 DE_NULL, // const void* pNext;
1799 0u, // VkPipelineDynamicStateCreateFlags flags;
1800 1u, // uint32_t dynamicStateCount;
1801 &dynamicState // const VkDynamicState* pDynamicStates;
1802 };
1803
1804 m_shaderModules[0] = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("vert"), 0);
1805 m_shaderModules[1] = createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("frag"), 0);
1806
1807 m_pipelines.reserve(2);
1808
1809 // Create first pipeline
1810 m_pipelines.emplace_back(vk, vkDevice, m_param.pipelineConstructionType);
1811 m_pipelines.back()
1812 .setDynamicState(&dynamicStateParams)
1813 .setDefaultRasterizationState()
1814 .setupVertexInputState()
1815 .setupPreRasterizationShaderState(viewport, scissor, *m_pipelineLayout, m_renderPasses[0].get(), 0u, m_shaderModules[0].get())
1816 .setupFragmentShaderState(*m_pipelineLayout, m_renderPasses[0].get(), 0u, m_shaderModules[1].get(), &depthStencilStateParams, &multisampleStateParams)
1817 .setupFragmentOutputState(m_renderPasses[0].get(), 0u, &colorBlendStateParams[0], &multisampleStateParams)
1818 .setMonolithicPipelineLayout(*m_pipelineLayout)
1819 .buildPipeline();
1820
1821 // Create second pipeline
1822 m_pipelines.emplace_back(vk, vkDevice, m_param.pipelineConstructionType);
1823 m_pipelines.back()
1824 .setDynamicState(&dynamicStateParams)
1825 .setDefaultRasterizationState()
1826 .setupVertexInputState()
1827 .setupPreRasterizationShaderState(viewport, scissor, *m_pipelineLayout, m_renderPasses[1].get(), 0u, m_shaderModules[0].get())
1828 .setupFragmentShaderState(*m_pipelineLayout, m_renderPasses[1].get(), 0u, m_shaderModules[1].get(), &depthStencilStateParams, &multisampleStateParams)
1829 .setupFragmentOutputState(m_renderPasses[1].get(), 0u, &colorBlendStateParams[1], &multisampleStateParams)
1830 .setMonolithicPipelineLayout(*m_pipelineLayout)
1831 .buildPipeline();
1832 }
1833
1834 void BlendOperationAdvancedTestCoherentInstance::prepareRenderPass (VkFramebuffer framebuffer, VkPipeline pipeline, VkRenderPass renderpass, deBool secondDraw)
1835 {
1836 const DeviceInterface& vk = m_context.getDeviceInterface();
1837
1838 VkClearValue attachmentClearValue = makeClearValueColor(clearColorVec4);
1839
1840 beginRenderPass(vk, *m_cmdBuffer, renderpass, framebuffer, makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()),
1841 (secondDraw ? 0u : 1u),
1842 (secondDraw ? DE_NULL : &attachmentClearValue));
1843
1844 vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
1845 VkDeviceSize offsets = 0u;
1846 vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &offsets);
1847
1848 // There are two different renderpasses, each of them draw
1849 // one half of the colors.
1850 deBool skippedColors = 0u;
1851 for (deUint32 color = 0; color < DE_LENGTH_OF_ARRAY(srcColors)/2; color++)
1852 {
1853 // Skip ill-formed colors when we have non-premultiplied destination colors.
1854 if (m_param.premultipliedDstColor == VK_FALSE)
1855 {
1856 deBool skipColor = false;
1857 for (deUint32 i = 0; i < m_param.colorAttachmentsCount; i++)
1858 {
1859 Vec4 calculatedColor = calculateFinalColor(m_param, m_param.blendOps[i], srcColors[color], dstColors[color]);
1860 if (calculatedColor.w() <= 0.0f && calculatedColor != Vec4(0.0f))
1861 {
1862 // Skip ill-formed colors, because the spec says the result is undefined.
1863 skippedColors++;
1864 skipColor = true;
1865 break;
1866 }
1867 }
1868 if (skipColor)
1869 continue;
1870 }
1871 deInt32 x = 0;
1872 deInt32 y = 0;
1873 getCoordinates(color, x, y);
1874
1875 deUint32 index = secondDraw ? (color + DE_LENGTH_OF_ARRAY(srcColors) / 2) : color;
1876
1877 // Set source color as push constant
1878 vk.cmdPushConstants(*m_cmdBuffer, *m_pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0u, sizeof(Vec4), &srcColors[index]);
1879 VkRect2D scissor = makeRect2D(x, y, 1u, 1u);
1880 vk.cmdSetScissor(*m_cmdBuffer, 0u, 1u, &scissor);
1881
1882 // To set destination color, we do clear attachment restricting the area to the respective pixel of each color attachment.
1883 // Only clear in the first draw, for the second draw the destination color is the result of the first draw's blend.
1884 if (secondDraw == DE_FALSE)
1885 {
1886 std::vector<VkClearAttachment> attachments;
1887 VkClearValue clearValue = vk::makeClearValueColorVec4(dstColors[index]);
1888
1889 const VkClearAttachment attachment =
1890 {
1891 VK_IMAGE_ASPECT_COLOR_BIT,
1892 0u,
1893 clearValue
1894 };
1895
1896 const VkClearRect rect =
1897 {
1898 scissor,
1899 0u,
1900 1u
1901 };
1902 vk.cmdClearAttachments(*m_cmdBuffer, 1u, &attachment, 1u, &rect);
1903 }
1904
1905 // Draw
1906 vk.cmdDraw(*m_cmdBuffer, (deUint32)m_vertices.size(), 1u, 0u, 0u);
1907 }
1908
1909 // If we break this assert, then we are not testing anything in this test.
1910 DE_ASSERT(skippedColors < (DE_LENGTH_OF_ARRAY(srcColors) / 2));
1911
1912 // Log number of skipped colors
1913 if (skippedColors != 0u)
1914 {
1915 tcu::TestLog& log = m_context.getTestContext().getLog();
1916 log << tcu::TestLog::Message << "Skipped " << skippedColors << " out of " << (DE_LENGTH_OF_ARRAY(srcColors) / 2) << " color cases due to ill-formed colors" << tcu::TestLog::EndMessage;
1917 }
1918 endRenderPass(vk, *m_cmdBuffer);
1919 }
1920
1921 void BlendOperationAdvancedTestCoherentInstance::prepareCommandBuffer ()
1922 {
1923 const DeviceInterface& vk = m_context.getDeviceInterface();
1924
1925 beginCommandBuffer(vk, *m_cmdBuffer, 0u);
1926
1927 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,
1928 0u, DE_NULL, 0u, DE_NULL, (deUint32)m_imageLayoutBarriers.size(), m_imageLayoutBarriers.data());
1929
1930 prepareRenderPass(m_framebuffers[0].get(), m_pipelines[0].getPipeline(), m_renderPasses[0].get(), false);
1931
1932 if (m_param.coherentOperations == DE_FALSE)
1933 {
1934 const VkImageMemoryBarrier colorImageBarrier =
1935 {
1936 VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
1937 DE_NULL, // const void* pNext;
1938 (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
1939 VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT), // VkAccessFlags srcAccessMask;
1940 (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
1941 VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT), // VkAccessFlags dstAccessMask;
1942 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout oldLayout;
1943 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout;
1944 VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
1945 VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex;
1946 *m_colorImage, // VkImage image;
1947 { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }, // VkImageSubresourceRange subresourceRange;
1948 };
1949 vk.cmdPipelineBarrier(*m_cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
1950 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, (VkDependencyFlags)0,
1951 0u, DE_NULL, 0u, DE_NULL, 1u, &colorImageBarrier);
1952 }
1953
1954 prepareRenderPass(m_framebuffers[1].get(), m_pipelines[1].getPipeline(), m_renderPasses[1].get(), true);
1955
1956 endCommandBuffer(vk, *m_cmdBuffer);
1957 }
1958
1959 BlendOperationAdvancedTestCoherentInstance::BlendOperationAdvancedTestCoherentInstance (Context& context,
1960 const BlendOperationAdvancedParam param)
1961 : TestInstance (context)
1962 , m_param (param)
1963 , m_renderSize (tcu::UVec2(widthArea, heightArea))
1964 , m_colorFormat (param.format)
1965 , m_shaderStageCount (0)
1966 {
1967 const DeviceInterface& vk = m_context.getDeviceInterface();
1968 const VkDevice vkDevice = m_context.getDevice();
1969 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
1970
1971 // Create vertex buffer
1972 {
1973 m_vertices = createPoints();
1974 DE_ASSERT((deUint32)m_vertices.size() == 6);
1975
1976 m_vertexBuffer = createBufferAndBindMemory(m_context, m_vertices.size() * sizeof(Vec4), VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, &m_vertexBufferMemory);
1977 // Load vertices into vertex buffer
1978 deMemcpy(m_vertexBufferMemory->getHostPtr(), m_vertices.data(), m_vertices.size() * sizeof(Vec4));
1979 flushAlloc(vk, vkDevice, *m_vertexBufferMemory);
1980 }
1981
1982 // Create render passes
1983 m_renderPasses.emplace_back(makeTestRenderPass(param, vk, vkDevice, m_colorFormat, VK_ATTACHMENT_LOAD_OP_CLEAR));
1984 m_renderPasses.emplace_back(makeTestRenderPass(param, vk, vkDevice, m_colorFormat, VK_ATTACHMENT_LOAD_OP_LOAD));
1985
1986 const VkComponentMapping componentMappingRGBA = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
1987
1988 // Create color image
1989 m_colorImage = createImage2DAndBindMemory(m_context,
1990 m_colorFormat,
1991 m_renderSize.x(),
1992 m_renderSize.y(),
1993 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
1994 VK_SAMPLE_COUNT_1_BIT,
1995 &m_colorImageAlloc);
1996 // Set up image layout transition barriers
1997 {
1998 VkImageMemoryBarrier colorImageBarrier =
1999 {
2000 VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
2001 DE_NULL, // const void* pNext;
2002 0u, // VkAccessFlags srcAccessMask;
2003 (VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
2004 VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT), // VkAccessFlags dstAccessMask;
2005 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
2006 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout;
2007 VK_QUEUE_FAMILY_IGNORED, // deUint32 srcQueueFamilyIndex;
2008 VK_QUEUE_FAMILY_IGNORED, // deUint32 dstQueueFamilyIndex;
2009 *m_colorImage, // VkImage image;
2010 { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }, // VkImageSubresourceRange subresourceRange;
2011 };
2012
2013 m_imageLayoutBarriers.emplace_back(colorImageBarrier);
2014 }
2015
2016 // Create color attachment view
2017 {
2018 VkImageViewCreateInfo colorAttachmentViewParams =
2019 {
2020 VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // VkStructureType sType;
2021 DE_NULL, // const void* pNext;
2022 0u, // VkImageViewCreateFlags flags;
2023 *m_colorImage, // VkImage image;
2024 VK_IMAGE_VIEW_TYPE_2D, // VkImageViewType viewType;
2025 m_colorFormat, // VkFormat format;
2026 componentMappingRGBA, // VkComponentMapping components;
2027 { VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }, // VkImageSubresourceRange subresourceRange;
2028 };
2029
2030 m_colorAttachmentView = createImageView(vk, vkDevice, &colorAttachmentViewParams);
2031 }
2032
2033 // Create framebuffers
2034 {
2035 VkFramebufferCreateInfo framebufferParams =
2036 {
2037 VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
2038 DE_NULL, // const void* pNext;
2039 0u, // VkFramebufferCreateFlags flags;
2040 m_renderPasses[0].get(), // VkRenderPass renderPass;
2041 1u, // deUint32 attachmentCount;
2042 &m_colorAttachmentView.get(), // const VkImageView* pAttachments;
2043 (deUint32)m_renderSize.x(), // deUint32 width;
2044 (deUint32)m_renderSize.y(), // deUint32 height;
2045 1u, // deUint32 layers;
2046 };
2047
2048 m_framebuffers.emplace_back(createFramebuffer(vk, vkDevice, &framebufferParams));
2049 framebufferParams.renderPass = m_renderPasses[1].get();
2050 m_framebuffers.emplace_back(createFramebuffer(vk, vkDevice, &framebufferParams));
2051 }
2052
2053 // Create pipeline layout
2054 {
2055 const VkPushConstantRange pushConstantRange =
2056 {
2057 VK_SHADER_STAGE_FRAGMENT_BIT, // VkShaderStageFlags stageFlags
2058 0, // deUint32 offset
2059 sizeof(Vec4) // deUint32 size
2060 };
2061
2062 const VkPipelineLayoutCreateInfo pipelineLayoutParams =
2063 {
2064 VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // VkStructureType sType;
2065 DE_NULL, // const void* pNext;
2066 0u, // VkPipelineLayoutCreateFlags flags;
2067 0u, // deUint32 setLayoutCount;
2068 DE_NULL, // const VkDescriptorSetLayout* pSetLayouts;
2069 1u, // deUint32 pushConstantRangeCount;
2070 &pushConstantRange // const VkPushConstantRange* pPushConstantRanges;
2071 };
2072
2073 m_pipelineLayout = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
2074 }
2075
2076 // Create pipeline
2077 buildPipeline();
2078
2079 // Create command pool
2080 m_cmdPool = createCommandPool(vk, vkDevice, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
2081
2082 // Create command buffer
2083 m_cmdBuffer = allocateCommandBuffer(vk, vkDevice, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
2084 }
2085
2086 tcu::TestStatus BlendOperationAdvancedTestCoherentInstance::iterate (void)
2087 {
2088 const DeviceInterface& vk = m_context.getDeviceInterface();
2089 const VkDevice vkDevice = m_context.getDevice();
2090 const VkQueue queue = m_context.getUniversalQueue();
2091 tcu::TestLog& log = m_context.getTestContext().getLog();
2092
2093 // Log the blend operations to test
2094 {
2095 DE_ASSERT(m_param.blendOps.size() == 2u);
2096 log << tcu::TestLog::Message << "First depth op: " << de::toLower(getBlendOpStr(m_param.blendOps[0]).toString().substr(3)) << tcu::TestLog::EndMessage;
2097 log << tcu::TestLog::Message << "Second depth op: " << de::toLower(getBlendOpStr(m_param.blendOps[1]).toString().substr(3)) << tcu::TestLog::EndMessage;
2098
2099 }
2100
2101 prepareCommandBuffer();
2102
2103 submitCommandsAndWait(vk, vkDevice, queue, m_cmdBuffer.get());
2104 return verifyTestResult();
2105 }
2106
2107 tcu::TestStatus BlendOperationAdvancedTestCoherentInstance::verifyTestResult (void)
2108 {
2109 deBool compareOk = DE_TRUE;
2110 const DeviceInterface& vk = m_context.getDeviceInterface();
2111 const VkDevice vkDevice = m_context.getDevice();
2112 const VkQueue queue = m_context.getUniversalQueue();
2113 const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
2114 Allocator& allocator = m_context.getDefaultAllocator();
2115 tcu::TextureLevel refImage (vk::mapVkFormat(m_colorFormat), 32, 32);
2116
2117 tcu::clear(refImage.getAccess(), clearColorVec4);
2118
2119 // Generate reference image
2120 for (deUint32 color = 0; color < DE_LENGTH_OF_ARRAY(srcColors)/2; color++)
2121 {
2122 deUint32 secondDrawColorIndex = color + DE_LENGTH_OF_ARRAY(srcColors)/2;
2123 // Calculate first draw final color
2124 Vec4 rectColorTmp = calculateFinalColor(m_param, m_param.blendOps[0], srcColors[color], dstColors[color]);
2125
2126 if (m_param.premultipliedDstColor == VK_FALSE)
2127 {
2128 if (rectColorTmp.w() > 0.0f)
2129 {
2130 rectColorTmp.x() = rectColorTmp.x() / rectColorTmp.w();
2131 rectColorTmp.y() = rectColorTmp.y() / rectColorTmp.w();
2132 rectColorTmp.z() = rectColorTmp.z() / rectColorTmp.w();
2133 }
2134 else
2135 {
2136 // Skip the color check if it is ill-formed.
2137 if (rectColorTmp != Vec4(0.0f))
2138 continue;
2139 }
2140 }
2141 // Calculate second draw final color
2142 Vec4 rectColor = calculateFinalColor(m_param, m_param.blendOps[1], srcColors[secondDrawColorIndex], rectColorTmp);
2143 if (m_param.premultipliedDstColor == VK_FALSE)
2144 {
2145 if (rectColor.w() > 0.0f)
2146 {
2147 rectColor.x() = rectColor.x() / rectColor.w();
2148 rectColor.y() = rectColor.y() / rectColor.w();
2149 rectColor.z() = rectColor.z() / rectColor.w();
2150 }
2151 else
2152 {
2153 // Skip the color check if it is ill-formed.
2154 if (rectColor != Vec4(0.0f))
2155 continue;
2156 }
2157 }
2158
2159 deInt32 x = 0;
2160 deInt32 y = 0;
2161 getCoordinates(color, x, y);
2162 tcu::clear(tcu::getSubregion(refImage.getAccess(), x, y, 1u, 1u), rectColor);
2163 }
2164
2165 de::MovePtr<tcu::TextureLevel> result = vkt::pipeline::readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, *m_colorImage, m_colorFormat, m_renderSize);
2166 std::ostringstream name;
2167 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));
2168
2169 // R8G8B8A8 threshold was derived experimentally.
2170 compareOk = tcu::floatThresholdCompare(m_context.getTestContext().getLog(),
2171 "FloatImageCompare",
2172 name.str().c_str(),
2173 refImage.getAccess(),
2174 result->getAccess(),
2175 clearColorVec4,
2176 m_colorFormat == VK_FORMAT_R8G8B8A8_UNORM ? Vec4(0.13f, 0.13f, 0.13f, 0.13f) : Vec4(0.01f, 0.01f, 0.01f, 0.01f),
2177 tcu::COMPARE_LOG_RESULT);
2178 if (!compareOk)
2179 return tcu::TestStatus::fail("Image mismatch");
2180
2181 return tcu::TestStatus::pass("Result images matches references");
2182 }
2183
2184 TestInstance* BlendOperationAdvancedTest::createInstance (Context& context) const
2185 {
2186 if (m_param.testMode == TEST_MODE_GENERIC)
2187 return new BlendOperationAdvancedTestInstance(context, m_param);
2188 else
2189 return new BlendOperationAdvancedTestCoherentInstance(context, m_param);
2190 }
2191
2192 } // anonymous
2193
2194 tcu::TestCaseGroup* createBlendOperationAdvancedTests (tcu::TestContext& testCtx, PipelineConstructionType pipelineConstructionType)
2195 {
2196 enum nonpremultiplyEnum
2197 {
2198 PREMULTIPLY_SRC = 1u,
2199 PREMULTIPLY_DST = 2u
2200 };
2201 deUint32 premultiplyModes[] = { 0u, PREMULTIPLY_SRC, PREMULTIPLY_DST, PREMULTIPLY_SRC | PREMULTIPLY_DST };
2202 deUint32 colorAttachmentCounts[] = { 1u, 2u, 4u, 8u, 16u };
2203 deBool coherentOps[] = { DE_FALSE, DE_TRUE };
2204 VkBlendOp blendOps[] =
2205 {
2206 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,
2207 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,
2208 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,
2209 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,
2210 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,
2211 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,
2212 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,
2213 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,
2214 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,
2215 };
2216
2217 de::MovePtr<tcu::TestCaseGroup> tests (new tcu::TestCaseGroup(testCtx, "blend_operation_advanced", "VK_EXT_blend_operation_advanced tests"));
2218 de::Random rnd (deStringHash(tests->getName()));
2219
2220 de::MovePtr<tcu::TestCaseGroup> opsTests (new tcu::TestCaseGroup(testCtx, "ops", "Test each blend operation advance op"));
2221
2222
2223 for (deUint32 colorAttachmentCount = 0u; colorAttachmentCount < DE_LENGTH_OF_ARRAY(colorAttachmentCounts); colorAttachmentCount++)
2224 {
2225 for (deUint32 overlap = 0; overlap <= VK_BLEND_OVERLAP_CONJOINT_EXT; overlap++)
2226 {
2227 for (deUint32 premultiply = 0u; premultiply < DE_LENGTH_OF_ARRAY(premultiplyModes); premultiply++)
2228 {
2229 deUint32 testNumber = 0u;
2230 for (deUint64 blendOp = 0u; blendOp < DE_LENGTH_OF_ARRAY(blendOps); blendOp++)
2231 {
2232 deBool isAdditionalRGBBlendOp = blendOps[blendOp] >= VK_BLEND_OP_PLUS_EXT && blendOps[blendOp] < VK_BLEND_OP_MAX_ENUM;
2233
2234 // Additional RGB Blend operations are not affected by the blend overlap modes
2235 if (isAdditionalRGBBlendOp && overlap != VK_BLEND_OVERLAP_UNCORRELATED_EXT)
2236 continue;
2237
2238 BlendOperationAdvancedParam testParams;
2239 testParams.pipelineConstructionType = pipelineConstructionType;
2240 testParams.testMode = TEST_MODE_GENERIC;
2241 testParams.overlap = (VkBlendOverlapEXT) overlap;
2242 testParams.coherentOperations = DE_FALSE;
2243 testParams.colorAttachmentsCount = colorAttachmentCounts[colorAttachmentCount];
2244 testParams.independentBlend = DE_FALSE;
2245 testParams.premultipliedSrcColor = (premultiplyModes[premultiply] & PREMULTIPLY_SRC) ? VK_TRUE : VK_FALSE;
2246 testParams.premultipliedDstColor = (premultiplyModes[premultiply] & PREMULTIPLY_DST) ? VK_TRUE : VK_FALSE;
2247 testParams.testNumber = testNumber++;
2248 testParams.format = VK_FORMAT_R16G16B16A16_SFLOAT;
2249
2250 for (deUint32 numColorAtt = 0; numColorAtt < colorAttachmentCounts[colorAttachmentCount]; numColorAtt++)
2251 testParams.blendOps.push_back(blendOps[blendOp]);
2252 opsTests->addChild(newTestCase<BlendOperationAdvancedTest>(testCtx, testParams));
2253
2254 testParams.format = VK_FORMAT_R8G8B8A8_UNORM;
2255 opsTests->addChild(newTestCase<BlendOperationAdvancedTest>(testCtx, testParams));
2256 }
2257 }
2258 }
2259 }
2260 tests->addChild(opsTests.release());
2261
2262 // Independent Blend Tests: test more than one color attachment.
2263 de::MovePtr<tcu::TestCaseGroup> independentTests (new tcu::TestCaseGroup(testCtx, "independent", "Test independent blend feature"));
2264 deUint32 testNumber = 0u;
2265
2266 for (deUint32 colorAttachmentCount = 1u; colorAttachmentCount < DE_LENGTH_OF_ARRAY(colorAttachmentCounts); colorAttachmentCount++)
2267 {
2268 BlendOperationAdvancedParam testParams;
2269 testParams.pipelineConstructionType = pipelineConstructionType;
2270 testParams.testMode = TEST_MODE_GENERIC;
2271 testParams.overlap = VK_BLEND_OVERLAP_UNCORRELATED_EXT;
2272 testParams.coherentOperations = DE_FALSE;
2273 testParams.colorAttachmentsCount = colorAttachmentCounts[colorAttachmentCount];
2274 testParams.independentBlend = DE_TRUE;
2275 testParams.premultipliedSrcColor = VK_TRUE;
2276 testParams.premultipliedDstColor = VK_TRUE;
2277 testParams.testNumber = testNumber++;
2278 testParams.format = VK_FORMAT_R16G16B16A16_SFLOAT;
2279
2280 for (deUint32 numColorAtt = 0; numColorAtt < colorAttachmentCounts[colorAttachmentCount]; numColorAtt++)
2281 {
2282 deUint32 i = de::randomScalar<deUint32>(rnd, 0, DE_LENGTH_OF_ARRAY(blendOps) - 1);
2283 testParams.blendOps.push_back(blendOps[i]);
2284 }
2285 independentTests->addChild(newTestCase<BlendOperationAdvancedTest>(testCtx, testParams));
2286
2287 testParams.format = VK_FORMAT_R8G8B8A8_UNORM;
2288 independentTests->addChild(newTestCase<BlendOperationAdvancedTest>(testCtx, testParams));
2289 }
2290
2291 tests->addChild(independentTests.release());
2292
2293 // Coherent tests, do two consecutive advanced blending operations on the same color attachment.
2294 de::MovePtr<tcu::TestCaseGroup> coherentTests (new tcu::TestCaseGroup(testCtx, "coherent", "Test coherent memory"));
2295 testNumber = 0u;
2296
2297 for (deUint32 coherent = 0u; coherent < DE_LENGTH_OF_ARRAY(coherentOps); coherent++)
2298 {
2299 BlendOperationAdvancedParam testParams;
2300 testParams.pipelineConstructionType = pipelineConstructionType;
2301 testParams.testMode = TEST_MODE_COHERENT;
2302 testParams.overlap = VK_BLEND_OVERLAP_UNCORRELATED_EXT;
2303 testParams.coherentOperations = coherentOps[coherent];
2304 testParams.colorAttachmentsCount = 1u;
2305 testParams.independentBlend = DE_FALSE;
2306 testParams.premultipliedSrcColor = VK_TRUE;
2307 testParams.premultipliedDstColor = VK_TRUE;
2308 testParams.testNumber = testNumber++;
2309 testParams.format = VK_FORMAT_R16G16B16A16_SFLOAT;
2310
2311 // We do two consecutive advanced blending operations
2312 deUint32 i = de::randomScalar<deUint32>(rnd, 0, DE_LENGTH_OF_ARRAY(blendOps) - 1);
2313 testParams.blendOps.push_back(blendOps[i]);
2314 i = de::randomScalar<deUint32>(rnd, 0, DE_LENGTH_OF_ARRAY(blendOps) - 1);
2315 testParams.blendOps.push_back(blendOps[i]);
2316
2317 coherentTests->addChild(newTestCase<BlendOperationAdvancedTest>(testCtx, testParams));
2318
2319 testParams.format = VK_FORMAT_R8G8B8A8_UNORM;
2320 coherentTests->addChild(newTestCase<BlendOperationAdvancedTest>(testCtx, testParams));
2321 }
2322 tests->addChild(coherentTests.release());
2323
2324
2325 return tests.release();
2326 }
2327
2328 } // pipeline
2329
2330 } // vkt
2331