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