1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2014 The Android Open Source Project
6 * Copyright (c) 2016 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 Tessellation Primitive Discard Tests
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktTessellationPrimitiveDiscardTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28
29 #include "tcuTestLog.hpp"
30
31 #include "vkDefs.hpp"
32 #include "vkQueryUtil.hpp"
33 #include "vkBuilderUtil.hpp"
34 #include "vkImageUtil.hpp"
35 #include "vkTypeUtil.hpp"
36 #include "vkCmdUtil.hpp"
37 #include "vkObjUtil.hpp"
38 #include "vkBarrierUtil.hpp"
39
40 #include "deUniquePtr.hpp"
41 #include "deStringUtil.hpp"
42
43 #include <string>
44 #include <vector>
45
46 namespace vkt
47 {
48 namespace tessellation
49 {
50
51 using namespace vk;
52
53 namespace
54 {
55
56 struct CaseDefinition
57 {
58 TessPrimitiveType primitiveType;
59 SpacingMode spacingMode;
60 Winding winding;
61 bool usePointMode;
62 bool useLessThanOneInnerLevels;
63 };
64
lessThanOneInnerLevelsDefined(const CaseDefinition & caseDef)65 bool lessThanOneInnerLevelsDefined (const CaseDefinition& caseDef)
66 {
67 // From Vulkan API specification:
68 // >> When tessellating triangles or quads (with/without point mode) with fractional odd spacing, the tessellator
69 // >> ***may*** produce interior vertices that are positioned on the edge of the patch if an inner
70 // >> tessellation level is less than or equal to one.
71 return !((caseDef.primitiveType == vkt::tessellation::TESSPRIMITIVETYPE_QUADS ||
72 caseDef.primitiveType == vkt::tessellation::TESSPRIMITIVETYPE_TRIANGLES) &&
73 caseDef.spacingMode == vkt::tessellation::SPACINGMODE_FRACTIONAL_ODD);
74 }
75
intPow(int base,int exp)76 int intPow (int base, int exp)
77 {
78 DE_ASSERT(exp >= 0);
79 if (exp == 0)
80 return 1;
81 else
82 {
83 const int sub = intPow(base, exp/2);
84 if (exp % 2 == 0)
85 return sub*sub;
86 else
87 return sub*sub*base;
88 }
89 }
90
genAttributes(bool useLessThanOneInnerLevels)91 std::vector<float> genAttributes (bool useLessThanOneInnerLevels)
92 {
93 // Generate input attributes (tessellation levels, and position scale and
94 // offset) for a number of primitives. Each primitive has a different
95 // combination of tessellatio levels; each level is either a valid
96 // value or an "invalid" value (negative or zero, chosen from
97 // invalidTessLevelChoices).
98
99 // \note The attributes are generated in such an order that all of the
100 // valid attribute tuples come before the first invalid one both
101 // in the result vector, and when scanning the resulting 2d grid
102 // of primitives is scanned in y-major order. This makes
103 // verification somewhat simpler.
104
105 static const float baseTessLevels[6] = { 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f };
106 static const float invalidTessLevelChoices[] = { -0.42f, 0.0f };
107 const int numChoices = 1 + DE_LENGTH_OF_ARRAY(invalidTessLevelChoices);
108 float choices[6][numChoices];
109 std::vector<float> result;
110
111 for (int levelNdx = 0; levelNdx < 6; levelNdx++)
112 for (int choiceNdx = 0; choiceNdx < numChoices; choiceNdx++)
113 choices[levelNdx][choiceNdx] = (choiceNdx == 0 || !useLessThanOneInnerLevels) ? baseTessLevels[levelNdx] : invalidTessLevelChoices[choiceNdx-1];
114
115 {
116 const int numCols = intPow(numChoices, 6/2); // sqrt(numChoices**6) == sqrt(number of primitives)
117 const int numRows = numCols;
118 int index = 0;
119 int i[6];
120 // We could do this with some generic combination-generation function, but meh, it's not that bad.
121 for (i[2] = 0; i[2] < numChoices; i[2]++) // First outer
122 for (i[3] = 0; i[3] < numChoices; i[3]++) // Second outer
123 for (i[4] = 0; i[4] < numChoices; i[4]++) // Third outer
124 for (i[5] = 0; i[5] < numChoices; i[5]++) // Fourth outer
125 for (i[0] = 0; i[0] < numChoices; i[0]++) // First inner
126 for (i[1] = 0; i[1] < numChoices; i[1]++) // Second inner
127 {
128 for (int j = 0; j < 6; j++)
129 result.push_back(choices[j][i[j]]);
130
131 {
132 const int col = index % numCols;
133 const int row = index / numCols;
134 // Position scale.
135 result.push_back((float)2.0f / (float)numCols);
136 result.push_back((float)2.0f / (float)numRows);
137 // Position offset.
138 result.push_back((float)col / (float)numCols * 2.0f - 1.0f);
139 result.push_back((float)row / (float)numRows * 2.0f - 1.0f);
140 }
141
142 index++;
143 }
144 }
145
146 return result;
147 }
148
149 //! Check that white pixels are found around every non-discarded patch,
150 //! and that only black pixels are found after the last non-discarded patch.
151 //! Returns true on successful comparison.
verifyResultImage(tcu::TestLog & log,const int numPrimitives,const int numAttribsPerPrimitive,const TessPrimitiveType primitiveType,const std::vector<float> & attributes,const tcu::ConstPixelBufferAccess pixels)152 bool verifyResultImage (tcu::TestLog& log,
153 const int numPrimitives,
154 const int numAttribsPerPrimitive,
155 const TessPrimitiveType primitiveType,
156 const std::vector<float>& attributes,
157 const tcu::ConstPixelBufferAccess pixels)
158 {
159 const tcu::Vec4 black(0.0f, 0.0f, 0.0f, 1.0f);
160 const tcu::Vec4 white(1.0f, 1.0f, 1.0f, 1.0f);
161
162 int lastWhitePixelRow = 0;
163 int secondToLastWhitePixelRow = 0;
164 int lastWhitePixelColumnOnSecondToLastWhitePixelRow = 0;
165
166 for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
167 {
168 const float* const attr = &attributes[numAttribsPerPrimitive*patchNdx];
169 const bool validLevels = !isPatchDiscarded(primitiveType, &attr[2]);
170
171 if (validLevels)
172 {
173 // Not a discarded patch; check that at least one white pixel is found in its area.
174
175 const float* const scale = &attr[6];
176 const float* const offset = &attr[8];
177 const int x0 = (int)(( offset[0] + 1.0f) * 0.5f * (float)pixels.getWidth()) - 1;
178 const int x1 = (int)((scale[0] + offset[0] + 1.0f) * 0.5f * (float)pixels.getWidth()) + 1;
179 const int y0 = (int)(( offset[1] + 1.0f) * 0.5f * (float)pixels.getHeight()) - 1;
180 const int y1 = (int)((scale[1] + offset[1] + 1.0f) * 0.5f * (float)pixels.getHeight()) + 1;
181 bool pixelOk = false;
182
183 if (y1 > lastWhitePixelRow)
184 {
185 secondToLastWhitePixelRow = lastWhitePixelRow;
186 lastWhitePixelRow = y1;
187 }
188 lastWhitePixelColumnOnSecondToLastWhitePixelRow = x1;
189
190 for (int y = y0; y <= y1 && !pixelOk; y++)
191 for (int x = x0; x <= x1 && !pixelOk; x++)
192 {
193 if (!de::inBounds(x, 0, pixels.getWidth()) || !de::inBounds(y, 0, pixels.getHeight()))
194 continue;
195
196 if (pixels.getPixel(x, y) == white)
197 pixelOk = true;
198 }
199
200 if (!pixelOk)
201 {
202 log << tcu::TestLog::Message
203 << "Failure: expected at least one white pixel in the rectangle "
204 << "[x0=" << x0 << ", y0=" << y0 << ", x1=" << x1 << ", y1=" << y1 << "]"
205 << tcu::TestLog::EndMessage
206 << tcu::TestLog::Message
207 << "Note: the rectangle approximately corresponds to the patch with these tessellation levels: "
208 << getTessellationLevelsString(&attr[0], &attr[1])
209 << tcu::TestLog::EndMessage;
210
211 return false;
212 }
213 }
214 else
215 {
216 // First discarded primitive patch; the remaining are guaranteed to be discarded ones as well.
217
218 for (int y = 0; y < pixels.getHeight(); y++)
219 for (int x = 0; x < pixels.getWidth(); x++)
220 {
221 if (y > lastWhitePixelRow || (y > secondToLastWhitePixelRow && x > lastWhitePixelColumnOnSecondToLastWhitePixelRow))
222 {
223 if (pixels.getPixel(x, y) != black)
224 {
225 log << tcu::TestLog::Message
226 << "Failure: expected all pixels to be black in the area "
227 << (lastWhitePixelColumnOnSecondToLastWhitePixelRow < pixels.getWidth()-1
228 ? std::string() + "y > " + de::toString(lastWhitePixelRow) + " || (y > " + de::toString(secondToLastWhitePixelRow)
229 + " && x > " + de::toString(lastWhitePixelColumnOnSecondToLastWhitePixelRow) + ")"
230 : std::string() + "y > " + de::toString(lastWhitePixelRow))
231 << " (they all correspond to patches that should be discarded)"
232 << tcu::TestLog::EndMessage
233 << tcu::TestLog::Message << "Note: pixel " << tcu::IVec2(x, y) << " isn't black" << tcu::TestLog::EndMessage;
234
235 return false;
236 }
237 }
238 }
239 break;
240 }
241 }
242 return true;
243 }
244
expectedVertexCount(const int numPrimitives,const int numAttribsPerPrimitive,const TessPrimitiveType primitiveType,const SpacingMode spacingMode,const std::vector<float> & attributes)245 int expectedVertexCount (const int numPrimitives,
246 const int numAttribsPerPrimitive,
247 const TessPrimitiveType primitiveType,
248 const SpacingMode spacingMode,
249 const std::vector<float>& attributes)
250 {
251 int count = 0;
252 for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
253 count += referenceVertexCount(primitiveType, spacingMode, true, &attributes[numAttribsPerPrimitive*patchNdx+0], &attributes[numAttribsPerPrimitive*patchNdx+2]);
254 return count;
255 }
256
initPrograms(vk::SourceCollections & programCollection,const CaseDefinition caseDef)257 void initPrograms (vk::SourceCollections& programCollection, const CaseDefinition caseDef)
258 {
259 // Vertex shader
260 {
261 std::ostringstream src;
262 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
263 << "\n"
264 << "layout(location = 0) in highp float in_v_attr;\n"
265 << "layout(location = 0) out highp float in_tc_attr;\n"
266 << "\n"
267 << "void main (void)\n"
268 << "{\n"
269 << " in_tc_attr = in_v_attr;\n"
270 << "}\n";
271
272 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
273 }
274
275 // Tessellation control shader
276 {
277 std::ostringstream src;
278 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
279 << "#extension GL_EXT_tessellation_shader : require\n"
280 << "\n"
281 << "layout(vertices = 1) out;\n"
282 << "\n"
283 << "layout(location = 0) in highp float in_tc_attr[];\n"
284 << "\n"
285 << "layout(location = 0) patch out highp vec2 in_te_positionScale;\n"
286 << "layout(location = 1) patch out highp vec2 in_te_positionOffset;\n"
287 << "\n"
288 << "void main (void)\n"
289 << "{\n"
290 << " in_te_positionScale = vec2(in_tc_attr[6], in_tc_attr[7]);\n"
291 << " in_te_positionOffset = vec2(in_tc_attr[8], in_tc_attr[9]);\n"
292 << "\n"
293 << " gl_TessLevelInner[0] = in_tc_attr[0];\n"
294 << " gl_TessLevelInner[1] = in_tc_attr[1];\n"
295 << "\n"
296 << " gl_TessLevelOuter[0] = in_tc_attr[2];\n"
297 << " gl_TessLevelOuter[1] = in_tc_attr[3];\n"
298 << " gl_TessLevelOuter[2] = in_tc_attr[4];\n"
299 << " gl_TessLevelOuter[3] = in_tc_attr[5];\n"
300 << "}\n";
301
302 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
303 }
304
305 // Tessellation evaluation shader
306 // When using point mode we need two variants of the shader, one for the case where
307 // shaderTessellationAndGeometryPointSize is enabled (in which the tessellation evaluation
308 // shader needs to write to gl_PointSize for it to be defined) and one for the case where
309 // it is disabled, in which we can't write to gl_PointSize but it has a default value
310 // of 1.0
311 {
312 const deUint32 numVariants = caseDef.usePointMode ? 2 : 1;
313 for (deUint32 variant = 0; variant < numVariants; variant++)
314 {
315 const bool needPointSizeWrite = caseDef.usePointMode && variant == 1;
316
317 std::ostringstream src;
318 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
319 << "#extension GL_EXT_tessellation_shader : require\n";
320 if (needPointSizeWrite)
321 {
322 src << "#extension GL_EXT_tessellation_point_size : require\n";
323 }
324 src << "\n"
325 << "layout(" << getTessPrimitiveTypeShaderName(caseDef.primitiveType) << ", "
326 << getSpacingModeShaderName(caseDef.spacingMode) << ", "
327 << getWindingShaderName(caseDef.winding)
328 << (caseDef.usePointMode ? ", point_mode" : "") << ") in;\n"
329 << "\n"
330 << "layout(location = 0) patch in highp vec2 in_te_positionScale;\n"
331 << "layout(location = 1) patch in highp vec2 in_te_positionOffset;\n"
332 << "\n"
333 << "layout(set = 0, binding = 0, std430) coherent restrict buffer Output {\n"
334 << " int numInvocations;\n"
335 << "} sb_out;\n"
336 << "\n"
337 << "void main (void)\n"
338 << "{\n"
339 << " atomicAdd(sb_out.numInvocations, 1);\n"
340 << "\n"
341 << " gl_Position = vec4(gl_TessCoord.xy*in_te_positionScale + in_te_positionOffset, 0.0, 1.0);\n";
342 if (needPointSizeWrite)
343 {
344 src << " gl_PointSize = 1.0;\n";
345 }
346 src << "}\n";
347
348 programCollection.glslSources.add(needPointSizeWrite ? "tese_psw" : "tese") << glu::TessellationEvaluationSource(src.str());
349 }
350 }
351
352 // Fragment shader
353 {
354 std::ostringstream src;
355 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
356 << "\n"
357 << "layout(location = 0) out mediump vec4 o_color;\n"
358 << "\n"
359 << "void main (void)\n"
360 << "{\n"
361 << " o_color = vec4(1.0);\n"
362 << "}\n";
363
364 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
365 }
366 }
367
368 /*--------------------------------------------------------------------*//*!
369 * \brief Test that patch is discarded if relevant outer level <= 0.0
370 *
371 * Draws patches with different combinations of tessellation levels,
372 * varying which levels are negative. Verifies by checking that white
373 * pixels exist inside the area of valid primitives, and only black pixels
374 * exist inside the area of discarded primitives. An additional sanity
375 * test is done, checking that the number of primitives written by shader is
376 * correct.
377 *//*--------------------------------------------------------------------*/
test(Context & context,const CaseDefinition caseDef)378 tcu::TestStatus test (Context& context, const CaseDefinition caseDef)
379 {
380 requireFeatures(context.getInstanceInterface(), context.getPhysicalDevice(), FEATURE_TESSELLATION_SHADER | FEATURE_VERTEX_PIPELINE_STORES_AND_ATOMICS);
381
382 const DeviceInterface& vk = context.getDeviceInterface();
383 const VkDevice device = context.getDevice();
384 const VkQueue queue = context.getUniversalQueue();
385 const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
386 Allocator& allocator = context.getDefaultAllocator();
387
388 const std::vector<float> attributes = genAttributes(caseDef.useLessThanOneInnerLevels);
389 const int numAttribsPerPrimitive = 6 + 2 + 2; // Tess levels, scale, offset.
390 const int numPrimitives = static_cast<int>(attributes.size() / numAttribsPerPrimitive);
391 const int numExpectedVertices = expectedVertexCount(numPrimitives, numAttribsPerPrimitive, caseDef.primitiveType, caseDef.spacingMode, attributes);
392
393 // Check the convenience assertion that all discarded patches come after the last non-discarded patch.
394 {
395 bool discardedPatchEncountered = false;
396 for (int patchNdx = 0; patchNdx < numPrimitives; ++patchNdx)
397 {
398 const bool discard = isPatchDiscarded(caseDef.primitiveType, &attributes[numAttribsPerPrimitive*patchNdx + 2]);
399 DE_ASSERT(discard || !discardedPatchEncountered);
400 discardedPatchEncountered = discard;
401 }
402 DE_UNREF(discardedPatchEncountered);
403 }
404
405 // Vertex input attributes buffer
406
407 const VkFormat vertexFormat = VK_FORMAT_R32_SFLOAT;
408 const deUint32 vertexStride = tcu::getPixelSize(mapVkFormat(vertexFormat));
409 const VkDeviceSize vertexDataSizeBytes = sizeInBytes(attributes);
410 const Buffer vertexBuffer (vk, device, allocator, makeBufferCreateInfo(vertexDataSizeBytes, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT), MemoryRequirement::HostVisible);
411
412 DE_ASSERT(static_cast<int>(attributes.size()) == numPrimitives * numAttribsPerPrimitive);
413 DE_ASSERT(sizeof(attributes[0]) == vertexStride);
414
415 {
416 const Allocation& alloc = vertexBuffer.getAllocation();
417
418 deMemcpy(alloc.getHostPtr(), &attributes[0], static_cast<std::size_t>(vertexDataSizeBytes));
419 flushAlloc(vk, device, alloc);
420 // No barrier needed, flushed memory is automatically visible
421 }
422
423 // Output buffer: number of invocations
424
425 const VkDeviceSize resultBufferSizeBytes = sizeof(deInt32);
426 const Buffer resultBuffer (vk, device, allocator, makeBufferCreateInfo(resultBufferSizeBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT), MemoryRequirement::HostVisible);
427
428 {
429 const Allocation& alloc = resultBuffer.getAllocation();
430
431 deMemset(alloc.getHostPtr(), 0, static_cast<std::size_t>(resultBufferSizeBytes));
432 flushAlloc(vk, device, alloc);
433 }
434
435 // Color attachment
436
437 const tcu::IVec2 renderSize = tcu::IVec2(256, 256);
438 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
439 const VkImageSubresourceRange colorImageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
440 const Image colorAttachmentImage (vk, device, allocator,
441 makeImageCreateInfo(renderSize, colorFormat, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
442 MemoryRequirement::Any);
443
444 // Color output buffer: image will be copied here for verification
445
446 const VkDeviceSize colorBufferSizeBytes = renderSize.x()*renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
447 const Buffer colorBuffer(vk, device, allocator,
448 makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
449
450 // Descriptors
451
452 const Unique<VkDescriptorSetLayout> descriptorSetLayout(DescriptorSetLayoutBuilder()
453 .addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
454 .build(vk, device));
455
456 const Unique<VkDescriptorPool> descriptorPool(DescriptorPoolBuilder()
457 .addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
458 .build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
459
460 const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
461 const VkDescriptorBufferInfo resultBufferInfo = makeDescriptorBufferInfo(resultBuffer.get(), 0ull, resultBufferSizeBytes);
462
463 DescriptorSetUpdateBuilder()
464 .writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo)
465 .update(vk, device);
466
467 // Pipeline
468
469 const Unique<VkImageView> colorAttachmentView (makeImageView(vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
470 const Unique<VkRenderPass> renderPass (makeRenderPass(vk, device, colorFormat));
471 const Unique<VkFramebuffer> framebuffer (makeFramebuffer(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y()));
472 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayout(vk, device, *descriptorSetLayout));
473 const Unique<VkCommandPool> cmdPool (makeCommandPool(vk, device, queueFamilyIndex));
474 const Unique<VkCommandBuffer> cmdBuffer (allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
475 const bool needPointSizeWrite = getPhysicalDeviceFeatures(context.getInstanceInterface(), context.getPhysicalDevice()).shaderTessellationAndGeometryPointSize && caseDef.usePointMode;
476
477 const Unique<VkPipeline> pipeline(GraphicsPipelineBuilder()
478 .setRenderSize (renderSize)
479 .setPatchControlPoints (numAttribsPerPrimitive)
480 .setVertexInputSingleAttribute(vertexFormat, vertexStride)
481 .setShader (vk, device, VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"), DE_NULL)
482 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"), DE_NULL)
483 .setShader (vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, context.getBinaryCollection().get(needPointSizeWrite ? "tese_psw" : "tese"), DE_NULL)
484 .setShader (vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"), DE_NULL)
485 .build (vk, device, *pipelineLayout, *renderPass));
486
487 context.getTestContext().getLog()
488 << tcu::TestLog::Message
489 << "Note: rendering " << numPrimitives << " patches; first patches have valid relevant outer levels, "
490 << "but later patches have one or more invalid (i.e. less than or equal to 0.0) relevant outer levels"
491 << tcu::TestLog::EndMessage;
492
493 // Draw commands
494
495 beginCommandBuffer(vk, *cmdBuffer);
496
497 // Change color attachment image layout
498 {
499 const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
500 (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
501 VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
502 *colorAttachmentImage, colorImageSubresourceRange);
503
504 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u,
505 0u, DE_NULL, 0u, DE_NULL, 1u, &colorAttachmentLayoutBarrier);
506 }
507
508 // Begin render pass
509 {
510 const VkRect2D renderArea = makeRect2D(renderSize);
511 const tcu::Vec4 clearColor = tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f);
512
513 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
514 }
515
516 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
517 vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
518 {
519 const VkDeviceSize vertexBufferOffset = 0ull;
520 vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
521 }
522
523 vk.cmdDraw(*cmdBuffer, static_cast<deUint32>(attributes.size()), 1u, 0u, 0u);
524 endRenderPass(vk, *cmdBuffer);
525
526 // Copy render result to a host-visible buffer
527 copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, *colorBuffer, renderSize);
528 {
529 const VkBufferMemoryBarrier shaderWriteBarrier = makeBufferMemoryBarrier(
530 VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT, *resultBuffer, 0ull, resultBufferSizeBytes);
531
532 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0u,
533 0u, DE_NULL, 1u, &shaderWriteBarrier, 0u, DE_NULL);
534 }
535
536 endCommandBuffer(vk, *cmdBuffer);
537 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
538
539 {
540 // Log rendered image
541 const Allocation& colorBufferAlloc = colorBuffer.getAllocation();
542
543 invalidateAlloc(vk, device, colorBufferAlloc);
544
545 const tcu::ConstPixelBufferAccess imagePixelAccess (mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, colorBufferAlloc.getHostPtr());
546 tcu::TestLog& log = context.getTestContext().getLog();
547
548 log << tcu::TestLog::Image("color0", "Rendered image", imagePixelAccess);
549
550 // Verify case result
551 const Allocation& resultAlloc = resultBuffer.getAllocation();
552
553 invalidateAlloc(vk, device, resultAlloc);
554
555 const deInt32 numResultVertices = *static_cast<deInt32*>(resultAlloc.getHostPtr());
556
557 if (!lessThanOneInnerLevelsDefined(caseDef) && caseDef.useLessThanOneInnerLevels)
558 {
559 // Since we cannot explicitly determine whether or not such interior vertices are going to be
560 // generated, we will not verify the number of generated vertices for fractional odd + quads/triangles
561 // tessellation configurations.
562 log << tcu::TestLog::Message
563 << "Note: shader invocations generated " << numResultVertices << " vertices (not verified as number of vertices is implementation-dependent)"
564 << tcu::TestLog::EndMessage;
565 }
566 else if (numResultVertices < numExpectedVertices)
567 {
568 log << tcu::TestLog::Message
569 << "Failure: expected " << numExpectedVertices << " vertices from shader invocations, but got only " << numResultVertices
570 << tcu::TestLog::EndMessage;
571 return tcu::TestStatus::fail("Wrong number of tessellation coordinates");
572 }
573 else if (numResultVertices == numExpectedVertices)
574 {
575 log << tcu::TestLog::Message
576 << "Note: shader invocations generated " << numResultVertices << " vertices"
577 << tcu::TestLog::EndMessage;
578 }
579 else
580 {
581 log << tcu::TestLog::Message
582 << "Note: shader invocations generated " << numResultVertices << " vertices (expected " << numExpectedVertices << ", got "
583 << (numResultVertices - numExpectedVertices) << " extra)"
584 << tcu::TestLog::EndMessage;
585 }
586
587 return (verifyResultImage(log, numPrimitives, numAttribsPerPrimitive, caseDef.primitiveType, attributes, imagePixelAccess)
588 ? tcu::TestStatus::pass("OK") : tcu::TestStatus::fail("Image verification failed"));
589 }
590 }
591
592 } // anonymous
593
594 //! These tests correspond to dEQP-GLES31.functional.tessellation.primitive_discard.*
595 //! \note Original test used transform feedback (TF) to capture the number of output vertices. The behavior of TF differs significantly from SSBO approach,
596 //! especially for non-point_mode rendering. TF returned all coordinates, while SSBO computes the count based on the number of shader invocations
597 //! which yields a much smaller number because invocations for duplicate coordinates are often eliminated.
598 //! Because of this, the test was changed to:
599 //! - always compute the number of expected coordinates as if point_mode was enabled
600 //! - not fail if implementation returned more coordinates than expected
createPrimitiveDiscardTests(tcu::TestContext & testCtx)601 tcu::TestCaseGroup* createPrimitiveDiscardTests (tcu::TestContext& testCtx)
602 {
603 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "primitive_discard", "Test primitive discard with relevant outer tessellation level <= 0.0"));
604
605 for (int primitiveTypeNdx = 0; primitiveTypeNdx < TESSPRIMITIVETYPE_LAST; primitiveTypeNdx++)
606 for (int spacingModeNdx = 0; spacingModeNdx < SPACINGMODE_LAST; spacingModeNdx++)
607 for (int windingNdx = 0; windingNdx < WINDING_LAST; windingNdx++)
608 for (int usePointModeNdx = 0; usePointModeNdx <= 1; usePointModeNdx++)
609 for (int lessThanOneInnerLevelsNdx = 0; lessThanOneInnerLevelsNdx <= 1; lessThanOneInnerLevelsNdx++)
610 {
611 const CaseDefinition caseDef =
612 {
613 (TessPrimitiveType)primitiveTypeNdx,
614 (SpacingMode)spacingModeNdx,
615 (Winding)windingNdx,
616 (usePointModeNdx != 0),
617 (lessThanOneInnerLevelsNdx != 0)
618 };
619
620 if (lessThanOneInnerLevelsDefined(caseDef) && !caseDef.useLessThanOneInnerLevels)
621 continue; // No point generating a separate case as <= 1 inner level behavior is well-defined
622
623 const std::string caseName = std::string() + getTessPrimitiveTypeShaderName(caseDef.primitiveType)
624 + "_" + getSpacingModeShaderName(caseDef.spacingMode)
625 + "_" + getWindingShaderName(caseDef.winding)
626 + (caseDef.usePointMode ? "_point_mode" : "")
627 + (caseDef.useLessThanOneInnerLevels ? "" : "_valid_levels");
628
629 addFunctionCaseWithPrograms(group.get(), caseName, "", checkSupportCase, initPrograms, test, caseDef);
630 }
631
632 return group.release();
633 }
634
635 } // tessellation
636 } // vkt
637