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