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 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 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 103 inline bool isTessellationStage (const Flags flags) 104 { 105 return (flags & (FLAG_TESSELLATION_EVALUATION_SET | FLAG_TESSELLATION_ADD)) != 0; 106 } 107 108 inline bool isGeometryStage (const Flags flags) 109 { 110 return (flags & (FLAG_GEOMETRY_SET | FLAG_GEOMETRY_ADD)) != 0; 111 } 112 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 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 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(), 1u)); 335 const Unique<VkPipelineLayout> pipelineLayout (makePipelineLayoutWithoutDescriptors(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 invalidateMappedMemoryRange(vk, device, alloc.getMemory(), alloc.getOffset(), colorBufferSizeBytes); 396 tcu::ConstPixelBufferAccess image(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1, alloc.getHostPtr()); 397 398 tcu::TestLog& log = context.getTestContext().getLog(); 399 log << tcu::LogImage("color0", "", image); 400 401 if (verifyImage(log, image, expectedPointSize)) 402 return tcu::TestStatus::pass("OK"); 403 else 404 return tcu::TestStatus::fail("Didn't render expected point"); 405 } 406 } 407 408 std::string getTestCaseName (const Flags flags) 409 { 410 std::ostringstream buf; 411 412 // join per-bit descriptions into a single string with '_' separator 413 if (flags & FLAG_VERTEX_SET) buf << "vertex_set"; 414 if (flags & FLAG_TESSELLATION_EVALUATION_SET) buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1)) ? ("_") : ("")) << "evaluation_set"; 415 if (flags & FLAG_TESSELLATION_ADD) buf << ((flags & (FLAG_TESSELLATION_ADD-1)) ? ("_") : ("")) << "control_pass_eval_add"; 416 if (flags & FLAG_GEOMETRY_SET) buf << ((flags & (FLAG_GEOMETRY_SET-1)) ? ("_") : ("")) << "geometry_set"; 417 if (flags & FLAG_GEOMETRY_ADD) buf << ((flags & (FLAG_GEOMETRY_ADD-1)) ? ("_") : ("")) << "geometry_add"; 418 419 return buf.str(); 420 } 421 422 std::string getTestCaseDescription (const Flags flags) 423 { 424 std::ostringstream buf; 425 426 // join per-bit descriptions into a single string with ", " separator 427 if (flags & FLAG_VERTEX_SET) buf << "set point size in vertex shader"; 428 if (flags & FLAG_TESSELLATION_EVALUATION_SET) buf << ((flags & (FLAG_TESSELLATION_EVALUATION_SET-1)) ? (", ") : ("")) << "set point size in tessellation evaluation shader"; 429 if (flags & FLAG_TESSELLATION_ADD) buf << ((flags & (FLAG_TESSELLATION_ADD-1)) ? (", ") : ("")) << "add to point size in tessellation shader"; 430 if (flags & FLAG_GEOMETRY_SET) buf << ((flags & (FLAG_GEOMETRY_SET-1)) ? (", ") : ("")) << "set point size in geometry shader"; 431 if (flags & FLAG_GEOMETRY_ADD) buf << ((flags & (FLAG_GEOMETRY_ADD-1)) ? (", ") : ("")) << "add to point size in geometry shader"; 432 433 return buf.str(); 434 } 435 436 } // anonymous 437 438 //! Ported from dEQP-GLES31.functional.tessellation_geometry_interaction.point_size.* 439 //! with the exception of the default 1.0 point size cases (not valid in Vulkan). 440 tcu::TestCaseGroup* createGeometryPointSizeTests (tcu::TestContext& testCtx) 441 { 442 de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "point_size", "Test point size")); 443 444 static const Flags caseFlags[] = 445 { 446 FLAG_VERTEX_SET, 447 FLAG_TESSELLATION_EVALUATION_SET, 448 FLAG_GEOMETRY_SET, 449 FLAG_VERTEX_SET | FLAG_TESSELLATION_EVALUATION_SET, 450 FLAG_VERTEX_SET | FLAG_GEOMETRY_SET, 451 FLAG_VERTEX_SET | FLAG_TESSELLATION_EVALUATION_SET | FLAG_GEOMETRY_SET, 452 FLAG_VERTEX_SET | FLAG_TESSELLATION_ADD | FLAG_GEOMETRY_ADD, 453 }; 454 455 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(caseFlags); ++ndx) 456 { 457 const std::string name = getTestCaseName (caseFlags[ndx]); 458 const std::string desc = getTestCaseDescription(caseFlags[ndx]); 459 460 addFunctionCaseWithPrograms(group.get(), name, desc, initPrograms, test, caseFlags[ndx]); 461 } 462 463 return group.release(); 464 } 465 466 } // tessellation 467 } // vkt 468