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 Geometry Interaction - Point Size
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktTessellationGeometryPassthroughTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27 #include "vktTessellationUtil.hpp"
28
29 #include "tcuTestLog.hpp"
30
31 #include "vkDefs.hpp"
32 #include "vkBarrierUtil.hpp"
33 #include "vkQueryUtil.hpp"
34 #include "vkBuilderUtil.hpp"
35 #include "vkTypeUtil.hpp"
36 #include "vkImageUtil.hpp"
37 #include "vkCmdUtil.hpp"
38 #include "vkObjUtil.hpp"
39 #include "vkBufferWithMemory.hpp"
40 #include "vkImageWithMemory.hpp"
41
42 #include "deUniquePtr.hpp"
43
44 #include <string>
45 #include <vector>
46
47 namespace vkt
48 {
49 namespace tessellation
50 {
51
52 using namespace vk;
53
54 namespace
55 {
56
57 enum Constants
58 {
59 RENDER_SIZE = 32,
60 };
61
62 enum FlagBits
63 {
64 FLAG_VERTEX_SET = 1u << 0, // !< set gl_PointSize in vertex shader
65 FLAG_TESSELLATION_EVALUATION_SET = 1u << 1, // !< set gl_PointSize in tessellation evaluation shader
66 FLAG_TESSELLATION_ADD = 1u << 2, // !< read and add to gl_PointSize in tessellation shader pair
67 FLAG_GEOMETRY_SET = 1u << 3, // !< set gl_PointSize in geometry shader
68 FLAG_GEOMETRY_ADD = 1u << 4, // !< read and add to gl_PointSize in geometry shader
69 };
70 typedef uint32_t Flags;
71
checkPointSizeRequirements(const InstanceInterface & vki,const VkPhysicalDevice physDevice,const int maxPointSize)72 void checkPointSizeRequirements(const InstanceInterface &vki, const VkPhysicalDevice physDevice, const int maxPointSize)
73 {
74 const VkPhysicalDeviceProperties properties = getPhysicalDeviceProperties(vki, physDevice);
75 if (maxPointSize > static_cast<int>(properties.limits.pointSizeRange[1]))
76 throw tcu::NotSupportedError("Test requires point size " + de::toString(maxPointSize));
77 // Point size granularity must be 1.0 at most, so no need to check it for this test.
78 }
79
getExpectedPointSize(const Flags flags)80 int getExpectedPointSize(const Flags flags)
81 {
82 int addition = 0;
83
84 // geometry
85 if (flags & FLAG_GEOMETRY_SET)
86 return 6;
87 else if (flags & FLAG_GEOMETRY_ADD)
88 addition += 2;
89
90 // tessellation
91 if (flags & FLAG_TESSELLATION_EVALUATION_SET)
92 return 4 + addition;
93 else if (flags & FLAG_TESSELLATION_ADD)
94 addition += 2;
95
96 // vertex
97 if (flags & FLAG_VERTEX_SET)
98 return 2 + addition;
99
100 // undefined
101 DE_ASSERT(false);
102 return -1;
103 }
104
isTessellationStage(const Flags flags)105 inline bool isTessellationStage(const Flags flags)
106 {
107 return (flags & (FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD)) != 0;
108 }
109
isGeometryStage(const Flags flags)110 inline bool isGeometryStage(const Flags flags)
111 {
112 return (flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD)) != 0;
113 }
114
verifyImage(tcu::TestLog & log,const tcu::ConstPixelBufferAccess image,const int expectedSize)115 bool verifyImage(tcu::TestLog &log, const tcu::ConstPixelBufferAccess image, const int expectedSize)
116 {
117 log << tcu::TestLog::Message << "Verifying rendered point size. Expecting " << expectedSize << " pixels."
118 << tcu::TestLog::EndMessage;
119
120 bool resultAreaFound = false;
121 tcu::IVec4 resultArea;
122 const tcu::Vec4 black(0.0, 0.0, 0.0, 1.0);
123
124 // Find rasterization output area
125
126 for (int y = 0; y < image.getHeight(); ++y)
127 for (int x = 0; x < image.getWidth(); ++x)
128 if (image.getPixel(x, y) != black)
129 {
130 if (!resultAreaFound)
131 {
132 // first fragment
133 resultArea = tcu::IVec4(x, y, x + 1, y + 1);
134 resultAreaFound = true;
135 }
136 else
137 {
138 // union area
139 resultArea.x() = de::min(resultArea.x(), x);
140 resultArea.y() = de::min(resultArea.y(), y);
141 resultArea.z() = de::max(resultArea.z(), x + 1);
142 resultArea.w() = de::max(resultArea.w(), y + 1);
143 }
144 }
145
146 if (!resultAreaFound)
147 {
148 log << tcu::TestLog::Message << "Verification failed, could not find any point fragments."
149 << tcu::TestLog::EndMessage;
150 return false;
151 }
152
153 const tcu::IVec2 pointSize = resultArea.swizzle(2, 3) - resultArea.swizzle(0, 1);
154
155 if (pointSize.x() != pointSize.y())
156 {
157 log << tcu::TestLog::Message << "ERROR! Rasterized point is not a square. Point size was " << pointSize
158 << tcu::TestLog::EndMessage;
159 return false;
160 }
161
162 if (pointSize.x() != expectedSize)
163 {
164 log << tcu::TestLog::Message << "ERROR! Point size invalid, expected " << expectedSize << ", got "
165 << pointSize.x() << tcu::TestLog::EndMessage;
166 return false;
167 }
168
169 return true;
170 }
171
initPrograms(vk::SourceCollections & programCollection,const Flags flags)172 void initPrograms(vk::SourceCollections &programCollection, const Flags flags)
173 {
174 // Vertex shader
175 {
176 std::ostringstream src;
177 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
178 << "\n"
179 << "void main (void)\n"
180 << "{\n"
181 << " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n";
182
183 if (flags & FLAG_VERTEX_SET)
184 src << " gl_PointSize = 2.0;\n";
185
186 src << "}\n";
187
188 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
189 }
190
191 // Fragment shader
192 {
193 std::ostringstream src;
194 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
195 << "layout(location = 0) out mediump vec4 fragColor;\n"
196 << "\n"
197 << "void main (void)\n"
198 << "{\n"
199 << " fragColor = vec4(1.0);\n"
200 << "}\n";
201
202 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
203 }
204
205 if (isTessellationStage(flags))
206 {
207 // Tessellation control shader
208 {
209 std::ostringstream src;
210 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
211 << "#extension GL_EXT_tessellation_shader : require\n"
212 << "#extension GL_EXT_tessellation_point_size : require\n"
213 << "layout(vertices = 1) out;\n"
214 << "\n"
215 << "void main (void)\n"
216 << "{\n"
217 << " gl_TessLevelOuter[0] = 3.0;\n"
218 << " gl_TessLevelOuter[1] = 3.0;\n"
219 << " gl_TessLevelOuter[2] = 3.0;\n"
220 << " gl_TessLevelInner[0] = 3.0;\n"
221 << "\n"
222 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n";
223
224 if (flags & FLAG_TESSELLATION_ADD)
225 src << " // pass as is to eval\n"
226 << " gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n";
227
228 src << "}\n";
229
230 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(src.str());
231 }
232
233 // Tessellation evaluation shader
234 {
235 std::ostringstream src;
236 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
237 << "#extension GL_EXT_tessellation_shader : require\n"
238 << "#extension GL_EXT_tessellation_point_size : require\n"
239 << "layout(triangles, point_mode) in;\n"
240 << "\n"
241 << "void main (void)\n"
242 << "{\n"
243 << " // hide all but one vertex\n"
244 << " if (gl_TessCoord.x < 0.99)\n"
245 << " gl_Position = vec4(-2.0, 0.0, 0.0, 1.0);\n"
246 << " else\n"
247 << " gl_Position = gl_in[0].gl_Position;\n";
248
249 if (flags & FLAG_TESSELLATION_ADD)
250 src << "\n"
251 << " // add to point size\n"
252 << " gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
253 else if (flags & FLAG_TESSELLATION_EVALUATION_SET)
254 src << "\n"
255 << " // set point size\n"
256 << " gl_PointSize = 4.0;\n";
257
258 src << "}\n";
259
260 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(src.str());
261 }
262 }
263
264 if (isGeometryStage(flags))
265 {
266 // Geometry shader
267 std::ostringstream src;
268 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_310_ES) << "\n"
269 << "#extension GL_EXT_geometry_shader : require\n"
270 << "#extension GL_EXT_geometry_point_size : require\n"
271 << "layout(points) in;\n"
272 << "layout(points, max_vertices = 1) out;\n"
273 << "\n"
274 << "void main (void)\n"
275 << "{\n"
276 << " gl_Position = gl_in[0].gl_Position;\n";
277
278 if (flags & FLAG_GEOMETRY_SET)
279 src << " gl_PointSize = 6.0;\n";
280 else if (flags & FLAG_GEOMETRY_ADD)
281 src << " gl_PointSize = gl_in[0].gl_PointSize + 2.0;\n";
282
283 src << "\n"
284 << " EmitVertex();\n"
285 << "}\n";
286
287 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
288 }
289 }
290
test(Context & context,const Flags flags)291 tcu::TestStatus test(Context &context, const Flags flags)
292 {
293 const int expectedPointSize = getExpectedPointSize(flags);
294 {
295 const InstanceInterface &vki = context.getInstanceInterface();
296 const VkPhysicalDevice physDevice = context.getPhysicalDevice();
297
298 requireFeatures(vki, physDevice,
299 FEATURE_TESSELLATION_SHADER | FEATURE_GEOMETRY_SHADER |
300 FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE);
301 checkPointSizeRequirements(vki, physDevice, expectedPointSize);
302 }
303 {
304 tcu::TestLog &log = context.getTestContext().getLog();
305
306 if (flags & FLAG_VERTEX_SET)
307 log << tcu::TestLog::Message << "Setting point size in vertex shader to 2.0." << tcu::TestLog::EndMessage;
308 if (flags & FLAG_TESSELLATION_EVALUATION_SET)
309 log << tcu::TestLog::Message << "Setting point size in tessellation evaluation shader to 4.0."
310 << tcu::TestLog::EndMessage;
311 if (flags & FLAG_TESSELLATION_ADD)
312 log << tcu::TestLog::Message
313 << "Reading point size in tessellation control shader and adding 2.0 to it in evaluation."
314 << tcu::TestLog::EndMessage;
315 if (flags & FLAG_GEOMETRY_SET)
316 log << tcu::TestLog::Message << "Setting point size in geometry shader to 6.0." << tcu::TestLog::EndMessage;
317 if (flags & FLAG_GEOMETRY_ADD)
318 log << tcu::TestLog::Message << "Reading point size in geometry shader and adding 2.0."
319 << tcu::TestLog::EndMessage;
320 }
321
322 const DeviceInterface &vk = context.getDeviceInterface();
323 const VkDevice device = context.getDevice();
324 const VkQueue queue = context.getUniversalQueue();
325 const uint32_t queueFamilyIndex = context.getUniversalQueueFamilyIndex();
326 Allocator &allocator = context.getDefaultAllocator();
327
328 // Color attachment
329
330 const tcu::IVec2 renderSize = tcu::IVec2(RENDER_SIZE, RENDER_SIZE);
331 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
332 const VkImageSubresourceRange colorImageSubresourceRange =
333 makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
334 const ImageWithMemory colorAttachmentImage(
335 vk, device, allocator,
336 makeImageCreateInfo(renderSize, colorFormat,
337 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, 1u),
338 MemoryRequirement::Any);
339
340 // Color output buffer
341
342 const VkDeviceSize colorBufferSizeBytes =
343 renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
344 const BufferWithMemory colorBuffer(vk, device, allocator,
345 makeBufferCreateInfo(colorBufferSizeBytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT),
346 MemoryRequirement::HostVisible);
347
348 // Pipeline
349
350 const Unique<VkImageView> colorAttachmentView(makeImageView(
351 vk, device, *colorAttachmentImage, VK_IMAGE_VIEW_TYPE_2D, colorFormat, colorImageSubresourceRange));
352 const Unique<VkRenderPass> renderPass(makeRenderPass(vk, device, colorFormat));
353 const Unique<VkFramebuffer> framebuffer(
354 makeFramebuffer(vk, device, *renderPass, *colorAttachmentView, renderSize.x(), renderSize.y()));
355 const Unique<VkPipelineLayout> pipelineLayout(makePipelineLayout(vk, device));
356 const Unique<VkCommandPool> cmdPool(makeCommandPool(vk, device, queueFamilyIndex));
357 const Unique<VkCommandBuffer> cmdBuffer(
358 allocateCommandBuffer(vk, device, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
359
360 GraphicsPipelineBuilder pipelineBuilder;
361
362 pipelineBuilder.setPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
363 .setRenderSize(renderSize)
364 .setPatchControlPoints(1)
365 .setShader(vk, device, VK_SHADER_STAGE_VERTEX_BIT, context.getBinaryCollection().get("vert"), DE_NULL)
366 .setShader(vk, device, VK_SHADER_STAGE_FRAGMENT_BIT, context.getBinaryCollection().get("frag"), DE_NULL);
367
368 if (isTessellationStage(flags))
369 pipelineBuilder
370 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, context.getBinaryCollection().get("tesc"),
371 DE_NULL)
372 .setShader(vk, device, VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
373 context.getBinaryCollection().get("tese"), DE_NULL);
374
375 if (isGeometryStage(flags))
376 pipelineBuilder.setShader(vk, device, VK_SHADER_STAGE_GEOMETRY_BIT, context.getBinaryCollection().get("geom"),
377 DE_NULL);
378
379 const Unique<VkPipeline> pipeline(pipelineBuilder.build(vk, device, *pipelineLayout, *renderPass));
380
381 // Draw commands
382
383 beginCommandBuffer(vk, *cmdBuffer);
384
385 {
386 const VkImageMemoryBarrier colorAttachmentLayoutBarrier = makeImageMemoryBarrier(
387 (VkAccessFlags)0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED,
388 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, *colorAttachmentImage, colorImageSubresourceRange);
389
390 vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
391 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0u, 0u, DE_NULL, 0u, DE_NULL, 1u,
392 &colorAttachmentLayoutBarrier);
393 }
394
395 // Begin render pass
396 {
397 const VkRect2D renderArea = makeRect2D(renderSize);
398 const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
399
400 beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, renderArea, clearColor);
401 }
402
403 vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
404
405 vk.cmdDraw(*cmdBuffer, 1u, 1u, 0u, 0u);
406 endRenderPass(vk, *cmdBuffer);
407
408 // Copy render result to a host-visible buffer
409 copyImageToBuffer(vk, *cmdBuffer, *colorAttachmentImage, *colorBuffer, renderSize);
410
411 endCommandBuffer(vk, *cmdBuffer);
412 submitCommandsAndWait(vk, device, queue, *cmdBuffer);
413
414 // Verify results
415 {
416 const Allocation &alloc = colorBuffer.getAllocation();
417 tcu::TestLog &log = context.getTestContext().getLog();
418
419 invalidateAlloc(vk, device, alloc);
420
421 tcu::ConstPixelBufferAccess image(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1,
422 alloc.getHostPtr());
423
424 log << tcu::LogImage("color0", "", image);
425
426 if (verifyImage(log, image, expectedPointSize))
427 return tcu::TestStatus::pass("OK");
428 else
429 return tcu::TestStatus::fail("Didn't render expected point");
430 }
431 }
432
getTestCaseName(const Flags flags)433 std::string getTestCaseName(const Flags flags)
434 {
435 std::ostringstream buf;
436
437 // join per-bit descriptions into a single string with '_' separator
438 if (flags & FLAG_VERTEX_SET)
439 buf << "vertex_set";
440 if (flags & FLAG_TESSELLATION_EVALUATION_SET)
441 buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET - 1)) ? ("_") : ("")) << "evaluation_set";
442 if (flags & FLAG_TESSELLATION_ADD)
443 buf << ((flags & (FLAG_TESSELLATION_ADD - 1)) ? ("_") : ("")) << "control_pass_eval_add";
444 if (flags & FLAG_GEOMETRY_SET)
445 buf << ((flags & (FLAG_GEOMETRY_SET - 1)) ? ("_") : ("")) << "geometry_set";
446 if (flags & FLAG_GEOMETRY_ADD)
447 buf << ((flags & (FLAG_GEOMETRY_ADD - 1)) ? ("_") : ("")) << "geometry_add";
448
449 return buf.str();
450 }
451
checkSupportTess(Context & context,const Flags flags)452 void checkSupportTess(Context &context, const Flags flags)
453 {
454 #ifndef CTS_USES_VULKANSC
455 if (isTessellationStage(flags))
456 if (const vk::VkPhysicalDevicePortabilitySubsetFeaturesKHR *const features = getPortability(context))
457 checkPointMode(*features);
458 #else
459 DE_UNREF(context);
460 DE_UNREF(flags);
461 #endif // CTS_USES_VULKANSC
462 }
463
464 } // namespace
465
466 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.point_size.*
467 //! with the exception of the default 1.0 point size cases (not valid in Vulkan).
createGeometryPointSizeTests(tcu::TestContext & testCtx)468 tcu::TestCaseGroup *createGeometryPointSizeTests(tcu::TestContext &testCtx)
469 {
470 de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "point_size"));
471
472 static const Flags caseFlags[] = {
473 FLAG_VERTEX_SET,
474 FLAG_TESSELLATION_EVALUATION_SET,
475 FLAG_GEOMETRY_SET,
476 FLAG_VERTEX_SET | FLAG_TESSELLATION_EVALUATION_SET,
477 FLAG_VERTEX_SET | FLAG_GEOMETRY_SET,
478 FLAG_VERTEX_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_GEOMETRY_SET,
479 FLAG_VERTEX_SET | FLAG_TESSELLATION_ADD | FLAG_GEOMETRY_ADD,
480 };
481
482 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseFlags); ++ndx)
483 {
484 const std::string name = getTestCaseName(caseFlags[ndx]);
485
486 addFunctionCaseWithPrograms(group.get(), name, checkSupportTess, initPrograms, test, caseFlags[ndx]);
487 }
488
489 return group.release();
490 }
491
492 } // namespace tessellation
493 } // namespace vkt
494