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